aboutsummaryrefslogtreecommitdiff
path: root/src/post/frontmatter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/post/frontmatter.rs')
-rw-r--r--src/post/frontmatter.rs114
1 files changed, 114 insertions, 0 deletions
diff --git a/src/post/frontmatter.rs b/src/post/frontmatter.rs
new file mode 100644
index 0000000..1cc8032
--- /dev/null
+++ b/src/post/frontmatter.rs
@@ -0,0 +1,114 @@
+/// This code was borrowed from @fasterthanlime.
+
+use anyhow::{Result};
+use serde::{Serialize, Deserialize};
+
+#[derive(Eq, PartialEq, Deserialize, Default, Debug, Serialize, Clone)]
+pub struct Data {
+ pub title: String,
+ pub date: String,
+ pub series: Option<String>,
+ pub tags: Option<Vec<String>>,
+ pub slides_link: Option<String>,
+ pub image: Option<String>,
+ pub thumb: Option<String>,
+ pub show: Option<bool>,
+}
+
+enum State {
+ SearchForStart,
+ ReadingMarker { count: usize, end: bool },
+ ReadingFrontMatter { buf: String, line_start: bool },
+ SkipNewline { end: bool },
+}
+
+#[derive(Debug, thiserror::Error)]
+enum Error {
+ #[error("EOF while parsing frontmatter")]
+ EOF,
+ #[error("Error parsing yaml: {0:?}")]
+ Yaml(#[from] serde_yaml::Error),
+}
+
+impl Data {
+ pub fn parse(input: &str) -> Result<(Data, usize)> {
+ let mut state = State::SearchForStart;
+
+ let mut payload = None;
+ let offset;
+
+ let mut chars = input.char_indices();
+ 'parse: loop {
+ let (idx, ch) = match chars.next() {
+ Some(x) => x,
+ None => return Err(Error::EOF)?,
+ };
+ match &mut state {
+ State::SearchForStart => match ch {
+ '-' => {
+ state = State::ReadingMarker {
+ count: 1,
+ end: false,
+ };
+ }
+ '\n' | '\t' | ' ' => {
+ // ignore whitespace
+ }
+ _ => {
+ panic!("Start of frontmatter not found");
+ }
+ },
+ State::ReadingMarker { count, end } => match ch {
+ '-' => {
+ *count += 1;
+ if *count == 3 {
+ state = State::SkipNewline { end: *end };
+ }
+ }
+ _ => {
+ panic!("Malformed frontmatter marker");
+ }
+ },
+ State::SkipNewline { end } => match ch {
+ '\n' => {
+ if *end {
+ offset = idx + 1;
+ break 'parse;
+ } else {
+ state = State::ReadingFrontMatter {
+ buf: String::new(),
+ line_start: true,
+ };
+ }
+ }
+ _ => panic!("Expected newline, got {:?}",),
+ },
+ State::ReadingFrontMatter { buf, line_start } => match ch {
+ '-' if *line_start => {
+ let mut state_temp = State::ReadingMarker {
+ count: 1,
+ end: true,
+ };
+ std::mem::swap(&mut state, &mut state_temp);
+ if let State::ReadingFrontMatter { buf, .. } = state_temp {
+ payload = Some(buf);
+ } else {
+ unreachable!();
+ }
+ }
+ ch => {
+ buf.push(ch);
+ *line_start = ch == '\n';
+ }
+ },
+ }
+ }
+
+ // unwrap justification: option set in state machine, Rust can't statically analyze it
+ let payload = payload.unwrap();
+
+ let fm: Self = serde_yaml::from_str(&payload)?;
+
+ Ok((fm, offset))
+ }
+}