obsidian_export/
frontmatter.rs

1use serde_yaml::Result;
2
3/// YAML front matter from an Obsidian note.
4///
5/// This is essentially an alias of [`serde_yaml::Mapping`] so all the methods available on that
6/// type are available with `Frontmatter` as well.
7///
8/// # Examples
9///
10/// ```
11/// # use obsidian_export::Frontmatter;
12/// use serde_yaml::Value;
13///
14/// let mut frontmatter = Frontmatter::new();
15/// let key = Value::String("foo".to_string());
16///
17/// frontmatter.insert(key.clone(), Value::String("bar".to_string()));
18///
19/// assert_eq!(
20///     frontmatter.get(&key),
21///     Some(&Value::String("bar".to_string())),
22/// )
23/// ```
24pub type Frontmatter = serde_yaml::Mapping;
25
26// Would be nice to rename this to just from_str, but that would be a breaking change.
27#[allow(clippy::module_name_repetitions)]
28pub fn frontmatter_from_str(mut s: &str) -> Result<Frontmatter> {
29    if s.is_empty() {
30        s = "{}";
31    }
32    let frontmatter: Frontmatter = serde_yaml::from_str(s)?;
33    Ok(frontmatter)
34}
35
36// Would be nice to rename this to just to_str, but that would be a breaking change.
37#[allow(clippy::module_name_repetitions)]
38pub fn frontmatter_to_str(frontmatter: &Frontmatter) -> Result<String> {
39    if frontmatter.is_empty() {
40        return Ok("---\n---\n".to_owned());
41    }
42
43    let mut buffer = String::new();
44    buffer.push_str("---\n");
45    buffer.push_str(&serde_yaml::to_string(&frontmatter)?);
46    buffer.push_str("---\n");
47    Ok(buffer)
48}
49
50/// Available strategies for the inclusion of frontmatter in notes.
51#[derive(Debug, Clone, Copy)]
52// Would be nice to rename this to just Strategy, but that would be a breaking change.
53#[allow(clippy::module_name_repetitions)]
54#[non_exhaustive]
55pub enum FrontmatterStrategy {
56    /// Copy frontmatter when a note has frontmatter defined.
57    Auto,
58    /// Always add frontmatter header, including empty frontmatter when none was originally
59    /// specified.
60    Always,
61    /// Never add any frontmatter to notes.
62    Never,
63}
64
65#[cfg(test)]
66mod tests {
67    use pretty_assertions::assert_eq;
68    use serde_yaml::Value;
69
70    use super::*;
71
72    #[test]
73    fn empty_string_should_yield_empty_frontmatter() {
74        assert_eq!(frontmatter_from_str("").unwrap(), Frontmatter::new());
75    }
76
77    #[test]
78    fn empty_frontmatter_to_str() {
79        let frontmatter = Frontmatter::new();
80        assert_eq!(
81            frontmatter_to_str(&frontmatter).unwrap(),
82            format!("---\n---\n")
83        );
84    }
85
86    #[test]
87    fn nonempty_frontmatter_to_str() {
88        let mut frontmatter = Frontmatter::new();
89        frontmatter.insert(Value::String("foo".into()), Value::String("bar".into()));
90        assert_eq!(
91            frontmatter_to_str(&frontmatter).unwrap(),
92            format!("---\nfoo: bar\n---\n")
93        );
94    }
95}