Split ApplicationState into a Base and add an Elevated version

This commit is contained in:
Mike Griese 2021-08-31 16:15:22 -05:00
parent 42c3eea136
commit eb243f5e11
7 changed files with 251 additions and 24 deletions

View file

@ -103,28 +103,17 @@ using namespace ::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
ApplicationState::ApplicationState(std::filesystem::path path) noexcept :
BaseApplicationState{ path } {}
// Returns the application-global ApplicationState object.
Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance()
{
static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath() / stateFileName);
state->Reload();
return *state;
}
// 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();
// }
void ApplicationState::FromJson(const Json::Value& root) const noexcept
{
auto state = _state.lock();

View file

@ -14,10 +14,7 @@ Abstract:
#include "BaseApplicationState.h"
#include "ApplicationState.g.h"
#include <inc/cppwinrt_utils.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:
@ -28,15 +25,14 @@ Abstract:
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct ApplicationState : ApplicationStateT<ApplicationState>, public BaseApplicationState
struct ApplicationState : public BaseApplicationState, ApplicationStateT<ApplicationState>
{
static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance();
ApplicationState(std::filesystem::path path) noexcept;
~ApplicationState();
void FromJson(const Json::Value& root) const noexcept override;
Json::Value ToJson() const noexcept override;
virtual void FromJson(const Json::Value& root) const noexcept override;
virtual Json::Value ToJson() const noexcept override;
// // Methods
// void Reload() const noexcept;

View file

@ -18,7 +18,7 @@ BaseApplicationState::BaseApplicationState(std::filesystem::path path) noexcept
_path{ std::move(path) },
_throttler{ std::chrono::seconds(1), [this]() { _write(); } }
{
_read();
// _read();
}
// The destructor ensures that the last write is flushed to disk before returning.
@ -62,7 +62,7 @@ try
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
}
this->FromJson(root);
FromJson(root);
}
CATCH_LOG()

View file

@ -0,0 +1,163 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ElevatedState.h"
#include "CascadiaSettings.h"
#include "ElevatedState.g.cpp"
#include "JsonUtils.h"
#include "FileUtils.h"
constexpr std::wstring_view stateFileName{ L"elevated-state.json" };
namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
// This trait exists in order to serialize the std::unordered_set for GeneratedProfiles.
template<typename T>
struct ConversionTrait<std::unordered_set<T>>
{
std::unordered_set<T> FromJson(const Json::Value& json) const
{
ConversionTrait<T> trait;
std::unordered_set<T> val;
val.reserve(json.size());
for (const auto& element : json)
{
val.emplace(trait.FromJson(element));
}
return val;
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const std::unordered_set<T>& val)
{
ConversionTrait<T> trait;
Json::Value json{ Json::arrayValue };
for (const auto& key : val)
{
json.append(trait.ToJson(key));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("{}[]", ConversionTrait<GUID>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IVector<T>>
{
winrt::Windows::Foundation::Collections::IVector<T> FromJson(const Json::Value& json) const
{
ConversionTrait<T> trait;
std::vector<T> val;
val.reserve(json.size());
for (const auto& element : json)
{
val.push_back(trait.FromJson(element));
}
return winrt::single_threaded_vector(move(val));
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IVector<T>& val)
{
ConversionTrait<T> trait;
Json::Value json{ Json::arrayValue };
for (const auto& key : val)
{
json.append(trait.ToJson(key));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("vector ({})", ConversionTrait<T>{}.TypeDescription());
}
};
}
using namespace ::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
ElevatedState::ElevatedState(std::filesystem::path path) noexcept :
BaseApplicationState{ path } {}
// Returns the application-global ElevatedState object.
Microsoft::Terminal::Settings::Model::ElevatedState ElevatedState::SharedInstance()
{
// TODO! place in a totally different file! and path!
static auto state = winrt::make_self<ElevatedState>(GetBaseSettingsPath() / stateFileName);
state->Reload();
return *state;
}
void ElevatedState::FromJson(const Json::Value& root) const noexcept
{
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_ELEVATED_STATE_GEN(type, name, key, ...) state->name = JsonUtils::GetValueForKey<std::optional<type>>(root, key);
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
#undef MTSM_ELEVATED_STATE_GEN
}
Json::Value ElevatedState::ToJson() const noexcept
{
Json::Value root{ Json::objectValue };
{
auto state = _state.lock_shared();
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) JsonUtils::SetValueForKey(root, key, state->name);
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
#undef MTSM_ELEVATED_STATE_GEN
}
return root;
}
// Generate all getter/setters
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) \
type ElevatedState::name() const noexcept \
{ \
const auto state = _state.lock_shared(); \
const auto& value = state->name; \
return value ? *value : type{ __VA_ARGS__ }; \
} \
\
void ElevatedState::name(const type& value) noexcept \
{ \
{ \
auto state = _state.lock(); \
state->name.emplace(value); \
} \
\
_throttler(); \
}
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
#undef MTSM_ELEVATED_STATE_GEN
}

View file

@ -0,0 +1,57 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- ElevatedState.h
Abstract:
- If the CascadiaSettings class were AppData, then this class would be LocalAppData.
Put anything in here that you wouldn't want to be stored next to user-editable settings.
- Modify ElevatedState.idl and MTSM_ELEVATED_STATE_FIELDS to add new fields.
--*/
#pragma once
#include "BaseApplicationState.h"
#include "ElevatedState.g.h"
#include <inc/cppwinrt_utils.h>
// This macro generates all getters and setters for ElevatedState.
// It provides X with the following arguments:
// (type, function name, JSON key, ...variadic construction arguments)
#define MTSM_ELEVATED_STATE_FIELDS(X) \
X(Windows::Foundation::Collections::IVector<hstring>, AllowedCommandlines, "allowedCommandlines")
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct ElevatedState : ElevatedStateT<ElevatedState>, public BaseApplicationState
{
static Microsoft::Terminal::Settings::Model::ElevatedState SharedInstance();
ElevatedState(std::filesystem::path path) noexcept;
void FromJson(const Json::Value& root) const noexcept override;
Json::Value ToJson() const noexcept override;
// State getters/setters
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) \
type name() const noexcept; \
void name(const type& value) noexcept;
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
#undef MTSM_ELEVATED_STATE_GEN
private:
struct state_t
{
#define MTSM_ELEVATED_STATE_GEN(type, name, key, ...) std::optional<type> name{ __VA_ARGS__ };
MTSM_ELEVATED_STATE_FIELDS(MTSM_ELEVATED_STATE_GEN)
#undef MTSM_ELEVATED_STATE_GEN
};
til::shared_mutex<state_t> _state;
};
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(ElevatedState);
}

View file

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings.Model
{
[default_interface] runtimeclass ElevatedState {
static ElevatedState SharedInstance();
void Reload();
String FilePath { get; };
Windows.Foundation.Collections.IVector<String> AllowedCommandlines { get; set; };
}
}

View file

@ -36,6 +36,9 @@
<ClInclude Include="ApplicationState.h">
<DependentUpon>ApplicationState.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ElevatedState.h">
<DependentUpon>ElevatedState.idl</DependentUpon>
</ClInclude>
<ClInclude Include="CascadiaSettings.h">
<DependentUpon>CascadiaSettings.idl</DependentUpon>
</ClInclude>
@ -109,6 +112,9 @@
<ClCompile Include="ApplicationState.cpp">
<DependentUpon>ApplicationState.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ElevatedState.cpp">
<DependentUpon>ElevatedState.idl</DependentUpon>
</ClCompile>
<ClCompile Include="CascadiaSettings.cpp">
<DependentUpon>CascadiaSettings.idl</DependentUpon>
</ClCompile>
@ -158,6 +164,7 @@
<Midl Include="ActionArgs.idl" />
<Midl Include="ActionMap.idl" />
<Midl Include="ApplicationState.idl" />
<Midl Include="ElevatedState.idl" />
<Midl Include="CascadiaSettings.idl" />
<Midl Include="ColorScheme.idl" />
<Midl Include="Command.idl" />