| // Copyright 2021 The Chromium OS 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 RMAD_UTILS_JSON_STORE_H_ |
| #define RMAD_UTILS_JSON_STORE_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/memory/ref_counted.h> |
| #include <base/values.h> |
| |
| namespace rmad { |
| |
| // A class to store a JSON dictionary and keep in sync with a file. |
| class JsonStore : public base::RefCounted<JsonStore> { |
| public: |
| enum ReadError { |
| READ_ERROR_NONE = 0, |
| READ_ERROR_JSON_PARSE = 1, |
| READ_ERROR_JSON_TYPE = 2, |
| READ_ERROR_FILE_ACCESS_DENIED = 3, |
| READ_ERROR_FILE_OTHER = 4, |
| READ_ERROR_FILE_LOCKED = 5, |
| READ_ERROR_NO_SUCH_FILE = 6, |
| READ_ERROR_MAX_ENUM |
| }; |
| |
| explicit JsonStore(const base::FilePath& file_path); |
| |
| // Set a (key, value) pair to the dictionary. Return true if there's no |
| // update or the updated data is successfully written to the file, false if |
| // the update cannot be written to the file. |
| bool SetValue(const std::string& key, base::Value&& value); |
| |
| // Set a (key, value) pair to the dictionary for types supported by |
| // base::Value (bool, int, double, string), or nested vector of these types. |
| // Return true if there's no update or the updated data is successfully |
| // written to the file, false if the update cannot be written to the file. |
| template <typename T> |
| bool SetValue(const std::string& key, const T& value) { |
| return SetValue(key, ConvertToValue(value)); |
| } |
| |
| // Get the value associated to the key, and copy to `value` for types |
| // supported by base::Value. |
| // If value is null then just the existence and type of the key value is |
| // checked. |
| // If the key is not found, `value` is not modified by the function. Return |
| // true if the key is found in the dictionary, false if the key is not found. |
| template <typename T> |
| bool GetValue(const std::string& key, T* result) const { |
| DCHECK(data_.is_dict()); |
| return GetValueInternal(data_.FindKey(key), result); |
| } |
| |
| // Get the value associated to the key, and assign its const pointer to |
| // `result`. If the key is not found, `result` is not modified by the |
| // function. Return true if the key is found in the dictionary, false if the |
| // key is not found. |
| bool GetValue(const std::string& key, const base::Value** result) const; |
| |
| // Get the value associated to the key, and copy to `result`. If the key is |
| // not found, `result` is not modified by the function. Return true if the key |
| // is found in the dictionary, false if the key is not found. |
| bool GetValue(const std::string& key, base::Value* result) const; |
| |
| // Get the complete copy of the dictionary. |
| base::Value GetValues() const; |
| |
| // Clear the dictionary. Return true on success, false if failed to write to |
| // the file. |
| bool Clear(); |
| |
| // Clear the dictionary and delete the file that stores the data in storage. |
| // Return true on success, false if failed to clear or delete the file. |
| bool ClearAndDeleteFile(); |
| |
| // Get read status of the file. |
| ReadError GetReadError() const { return read_error_; } |
| |
| // Return true if the file existed when read was attempted. |
| bool Exists() const { return read_error_ != READ_ERROR_NO_SUCH_FILE; } |
| |
| // Return true if the file cannot be written, such as access denied, or the |
| // file already exists but contains invalid JSON format. |
| bool ReadOnly() const { return read_only_; } |
| |
| private: |
| // Hide the destructor so we don't accidentally delete this while there are |
| // references to it. |
| friend class base::RefCounted<JsonStore>; |
| ~JsonStore() = default; |
| |
| // Read result returned from internal read tasks. |
| struct ReadResult; |
| |
| // Convert the input type to base::Value. The input type should be supported |
| // by base::Value (bool, int, double, string). |
| template <typename T> |
| base::Value ConvertToValue(const T& value) { |
| return base::Value(value); |
| } |
| |
| // Convert a vector to base::Value. The vector type should be supported |
| // by base::Value (bool, int, double, string) or vector/map of these types. |
| template <typename T> |
| base::Value ConvertToValue(const std::vector<T>& values) { |
| base::Value list(base::Value::Type::LIST); |
| for (const auto& value : values) { |
| list.Append(ConvertToValue(value)); |
| } |
| return list; |
| } |
| |
| // Convert a map to base::Value. The value type should be supported by |
| // base::Value (bool, int, double, string) or vector/map of these types. |
| // TODO(chenghan): Support more types, e.g. unordered_map. |
| template <typename T> |
| base::Value ConvertToValue(const std::map<std::string, T>& values) { |
| base::Value dict(base::Value::Type::DICTIONARY); |
| for (const auto& [key, value] : values) { |
| dict.SetKey(key, ConvertToValue(value)); |
| } |
| return dict; |
| } |
| |
| static bool GetValueInternal(const base::Value* data, bool* result); |
| static bool GetValueInternal(const base::Value* data, int* result); |
| static bool GetValueInternal(const base::Value* data, double* result); |
| static bool GetValueInternal(const base::Value* data, std::string* result); |
| |
| template <typename T> |
| static bool GetValueInternal(const base::Value* data, |
| std::vector<T>* result) { |
| if (!data || !data->is_list()) { |
| return false; |
| } |
| std::vector<T> r; |
| for (const auto& child_data : data->GetList()) { |
| if (T child_result; GetValueInternal(&child_data, &child_result)) { |
| r.push_back(child_result); |
| } else { |
| return false; |
| } |
| } |
| if (result) { |
| *result = std::move(r); |
| } |
| return true; |
| } |
| |
| template <typename T> |
| static bool GetValueInternal(const base::Value* data, |
| std::map<std::string, T>* result) { |
| if (!data || !data->is_dict()) { |
| return false; |
| } |
| std::map<std::string, T> r; |
| for (const auto& [key, child_data] : data->DictItems()) { |
| if (T child_result; GetValueInternal(&child_data, &child_result)) { |
| r.insert({key, child_result}); |
| } else { |
| return false; |
| } |
| } |
| if (result) { |
| *result = std::move(r); |
| } |
| return true; |
| } |
| |
| void InitFromFile(); |
| std::unique_ptr<JsonStore::ReadResult> ReadFromFile(); |
| bool WriteToFile(); |
| |
| const base::FilePath file_path_; |
| base::Value data_; |
| ReadError read_error_; |
| bool read_only_; |
| }; |
| |
| } // namespace rmad |
| |
| #endif // RMAD_UTILS_JSON_STORE_H_ |