#pragma once // owned_manifest — the set of files the bank system ITSELF created; every file the // capture path writes gets recorded here so prune can tell the system's own orphans // (owned ∩ present − referenced) apart from hand-dropped files. Writes and persists // the manifest only — no prune logic lives here. // // NOT a mirror of the bank index: removing/moving an index entry does NOT remove // the file's manifest record (the manifest tracks files *created*; prune reconciles // manifest-vs-index later). Only the capture add-path adds to it — no remove verb. // // Paths are ALWAYS project-relative (same invariant as Sample.relativePath). add() // rejects an absolute path rather than guess a relativization — the pure model has // no project root, so "normalizing" could point at the wrong file. #include #include #include namespace reasampler::model { // Outcome of an add(). Mirrors BankModel::AddResult's honesty — the op reports what // happened rather than silently mutating on a bad request. // - Added: the path was new and recorded. // - AlreadyPresent: the path was already in the manifest (dedup no-op). // - RejectedEmptyPath: the path was empty. // - RejectedAbsolutePath: the path was absolute (relative-paths-only invariant). enum class ManifestAddResult { Added, AlreadyPresent, RejectedEmptyPath, RejectedAbsolutePath, }; // The owned-file manifest: an insertion-ordered, deduplicated set of project-relative // paths the capture path has created. Insertion order is preserved so serialize() // round-trips byte-identically (deterministic ext-state, mirror of the index). class OwnedFileManifest { public: OwnedFileManifest() = default; // Record a project-relative path as owned. Rejects an empty or absolute path (no // mutation). A path already present is a dedup no-op (AlreadyPresent), so a repeat // capture of an identical request does not double-record. ManifestAddResult add(const std::string& relativePath); // True iff the exact path string is recorded. Prune uses this to attribute a // present file to the bank system. Exact string match — path normalization (if // any) is the caller's concern, consistent across add and query. bool contains(const std::string& relativePath) const; // The owned paths in insertion order. Prune unions this with the on-disk file // set; here it is the round-trip + query surface. const std::vector& paths() const { return paths_; } std::size_t size() const { return paths_.size(); } bool empty() const { return paths_.empty(); } bool operator==(const OwnedFileManifest& o) const { return paths_ == o.paths_; } // -- Persistence --------------------------------------------------------- // Serialize to a JSON string (lossless round-trip): deserialize(serialize(x)) == x. // An empty manifest serializes to a well-formed empty shape (round-trips to empty). std::string serialize() const; // Parse a manifest JSON produced by serialize(). std::nullopt on malformed input // (the persist shell warns + falls back to an empty manifest, mirroring the bank / // view malformed handling). An empty/absent stored value is the caller's concern // (an empty string is not valid JSON) — the shell maps absence to a fresh manifest. static std::optional deserialize(const std::string& json); private: std::vector paths_; // insertion order; deduplicated }; } // namespace reasampler::model