blob: 52c6cb6583981684b1e55ce6b5b1c113698bab51 [file] [log] [blame]
// 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_