sable-frontmatter/src/json.rs@main
raw
1
3use miette::{SourceOffset, SourceSpan};
4
5use crate::Metadata;
6
7#[derive(Debug, miette::Diagnostic, thiserror::Error)]
9pub enum Error {
10 #[error("frontmatter is not valid json")]
14 Invalid,
15 #[error("json frontmatter exceeded parse depth of 128 braces (come on :/)")]
19 DepthExceeded,
20 #[error(transparent)]
22 Parse(ParseError),
23}
24
25#[derive(Debug, miette::Diagnostic, thiserror::Error)]
27#[error("failed to deserialize toml frontmatter")]
28pub struct ParseError {
29 #[source_code]
31 src: String,
32 #[label("{err}")]
34 location: SourceSpan,
35
36 #[source]
38 err: serde_json::Error,
39}
40
41pub fn parse(data: &str) -> Result<(Option<Metadata>, &str), Error> {
49 const MAX_DEPTH: usize = 128;
50
51 let data = data.trim_start();
52
53 if !data.starts_with('{') {
54 return Err(Error::Invalid);
55 }
56
57 let mut depth = 0;
58
59 let mut braces = 0;
60
61 let mut is_in_string = false;
62 let mut escape_next = false;
63
64 let mut split_point = 0;
65
66 for (i, ch) in data.char_indices() {
67 if escape_next {
68 escape_next = false;
69 continue;
70 }
71
72 match ch {
73 '"' if !is_in_string => {
74 is_in_string = true;
75 }
76 '"' if is_in_string => {
77 is_in_string = false;
78 }
79 '\\' if is_in_string => {
80 escape_next = true;
81 }
82 '{' if !is_in_string => {
83 depth += 1;
84
85 braces += 1;
86
87 if depth > MAX_DEPTH {
88 return Err(Error::DepthExceeded);
89 }
90 }
91 '}' if !is_in_string => {
92 braces -= 1;
93
94 if depth > 0 {
95 depth = depth.saturating_sub(1);
96 }
97
98 if braces == 0 {
99 split_point = i;
100
101 break;
102 }
103 }
104 _ => {}
105 }
106 }
107
108 if braces != 0 {
109 return Err(Error::Invalid);
110 }
111
112 let (frontmatter, body) = data.split_at(split_point);
113
114 let parsed = serde_json::from_str(frontmatter).map_err(|err| {
115 Error::Parse(ParseError {
116 src: frontmatter.to_string(),
117 location: SourceSpan::new(
118 SourceOffset::from_location(frontmatter, err.line(), err.column()),
119 1,
120 ),
121 err,
122 })
123 })?;
124
125 Ok((Some(parsed), body))
126}
127