Generic JSON-directory loader with debounced live reload. More...
#include <phosphor-fsloader/include/PhosphorFsLoader/DirectoryLoader.h>
Classes | |
| struct | Entry |
Tracked entry — mirrors ParsedEntry minus the payload. More... | |
Signals | |
| void | entriesChanged () |
| Fired after every rescan, coalesced by the 50 ms debounce — regardless of whether the discovered entry set or any underlying payload actually changed. | |
Public Member Functions | |
| DirectoryLoader (IDirectoryLoaderSink &sink, QObject *parent=nullptr) | |
| Construct with a borrowed sink. | |
| ~DirectoryLoader () override | |
| DirectoryLoader (const DirectoryLoader &)=delete | |
| DirectoryLoader & | operator= (const DirectoryLoader &)=delete |
| int | loadFromDirectory (const QString &directory, LiveReload liveReload=LiveReload::Off) |
| Register a directory for scanning + (optionally) watching. | |
| int | loadFromDirectories (const QStringList &directories, LiveReload liveReload=LiveReload::Off, RegistrationOrder order=RegistrationOrder::LowestPriorityFirst) |
| Register multiple directories in caller-declared priority order. | |
| void | requestRescan () |
| Trigger a debounced rescan. | |
| int | registeredCount () const |
| Count of entries currently tracked by the loader. | |
| QList< Entry > | entries () const |
Current entries (post last rescan), sorted by key for deterministic order across platforms and Qt versions. | |
| void | setDebounceIntervalForTest (int ms) |
| Test-only: override the debounce interval (default 50 ms). | |
| void | setMaxEntriesForTest (int cap) |
Test-only: override the per-rescan entry cap (default kMaxEntries). | |
| QString | watchedAncestorForTest (const QString &target) const |
Test-only: ancestor-watch introspection forwarded to the underlying WatchedDirectorySet. | |
| bool | hasParentWatchForTest (const QString &path) const |
Test-only: parent-watch introspection forwarded to the underlying WatchedDirectorySet. | |
Static Public Attributes | |
| static constexpr qint64 | kMaxFileBytes = 1 * 1024 * 1024 |
| Per-file size cap. | |
| static constexpr int | kMaxEntries = 10'000 |
| Hard cap on entries parsed per rescan (summed across every registered directory). | |
Generic JSON-directory loader with debounced live reload.
A thin specialisation of WatchedDirectorySet that scans top-level *.json in every registered directory, applies user-wins layering on key collision (caller passes dirs system-first / user-last per the loadFromDirectories docstring), and dispatches per-file parsing + batch commits to a consumer-supplied IDirectoryLoaderSink.
Watching, debouncing, parent-watch promotion, and rescan-during-rescan race handling all live in the underlying WatchedDirectorySet — DirectoryLoader adds only the JSON-specific concerns:
• Top-level *.json filtering. • Per-file size cap (kMaxFileBytes) and per-rescan entry cap (kMaxEntries) DoS guards. • Sink dispatch (parseFile per file, one commitBatch per scan). • Stale-entry purge: deleted files' keys are reported to the sink as removedKeys so the sink can unregister them.
Loaders that need a different on-disk shape (subdirectory layouts, non-JSON file extensions, custom filename validation) implement IScanStrategy directly against WatchedDirectorySet rather than extending this class.
GUI-thread only. Inherits the threading constraint from WatchedDirectorySet.
|
explicit |
Construct with a borrowed sink.
sink must outlive the loader. Taken by reference rather than raw pointer so there is no need for a null-check at the call site — "sink is always valid" is a compile-time guarantee.
|
override |
|
delete |
| QList< Entry > PhosphorFsLoader::DirectoryLoader::entries | ( | ) | const |
Current entries (post last rescan), sorted by key for deterministic order across platforms and Qt versions.
|
signal |
Fired after every rescan, coalesced by the 50 ms debounce — regardless of whether the discovered entry set or any underlying payload actually changed.
This is deliberately a "rescan completed" signal rather than a "content changed" signal — the loader has no visibility into the sink's payload semantics, so it cannot diff at this layer. Sinks that need change-only consumer signals layer their own diff on top: CurveLoader and ProfileLoader each maintain a lastBatchChanged flag in their sink and gate curvesChanged / profilesChanged on it.
Tests and debug tooling rely on the per-rescan emission to observe rescans without payload inspection — do not weaken this contract without updating the loader-sink consumers and the test suite. The three sister registries (ShaderRegistry, AnimationShaderRegistry, ScriptedAlgorithmLoader) gate their public content-changed signals at the registry level via SHA-1 signature or QHash diff because they own their parse output; DirectoryLoader gates one layer up.
| bool PhosphorFsLoader::DirectoryLoader::hasParentWatchForTest | ( | const QString & | path | ) | const |
Test-only: parent-watch introspection forwarded to the underlying WatchedDirectorySet.
| int PhosphorFsLoader::DirectoryLoader::loadFromDirectories | ( | const QStringList & | directories, |
| LiveReload | liveReload = LiveReload::Off, |
||
| RegistrationOrder | order = RegistrationOrder::LowestPriorityFirst |
||
| ) |
Register multiple directories in caller-declared priority order.
RegistrationOrder::LowestPriorityFirst (the default) takes input in [sys-lowest, ..., sys-highest, user] order — the same shape the daemon's setupAnimationProfiles already builds via std::reverse(locateAll(...)) + user-dir append. Passing HighestPriorityFirst lets callers feed locateAll's natural output (with the user dir prepended) directly without their own pre-reverse — the base normalises before the strategy runs, so higher-priority entries always override on key collision.
Same one-way liveReload semantics as loadFromDirectory.
Empty directories is a no-op: no scan runs and the return value is the count of entries currently tracked from PRIOR registrations (not zero). Callers needing "force a rescan with no new dirs" should use requestRescan() instead.
| int PhosphorFsLoader::DirectoryLoader::loadFromDirectory | ( | const QString & | directory, |
| LiveReload | liveReload = LiveReload::Off |
||
| ) |
Register a directory for scanning + (optionally) watching.
Idempotent on the directory path — adding the same directory twice is a no-op on the second call. Returns the count of entries CURRENTLY registered after the scan (not the delta).
liveReload is a one-way enable: once any call passes LiveReload::On, the loader keeps watching for the rest of its lifetime.
The default is Off because this is a library primitive — tests and batch imports compose against it. Consumer wrappers above this (CurveLoader/ProfileLoader and the shader registries) inherit the default, but can override it via the explicit LiveReload::On argument production callers pass.
|
delete |
| int PhosphorFsLoader::DirectoryLoader::registeredCount | ( | ) | const |
Count of entries currently tracked by the loader.
| void PhosphorFsLoader::DirectoryLoader::requestRescan | ( | ) |
Trigger a debounced rescan.
Forwards to the underlying WatchedDirectorySet.
| void PhosphorFsLoader::DirectoryLoader::setDebounceIntervalForTest | ( | int | ms | ) |
Test-only: override the debounce interval (default 50 ms).
| void PhosphorFsLoader::DirectoryLoader::setMaxEntriesForTest | ( | int | cap | ) |
Test-only: override the per-rescan entry cap (default kMaxEntries).
Lets the cap regression test trip the guard with 3-digit file counts rather than having to materialise 10k files.
| QString PhosphorFsLoader::DirectoryLoader::watchedAncestorForTest | ( | const QString & | target | ) | const |
Test-only: ancestor-watch introspection forwarded to the underlying WatchedDirectorySet.
|
staticconstexpr |
Hard cap on entries parsed per rescan (summed across every registered directory).
At 10k entries a rescan has already burned the 50 ms debounce budget many times over.
|
staticconstexpr |
Per-file size cap.
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.