Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
PhosphorFsLoader Namespace Reference

Namespaces

namespace  detail
 

Classes

class  DirectoryLoader
 Generic JSON-directory loader with debounced live reload. More...
 
class  IDirectoryLoaderSink
 Consumer-supplied strategy for a DirectoryLoader. More...
 
class  IScanStrategy
 Pluggable enumeration / parse / commit policy for WatchedDirectorySet. More...
 
struct  JsonEnvelope
 Result of a successful envelope validation. More...
 
class  MetadataPackScanStrategy
 Reusable scan strategy for metadata.json-driven subdirectory pack registries. More...
 
struct  ParsedEntry
 One parsed file's payload + metadata. More...
 
class  SchemaValidator
 Validates parsed JSON documents against a JSON Schema (Draft 7). More...
 
class  WatchedDirectorySet
 Watcher + debounce + rescan scaffolding for filesystem-backed loaders. More...
 

Enumerations

enum class  AbsolutePathPolicy { Reject , Trust }
 What to do with a declared path that is already absolute. More...
 
enum class  LiveReload : quint8 { Off , On }
 Opt-in policy for directory watching. More...
 
enum class  RegistrationOrder : quint8 { LowestPriorityFirst , HighestPriorityFirst }
 Caller's declared priority direction for registerDirectories / setDirectories input. More...
 

Functions

std::optional< JsonEnvelopevalidateJsonEnvelope (const QString &filePath, const QLoggingCategory &category)
 Validate the default envelope used by DirectoryLoader sinks.
 
std::optional< QString > resolveWithinDirectory (const QString &declaredPath, const QString &directory, AbsolutePathPolicy policy)
 Resolve declaredPath against directory, refusing anything that lands outside it.
 
PHOSPHORFSLOADER_EXPORT void logSchemaErrors (const QLoggingCategory &category, const QList< SchemaValidator::Error > &errors)
 Log each schema violation at category, one per line.
 

Variables

constexpr qint64 kMaxJsonFileBytes = 1 * 1024 * 1024
 Per-file size cap for JSON metadata reads.
 

Enumeration Type Documentation

◆ AbsolutePathPolicy

What to do with a declared path that is already absolute.

Enumerator
Reject 

Do not trust it as-is: the path is still subject to the containment check, so only an absolute path that resolves INSIDE directory survives.

Correct for anything a PACK FILE declares: a pack ships its own assets, so an absolute path outside the pack can only be a mistake or an escape.

Trust 

Accept it unchecked.

Correct only for a RUNTIME override the user chose through a file picker or D-Bus, where an absolute path outside any pack is the entire point.

◆ LiveReload

enum class PhosphorFsLoader::LiveReload : quint8
strong

Opt-in policy for directory watching.

On installs a QFileSystemWatcher on every scanned directory (or its parent, if the target doesn't exist yet — so fresh installs that create the user-data dir later still pick up edits without a restart). Edits trigger a 50 ms debounced rescan.

Off is fire-and-forget — useful for tests, batch imports, and consumers that want explicit refresh semantics via requestRescan.

Enumerator
Off 
On 

◆ RegistrationOrder

enum class PhosphorFsLoader::RegistrationOrder : quint8
strong

Caller's declared priority direction for registerDirectories / setDirectories input.

Strategies in this library always see the canonical scan order: [lowest-priority, ..., highest-priority], suitable for reverse- iterate-first-wins. The base normalises the caller's input into this shape before storing it in m_directories and handing it to the strategy, so the canonical convention is enforced once at the registration boundary instead of being repeated as a comment-driven contract every strategy author has to remember.

Pick the value that matches how your input is already ordered — do NOT pre-reverse and then claim the opposite. Both spellings are equivalent; the enum's only job is to keep the next strategy author from silently inverting every override by feeding locateAll's natural (highest-first) output as if it were the canonical (lowest-first) shape.

Enumerator
LowestPriorityFirst 

[sys-lowest, ..., sys-highest, user] — the canonical strategy view.

The daemon's curve/profile/script setup explicitly builds this shape via std::reverse(locateAll(...)) + userDir.append. Pass-through; the base stores the list verbatim.

HighestPriorityFirst 

[user, sys-highest, ..., sys-lowest] — the natural output of QStandardPaths::locateAll(GenericDataLocation, ...) followed by a prepend(userDir).

The base reverses internally before storing, so the strategy still sees LowestPriorityFirst.

Function Documentation

◆ logSchemaErrors()

PHOSPHORFSLOADER_EXPORT void PhosphorFsLoader::logSchemaErrors ( const QLoggingCategory &  category,
const QList< SchemaValidator::Error > &  errors 
)

Log each schema violation at category, one per line.

Each error is logged as " <json-pointer>: <message>" (or "(root)" for a document-root failure). Centralizes the per-error diagnostic format every loader shares; callers emit their own "skipping X" headline first.

◆ resolveWithinDirectory()

std::optional< QString > PhosphorFsLoader::resolveWithinDirectory ( const QString &  declaredPath,
const QString &  directory,
AbsolutePathPolicy  policy 
)
inline

Resolve declaredPath against directory, refusing anything that lands outside it.

Returns nullopt on refusal, the resolved absolute path on acceptance.

This exists because there is exactly one correct way to write this check and it is easy to write a weaker one. Three properties matter:

  • QDir::filePath / absoluteFilePath return an ABSOLUTE argument unchanged and never normalise .., so neither is a containment check.
  • A lexical comparison (QDir::cleanPath) does not resolve symlinks, so a link inside the directory pointing out of it passes.
  • Canonicalisation returns empty for a path that does not exist YET. Simply falling back to lexical there fails open, because a symlinked intermediate component is invisible to a lexical compare — and a not-yet-existing leaf is the live-reload case, not an edge case. So the deepest existing ancestor is canonicalised and the missing tail re-appended. The two domains are never mixed.

Subdirectories INSIDE directory stay legal, because containment is checked on the resolved path rather than by refusing separators. A declared name that resolves to the directory itself is refused: a directory is not a file any consumer here can use.

Existence is deliberately NOT checked. Callers report a missing file with their own diagnostics, and conflating "escaped" with "absent" would make both harder to debug.

Returns the CLEANED resolved path, so two LEXICAL spellings of one file (./x and sub//x) cannot reach watch keys, content signatures or path-keyed caches as distinct strings. (A symlinked intermediate component inside the pack can still yield a second spelling; only the canonical target is validated, the lexical form is what is returned.)

◆ validateJsonEnvelope()

std::optional< JsonEnvelope > PhosphorFsLoader::validateJsonEnvelope ( const QString &  filePath,
const QLoggingCategory &  category 
)
inline

Validate the default envelope used by DirectoryLoader sinks.

Every IDirectoryLoaderSink whose schema follows the default name == filename basename envelope (CurveLoader and ProfileLoader today) starts its parseFile with the same boilerplate:

  1. Open the file (skip on read error).
  2. Parse JSON (skip on malformed).
  3. Reject non-object roots.
  4. Require a non-empty "name" field.
  5. Reject mismatch between "name" and the file's completeBaseName (silent shadowing guard — a user copies widget.fade.json → custom.json and forgets to rename the inner field, the result registers under the original key while the file on disk suggests a different identity).

Loaders with a different envelope shape (e.g. ShaderRegistry, which keys by directory name and parses metadata.json with its own schema) do not use this helper — they roll their own parse pass.

On success this returns the parsed root with "name" stripped — sinks can pass root straight into a schema-specific fromJson without re-handling the bookkeeping field. On failure it logs a clear diagnostic at the supplied logging category and returns std::nullopt.

Parameters
filePathAbsolute path to the JSON file. Used for the read, for the completeBaseName diagnostic, and for log output.
categoryCaller's Q_LOGGING_CATEGORY so warnings are tagged with the consumer-specific category (e.g. "phosphoranimation.curveloader") rather than a generic shared category — operators filtering log output by category keep their existing rules.

Header-only inline so consumers don't pay a translation-unit boundary crossing per parse — every loader sink is in a single source file already, and this helper is on the per-file hot path.

Variable Documentation

◆ kMaxJsonFileBytes

constexpr qint64 PhosphorFsLoader::kMaxJsonFileBytes = 1 * 1024 * 1024
inlineconstexpr

Per-file size cap for JSON metadata reads.

Files larger than this are skipped with a warning — guards the GUI thread against a pathological user JSON. 1 MiB is far above any legitimate curve / profile / layout schema in this library's ecosystem.

Canonical home, in its own tiny header so a validator that needs only the figure (JsonEnvelopeValidator) does not pull in all of DirectoryLoader.h. DirectoryLoader::kMaxFileBytes aliases this.