Rose
WebCache.h
Go to the documentation of this file.
1 
9 #pragma once
10 
11 #include <algorithm>
12 #include <iostream>
13 #include <string>
14 #include <filesystem>
15 #include <map>
16 #include <mutex>
17 #include <optional>
18 #include <fstream>
19 #include <list>
20 #include <utility>
21 #include <future>
22 #include <vector>
23 #include "CommonSignals.h"
24 
25 namespace rose {
26 
27  struct WebCacheItem {
28  uint32_t key;
29  std::string_view name;
30  };
31 
33 
34  using namespace std::filesystem;
35 
40  class WebCache {
42 
43  public:
44  using key_t = uint32_t;
45  using local_id_t = std::string;
46  using result_t = std::tuple<long, key_t>;
47  using item_map_t = std::map<key_t, local_id_t>;
48 
49  using AsyncList = std::vector<std::future<result_t>>;
50 
57  template<typename T>
58  static std::chrono::system_clock::time_point fileClockToSystemClock(T fileTimePoint) {
59  using namespace std::chrono_literals;
60  using namespace std::chrono;
61 
62  auto sysWriteTime = time_point_cast<system_clock::duration>(
63  fileTimePoint - decltype(fileTimePoint)::clock::now() +
64  system_clock::now());
65  return sysWriteTime;
66  }
67 
73  static std::chrono::system_clock::time_point fileClockToSystemClock(const path &filePath) {
74  return fileClockToSystemClock(std::filesystem::last_write_time(filePath));
75  }
76 
77  WebCacheProtocol::signal_type cacheLoaded{};
78 
79  protected:
80  std::error_code mEc{};
81  std::string mRootURI{};
82  path mStoreRoot{};
83  mutable file_status mStoreStatus{};
84  std::mutex mMutex;
85 
86  item_map_t mItemMap{};
87 
88  AsyncList mAsyncList{};
89 
91  std::chrono::system_clock::duration mCacheValidDuration{};
92 
93  void asyncFetchItem(item_map_t::value_type& item) {
94  auto itemPath = mStoreRoot;
95  auto tempPath = mStoreRoot;
96  auto itemName = translateItemLocalId(item.second);
97  itemPath.append(itemName);
98  tempPath.append('.' + itemName);
99  if (!itemPath.empty()) {
100  std::optional<time_t> cacheFileTime{};
101  if (exists(itemPath))
102  if (cacheFileTime = cacheTime(itemPath); !cacheFileTime) {
103  cacheLoaded.transmit(item.first, 503);
104  return;
105  }
106 
107  mAsyncList.emplace_back(
108  std::async(std::launch::async, fetch, item.first, constructUrl(item.second), itemPath, tempPath,
109  cacheFileTime));
110  CommonSignals::getCommonSignals().frameSignal.connect(mFrameProtocol);
111  }
112  }
113 
114  public:
115  WebCache() = delete;
116 
117  virtual ~WebCache() = default;
118 
126  WebCache(const std::string &rootUri, const path &xdgDir, const std::string &storeRoot,
127  std::chrono::system_clock::duration duration);
128 
129  template<typename It>
130  WebCache(const std::string &rootUri, const path &xdgDir, const std::string &storeRoot,
131  std::chrono::system_clock::duration duration, It first, It last)
132  : WebCache(rootUri, xdgDir, storeRoot, duration) {
133  setCacheItem(first, last);
134  }
135 
141  void setCacheItem(key_t key, local_id_t localId) {
142  mItemMap[key] = std::move(localId);
143  }
144 
151  template<typename It>
152  void setCacheItem(It first, It last) {
153  while (first != last) {
154  setCacheItem(first->key, std::string(first->name));
155  first++;
156  }
157  }
158 
163  bool storeExists() const {
164  if (!status_known(mStoreStatus))
165  mStoreStatus = status(mStoreRoot);
166 
167  return exists(mStoreStatus) && is_directory(mStoreStatus);
168  }
169 
177  virtual std::string translateItemLocalId(local_id_t localId) {
178  return localId;
179  }
180 
186  path itemLocalPath(key_t key) {
187  if (auto item = mItemMap.find(key); item != mItemMap.end()) {
188  auto itemPath = mStoreRoot;
189  itemPath.append(translateItemLocalId(item->second));
190  return itemPath;
191  }
192  return path{};
193  }
194 
200  std::optional<path> localItemExists(key_t key) {
201  if (auto itemPath = itemLocalPath(key); !itemPath.empty()) {
202  if (exists(itemPath))
203  return itemPath;
204  }
205  return std::nullopt;
206  }
207 
214  std::optional<time_t> cacheTime(const path &itemPath) {
215  auto cacheFiletime = fileClockToSystemClock(std::filesystem::last_write_time(itemPath));
216  auto cacheFileAge = std::chrono::system_clock::now() - cacheFiletime;
217  if (cacheFileAge > mCacheValidDuration)
218  return std::chrono::system_clock::to_time_t(cacheFiletime);
219  return std::nullopt;
220  }
221 
230  static WebCache::result_t
231  fetch(WebCache::key_t key, const std::string &itemUrl, const path &itemPath, const path &tempPath,
232  std::optional<time_t> cacheFileTime);
233 
240  virtual std::string constructUrl(const local_id_t &localId) {
241  return mRootURI + localId;
242  }
243 
244  bool itemKnown(key_t key) {
245  return mItemMap.find(key) != mItemMap.end();
246  }
247 
251  bool fetchAll() {
252  AsyncList asyncList{};
253  std::lock_guard<std::mutex> lockGuard{mMutex};
254 
255  for (auto &item : mItemMap) {
256  asyncFetchItem(item);
257  }
258  return !mAsyncList.empty();
259  }
260 
261  bool fetchItem(key_t key) {
262  auto item = mItemMap.find(key);
263  if (item != mItemMap.end()) {
264  asyncFetchItem(*item);
265  }
266  return !mAsyncList.empty();
267  }
268 
269  bool pendingFutures() const {
270  return !mAsyncList.empty();
271  }
272 
273  bool processFutures() {
274  if (mAsyncList.empty())
275  return false;
276 
277  std::lock_guard<std::mutex> lockGuard{mMutex};
278  std::chrono::milliseconds span{100};
279  for (auto &item : mAsyncList) {
280  try {
281  if (item.valid()) {
282  if (auto futureStatus = item.wait_for(span); futureStatus == std::future_status::ready) {
283  auto[status, key] = item.get();
284  cacheLoaded.transmit(key,status);
285  }
286  }
287  } catch (const std::exception &e) {
288  std::cout << __PRETTY_FUNCTION__ << ' ' << e.what() << '\n';
289  }
290  }
291 
292  mAsyncList.erase(std::remove_if(mAsyncList.begin(), mAsyncList.end(),
293  [](std::future<result_t> &f) -> bool { return !f.valid(); }),
294  mAsyncList.end());
295 
296  return !mAsyncList.empty();
297  }
298  };
299 }
A convenience structure that composes Signal and Slot types from a protocol signature, and provides a Slot factory.
Definition: Signals.h:122
path itemLocalPath(key_t key)
Get the filesystem path of a local item by key.
Definition: WebCache.h:186
static std::chrono::system_clock::time_point fileClockToSystemClock(const path &filePath)
Convert a filesystem time to a system clock time point.
Definition: WebCache.h:73
virtual std::string translateItemLocalId(local_id_t localId)
Translate a local id.
Definition: WebCache.h:177
std::optional< path > localItemExists(key_t key)
Get the local path to an item if it is known and exists in the local store.
Definition: WebCache.h:200
std::shared_ptr< Slot< Args... > > slot_type
Composed Slot type.
Definition: Signals.h:123
Fetch web resources caching them in the local filesystem following XDG specifications.
Definition: WebCache.h:40
void setCacheItem(key_t key, local_id_t localId)
Add or change a cache item.
Definition: WebCache.h:141
bool storeExists() const
Test to see if the store exists.
Definition: WebCache.h:163
bool fetchAll()
Fetch all cache items which have not been previously cached or have expired cache times...
Definition: WebCache.h:251
static std::chrono::system_clock::time_point fileClockToSystemClock(T fileTimePoint)
Convert a filesystem time to a system clock time point.
Definition: WebCache.h:58
Definition: WebCache.h:27
void setCacheItem(It first, It last)
Add or change cache items from a container.
Definition: WebCache.h:152
std::optional< time_t > cacheTime(const path &itemPath)
Get the time the item was cached if the cache time has expired.
Definition: WebCache.h:214
ToDo: There is an issue that the initial scroll interaction is lost if the click/press lands on a Wid...
Definition: CelestialOverlay.cpp:13
The transmitter portion of a Signal-Slot transmitter receiver pair.
Definition: Signals.h:40
virtual std::string constructUrl(const local_id_t &localId)
Construct the appropriate URL for the item.
Definition: WebCache.h:240
std::mutex mMutex
Mutex for locking the WebFileCache.
Definition: WebCache.h:84