| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_UI_LIB_ESCHER_FS_HACK_FILESYSTEM_H_ |
| #define SRC_UI_LIB_ESCHER_FS_HACK_FILESYSTEM_H_ |
| |
| #include <lib/fit/function.h> |
| |
| #include <functional> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include "src/lib/fxl/memory/ref_counted.h" |
| |
| #ifdef __Fuchsia__ |
| #include <lib/vfs/cpp/pseudo_dir.h> |
| #endif |
| |
| namespace escher { |
| |
| class HackFilesystem; |
| using HackFilesystemPtr = fxl::RefPtr<HackFilesystem>; |
| using HackFileContents = std::string; |
| using HackFilePath = std::string; |
| using HackFilePathSet = std::unordered_set<HackFilePath>; |
| class HackFilesystemWatcher; |
| using HackFilesystemWatcherFunc = fit::function<void(HackFilePath)>; |
| |
| // An in-memory file system that could be watched for content change. |
| class HackFilesystem : public fxl::RefCountedThreadSafe<HackFilesystem> { |
| public: |
| // Create a platform-appropriate HackFileSystem (e.g. for Fuchsia or Linux). |
| static HackFilesystemPtr New(); |
| #ifdef __Fuchsia__ |
| static HackFilesystemPtr New(const std::shared_ptr<vfs::PseudoDir>& root_dir); |
| #endif |
| virtual ~HackFilesystem(); |
| |
| // Return the contents of the file, which can be empty if the path doesn't |
| // exist (HackFilesystem doesn't distinguish between empty and non-existent |
| // files). |
| HackFileContents ReadFile(const HackFilePath& path) const; |
| |
| // Set the file contents and notify watchers of the change. |
| void WriteFile(const HackFilePath& path, HackFileContents new_contents); |
| |
| // The watcher will be notified whenever any of the paths that it cares |
| // about change. To stop watching, simply release the unique_ptr. |
| std::unique_ptr<HackFilesystemWatcher> RegisterWatcher(HackFilesystemWatcherFunc func); |
| |
| // Load the specified files from the real filesystem, given a root directory. |
| // On Fuchsia the default root is "/pkg/data/"; on Linux, the default is |
| // "../test_data/escher", which points to a directory of escher test data |
| // relative to the test binary itself. |
| virtual bool InitializeWithRealFiles(const std::vector<HackFilePath>& paths, const char* root = |
| #ifdef __Fuchsia__ |
| "/pkg/data" |
| #else |
| "../test_data/" |
| "escher" |
| #endif |
| ) = 0; |
| |
| // Notifies all watchers that their watched file has changed (it actually hasn't). |
| void InvalidateFile(const HackFilePath& path); |
| |
| // Calls |InvalidateFile()| for each file in the filesystem. |
| void InvalidateAllFiles(); |
| |
| // If the hack file system was initialized with a call to |InitializeWithRealFiles| |
| // then the member variable base_path_ is set to be the absolute path of the |
| // root path that was provided. If the file system was not initialized, then the |
| // optional return value will be null. |
| const std::optional<std::string>& base_path() const { return base_path_; } |
| |
| protected: |
| HackFilesystem() = default; |
| static bool LoadFile(HackFilesystem* fs, const HackFilePath& root, const HackFilePath& path); |
| |
| std::optional<std::string> base_path_; |
| |
| private: |
| friend class HackFilesystemWatcher; |
| |
| std::unordered_map<HackFilePath, HackFileContents> files_; |
| std::unordered_set<HackFilesystemWatcher*> watchers_; |
| }; |
| |
| // Allows clients to be notified about changes in the specified files. There is |
| // no public constructor; instances of HackFilesystemWatcher must be obtained |
| // via HackFilesystem::RegisterWatcher(). |
| class HackFilesystemWatcher final { |
| public: |
| ~HackFilesystemWatcher(); |
| |
| // Start receiving notifications when the file identified by |path| changes. |
| void AddPath(HackFilePath path) { paths_to_watch_.insert(std::move(path)); } |
| |
| // Read the contents of the specified file, and receive notifications if it |
| // subsequently changes. |
| HackFileContents ReadFile(const HackFilePath& path) { |
| AddPath(path); |
| return filesystem_->ReadFile(path); |
| } |
| |
| // Return true if notifications will be received when |path| changes. |
| bool IsWatchingPath(const HackFilePath& path) { |
| return paths_to_watch_.find(path) != paths_to_watch_.end(); |
| } |
| |
| // Clear watcher to the default state; no notifications will be received until |
| // paths are added by calling AddPath() or ReadFile(). |
| void ClearPaths() { paths_to_watch_.clear(); } |
| |
| private: |
| friend class HackFilesystem; |
| |
| explicit HackFilesystemWatcher(HackFilesystem* filesystem, HackFilesystemWatcherFunc callback); |
| |
| HackFilesystem* const filesystem_; |
| HackFilesystemWatcherFunc callback_; |
| HackFilePathSet paths_to_watch_; |
| }; |
| |
| } // namespace escher |
| |
| #endif // SRC_UI_LIB_ESCHER_FS_HACK_FILESYSTEM_H_ |