serde-frontmatter 0.1.0

Serde support for Jekyll front matter
Documentation
//! This crate is a Rust library for using the [Serde](https://github.com/serde-rs/serde) serialization framework
//! with Jekyll-style front matter.
//!
//! ## Examples
//!
//! ```
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
//! pub struct MyData {
//!     pub title: String
//! }
//!
//! fn main() {
//!    // Serialize
//!    let front_matter = MyData { title: "Hello, World!".to_string() };
//!    let content = "This is some content";
//!    let output = serde_frontmatter::serialize(front_matter, content).unwrap();
//!    assert_eq!("---\ntitle: \"Hello, World!\"\n\n---\nThis is some content", output);
//!
//!    // Deserialize
//!    let input = "---\ntitle: Hello, World!\n---\nThis is some content";
//!    let (front_matter, content) = serde_frontmatter::deserialize::<MyData>(input).unwrap();
//!    assert_eq!(front_matter, MyData { title: "Hello, World!".to_string() });
//!    assert_eq!(content, "\nThis is some content");
//! }
//! ```

use serde::de::DeserializeOwned;
use serde::Serialize;

/// Errors generated by this crate
#[derive(Debug)]
pub enum SerdeFMError {
    /// Errors originating from serde-yaml
    YamlParseError(serde_yaml::Error),

    /// Front matter could not be found
    MissingFrontMatter,
}

impl From<serde_yaml::Error> for SerdeFMError {
    fn from(e: serde_yaml::Error) -> SerdeFMError {
        Self::YamlParseError(e)
    }
}

/// Deserialize a string containing front matter into a struct and the content of the string
pub fn deserialize<T: DeserializeOwned>(data: &str) -> Result<(T, String), SerdeFMError> {
    // We need frontmatter to be the first thing in the file
    if !data.starts_with("---") {
        return Err(SerdeFMError::MissingFrontMatter);
    }

    // Use split to find the frontmatter content
    let split_data = data.split("---").map(Into::into).collect::<Vec<String>>();

    // Use the second item in the vector as the frontmatter
    let frontmatter = match split_data.get(1) {
        Some(fm) => Ok(fm),
        None => Err(SerdeFMError::MissingFrontMatter),
    }?;
    let content = match split_data.get(2) {
        Some(content) => content.clone(),
        None => String::new(),
    };

    // Parse the frontmatter
    Ok((serde_yaml::from_str(frontmatter.as_ref())?, content))
}

/// Serialize a struct and data into a string containing front matter
pub fn serialize<T: Serialize>(front_matter: T, content: &str) -> Result<String, SerdeFMError> {
    // Serialize the frontmatter
    let frontmatter = serde_yaml::to_string(&front_matter)?;

    // Return the result
    Ok(format!("{}\n---\n{}", frontmatter, content))
}

#[cfg(test)]
mod tests {
    //! These test cases are stolen from: https://github.com/azdle/rust-frontmatter/blob/master/src/lib.rs

    use super::*;
    use serde::{Deserialize, Serialize};

    #[derive(Serialize, Deserialize)]
    pub struct FM {
        pub title: String,
    }

    #[test]
    fn test_valid() {
        let test_string = "---\ntitle: Valid Yaml Test\n---\nsomething that's not yaml";

        let (matter, content) = deserialize::<FM>(&test_string).unwrap();
        assert_eq!(matter.title, "Valid Yaml Test");
        assert_eq!(content, "\nsomething that's not yaml");
    }

    #[test]
    fn test_invalid() {
        let test_string = "something that's not yaml even if it has\n---\nsome: yaml\n--";

        let result = deserialize::<FM>(&test_string);
        assert!(result.is_err());
    }
}