pub struct FileIndex { /* private fields */ }Expand description
The persistent registry: a snapshot with tombstones, living under the
registry key of a workspace document — the document the root’s
registry-pointer relation targets.
The host document can be either shape (MetaCarrier): a bare config file
(registry.yaml, registry.figl, …) whose whole content is metadata, or a
prose document (registry.md) whose fenced frontmatter carries the records.
Writes splice only the registry value back through the carrier-aware
editor, so the host’s other keys (title, part_of — the self-description
that makes the registry a first-class node of the tree), its comments
outside the records, its body, and its fence style all survive.
The rendered records are one per line (in YAML hosts), sorted by ID; a live
record is id: path, a tombstone is id: null (DESIGN §5’s diff-friendly
shape). This type is pure — text in (FileIndex::parse), text out
(FileIndex::render) — so any storage backend can host it; the caller
owns the I/O and can consult is_dirty to skip
no-op writes.
Implementations§
Source§impl FileIndex
impl FileIndex
Sourcepub fn new(format: Format) -> Self
pub fn new(format: Format) -> Self
An empty registry with no host document — see the host field. Records
resolve in memory; nothing is staged for writing.
Sourcepub fn set_host(&mut self, path: impl Into<PathBuf>, text: &str) -> Result<()>
pub fn set_host(&mut self, path: impl Into<PathBuf>, text: &str) -> Result<()>
Give a registry a host document to persist into, adopting text as its
current contents.
The bootstrap seam: a workspace that only discovers it needs a registry
after a mutation has minted an ID (check --fix declines to create one
until a fix actually registers something) creates the document, then hands
it here so the write renders against the real host — its title, its
part_of, its fence style — rather than against nothing.
This store’s records stay authoritative. Only the write target is
adopted: the host’s text, carrier, and already-persisted record state, so
render splices a correct diff into it. Records the host
happens to carry are not merged into memory — they were not part of what
this store was built from, and adopting them here would resurrect, as live
records, whatever a scan or a caller had deliberately left out. They are
not lost either: render only ever touches the records it knows about,
so their lines survive in the document and are read back normally by the
next parse.
Sourcepub fn parse(path: &Path, text: &str) -> Result<Self>
pub fn parse(path: &Path, text: &str) -> Result<Self>
Parse the registry out of its host document. path picks the carrier
(a config extension means the whole file is metadata; anything else is
searched for a fenced block); the records are read from the metadata’s
registry key. A host with no registry key is an empty registry —
the rest of its metadata is left alone.
Sourcepub fn render(&mut self) -> Result<String>
pub fn render(&mut self) -> Result<String>
Render the host document with the current records applied to its
registry key. Each changed record is a scalar upsert
(registry.<id> = path / null), so everything else in the host —
title, part_of, comments, body, fences, existing record lines — is
untouched, whatever the carrier. Records never reorder; new ones land
in ID order.
Sourcepub fn is_dirty(&self) -> bool
pub fn is_dirty(&self) -> bool
Whether the registry changed since it was parsed/created (i.e. needs a
write). Cleared by mark_clean.
Sourcepub fn mark_clean(&mut self)
pub fn mark_clean(&mut self)
Mark the registry as persisted.
Sourcepub fn is_tombstoned(&self, id: &Id) -> bool
pub fn is_tombstoned(&self, id: &Id) -> bool
Whether id is retired: known but no longer resolving.
Trait Implementations§
Source§impl IdIndex for FileIndex
impl IdIndex for FileIndex
Source§fn is_known(&self, id: &Id) -> bool
fn is_known(&self, id: &Id) -> bool
A tombstoned id no longer resolves but stays known forever, so it can never be reminted to mean something else.
Source§impl IndexStore for FileIndex
impl IndexStore for FileIndex
Source§fn register(&mut self, id: &Id, path: &Path)
fn register(&mut self, id: &Id, path: &Path)
Registering an id retires its tombstone, because the id is live
again and a record cannot be both. This is not a hypothetical pairing:
restore from the recycle bin re-registers the very id recycle
tombstoned, so the sequence runs whenever a delete is undone.
render has always resolved the two in this direction —
it lays the live records down over the tombstones — so without this the
store disagrees with its own serialization until the process restarts,
and a round trip through the registry document silently “changes” it.
Nothing is lost by forgetting the tombstone: is_known stays true
through resolve while the id is live, so mint-by-rejection cannot
reissue it, and retiring it again tombstones it again.
Registering maintains the tombstone set in both directions, which is
what makes “an ID is never reissued” (DESIGN §10) true of this store
rather than merely intended.
Retires whatever it displaces. Taking a path out from under the id
currently carrying it evicts that id from the live map — the bijection
repair InMemoryIndex::register performs and documents. Eviction alone
would forget the id entirely, so is_known
would go from true to false and a later mint could reissue it while the
displaced document still spells it in its own frontmatter. Reaching that
needs a displacement to slip past registration_conflict /
move_conflict, which is precisely the case this store is the last line
of defence for, so the displaced id earns a tombstone on the way out.
Un-retires what it registers. An id being registered is live, and a
record cannot be both live and retired. This runs whenever a delete is
undone: restore re-registers the very id recycle tombstoned.
render has always resolved the pair this way — it lays
the live records over the tombstones — so without this the store
disagrees with its own serialization until the process restarts. Nothing
is lost by forgetting the tombstone, because the id is is_known through
resolve while it is live, and the clause above tombstones it again if it
is ever displaced.
The two clauses only work together. Un-retiring without retiring the displaced would make the forgetting easier to reach: an id restored from the bin and then displaced would have no tombstone left to fall back on.
Source§fn set_path(&mut self, id: &Id, new_path: &Path)
fn set_path(&mut self, id: &Id, new_path: &Path)
Moving an id onto a path is registering it there — the same
bijection-safe eviction in both directions — so this delegates rather
than restating it, exactly as InMemoryIndex::set_path delegates to
its own register.
Source§fn unregister(&mut self, id: &Id)
fn unregister(&mut self, id: &Id)
Retire to a tombstone: the ID stops resolving but stays known forever.
Source§fn pending_write(&mut self) -> Result<Option<(PathBuf, String)>>
fn pending_write(&mut self) -> Result<Option<(PathBuf, String)>>
The registry’s write, rendered against its host document. None — and
crucially still dirty — when there is no host to write to.
Source§fn checkpoint(&mut self)
fn checkpoint(&mut self)
Source§fn rollback(&mut self)
fn rollback(&mut self)
checkpoint — the mutation
failed and its writes were unwound, so the in-memory store must forget it
too, or it would claim a move that never happened.