1use serde::de::DeserializeOwned;
93
94pub struct Document<T: DeserializeOwned> {
104 pub metadata: T,
107 pub content: String,
109}
110
111pub struct YamlFrontMatter;
115
116impl YamlFrontMatter {
117 pub fn parse<T: DeserializeOwned>(
118 markdown: &str,
119 ) -> Result<Document<T>, Box<dyn std::error::Error>> {
120 let yaml = YamlFrontMatter::extract(markdown)?;
121 let metadata = serde_yaml::from_str::<T>(yaml.0.as_str())?;
122
123 Ok(Document {
124 metadata,
125 content: yaml.1,
126 })
127 }
128
129 fn extract(markdown: &str) -> Result<(String, String), Box<dyn std::error::Error>> {
130 let mut front_matter = String::default();
131 let mut sentinel = false;
132 let mut front_matter_lines = 0;
133 let lines = markdown.lines();
134
135 for line in lines.clone() {
136 front_matter_lines += 1;
137
138 if line.trim() == "---" {
139 if sentinel {
140 break;
141 }
142
143 sentinel = true;
144 continue;
145 }
146
147 if sentinel {
148 front_matter.push_str(line);
149 front_matter.push('\n');
150 }
151 }
152
153 Ok((
154 front_matter,
155 lines
156 .skip(front_matter_lines)
157 .collect::<Vec<&str>>()
158 .join("\n"),
159 ))
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use serde::{Deserialize, __private::doc};
166
167 const MARKDOWN: &'static str = r#"
168---
169title: "Installing The Rust Programming Language on Windows"
170description: "A tutorial on installing the Rust Programming Language on Windows."
171categories: [rust, tutorial, windows, install]
172date: 2021-09-13T03:48:00
173---
174
175# Installing The Rust Programming Language on Windows
176
177## Motivation
178
179In the past days I´ve been using Unix based systems to do my software
180development work, macOS and Ubuntu are both my main operative systems nowadays.
181
182But Windows is getting closer as well, as I get more involved into systems
183programming, I'm also getting into writing Rust crates which must be supported in
184different platforms, such as macOS, Linux and Windows.
185
186Currently I'm working on a crate called [local-ip-address](https://github.com/EstebanBorai/local-ip-address).
187
188The main goal of this crate is to list system's network interfaces along
189with related data such as interface name, interface family (AFINET or AFINET6 for instance),
190IP address, subnet mask and any other relevant properties.
191
192Given that every system has a particular way to gather network interfaces
193details, I decided to install Windows in my PC as a dual-boot option along with Ubuntu.
194
195This will give me first-class access to the popular Win32 API, which I'm using through [windows-rs](https://github.com/microsoft/windows-rs) crate.
196
197After having Windows up and running, I'm also installing Rust on Windows and I'm documenting
198it for future references.
199"#;
200
201 const FRONT_MATTER: &'static str = r#"title: "Installing The Rust Programming Language on Windows"
202description: "A tutorial on installing the Rust Programming Language on Windows."
203categories: [rust, tutorial, windows, install]
204date: 2021-09-13T03:48:00
205"#;
206
207 const CONTENT: &'static str = r#"
208# Installing The Rust Programming Language on Windows
209
210## Motivation
211
212In the past days I´ve been using Unix based systems to do my software
213development work, macOS and Ubuntu are both my main operative systems nowadays.
214
215But Windows is getting closer as well, as I get more involved into systems
216programming, I'm also getting into writing Rust crates which must be supported in
217different platforms, such as macOS, Linux and Windows.
218
219Currently I'm working on a crate called [local-ip-address](https://github.com/EstebanBorai/local-ip-address).
220
221The main goal of this crate is to list system's network interfaces along
222with related data such as interface name, interface family (AFINET or AFINET6 for instance),
223IP address, subnet mask and any other relevant properties.
224
225Given that every system has a particular way to gather network interfaces
226details, I decided to install Windows in my PC as a dual-boot option along with Ubuntu.
227
228This will give me first-class access to the popular Win32 API, which I'm using through [windows-rs](https://github.com/microsoft/windows-rs) crate.
229
230After having Windows up and running, I'm also installing Rust on Windows and I'm documenting
231it for future references."#;
232
233 #[derive(Deserialize)]
234 struct Metadata {
235 title: String,
236 description: String,
237 categories: Vec<String>,
238 date: String,
239 }
240
241 #[test]
242 fn retrieve_markdown_front_matter() {
243 let (front_matter, _) = super::YamlFrontMatter::extract(MARKDOWN).unwrap();
244
245 assert_eq!(front_matter, FRONT_MATTER);
246 }
247
248 #[test]
249 fn retrieve_markdown_content() {
250 let (_, content) = super::YamlFrontMatter::extract(MARKDOWN).unwrap();
251
252 assert_eq!(content, CONTENT);
253 }
254
255 #[test]
256 fn parses_markdown_into_document() {
257 let document = super::YamlFrontMatter::parse::<Metadata>(MARKDOWN).unwrap();
258 let metadata = document.metadata;
259
260 assert_eq!(
261 metadata.title,
262 "Installing The Rust Programming Language on Windows"
263 );
264 assert_eq!(
265 metadata.description,
266 "A tutorial on installing the Rust Programming Language on Windows."
267 );
268 assert_eq!(
269 metadata.categories,
270 vec!["rust", "tutorial", "windows", "install"]
271 );
272 assert_eq!(metadata.date, "2021-09-13T03:48:00");
273 }
274}