pub struct WorkspaceView { /* private fields */ }Expand description
One workspace, opened for reading, with everything an editor resolves once.
Implementations§
Source§impl WorkspaceView
impl WorkspaceView
Sourcepub fn discover(from: &Path) -> Result<Option<Self>, SessionError>
pub fn discover(from: &Path) -> Result<Option<Self>, SessionError>
Find the workspace from belongs to and open it for reading.
from may be a file or a directory; a file’s directory is where the walk
up starts. Ok(None) when no ancestor holds a root document, which is
the ordinary state of a markdown file that is simply a markdown file —
not an error, and a frontend should carry on without a workspace rather
than refuse to open it.
A directory holding two root candidates and no index/readme to break
the tie is an error: prov will not guess which is the root, and neither
should this.
Examples found in repository?
7fn main() {
8 let path = std::path::PathBuf::from(std::env::args().nth(1).expect("a document"));
9 let view = WorkspaceView::discover(&path).expect("discovery");
10 match &view {
11 Some(v) => println!("workspace: {}", v.root_dir().display()),
12 None => println!("workspace: none"),
13 }
14 let facets = view
15 .as_ref()
16 .map(|v| v.facets().clone())
17 .unwrap_or_default();
18 let schema = view.as_ref().map(|v| v.schema_for(&path));
19 let session =
20 DocumentSession::open_managed(&path, schema, facets.managed_key_names()).expect("open");
21
22 println!("\nkeys:");
23 for (key, facet) in facets.classify(session.meta()) {
24 let flags = [
25 facet.structural().then_some("structural"),
26 facet.managed().then_some("managed"),
27 ]
28 .into_iter()
29 .flatten()
30 .collect::<Vec<_>>()
31 .join(",");
32 println!(" {key:<14} {:<9} {flags}", facet.kind());
33 }
34
35 println!("\nlinks:");
36 for link in links_in(session.meta(), &facets) {
37 let landing = match &view {
38 Some(v) => v.resolve(&path, &link),
39 None => resolve_without_workspace(&path, &link),
40 };
41 let mark = if matches!(landing, Destination::Document { exists: true, .. }) {
42 "→"
43 } else {
44 "·"
45 };
46 println!(
47 " {mark} {:<10} {:<22} {}",
48 link.relation.name,
49 link.display(),
50 landing.describe()
51 );
52 }
53}Sourcepub fn open(
root_dir: impl Into<PathBuf>,
root_doc: impl Into<PathBuf>,
config: WorkspaceConfig,
) -> Result<Self, SessionError>
pub fn open( root_dir: impl Into<PathBuf>, root_doc: impl Into<PathBuf>, config: WorkspaceConfig, ) -> Result<Self, SessionError>
Open a workspace whose root and config are already known — the path a
caller that did its own discovery takes, and what
discover calls.
Sourcepub fn root_dir(&self) -> &Path
pub fn root_dir(&self) -> &Path
The workspace root directory — the absolute path every relative one here is joined to.
Examples found in repository?
7fn main() {
8 let path = std::path::PathBuf::from(std::env::args().nth(1).expect("a document"));
9 let view = WorkspaceView::discover(&path).expect("discovery");
10 match &view {
11 Some(v) => println!("workspace: {}", v.root_dir().display()),
12 None => println!("workspace: none"),
13 }
14 let facets = view
15 .as_ref()
16 .map(|v| v.facets().clone())
17 .unwrap_or_default();
18 let schema = view.as_ref().map(|v| v.schema_for(&path));
19 let session =
20 DocumentSession::open_managed(&path, schema, facets.managed_key_names()).expect("open");
21
22 println!("\nkeys:");
23 for (key, facet) in facets.classify(session.meta()) {
24 let flags = [
25 facet.structural().then_some("structural"),
26 facet.managed().then_some("managed"),
27 ]
28 .into_iter()
29 .flatten()
30 .collect::<Vec<_>>()
31 .join(",");
32 println!(" {key:<14} {:<9} {flags}", facet.kind());
33 }
34
35 println!("\nlinks:");
36 for link in links_in(session.meta(), &facets) {
37 let landing = match &view {
38 Some(v) => v.resolve(&path, &link),
39 None => resolve_without_workspace(&path, &link),
40 };
41 let mark = if matches!(landing, Destination::Document { exists: true, .. }) {
42 "→"
43 } else {
44 "·"
45 };
46 println!(
47 " {mark} {:<10} {:<22} {}",
48 link.relation.name,
49 link.display(),
50 landing.describe()
51 );
52 }
53}Sourcepub fn root_document(&self) -> PathBuf
pub fn root_document(&self) -> PathBuf
The root document, absolute.
Sourcepub fn config_document(&self) -> Option<PathBuf>
pub fn config_document(&self) -> Option<PathBuf>
The config document the root points at, absolute, when there is one. A
workspace that keeps all its policy in the root’s prov: block has none.
Sourcepub fn config(&self) -> &WorkspaceConfig
pub fn config(&self) -> &WorkspaceConfig
The effective config — defaults, overlaid by the root’s prov: block,
overlaid by the config document.
Sourcepub fn facets(&self) -> &Facets
pub fn facets(&self) -> &Facets
The classification this workspace’s vocabulary implies. See
crate::facets for why nothing acts on it.
Examples found in repository?
7fn main() {
8 let path = std::path::PathBuf::from(std::env::args().nth(1).expect("a document"));
9 let view = WorkspaceView::discover(&path).expect("discovery");
10 match &view {
11 Some(v) => println!("workspace: {}", v.root_dir().display()),
12 None => println!("workspace: none"),
13 }
14 let facets = view
15 .as_ref()
16 .map(|v| v.facets().clone())
17 .unwrap_or_default();
18 let schema = view.as_ref().map(|v| v.schema_for(&path));
19 let session =
20 DocumentSession::open_managed(&path, schema, facets.managed_key_names()).expect("open");
21
22 println!("\nkeys:");
23 for (key, facet) in facets.classify(session.meta()) {
24 let flags = [
25 facet.structural().then_some("structural"),
26 facet.managed().then_some("managed"),
27 ]
28 .into_iter()
29 .flatten()
30 .collect::<Vec<_>>()
31 .join(",");
32 println!(" {key:<14} {:<9} {flags}", facet.kind());
33 }
34
35 println!("\nlinks:");
36 for link in links_in(session.meta(), &facets) {
37 let landing = match &view {
38 Some(v) => v.resolve(&path, &link),
39 None => resolve_without_workspace(&path, &link),
40 };
41 let mark = if matches!(landing, Destination::Document { exists: true, .. }) {
42 "→"
43 } else {
44 "·"
45 };
46 println!(
47 " {mark} {:<10} {:<22} {}",
48 link.relation.name,
49 link.display(),
50 landing.describe()
51 );
52 }
53}Sourcepub fn vocabularies(&self) -> &BTreeMap<String, Vocabulary>
pub fn vocabularies(&self) -> &BTreeMap<String, Vocabulary>
The vocabularies the controlled fields point at, keyed by field name. A vocabulary that failed to load is simply absent.
Sourcepub fn prov(&self) -> &Workspace<StdFs, NoIdentity, FileIndex>
pub fn prov(&self) -> &Workspace<StdFs, NoIdentity, FileIndex>
prov’s read surface, for a frontend that wants more of it than this view exposes — a tree, a census, a title index.
Sourcepub fn content_schema(&self) -> Schema
pub fn content_schema(&self) -> Schema
The schema a content document in this workspace is edited under.
Sourcepub fn schema_for(&self, path: &Path) -> Schema
pub fn schema_for(&self, path: &Path) -> Schema
The schema path is edited under: the config-document schema for the
document that is this workspace’s config, and the content schema for
everything else.
A fact about which document this is, not a preference. prov.yaml is a
document whose keys are policy, and editing it under the content schema
would offer term pickers for fields it does not have and none for the
ones it does.
Examples found in repository?
7fn main() {
8 let path = std::path::PathBuf::from(std::env::args().nth(1).expect("a document"));
9 let view = WorkspaceView::discover(&path).expect("discovery");
10 match &view {
11 Some(v) => println!("workspace: {}", v.root_dir().display()),
12 None => println!("workspace: none"),
13 }
14 let facets = view
15 .as_ref()
16 .map(|v| v.facets().clone())
17 .unwrap_or_default();
18 let schema = view.as_ref().map(|v| v.schema_for(&path));
19 let session =
20 DocumentSession::open_managed(&path, schema, facets.managed_key_names()).expect("open");
21
22 println!("\nkeys:");
23 for (key, facet) in facets.classify(session.meta()) {
24 let flags = [
25 facet.structural().then_some("structural"),
26 facet.managed().then_some("managed"),
27 ]
28 .into_iter()
29 .flatten()
30 .collect::<Vec<_>>()
31 .join(",");
32 println!(" {key:<14} {:<9} {flags}", facet.kind());
33 }
34
35 println!("\nlinks:");
36 for link in links_in(session.meta(), &facets) {
37 let landing = match &view {
38 Some(v) => v.resolve(&path, &link),
39 None => resolve_without_workspace(&path, &link),
40 };
41 let mark = if matches!(landing, Destination::Document { exists: true, .. }) {
42 "→"
43 } else {
44 "·"
45 };
46 println!(
47 " {mark} {:<10} {:<22} {}",
48 link.relation.name,
49 link.display(),
50 landing.describe()
51 );
52 }
53}Sourcepub fn open_document(
&self,
path: impl AsRef<Path>,
) -> Result<DocumentSession, SessionError>
pub fn open_document( &self, path: impl AsRef<Path>, ) -> Result<DocumentSession, SessionError>
Open a document in this workspace as a DocumentSession, under the
schema schema_for picks.
The schema and nothing else — no keys are made read-only and no rows
are sunk, because both of those are the frontend’s to decide (see
crate::facets). Most editors want at least the first, which is three
lines rather than one:
let facets = view.facets();
let schema = Some(view.schema_for(&path));
let mut session = DocumentSession::open_managed(&path, schema, facets.managed_key_names())?;
session.metadata_mut().set_demoted(facets.structural_keys(session.meta()));Sourcepub fn resolve(&self, doc: &Path, link: &MetaLink) -> Destination
pub fn resolve(&self, doc: &Path, link: &MetaLink) -> Destination
Resolve a link written in the document at doc (absolute or
workspace-relative).
Path targets and id: handles resolve; a nominal ([[My File]]) target
does not, because resolving one needs a title index over the whole
workspace and that is a scan an editor should not do behind a keystroke.
Use resolve_nominal to pay for it deliberately.
Examples found in repository?
7fn main() {
8 let path = std::path::PathBuf::from(std::env::args().nth(1).expect("a document"));
9 let view = WorkspaceView::discover(&path).expect("discovery");
10 match &view {
11 Some(v) => println!("workspace: {}", v.root_dir().display()),
12 None => println!("workspace: none"),
13 }
14 let facets = view
15 .as_ref()
16 .map(|v| v.facets().clone())
17 .unwrap_or_default();
18 let schema = view.as_ref().map(|v| v.schema_for(&path));
19 let session =
20 DocumentSession::open_managed(&path, schema, facets.managed_key_names()).expect("open");
21
22 println!("\nkeys:");
23 for (key, facet) in facets.classify(session.meta()) {
24 let flags = [
25 facet.structural().then_some("structural"),
26 facet.managed().then_some("managed"),
27 ]
28 .into_iter()
29 .flatten()
30 .collect::<Vec<_>>()
31 .join(",");
32 println!(" {key:<14} {:<9} {flags}", facet.kind());
33 }
34
35 println!("\nlinks:");
36 for link in links_in(session.meta(), &facets) {
37 let landing = match &view {
38 Some(v) => v.resolve(&path, &link),
39 None => resolve_without_workspace(&path, &link),
40 };
41 let mark = if matches!(landing, Destination::Document { exists: true, .. }) {
42 "→"
43 } else {
44 "·"
45 };
46 println!(
47 " {mark} {:<10} {:<22} {}",
48 link.relation.name,
49 link.display(),
50 landing.describe()
51 );
52 }
53}Sourcepub fn resolve_nominal(
&self,
doc: &Path,
link: &MetaLink,
) -> Result<Destination, SessionError>
pub fn resolve_nominal( &self, doc: &Path, link: &MetaLink, ) -> Result<Destination, SessionError>
resolve, also resolving nominal targets against a
title index built by walking the workspace.
Separate because of what it costs: the index is a scan from the root, and
a [[My File]] link is the only kind that needs one. A frontend that
follows links from the keyboard should call resolve
first and fall back to this only when it comes back
Unresolvable.
Sourcepub fn backlinks_to(
&self,
target: impl AsRef<Path>,
) -> Result<Vec<Backlink>, SessionError>
pub fn backlinks_to( &self, target: impl AsRef<Path>, ) -> Result<Vec<Backlink>, SessionError>
Every inbound reference to target, walked from the workspace root.
prov keeps no stored backlink index — this is the census inverted, so it is always fresh and always a walk. Worth it on demand (“what points at this?”), not on every frame.