obsidian_export/
frontmatter.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
use serde_yaml::Result;

/// YAML front matter from an Obsidian note.
///
/// This is essentially an alias of [`serde_yaml::Mapping`] so all the methods available on that
/// type are available with `Frontmatter` as well.
///
/// # Examples
///
/// ```
/// # use obsidian_export::Frontmatter;
/// use serde_yaml::Value;
///
/// let mut frontmatter = Frontmatter::new();
/// let key = Value::String("foo".to_string());
///
/// frontmatter.insert(key.clone(), Value::String("bar".to_string()));
///
/// assert_eq!(
///     frontmatter.get(&key),
///     Some(&Value::String("bar".to_string())),
/// )
/// ```
pub type Frontmatter = serde_yaml::Mapping;

// Would be nice to rename this to just from_str, but that would be a breaking change.
#[allow(clippy::module_name_repetitions)]
pub fn frontmatter_from_str(mut s: &str) -> Result<Frontmatter> {
    if s.is_empty() {
        s = "{}";
    }
    let frontmatter: Frontmatter = serde_yaml::from_str(s)?;
    Ok(frontmatter)
}

// Would be nice to rename this to just to_str, but that would be a breaking change.
#[allow(clippy::module_name_repetitions)]
pub fn frontmatter_to_str(frontmatter: &Frontmatter) -> Result<String> {
    if frontmatter.is_empty() {
        return Ok("---\n---\n".to_owned());
    }

    let mut buffer = String::new();
    buffer.push_str("---\n");
    buffer.push_str(&serde_yaml::to_string(&frontmatter)?);
    buffer.push_str("---\n");
    Ok(buffer)
}

/// Available strategies for the inclusion of frontmatter in notes.
#[derive(Debug, Clone, Copy)]
// Would be nice to rename this to just Strategy, but that would be a breaking change.
#[allow(clippy::module_name_repetitions)]
#[non_exhaustive]
pub enum FrontmatterStrategy {
    /// Copy frontmatter when a note has frontmatter defined.
    Auto,
    /// Always add frontmatter header, including empty frontmatter when none was originally
    /// specified.
    Always,
    /// Never add any frontmatter to notes.
    Never,
}

#[cfg(test)]
mod tests {
    use pretty_assertions::assert_eq;
    use serde_yaml::Value;

    use super::*;

    #[test]
    fn empty_string_should_yield_empty_frontmatter() {
        assert_eq!(frontmatter_from_str("").unwrap(), Frontmatter::new());
    }

    #[test]
    fn empty_frontmatter_to_str() {
        let frontmatter = Frontmatter::new();
        assert_eq!(
            frontmatter_to_str(&frontmatter).unwrap(),
            format!("---\n---\n")
        );
    }

    #[test]
    fn nonempty_frontmatter_to_str() {
        let mut frontmatter = Frontmatter::new();
        frontmatter.insert(Value::String("foo".into()), Value::String("bar".into()));
        assert_eq!(
            frontmatter_to_str(&frontmatter).unwrap(),
            format!("---\nfoo: bar\n---\n")
        );
    }
}