serde_frontmatter/lib.rs
1//! This crate is a Rust library for using the [Serde](https://github.com/serde-rs/serde) serialization framework
2//! with Jekyll-style front matter.
3//!
4//! ## Examples
5//!
6//! ```
7//! use serde::{Deserialize, Serialize};
8//!
9//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
10//! pub struct MyData {
11//! pub title: String
12//! }
13//!
14//! fn main() {
15//! // Serialize
16//! let front_matter = MyData { title: "Hello, World!".to_string() };
17//! let content = "This is some content";
18//! let output = serde_frontmatter::serialize(front_matter, content).unwrap();
19//! assert_eq!("---\ntitle: \"Hello, World!\"\n\n---\nThis is some content", output);
20//!
21//! // Deserialize
22//! let input = "---\ntitle: Hello, World!\n---\nThis is some content";
23//! let (front_matter, content) = serde_frontmatter::deserialize::<MyData>(input).unwrap();
24//! assert_eq!(front_matter, MyData { title: "Hello, World!".to_string() });
25//! assert_eq!(content, "\nThis is some content");
26//! }
27//! ```
28
29use serde::de::DeserializeOwned;
30use serde::Serialize;
31
32/// Errors generated by this crate
33#[derive(Debug)]
34pub enum SerdeFMError {
35 /// Errors originating from serde-yaml
36 YamlParseError(serde_yaml::Error),
37
38 /// Front matter could not be found
39 MissingFrontMatter,
40}
41
42impl From<serde_yaml::Error> for SerdeFMError {
43 fn from(e: serde_yaml::Error) -> SerdeFMError {
44 Self::YamlParseError(e)
45 }
46}
47
48/// Deserialize a string containing front matter into a struct and the content of the string
49pub fn deserialize<T: DeserializeOwned>(data: &str) -> Result<(T, String), SerdeFMError> {
50 // We need frontmatter to be the first thing in the file
51 if !data.starts_with("---") {
52 return Err(SerdeFMError::MissingFrontMatter);
53 }
54
55 // Use split to find the frontmatter content
56 let split_data = data.split("---").map(Into::into).collect::<Vec<String>>();
57
58 // Use the second item in the vector as the frontmatter
59 let frontmatter = match split_data.get(1) {
60 Some(fm) => Ok(fm),
61 None => Err(SerdeFMError::MissingFrontMatter),
62 }?;
63 let content = match split_data.get(2) {
64 Some(content) => content.clone(),
65 None => String::new(),
66 };
67
68 // Parse the frontmatter
69 Ok((serde_yaml::from_str(frontmatter.as_ref())?, content))
70}
71
72/// Serialize a struct and data into a string containing front matter
73pub fn serialize<T: Serialize>(front_matter: T, content: &str) -> Result<String, SerdeFMError> {
74 // Serialize the frontmatter
75 let frontmatter = serde_yaml::to_string(&front_matter)?;
76
77 // Return the result
78 Ok(format!("{}\n---\n{}", frontmatter, content))
79}
80
81#[cfg(test)]
82mod tests {
83 //! These test cases are stolen from: https://github.com/azdle/rust-frontmatter/blob/master/src/lib.rs
84
85 use super::*;
86 use serde::{Deserialize, Serialize};
87
88 #[derive(Serialize, Deserialize)]
89 pub struct FM {
90 pub title: String,
91 }
92
93 #[test]
94 fn test_valid() {
95 let test_string = "---\ntitle: Valid Yaml Test\n---\nsomething that's not yaml";
96
97 let (matter, content) = deserialize::<FM>(&test_string).unwrap();
98 assert_eq!(matter.title, "Valid Yaml Test");
99 assert_eq!(content, "\nsomething that's not yaml");
100 }
101
102 #[test]
103 fn test_invalid() {
104 let test_string = "something that's not yaml even if it has\n---\nsome: yaml\n--";
105
106 let result = deserialize::<FM>(&test_string);
107 assert!(result.is_err());
108 }
109}