obsidian_export/context.rs
1use std::path::{Path, PathBuf};
2
3use crate::Frontmatter;
4
5#[derive(Debug, Clone)]
6/// Context holds metadata about a note which is being parsed.
7///
8/// This is used internally to keep track of nesting and help with constructing proper references
9/// to other notes.
10///
11/// It is also passed to [postprocessors][crate::Postprocessor] to provide contextual information
12/// and allow modification of a note's frontmatter.
13pub struct Context {
14 file_tree: Vec<PathBuf>,
15
16 /// The path where this note will be written to when exported.
17 ///
18 /// Changing this path will result in the note being written to that new path instead, but
19 /// beware: links will not be updated automatically. If this is changed by a
20 /// [postprocessor][crate::Postprocessor], it's up to that postprocessor to rewrite any
21 /// existing links to this new path.
22 pub destination: PathBuf,
23
24 /// The [Frontmatter] for this note. Frontmatter may be modified in-place (see
25 /// [`serde_yaml::Mapping`] for available methods) or replaced entirely.
26 ///
27 /// # Example
28 ///
29 /// Insert `foo: bar` into a note's frontmatter:
30 ///
31 /// ```
32 /// # use obsidian_export::Frontmatter;
33 /// # use obsidian_export::Context;
34 /// # use std::path::PathBuf;
35 /// use obsidian_export::serde_yaml::Value;
36 ///
37 /// # let mut context = Context::new(PathBuf::from("source"), PathBuf::from("destination"));
38 /// let key = Value::String("foo".to_string());
39 ///
40 /// context
41 /// .frontmatter
42 /// .insert(key.clone(), Value::String("bar".to_string()));
43 /// ```
44 pub frontmatter: Frontmatter,
45}
46
47impl Context {
48 /// Create a new `Context`
49 #[inline]
50 #[must_use]
51 pub fn new(src: PathBuf, dest: PathBuf) -> Self {
52 Self {
53 file_tree: vec![src],
54 destination: dest,
55 frontmatter: Frontmatter::new(),
56 }
57 }
58
59 /// Create a new `Context` which inherits from a parent Context.
60 #[inline]
61 #[must_use]
62 pub fn from_parent(context: &Self, child: &Path) -> Self {
63 let mut context = context.clone();
64 context.file_tree.push(child.to_path_buf());
65 context
66 }
67
68 /// Return the path of the file currently being parsed.
69 #[inline]
70 #[must_use]
71 pub fn current_file(&self) -> &PathBuf {
72 self.file_tree
73 .last()
74 .expect("Context not initialized properly, file_tree is empty")
75 }
76
77 /// Return the path of the root file.
78 ///
79 /// Typically this will yield the same element as `current_file`, but when a note is embedded
80 /// within another note, this will return the outer-most note.
81 #[inline]
82 #[must_use]
83 pub fn root_file(&self) -> &PathBuf {
84 self.file_tree
85 .first()
86 .expect("Context not initialized properly, file_tree is empty")
87 }
88
89 /// Return the note depth (nesting level) for this context.
90 #[inline]
91 #[must_use]
92 pub fn note_depth(&self) -> usize {
93 self.file_tree.len()
94 }
95
96 /// Return the list of files associated with this context.
97 ///
98 /// The first element corresponds to the root file, the final element corresponds to the file
99 /// which is currently being processed (see also `current_file`).
100 #[inline]
101 #[must_use]
102 pub fn file_tree(&self) -> Vec<PathBuf> {
103 self.file_tree.clone()
104 }
105}