use crate::plugins::Manager;
use super::{parse_html, LocatableToken, ParseError, TracebackError};
use humphrey_json::Value;
use std::path::Path;
#[derive(Clone, Debug)]
pub struct ParsedMarkdown {
pub(crate) frontmatter: Vec<(String, String)>,
pub(crate) markdown: Vec<LocatableToken>,
pub(crate) markdown_string: String,
pub(crate) html: Option<String>,
}
pub fn parse_markdown(
input: String,
path: &Path,
plugins: Option<&dyn Manager>,
) -> Result<ParsedMarkdown, TracebackError<ParseError>> {
let (lines_to_skip, frontmatter) = if input.starts_with("---\n") || input.starts_with("---\r\n")
{
let mut dashed_lines: u8 = 0;
let mut lines_to_skip = 0;
let mut frontmatter = Vec::new();
for (i, line) in input.lines().enumerate() {
if line.starts_with("---") {
dashed_lines += 1;
if dashed_lines == 2 {
lines_to_skip = i + 1;
break;
}
continue;
}
let e = || TracebackError {
path: path.to_path_buf(),
line: i as u32 + 1,
column: 0,
kind: ParseError::InvalidFrontmatter,
};
if dashed_lines == 1 {
let mut parts = line.splitn(2, ':');
let key = parts.next().ok_or_else(e)?.trim().to_string();
let value = parts
.next()
.ok_or_else(e)?
.trim()
.strip_prefix('"')
.and_then(|v| v.strip_suffix('"'))
.ok_or_else(e)?
.to_string();
frontmatter.push((key, value));
}
}
if dashed_lines != 2 {
return Err(TracebackError {
path: path.to_path_buf(),
kind: ParseError::UnexpectedEOF,
line: input.lines().count() as u32,
column: 0,
});
}
(lines_to_skip, frontmatter)
} else {
(0, Vec::new())
};
let raw_markdown = input
.lines()
.skip(lines_to_skip)
.collect::<Vec<_>>()
.join("\n");
let markdown = parse_html(&raw_markdown, path, plugins)?;
Ok(ParsedMarkdown {
frontmatter,
markdown,
markdown_string: raw_markdown,
html: None,
})
}
impl ParsedMarkdown {
pub fn to_value(&self) -> Value {
let mut v = self.frontmatter_to_value();
v["content"] = Value::String(self.html.as_ref().unwrap().clone());
v["markdown"] = Value::String(self.markdown_string.clone());
v
}
pub fn frontmatter_to_value(&self) -> Value {
let children = self
.frontmatter
.iter()
.map(|(key, value)| (key.clone(), Value::String(value.clone())))
.collect::<Vec<_>>();
Value::Object(children)
}
}