Skip to main content

Graph

Struct Graph 

Source
pub struct Graph<FS, Ix> { /* private fields */ }
Expand description

A readable workspace: a root, a filesystem to read it through, an id index to resolve id: references against, and the ReadSettings that say how its links are spelled.

This is the whole of what traversal needs, and — because FS is only ever bounded by ReadStorage and Ix by IdIndex — the whole of what it can do. There is no method here that changes a byte on disk or a registration in the index, and no way to add one without changing a trait bound in this crate.

prov’s Workspace owns one of these and forwards every read to it, adding the identity policy, the change/journal machinery, and the config layer on top. A consumer that only needs to see the workspace — a language server, a renderer, a viewer — can hold a Graph directly and link none of that.

Implementations§

Source§

impl<FS: ReadStorage, Ix: IdIndex> Graph<FS, Ix>

Source

pub async fn reachable_documents( &self, start: &Path, census: &[CensusEntry], content_bodies: &[PathBuf], ) -> Result<BTreeSet<PathBuf>>

reachable_set, minus any shadowed attachment payload (attach --opaque) — the population a pass may parse as a document.

A shadowed payload is still reachable (it must not be reported as an orphan, and it is still fixity-checked through its sidecar), but its bytes are an exhibit prov promised never to interpret. That is the same bound is_shadowed_payload already holds the flat title and id scans to; this is its reachability-walk counterpart, for prov’s vocabulary_findings and prov’s fixity_findings — the two passes that load every reachable path and read its frontmatter.

The listing is_shadowed_payload needs is built the same way prov’s orphans builds one: the direct children of every directory the reachable set occupies, so a shadow check costs a set lookup per candidate extension rather than a stat.

Source

pub async fn reachable_files( &self, start: impl AsRef<Path>, ) -> Result<BTreeSet<PathBuf>>

Every file the workspace reaches from start that actually exists on disk — reachable_set over a fresh walk, filtered to real files.

This is §8’s bounded walk expressed as a file set rather than a findings list: the same population check validates. prov’s Workspace::history_capture captures it (minus prov’s two byte-parking stores) precisely so that an event is a consistent cut across everything the workspace considers its own.

prov’s Workspace::history_capture: prov’s Workspace::history_capture

Source

pub async fn reachable_files_within( &self, start: impl AsRef<Path>, parked: &[PathBuf], ) -> Result<BTreeSet<PathBuf>>

reachable_files, told which directories are parked — see title_index_scoped.

Source

pub async fn census(&self, start: impl AsRef<Path>) -> Result<Vec<CensusEntry>>

Take a census of every forward link reachable from start: one CensusEntry per frontmatter relation edge and per body [[…]] wikilink, each carrying its LinkSite and Resolution.

This is the one traversal the backlink map, the integrity findings, and (via mutate) inbound-rename maintenance are all views over. Because it is read from the documents, it is ground truth: a stored backlink index heals toward the census, never the reverse.

Source

pub async fn census_within( &self, start: impl AsRef<Path>, parked: &[PathBuf], ) -> Result<Vec<CensusEntry>>

census, told which directories are parked — see title_index_scoped.

The backlink map for the workspace reachable from start: every resolved target to the inbound references (Backlinks) that reach it, path- and id-form alike. This is the census inverted — recomputed from the documents, so it is always fresh (the Route-N “reconcile-on-load”: no stored index to drift). Each target’s backlinks are sorted by source.

The inbound references to a single target (workspace-relative) reachable from start, sorted by source. The focused form of backlinks for “who links here?”.

Source

pub async fn walk(&self, start: &Path, parked: &[PathBuf]) -> Result<Walk>

The shared spanning-tree walk: gathers the forward-link census and the structural facts (StructuralFact, which depend on traversal state, not on a single link’s resolution) in one pass. Frontmatter edges may be spanning and so drive descent, the single-parent check, and the inverse check; body wikilinks are always overlay references — censused, never spanning.

Source§

impl<FS: ReadStorage, Ix> Graph<FS, Ix>

Source

pub async fn load(&self, path: &Path) -> Result<(String, Document)>

Read and parse the workspace-relative document at path, returning the raw text alongside. The building block traversal, validation, and mutation share.

Source

pub async fn document(&self, path: impl AsRef<Path>) -> Result<Document>

Read and parse the workspace-relative document at path, returning its full Document — the public counterpart to load, for a caller walking a Node tree who needs more than Node::title (the rest of the frontmatter, the body, the carrier) without re-reading and re-parsing the file by hand.

Unlike the traversal, which degrades a bad target to a NodeKind::Unreadable node, this surfaces the Error directly — a caller who names a path expects to know why it failed, not to receive a placeholder.

Source§

impl<FS: ReadStorage, Ix: IdIndex> Graph<FS, Ix>

Source

pub async fn manifest_of( &self, node: &Path, ) -> Result<Option<(PathBuf, Manifest)>>

The manifest document node declares, loaded and parsed, with its workspace-relative path. None when node declares no manifest.

A manifest is a record store (spec §5): prov re-lays-out its rows, so a markdown carrier has no stable home for them and is refused here, at the one choke point every reader passes through.

Source

pub async fn read_manifest(&self, path: &Path) -> Result<Manifest>

Read and parse the manifest document at path itself.

Source

pub async fn manifest_claims(&self, candidate: &Path, dir: &Path) -> bool

Whether the document at candidate is a manifest node whose manifest covers the directory dir — the authoritative half of the reverse lookup below.

Unreadable, unparsable and non-manifest candidates simply do not claim: this runs inside best-effort scans, where the question is “is this directory already accounted for”, and a damaged manifest is a finding check raises rather than a reason to abort a walk.

Source

pub async fn manifest_node_for(&self, dir: &Path) -> Result<Option<PathBuf>>

The node covering the directory dir, or None when nothing does — the counterpart of attachment_for for a whole directory. Probes the <dir>.<ext> convention and confirms each hit through the node’s own manifest pointer.

Source

pub async fn under_manifest(&self, path: &Path) -> Result<bool>

Whether any directory on path’s way down from the workspace root is covered by a manifest — the guard the loose-attachment sweeps use so a covered directory is never offered up for ten thousand sidecars.

Walks the ancestors rather than only the immediate parent, because a manifest claims its root recursively: photos/2019/a.jpg is covered by the node beside photos/.

Probe-only, and bounded on purpose. A node renamed away from its directory leaves no local evidence beside that directory (moving the archive to keep the convention is the thing rename deliberately does not do), so this can answer “no” where a census would answer “yes”. The caller is attach, which runs per file inside --all; making each one authoritative would cost a census per file. The residue is a covered file that also gains a sidecar — duplicated bookkeeping, not a contradiction, since both records are derived from the same bytes. The operation where a wrong “no” would matter — minting a second manifest over a whole archive — asks manifest_node_covering instead and pays for the census.

Source

pub async fn scan_covered(&self, root: &Path) -> Result<Vec<PathBuf>>

The opaque payloads under the covered directory root, as paths relative to it, sorted — what a manifest is built from and compared against.

Three exclusions, each deliberate. Hidden entries are skipped, as in every other prov walk. Files prov can read (a .md note, a .yaml store) are not payloads and stay ordinary documents — a manifest covers bytes, never shadows a document. And a nested manifest’s directory is left to its own node, so two manifests never claim the same file.

Source

pub async fn manifest_roots( &self, walk_docs: &BTreeSet<PathBuf>, ) -> BTreeSet<PathBuf>

The covered roots of every manifest reachable in walk_docs — the set a scan consults to know which directories are already accounted for. Damaged manifests contribute nothing (their damage is check’s to report).

Source§

impl<FS: ReadStorage, Ix> Graph<FS, Ix>

Source

pub async fn exists(&self, path: &Path) -> Result<bool>

Whether the workspace-relative path exists. Mirrors ReadStorage::try_exists, joined to the workspace root.

Source

pub async fn read_bytes(&self, path: &Path) -> Result<Vec<u8>>

Read the entire contents of the workspace-relative path as bytes. Mirrors ReadStorage::read, joined to the workspace root.

Source

pub async fn read_text(&self, path: &Path) -> Result<String>

Read the entire contents of the workspace-relative path as a string. Mirrors ReadStorage::read_to_string, joined to the workspace root.

Source

pub async fn listing(&self, path: &Path) -> Result<Vec<DirEntry>>

List the entries of the workspace-relative directory path. Mirrors ReadStorage::read_dir, joined to the workspace root.

Source

pub async fn stat(&self, path: &Path) -> Result<Metadata>

Metadata about the entry at the workspace-relative path. Mirrors ReadStorage::metadata, joined to the workspace root.

Source§

impl<FS, Ix: IdIndex> Graph<FS, Ix>

Resolve link (declared in the document at doc) to a workspace target, without nominal (alias) resolution — path and id: targets only. Use resolve_link_with when a TitleIndex is available and [[My File]]-style aliases should resolve.

Resolve link to a workspace target. Path targets resolve relative to doc’s directory; an id:<id> target resolves through the registry (the location-independent path that stays valid across moves); an alias-shaped target (a bare name) resolves through titles when one is supplied — Unique to its path, Ambiguous to Target::AmbiguousAlias, and Unknown falling through to a path (so a nominal link to nothing surfaces as a missing/broken path, exactly as before aliases existed). With titles None, alias resolution is off and this is the pure path/id resolver.

Source§

impl<FS: ReadStorage, Ix: IdIndex> Graph<FS, Ix>

Source

pub async fn title_index(&self) -> Result<TitleIndex>

Build the workspace’s TitleIndex by scanning every document under the root and registering it under its title and its file stem. This is a derived cache (DESIGN §5): rebuilt on demand, never persisted. It is what makes nominal ([[My File]]) references resolvable — a flat filesystem scan, deliberately independent of link resolution so that alias links can themselves be spanning (contents: alias) without a chicken-and-egg between “walk the tree” and “resolve the walk’s links.”

Source

pub async fn title_index_scoped( &self, start: &Path, parked: &[PathBuf], ) -> Result<TitleIndex>

The title index bounded to the directories the workspace reaches from start (DESIGN §8) — the reachability-scoped counterpart to title_index. Only documents in a directory some link path/id-reaches are indexed, so a [[alias]] resolves within the workspace without scanning target/, a vendored tree, or a nested workspace at the repo root.

Falls back to the full title_index when the spanning relation is addressed by alias: descending the tree then needs every title up front, so the scan cannot be bounded (the chicken-and-egg the flat scan was written to avoid). An overlay alias to an orphan (a doc no path/id link reaches) likewise falls outside the scope and reads as broken — which it effectively is. parked names the directories whose interiors are prov’s own bookkeeping — a history store’s events and blobs, the recycle bin’s items. They are reached like anything else (the root points at each store’s index document) but a title found inside one is not a place a reader can go, so indexing it would let [[Some Note]] resolve to a deleted copy or an old version — silently, since neither is anywhere the reader can see. The caller supplies them because which directories those are is a question about prov’s storage layout, and this crate has no opinion about it.

Source

pub async fn scan_ids(&self) -> Result<Vec<(Id, PathBuf)>>

Scan every document under the root for a self-stored id frontmatter field, returning the (id, path) pairs — the rebuildable id→path map for the frontmatter-only identity storage mode (IdStorage::FrontmatterOnly). Like title_index this is a flat filesystem scan, deliberately independent of link resolution (so it can bootstrap the very index that id links resolve through, with no chicken-and-egg).

Source

pub async fn content_documents(&self) -> Result<Vec<PathBuf>>

Every content document (Markdown/Djot/HTML) under the root, as sorted workspace-relative paths — the on-disk population the orphan check diffs against what the spanning tree reaches (DESIGN §8). Deliberately restricted to content documents: whole-file metadata sidecars (a config or registry document, a stray .yaml) are not prose a user orphans, so they are not candidates. A flat filesystem scan (hidden entries skipped), independent of link resolution, like the title/id scans beside it.

Source

pub async fn direct_child_files( &self, dirs: &BTreeSet<PathBuf>, ) -> Result<Vec<PathBuf>>

The workspace-relative direct-child files of each directory in dirs (non-recursive), skipping hidden entries and unreadable directories.

The bounded-scan primitive behind reachability-scoped discovery (DESIGN §8): it opens only the directories it is handed and never descends into subdirectories, so an unreached directory — a vendored tree, a nested prov workspace — is neither read nor reported. Callers filter the result for the file kind they care about (content documents for the orphan check, opaque payloads for attach --all).

Source

pub fn reached_dirs(reachable: &BTreeSet<PathBuf>) -> BTreeSet<PathBuf>

The directories the reachable set reachable occupies — each reached document’s own directory (the workspace root’s directory always among them, since the root document is reachable). The scope direct_child_files is bounded to: a directory is “known” precisely when a linked document lives directly in it.

Source§

impl<FS: ReadStorage, Ix: IdIndex> Graph<FS, Ix>

Source

pub async fn sidecar_claims(&self, candidate: &Path, payload: &Path) -> bool

Whether the document at candidate is an attachment sidecar whose content resolves to payload — the authoritative half of the reverse lookup, the <payload>.<ext> convention above being only the probe.

Requires is_attachment, so a separated prose node never reads as one: its body is a document in its own right, and prov must keep scanning it. Unreadable or unparsable candidates simply do not claim (this runs inside best-effort scans).

Source

pub async fn is_shadowed_payload( &self, path: &Path, listing: &BTreeSet<PathBuf>, ) -> bool

Whether path — a file prov can read — has been deliberately shadowed: claimed as an opaque payload by an attachment sidecar beside it. The promise attach --opaque makes, enforced: prov links, moves and fixity- checks the file (through its sidecar’s own content_hash) but never reads it as a document, so its title stays out of the title index, any id it shows stays out of the registry, any fields value it carries is never checked against a vocabulary, and any content_hash it shows is never treated as its own.

listing is the set of workspace-relative files the calling scan already enumerated (its directory read), so a shadow check costs a set lookup rather than a stat per metadata extension — this runs per file in the flat title and id scans, and per reachable path in the vocabulary and fixity passes (validate::Workspace::reachable_documents). A sidecar outside the listing therefore does not shadow, which is the same bound the scans themselves observe.

Source§

impl<FS: ReadStorage, Ix: IdIndex> Graph<FS, Ix>

Source

pub async fn tree(&self, start: impl AsRef<Path>) -> Result<Node>

Materialize the spanning tree rooted at start (a workspace-relative path). Missing, unreadable, cyclic, unresolved-ID, and ambiguous-alias targets become marked nodes. id:<id> targets resolve through the registry; nominal ([[My File]]) targets resolve through the title index, built once for the whole walk so spanning alias links (a contents: alias vocabulary) descend like any other.

Source

pub async fn tree_with( &self, start: impl AsRef<Path>, options: TreeOptions, ) -> Result<Node>

Materialize the spanning tree rooted at start, as tree, with TreeOptions controlling how an unresolved spanning target is represented. TreeOptions::default() is exactly tree()’s behavior.

Source

pub async fn tree_within( &self, start: impl AsRef<Path>, options: TreeOptions, parked: &[PathBuf], ) -> Result<Node>

tree_with, told which directories are parked — see title_index_scoped.

Source§

impl<FS, Ix> Graph<FS, Ix>

Source

pub fn new( fs: FS, root: impl Into<PathBuf>, index: Ix, settings: ReadSettings, ) -> Self

A graph over fs, rooted at root, resolving ids through index.

Source

pub fn fs(&self) -> &FS

The underlying filesystem.

Source

pub fn root(&self) -> &Path

The workspace root every path here is relative to.

Source

pub fn fs_path(&self, rel: impl AsRef<Path>) -> PathBuf

The absolute path of a workspace-relative one — root joined to rel.

The two path forms are deliberately kept apart: everything this crate returns (Node::path, Target::Path, a CensusEntry’s source) is workspace-relative and root-independent, so a graph can be re-rooted to a different directory without touching a single stored path. fs_path is the one place that independence is given up, for the caller that actually needs to open the file.

Source

pub fn index(&self) -> &Ix

The id index id: references resolve through.

Source

pub fn index_mut(&mut self) -> &mut Ix

The id index, mutably — for an owner that also writes to it.

Source

pub fn settings(&self) -> &ReadSettings

The settings this graph reads by.

Source

pub fn relations(&self) -> &RelationSet

The relation vocabulary — which metadata fields are links.

Source

pub fn workspace_id(&self) -> &str

What this workspace calls itself; empty means anonymous.

Source

pub fn id_storage(&self) -> IdStorage

Where a document’s stable id is persisted.

Source

pub fn read_scope(&self) -> ReadScope<'_>

Open a read scope: within it, a document read twice is parsed once. See crate::memo.

Source

pub fn memo_lock(&self) -> MutexGuard<'_, ReadMemo>

The memo itself, locked — for an owner that must forget what it wrote.

Trait Implementations§

Source§

impl<FS: Clone, Ix: Clone> Clone for Graph<FS, Ix>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<FS: Debug, Ix: Debug> Debug for Graph<FS, Ix>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<FS, Ix> !Freeze for Graph<FS, Ix>

§

impl<FS, Ix> RefUnwindSafe for Graph<FS, Ix>

§

impl<FS, Ix> Send for Graph<FS, Ix>
where FS: Send, Ix: Send,

§

impl<FS, Ix> Sync for Graph<FS, Ix>
where FS: Sync, Ix: Sync,

§

impl<FS, Ix> Unpin for Graph<FS, Ix>
where FS: Unpin, Ix: Unpin,

§

impl<FS, Ix> UnsafeUnpin for Graph<FS, Ix>
where FS: UnsafeUnpin, Ix: UnsafeUnpin,

§

impl<FS, Ix> UnwindSafe for Graph<FS, Ix>
where FS: UnwindSafe, Ix: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.