pull application state members into a base class

This commit is contained in:
Mike Griese 2021-08-30 12:47:10 -05:00
parent e4c5e8bd2a
commit 42c3eea136
7 changed files with 177 additions and 87 deletions

View file

@ -61,6 +61,9 @@ Author(s):
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"
#include <til/mutex.h>
#include <til/throttled_func.h>
// Common includes for most tests:
#include "../../inc/argb.h"
#include "../../inc/conattrs.hpp"

View file

@ -110,31 +110,44 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return *state;
}
ApplicationState::ApplicationState(std::filesystem::path path) noexcept :
_path{ std::move(path) },
_throttler{ std::chrono::seconds(1), [this]() { _write(); } }
{
_read();
}
// ApplicationState::ApplicationState(std::filesystem::path path) noexcept :
// _path{ std::move(path) },
// _throttler{ std::chrono::seconds(1), [this]() { _write(); } }
// {
// _read();
// }
// The destructor ensures that the last write is flushed to disk before returning.
ApplicationState::~ApplicationState()
{
// This will ensure that we not just cancel the last outstanding timer,
// but instead force it to run as soon as possible and wait for it to complete.
_throttler.flush();
}
// // The destructor ensures that the last write is flushed to disk before returning.
// ApplicationState::~ApplicationState()
// {
// // This will ensure that we not just cancel the last outstanding timer,
// // but instead force it to run as soon as possible and wait for it to complete.
// _throttler.flush();
// }
// Re-read the state.json from disk.
void ApplicationState::Reload() const noexcept
void ApplicationState::FromJson(const Json::Value& root) const noexcept
{
_read();
auto state = _state.lock();
// GetValueForKey() comes in two variants:
// * take a std::optional<T> reference
// * return std::optional<T> by value
// At the time of writing the former version skips missing fields in the json,
// but we want to explicitly clear state fields that were removed from state.json.
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) state->name = JsonUtils::GetValueForKey<std::optional<type>>(root, key);
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
}
// Returns the state.json path on the disk.
winrt::hstring ApplicationState::FilePath() const noexcept
Json::Value ApplicationState::ToJson() const noexcept
{
return winrt::hstring{ _path.wstring() };
Json::Value root{ Json::objectValue };
{
auto state = _state.lock_shared();
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) JsonUtils::SetValueForKey(root, key, state->name);
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
}
return root;
}
// Generate all getter/setters
@ -158,58 +171,4 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
// Deserializes the state.json at _path into this ApplicationState.
// * ANY errors during app state will result in the creation of a new empty state.
// * ANY errors during runtime will result in changes being partially ignored.
void ApplicationState::_read() const noexcept
try
{
const auto data = ReadUTF8FileIfExists(_path).value_or(std::string{});
if (data.empty())
{
return;
}
std::string errs;
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
Json::Value root;
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
{
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
}
auto state = _state.lock();
// GetValueForKey() comes in two variants:
// * take a std::optional<T> reference
// * return std::optional<T> by value
// At the time of writing the former version skips missing fields in the json,
// but we want to explicitly clear state fields that were removed from state.json.
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) state->name = JsonUtils::GetValueForKey<std::optional<type>>(root, key);
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
}
CATCH_LOG()
// Serialized this ApplicationState (in `context`) into the state.json at _path.
// * Errors are only logged.
// * _state->_writeScheduled is set to false, signaling our
// setters that _synchronize() needs to be called again.
void ApplicationState::_write() const noexcept
try
{
Json::Value root{ Json::objectValue };
{
auto state = _state.lock_shared();
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) JsonUtils::SetValueForKey(root, key, state->name);
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
}
Json::StreamWriterBuilder wbuilder;
const auto content = Json::writeString(wbuilder, root);
WriteUTF8FileAtomic(_path, content);
}
CATCH_LOG()
}

View file

@ -12,11 +12,12 @@ Abstract:
--*/
#pragma once
#include "BaseApplicationState.h"
#include "ApplicationState.g.h"
#include <inc/cppwinrt_utils.h>
#include <til/mutex.h>
#include <til/throttled_func.h>
// #include <til/mutex.h>
// #include <til/throttled_func.h>
// This macro generates all getters and setters for ApplicationState.
// It provides X with the following arguments:
@ -27,18 +28,21 @@ Abstract:
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct ApplicationState : ApplicationStateT<ApplicationState>
struct ApplicationState : ApplicationStateT<ApplicationState>, public BaseApplicationState
{
static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance();
ApplicationState(std::filesystem::path path) noexcept;
~ApplicationState();
// Methods
void Reload() const noexcept;
void FromJson(const Json::Value& root) const noexcept override;
Json::Value ToJson() const noexcept override;
// General getters/setters
winrt::hstring FilePath() const noexcept;
// // Methods
// void Reload() const noexcept;
// // General getters/setters
// winrt::hstring FilePath() const noexcept;
// State getters/setters
#define MTSM_APPLICATION_STATE_GEN(type, name, key, ...) \
@ -54,13 +58,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
};
void _write() const noexcept;
void _read() const noexcept;
std::filesystem::path _path;
til::shared_mutex<state_t> _state;
til::throttled_func_trailing<> _throttler;
// void _write() const noexcept;
// void _read() const noexcept;
// std::filesystem::path _path;
// til::throttled_func_trailing<> _throttler;
};
}

View file

@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "BaseApplicationState.h"
#include "CascadiaSettings.h"
// #include "ApplicationState.g.cpp"
#include "JsonUtils.h"
#include "FileUtils.h"
constexpr std::wstring_view stateFileName{ L"state.json" };
using namespace ::Microsoft::Terminal::Settings::Model;
// namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// {
BaseApplicationState::BaseApplicationState(std::filesystem::path path) noexcept :
_path{ std::move(path) },
_throttler{ std::chrono::seconds(1), [this]() { _write(); } }
{
_read();
}
// The destructor ensures that the last write is flushed to disk before returning.
BaseApplicationState::~BaseApplicationState()
{
// This will ensure that we not just cancel the last outstanding timer,
// but instead force it to run as soon as possible and wait for it to complete.
_throttler.flush();
}
// Re-read the state.json from disk.
void BaseApplicationState::Reload() const noexcept
{
_read();
}
// Returns the state.json path on the disk.
winrt::hstring BaseApplicationState::FilePath() const noexcept
{
return winrt::hstring{ _path.wstring() };
}
// Deserializes the state.json at _path into this ApplicationState.
// * ANY errors during app state will result in the creation of a new empty state.
// * ANY errors during runtime will result in changes being partially ignored.
void BaseApplicationState::_read() const noexcept
try
{
const auto data = ReadUTF8FileIfExists(_path).value_or(std::string{});
if (data.empty())
{
return;
}
std::string errs;
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
Json::Value root;
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
{
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
}
this->FromJson(root);
}
CATCH_LOG()
// Serialized this ApplicationState (in `context`) into the state.json at _path.
// * Errors are only logged.
// * _state->_writeScheduled is set to false, signaling our
// setters that _synchronize() needs to be called again.
void BaseApplicationState::_write() const noexcept
try
{
Json::Value root{ this->ToJson() };
Json::StreamWriterBuilder wbuilder;
const auto content = Json::writeString(wbuilder, root);
WriteUTF8FileAtomic(_path, content);
}
CATCH_LOG()
// }

View file

@ -0,0 +1,36 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- ApplicationState.h
Abstract:
- TODO!
--*/
#pragma once
// namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// {
struct BaseApplicationState
{
BaseApplicationState(std::filesystem::path path) noexcept;
~BaseApplicationState();
// Methods
void Reload() const noexcept;
// General getters/setters
winrt::hstring FilePath() const noexcept;
virtual void FromJson(const Json::Value& root) const noexcept = 0;
virtual Json::Value ToJson() const noexcept = 0;
protected:
void _write() const noexcept;
void _read() const noexcept;
std::filesystem::path _path;
til::throttled_func_trailing<> _throttler;
};
// }

View file

@ -21,6 +21,7 @@
<ClInclude Include="IconPathConverter.h">
<DependentUpon>IconPathConverter.idl</DependentUpon>
</ClInclude>
<ClInclude Include="BaseApplicationState.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="ActionArgs.h">
<DependentUpon>ActionArgs.idl</DependentUpon>
@ -88,6 +89,7 @@
<DependentUpon>IconPathConverter.idl</DependentUpon>
</ClCompile>
<ClCompile Include="init.cpp" />
<ClCompile Include="BaseApplicationState.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>

View file

@ -53,3 +53,6 @@ TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider);
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"
#include <til/mutex.h>
#include <til/throttled_func.h>