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