1use json::{self, JsonValue};
2
3#[derive(Debug)]
4pub enum ExtractError {
5 JsonError(json::Error),
6 KeyNotFound(),
7 JsonTooShort(),
8 MissingEnd(),
9}
10
11impl From<json::Error> for ExtractError {
12 fn from(err: json::Error) -> Self {
13 ExtractError::JsonError(err)
14 }
15}
16
17pub fn extract(s: &str, key: &str) -> Result<JsonValue, ExtractError> {
29 let mut in_string = false;
30 let mut is_key = true;
31 let mut level = 0;
32 let key_decorated = format!("\"{key}\"");
33 let mut it = s.chars().enumerate();
34
35 while let Some((i, c)) = it.next() {
36 match c {
37 '\\' => {
38 it.nth(0);
39 continue;
40 }
41 '"' => {
42 in_string = !in_string;
43 continue;
44 }
45 _ => (),
46 }
47 if !in_string {
48 match c {
49 ':' => is_key = false,
50 ',' => is_key = true,
51 '{' => level = level + 1,
52 '}' => level = level - 1,
53 _ => (),
54 }
55 }
56 if is_key && level == 1 && i > 0 {
57 if let Some(sub) = s.get(i - 1..i + key.len() + 1) {
58 if sub == key_decorated {
59 let start = i + key.len() + 2;
60 if s.len() <= start {
61 return Err(ExtractError::JsonTooShort());
62 }
63 let end = find_end(&s[start..])? + start;
64 return Ok(json::parse(&s[start..end])?);
65 }
66 }
67 }
68
69 }
70
71 Err(ExtractError::KeyNotFound())
72}
73
74fn find_end(s: &str) -> Result<usize, ExtractError> {
75 let mut level = 0;
76 let mut first_char: Option<char> = Default::default();
77
78 for (i, c) in s.chars().enumerate() {
79 if let None = first_char {
80 first_char = Some(c);
81 }
82 match c {
83 '{' | '[' => {
84 level = level + 1;
85 continue;
86 }
87 '}' | ']' => {
88 level = level - 1;
89 if level > 0 {
90 continue;
91 }
92 }
93 _ => ()
94 }
95 if level < 0 || level == 0 && (c == ',' || c == '}' || c == ']') {
96 return match first_char {
97 Some('{') | Some('[') => Ok(i + 1),
98 _ => Ok(i),
99 };
100 }
101 }
102
103 Err(ExtractError::MissingEnd())
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use json::{array, object};
110
111 #[test]
112 fn test() {
113 let value = extract(r#"{"foo": "bar"}"#, "foo").unwrap();
114 assert_eq!(value, "bar");
115
116 let value = extract(r#"{"foo": "bar","bar":"baz"}"#, "foo").unwrap();
117 assert_eq!(value, "bar");
118
119 let value = extract(r#"{"foo": "bar","bar":"baz"}"#, "bar").unwrap();
120 assert_eq!(value, "baz");
121
122 let value = extract(r#"{"foo":{"beep":"boop","bar":"oops"},"bar":"baz"}"#, "bar").unwrap();
123 assert_eq!(value, "baz");
124
125 let value = extract(r#"{"foo":[{"bar":"oops"}],"bar":"baz"}"#, "bar").unwrap();
126 assert_eq!(value, "baz");
127
128 let value = extract(r#"{"foo":{"bar":"baz"}}"#, "foo").unwrap();
129 assert_eq!(
130 value,
131 object! {
132 bar: "baz"
133 }
134 );
135
136 let value = extract(r#"{"foo":["bar","baz"]}"#, "foo").unwrap();
137 assert_eq!(
138 value,
139 array! {
140 "bar",
141 "baz"
142 }
143 );
144
145 let value = extract(r#"{"foo": "bar"}"#, "foo").unwrap();
146 assert_eq!(value, "bar");
147
148 let value = extract(r#"{"beep":"\\","foo":"bar"}"#, "foo").unwrap();
149 assert_eq!(value, "bar");
150
151 let value = extract(r#"{"foo":"bar\"baz"}"#, "foo").unwrap();
152 assert_eq!(value, "bar\"baz");
153
154 let value = extract(r#"{"_a":0,"a_":1,"_a_":2,"a":3}"#, "a").unwrap();
155 assert_eq!(value, 3);
156
157 extract(r#"{"foo"}"#, "foo").unwrap_err();
158 extract(r#"{"foo":"bar"}"#, "bar").unwrap_err();
159
160 let value = extract(r#"{"foo":{"bar":{"baz":"beep"}}}"#, "foo").unwrap();
161 assert_eq!(
162 value,
163 object! {
164 bar: {
165 baz: "beep"
166 }
167 }
168 );
169 }
170}