markdown_parser/
parser.rs1use enum_iterator_derive::IntoEnumIterator;
2use getset::{Getters, Setters};
3
4use crate::{error::Error, Adapter};
5
6pub type MarkdownResult = Result<Markdown, Error>;
7
8#[derive(Debug, Getters, Setters, Clone)]
13pub struct Markdown {
14 #[getset(get = "pub", set = "pub")]
15 content: String,
16 #[getset(get = "pub", set = "pub")]
17 front_matter: String,
18 #[getset(get = "pub", set = "pub")]
19 format: Format,
20}
21
22impl Markdown {
23 #[inline]
24 pub fn new(content: String, front_matter: String, format: Format) -> Self {
25 Markdown {
26 format,
27 content,
28 front_matter,
29 }
30 }
31
32 pub fn write_file<P>(&self, path: P) -> Result<(), crate::error::Error>
39 where
40 P: AsRef<std::path::Path>,
41 {
42 crate::fs::write_file(self, path)
43 }
44
45 #[inline]
46 pub fn bytes(&self) -> Vec<u8> {
48 let mut v = vec![];
49 let sp = self.format().separator();
50 v.extend_from_slice(sp.as_bytes());
51 v.extend_from_slice(self.front_matter().as_bytes());
52 v.extend_from_slice(b"\n");
53 v.extend_from_slice(sp.as_bytes());
54 v.extend_from_slice(b"\n");
55 v.extend_from_slice(self.content().as_bytes());
56
57 v
58 }
59
60 #[inline]
61 pub fn display(&self) -> String {
63 unsafe { String::from_utf8_unchecked(self.bytes()) }
64 }
65
66 #[cfg(feature = "adapt")]
78 pub fn adapt<A, T>(self) -> MarkdownResult
79 where
80 A: Adapter + Default,
81 T: serde::ser::Serialize + serde::de::DeserializeOwned,
82 {
83 A::default().adapt::<T>(self)
84 }
85}
86
87#[derive(Debug, Clone, IntoEnumIterator, PartialEq)]
93pub enum Format {
94 JSON,
95 YAML,
96 TOML,
97}
98
99impl Format {
100 #[inline]
101 fn separator(&self) -> &str {
103 match self {
104 Format::YAML => "---\n",
105 Format::TOML => "+++\n",
106 _ => "",
107 }
108 }
109
110 #[inline]
111 fn regex_patten(&self) -> &str {
113 match self {
114 Format::YAML => r"^[[:space:]]*\-\-\-\r?\n((?s).*?(?-s))\-\-\-\r?\n((?s).*(?-s))$",
115 Format::TOML => r"^[[:space:]]*\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n((?s).*(?-s))$",
116 Format::JSON => r"^[[:space:]]*\{\r?\n((?s).*?(?-s))\}\r?\n((?s).*(?-s))$",
117 }
118 }
119}
120
121pub fn parse(input: &str) -> MarkdownResult {
123 use enum_iterator::IntoEnumIterator;
124 for format in Format::into_enum_iter() {
125 let md = parse_format(input, format);
126 if md.is_ok() {
127 return md;
128 }
129 }
130
131 Err(crate::ParseError::MissingAllFormat.into())
132}
133
134pub fn parse_format(input: &str, format: Format) -> MarkdownResult {
136 let cap = regex::Regex::new(format.regex_patten())?
137 .captures(input)
138 .ok_or(crate::ParseError::BadFormat(format.clone()))?;
139
140 let front_matter = if Format::JSON.eq(&format) {
142 format!("{{\n{0}\n}}", cap[1].trim())
143 } else {
144 cap[1].trim().to_string()
145 };
146
147 Ok(Markdown {
148 format,
149 front_matter,
150 content: cap[2].trim().to_string(),
151 })
152}