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}