Atomic-write JSON configuration backend. More...
#include <phosphor-config/include/PhosphorConfig/JsonBackend.h>
Public Types | |
| enum class | SyncPolicy { Synchronous , Deferred } |
Sync timing policy. See setSyncPolicy for the full contract. More... | |
Public Member Functions | |
| JsonBackend (QString filePath) | |
Construct a backend backed by filePath. | |
| ~JsonBackend () override | |
| std::unique_ptr< IGroup > | group (const QString &name) override |
| Return a scoped view into the named group. | |
| void | reparseConfiguration () override |
| Re-read configuration from disk, discarding any pending in-memory changes. | |
| bool | sync () override |
| Request that pending writes reach disk. | |
| bool | commit () override |
| Bypasses the Deferred sync policy's debounce so the return value is a real commit result under every policy. | |
| void | deleteGroup (const QString &name) override |
| Delete an entire group and everything inside it. | |
| QString | readRootString (const QString &key, const QString &defaultValue={}) const override |
| Read/write ungrouped (root-level) keys. | |
| void | writeRootString (const QString &key, const QString &value) override |
| void | removeRootKey (const QString &key) override |
| QStringList | groupList () const override |
| Enumerate every top-level group name. | |
| void | setPathResolver (std::shared_ptr< IGroupPathResolver > resolver) override |
| Attach a custom path resolver. | |
| std::shared_ptr< IGroupPathResolver > | pathResolver () const override |
Currently-installed resolver, or nullptr when none is set (also the default for backends that ignore setPathResolver). | |
| void | setRootGroupName (const QString &name) |
Change the JSON object that receives ungrouped writeRootString calls. | |
| QString | rootGroupName () const |
| void | setVersionStamp (const QString &key, int version) override |
Stamp-on-sync: if non-empty, every sync() that writes the file will insert key = version when the key is absent from the root. | |
| std::pair< QString, int > | versionStamp () const override |
Currently-installed version stamp as {key, version}. | |
| void | applyVersionStamp (QJsonObject &root) const |
Insert the installed version stamp into root when a stamp is configured and root does not already carry the key. | |
| bool | applyMigration (const Schema &schema) override |
Run schema's migration chain against the in-memory root, and if any step bumps the version stamp, atomically rewrite the backing file and refresh the in-memory state. | |
| QJsonObject | jsonRootSnapshot () const |
| Read-only access to the in-memory document. | |
| QString | filePath () const |
Backing file path (useful for migration-chain callers that need to rewrite via writeJsonAtomically during schema upgrades). | |
| void | clearDirty () |
| Clear the dirty flag without writing. | |
| bool | isDirty () const override |
| Whether the in-memory document carries changes that have not been committed to disk. | |
| void | replaceRoot (QJsonObject root) |
Replace the in-memory document with root. | |
| void | setSyncPolicy (SyncPolicy policy, int debounceMs=500) |
Configure when sync() commits to disk. | |
| SyncPolicy | syncPolicy () const |
| bool | flushPending () |
| Flush to disk now, cancelling any pending deferred-sync timer first. | |
Public Member Functions inherited from PhosphorConfig::IBackend | |
| virtual | ~IBackend ()=default |
| IBackend (const IBackend &)=delete | |
| IBackend & | operator= (const IBackend &)=delete |
Static Public Member Functions | |
| static bool | writeJsonAtomically (const QString &filePath, const QJsonObject &root) |
Atomically write root to filePath (temp file + rename via QSaveFile). | |
Additional Inherited Members | |
Protected Member Functions inherited from PhosphorConfig::IBackend | |
| IBackend ()=default | |
Atomic-write JSON configuration backend.
Reads and writes a single JSON document. Writes are deferred until sync() is called and then committed via QSaveFile (temp file + rename, safe against crashes and power loss).
Group resolution order:
IGroupPathResolver is installed and returns a path, use it.The "root group" (see rootGroupName) holds ungrouped keys written via writeRootString. Defaults to "General" for compatibility with QSettings INI behaviour.
|
strong |
Sync timing policy. See setSyncPolicy for the full contract.
| Enumerator | |
|---|---|
| Synchronous |
Default — matches the pre-policy behaviour so switching an existing consumer requires no change. |
| Deferred |
The actual flush happens on the timer's timeout, on the same thread that owns the backend (i.e. whichever thread is pumping the event loop that owns the timer). Callers that need a guaranteed-committed state before proceeding (e.g. closing a save dialog, shutting down a daemon) call Requires a running event loop to actually fire. Tests that exercise this path must call |
|
explicit |
Construct a backend backed by filePath.
The file is loaded immediately; missing or malformed files start with an empty root object (a warning is logged for malformed content).
|
override |
|
overridevirtual |
Run schema's migration chain against the in-memory root, and if any step bumps the version stamp, atomically rewrite the backing file and refresh the in-memory state.
Returns true unless the atomic disk rewrite fails — in which case the backend stays at the unmigrated state and the next successful sync() retries. Consumers that need the "did migrations actually apply" bit should check versionStamp() before and after, not this return value.
Reimplemented from PhosphorConfig::IBackend.
| void PhosphorConfig::JsonBackend::applyVersionStamp | ( | QJsonObject & | root | ) | const |
Insert the installed version stamp into root when a stamp is configured and root does not already carry the key.
No-op otherwise.
This is the same step sync() applies on its way to disk, exposed so that a caller writing a root snapshot out of band (an export, say) can stamp it identically. A document written without the stamp reads back as version-less and re-runs the whole migration chain when it is imported.
| void PhosphorConfig::JsonBackend::clearDirty | ( | ) |
Clear the dirty flag without writing.
Used by migration (applyMigration) and by callers that stage something into the root, take a snapshot of it, then restore the root and need to put the flag back the way they found it (see isDirty()).
|
overridevirtual |
Bypasses the Deferred sync policy's debounce so the return value is a real commit result under every policy.
Equivalent to flushPending().
Reimplemented from PhosphorConfig::IBackend.
|
overridevirtual |
Delete an entire group and everything inside it.
Intermediate parents are pruned if they become empty (dot-path groups only).
Implements PhosphorConfig::IBackend.
| QString PhosphorConfig::JsonBackend::filePath | ( | ) | const |
Backing file path (useful for migration-chain callers that need to rewrite via writeJsonAtomically during schema upgrades).
| bool PhosphorConfig::JsonBackend::flushPending | ( | ) |
Flush to disk now, cancelling any pending deferred-sync timer first.
No-op only when the backend is not dirty — under Synchronous policy there is no timer to cancel, but a dirty root is still written, so this is a commit under every policy rather than a Deferred-only escape hatch. Returns the commit result (or true when there was nothing to flush). Safe to call from any context that could call sync().
|
overridevirtual |
Return a scoped view into the named group.
Caller owns the pointer; destroy it before asking for another group on this backend.
Implements PhosphorConfig::IBackend.
|
overridevirtual |
Enumerate every top-level group name.
Dot-path groups are returned with their full path ("Snapping", "Snapping.Behavior", ...). Groups produced by a plugged-in IGroupPathResolver appear in the resolver's preferred external form.
CONTRACT — reserved names. Whatever container an implementation uses to hold the ungrouped keys that readRootString / writeRootString / removeRootKey address must NOT appear in this list, nor may any of its descendants. Ungrouped keys are not addressable through group() and no caller can tell them apart from a real group by name, so a consumer that sweeps groupList() and calls deleteGroup on every name it does not recognise would wipe them. Enforcing it here rather than asking every consumer to know each backend's container name is what makes such a sweep safe.
The same applies to any root key an IGroupPathResolver reserves: the resolver owns that subtree exclusively and enumerates it itself.
Implementations: JsonBackend filters its rootGroupName (and the version stamp, and the resolver's reserved roots) out of the enumeration.
Implements PhosphorConfig::IBackend.
|
overridevirtual |
Whether the in-memory document carries changes that have not been committed to disk.
Pairs with clearDirty() for callers that snapshot/restore the root (see jsonRootSnapshot / replaceRoot) and need to put the flag back the way they found it.
Reimplemented from PhosphorConfig::IBackend.
| QJsonObject PhosphorConfig::JsonBackend::jsonRootSnapshot | ( | ) | const |
Read-only access to the in-memory document.
Implicitly shared (COW), so the copy is cheap and isolated from future mutations.
|
overridevirtual |
Currently-installed resolver, or nullptr when none is set (also the default for backends that ignore setPathResolver).
Reimplemented from PhosphorConfig::IBackend.
|
overridevirtual |
Read/write ungrouped (root-level) keys.
Implements PhosphorConfig::IBackend.
|
overridevirtual |
Implements PhosphorConfig::IBackend.
|
overridevirtual |
Re-read configuration from disk, discarding any pending in-memory changes.
Must not be called while a group view is live.
Discarding is load-bearing, not incidental: callers use this to roll back after a failed write, so an implementation that instead FLUSHED the pending changes would persist the very mutations the caller is asking it to drop. A store that cannot drop unsaved writes therefore cannot implement IBackend — which is why QSettings, whose destructor flushes and which offers no discard API, is confined to the read-only QSettingsBackend::readConfigFromDisk helper.
Implements PhosphorConfig::IBackend.
| void PhosphorConfig::JsonBackend::replaceRoot | ( | QJsonObject | root | ) |
Replace the in-memory document with root.
Marks the backend dirty — callers who paired this with a successful writeJsonAtomically may call clearDirty() immediately after to restore the "in-memory matches disk" invariant. Must not be called while any JsonGroup views are alive.
| QString PhosphorConfig::JsonBackend::rootGroupName | ( | ) | const |
|
overridevirtual |
Attach a custom path resolver.
Passing nullptr removes it. Safe to call at any time; the resolver is consulted on every group access, so it should be cheap.
Reimplemented from PhosphorConfig::IBackend.
| void PhosphorConfig::JsonBackend::setRootGroupName | ( | const QString & | name | ) |
Change the JSON object that receives ungrouped writeRootString calls.
Defaults to "General". Call before the first write.
| void PhosphorConfig::JsonBackend::setSyncPolicy | ( | SyncPolicy | policy, |
| int | debounceMs = 500 |
||
| ) |
Configure when sync() commits to disk.
debounceMs is only consulted for Deferred policy; it sets the coalescing window during which repeated sync() calls restart the same timer. Values <= 0 are clamped to 1 ms.
Safe to call at any time. Switching from Deferred back to Synchronous also flushes any pending deferred write so the in-memory and on-disk state agree on return.
|
overridevirtual |
Stamp-on-sync: if non-empty, every sync() that writes the file will insert key = version when the key is absent from the root.
Used by consumers who pair a JsonBackend with a Schema so fresh stores carry the current version from day one. Default disables the behaviour (empty key).
Reimplemented from PhosphorConfig::IBackend.
|
overridevirtual |
Request that pending writes reach disk.
No-op when nothing is dirty. Returns true on success (or when there was nothing to flush), false on an I/O error — backends log the reason before returning.
true means "accepted", NOT "committed": a backend is free to satisfy this by scheduling the write (JsonBackend's Deferred sync policy debounces it), in which case the write has not happened yet and may still fail. Callers that must know the bytes landed — anything moving a baseline or reporting success to a user — call commit() instead.
Implements PhosphorConfig::IBackend.
| SyncPolicy PhosphorConfig::JsonBackend::syncPolicy | ( | ) | const |
|
overridevirtual |
Currently-installed version stamp as {key, version}.
Returns an empty key when no stamp is installed. Used by shared-backend safety checks (see Store::Store) to detect a clobber.
Reimplemented from PhosphorConfig::IBackend.
|
static |
Atomically write root to filePath (temp file + rename via QSaveFile).
Exposed for use by MigrationRunner and other external callers that already have a QJsonObject in hand. Returns true on success.
|
overridevirtual |
Implements PhosphorConfig::IBackend.