/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: IRegistry.hpp Date: 2021-6-11 Author: Reece ***/ #pragma once namespace Aurora::Registry { using RegistryType = Data::EDataType; using RegistryValue = Data::TypedValue; /** * This registry implementation is a fairly reliable, small scale, preference, and app variable database * Do not consider this as a replacement for a "real" high performance kvp database, it is not, it's a small cache backed document serializer for user profiles * * Basic use case:-------------------------------------------------------------- * -> AllocRegistry() * -> Read("~/Registry/MyApp/DefSettings.dat") * -> Read("~/Registry/MyApp/UserSettings.dat") * -> menu.color = GetOrSetKey("theme-color", {.13, .13, .13}) * -> menu.colorChanaged := SetKey("theme-color", color) * -> app:onExit := SaveAll({}) * ----------------------------------------------------------------------------- * * Arbitrary write use case:---------------------------------------------------- * -> AllocRegistry() * -> OpenReadWriteback("~/Registry/MyApp/Settings.dat") * -> menu.color = GetOrSetKey("theme-color", {.13, .13, .13}) * -> menu.colorChanaged := SetKey("theme-color", color) * ----------------------------------------------------------------------------- * * Implementing a protected registry:------------------------------------------- * -> AllocRegistry() * -> Read("~/Registry/MyApp/PlayerProfile.dat") will read disk template to the cache, including one "genuine-player-setting" * -> SetWriteModeCache() * -> WriteKey("dev-secret-menu-enabled", {0}) * -> LockKey("dev-secret-menu-enabled"), no user write may pass to 'dev-reserved' * -> SetWriteModeCache() * -> SetKey("dev-secret-menu-enabled", {1}) originating from an evil script will fail, returning false * -> SetKey("genuine-ux-setting", {2, 2}) will successfully write to cache * -> SetKey("genuine-player-setting", {2, 2}) will be assumed to be legitimate, write to cache * -> GetKey("genuine-ux-setting") will return {2, 2} from the cache * -> GetKey("genuine-player-setting") will return {2, 2} from the cache * -> SetKey("some-leftover-magic", {.3, .4, .3}) will successfully write to cache * -> GetKey("some-leftover-magic") will return {.3, .4, .3} from the cache * -> OpenSave({}) * -> SetKey("genuine-ux-setting", GetKey("genuine-ux-setting")) will specifically write genuine-ux-setting to disk * -> var userSetting = GetKey("genuine-player-setting") will return genuine-player-setting from the cache * -> if (userSetting is inbounds of good constraints) then SetKey("genuine-player-setting", userSetting [=GetKey("genuine-ux-setting")]) will write the cached value back to disk * -> -- * -> Unload(), some-leftover-magic will be lost * * * -> Prevent malicious usage of development variables while ensuring spurious references work with read only reference values * -> You may write to the registry the default safe [key:value]s; for instance, zeroing out debug options * -> You may call `LockKey(...)` to ensure spurious writes, like an unchecked server server command or user request, will not overload the protected variable * * -> Some developers may wish to validate the registry dataset before locking the registry, * -> A general purpose vaildator, schema vaildator, or similar may wish to reset values back to a known good state (`SetWriteModeCache` + `SetKey`) before locking (`SetWriteModeNone`) an otherwise read only database * * ----------------------------------------------------------------------------- * */ class IRegistry { public: enum class EWriteMode { eWriteModeLocked, /// eWriteModeFileBuffer, /// Write to cache only eWriteModeFileStream, /// [Default] write database to disk on each set, saving to cache }; enum class EReadType { eClearCacheAndStore, eStoreNew, eStoreAll }; virtual bool Read(const AuString &path, EReadType read) = 0; /** * Puts the registry in read only mode */ virtual void SetWriteModeNone() = 0; /// read only, eqiv of ForEach((key) => { LockKey(key) }; SetWriteModeCache(); /** * Puts the registry in an in memory only mode. * -> Permit local read/write updates * -> Never allows SetKeys to write manipulated sates to an IO buffer or stream until sanitized or otherwise explicitly written back */ virtual void SetWriteModeCache() = 0; /** * Puts the registry in a live write mode */ virtual void SetWriteModeStream(const AuOptional &path) = 0; /** * Puts the registry in a write-back-like mode */ virtual void SetWriteModeBuffered(const AuOptional &path) = 0; virtual void WriteAllCacheBack() = 0; /// eqiv of ForEach((key) => { WriteKey(key, GetKey(key)); }; writes all updated cache entries back to io buffer or stream virtual void SaveBuffered() = 0; /** * Saves the current state of the registry */ virtual void SaveAll(const AuOptional &path) = 0; /** * Further calls to SetKeys will; update a database on write, if buffered; or write to a buffer and serialize on flush (`SaveBuffered`) */ virtual void OpenSave(const AuString &path, bool buffered = false) = 0; /** * equiv Read(path, EReadType::eClearCacheAndStore) */ virtual void OpenRead(const AuString &path) = 0; /** * equiv Read(path, EReadType::eClearCacheAndStore), OpenSave(path, false) */ virtual void OpenReadWriteback(const AuString &path) = 0; virtual void CloseSave() = 0; virtual bool KeyExists(const AuString &key, RegistryType &type) = 0; virtual bool GetOrCreateKey(const AuString &key, const RegistryValue &def, RegistryValue &value) = 0; /** * Write a key, to cache or to disk */ virtual bool SetKey(const AuString &key, const RegistryValue &value) = 0; /** * Read key from cache */ virtual bool GetKey(const AuString &key, RegistryValue &value) = 0; virtual void LockKey(const AuString &str) = 0; virtual void UnlockKey(const AuString &str) = 0; /** * @deprecated */ inline void Lock() { SetWriteModeCache(); } /** * @deprecated */ inline void Unlock() { OpenSave({}, false); } }; }