1use crate::span::is_single_line;
38use crate::{Parse, Source};
39use cfg_if::cfg_if;
40use spanned_json_parser::value::Value as JsonValue;
41use spanned_json_parser::{Position, parse};
42use tanzim_value::{Error, LocatedValue, Location, Map, Value};
43
44#[derive(Clone, Copy, Default)]
63pub struct Json;
64
65impl Json {
66 pub fn new() -> Self {
68 Self
69 }
70}
71
72impl Parse for Json {
73 fn name(&self) -> &str {
74 "JSON"
75 }
76
77 fn supported_format_list(&self) -> Vec<String> {
78 vec!["json".into()]
79 }
80
81 fn parse(
82 &self,
83 src: &Source,
84 bytes: &[u8],
85 _other_source_list: &[Source],
86 ) -> Result<LocatedValue, Error> {
87 #[cfg(any(feature = "tracing", feature = "logging"))]
88 let source = src.source();
89 #[cfg(any(feature = "tracing", feature = "logging"))]
90 let resource = src.resource();
91 cfg_if! {
92 if #[cfg(feature = "tracing")] {
93 tracing::debug!(msg = "Parsing JSON configuration", source = source, resource = resource, bytes = bytes.len());
94 } else if #[cfg(feature = "logging")] {
95 log::debug!("msg=\"Parsing JSON configuration\" source={source} resource={resource} bytes={}", bytes.len());
96 }
97 }
98 let text = match std::str::from_utf8(bytes) {
99 Ok(value) => value,
100 Err(_) => {
101 return Err(Error::InvalidUtf8 {
102 location: Box::new(Location::in_source(src.clone(), None, None, None)),
103 });
104 }
105 };
106 let single_line = is_single_line(bytes);
107 let parsed = match parse(text) {
108 Ok(value) => value,
109 Err(error) => {
110 return Err(Error::Parse {
111 location: Some(Box::new(location_from_position(
112 src,
113 text,
114 single_line,
115 &error.start,
116 Some(&error.end),
117 ))),
118 message: format!("{:?}", error.kind),
119 });
120 }
121 };
122 let location =
123 location_from_position(src, text, single_line, &parsed.start, Some(&parsed.end));
124 let result = convert_value(
125 src,
126 text,
127 single_line,
128 parsed.value,
129 &parsed.start,
130 location,
131 );
132 if result.is_ok() {
133 cfg_if! {
134 if #[cfg(feature = "tracing")] {
135 tracing::trace!(msg = "Parsed JSON configuration", source = source, resource = resource);
136 } else if #[cfg(feature = "logging")] {
137 log::trace!("msg=\"Parsed JSON configuration\" source={source} resource={resource}");
138 }
139 }
140 }
141 result
142 }
143
144 fn is_format_supported(&self, bytes: &[u8]) -> Option<bool> {
145 match std::str::from_utf8(bytes) {
146 Ok(text) => Some(parse(text).is_ok()),
147 Err(_) => Some(false),
148 }
149 }
150}
151
152pub fn unparse<V: AsRef<Value>>(
172 _source: &Source,
173 value: V,
174) -> Result<String, Box<dyn std::error::Error + Send + Sync + 'static>> {
175 let mut out = String::new();
176 write_json(&mut out, value.as_ref(), 0)?;
177 Ok(out)
178}
179
180fn write_json(
181 out: &mut String,
182 value: &Value,
183 indent: usize,
184) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
185 match value {
186 Value::Bool(value) => out.push_str(if *value { "true" } else { "false" }),
187 Value::Int(value) => out.push_str(&value.to_string()),
188 Value::Float(value) => {
189 if !value.is_finite() {
190 return Err(format!("cannot serialize non-finite float {value} as JSON").into());
191 }
192 out.push_str(&format!("{value:?}"));
193 }
194 Value::String(value) => write_json_string(out, value),
195 Value::List(values) => {
196 if values.is_empty() {
197 out.push_str("[]");
198 return Ok(());
199 }
200 out.push_str("[\n");
201 for (index, item) in values.iter().enumerate() {
202 push_indent(out, indent + 1);
203 write_json(out, item.value(), indent + 1)?;
204 if index + 1 < values.len() {
205 out.push(',');
206 }
207 out.push('\n');
208 }
209 push_indent(out, indent);
210 out.push(']');
211 }
212 Value::Map(map) => {
213 let entries = map.entries();
214 if entries.is_empty() {
215 out.push_str("{}");
216 return Ok(());
217 }
218 out.push_str("{\n");
219 for (index, (key, item)) in entries.iter().enumerate() {
220 push_indent(out, indent + 1);
221 write_json_string(out, key);
222 out.push_str(": ");
223 write_json(out, item.value(), indent + 1)?;
224 if index + 1 < entries.len() {
225 out.push(',');
226 }
227 out.push('\n');
228 }
229 push_indent(out, indent);
230 out.push('}');
231 }
232 Value::Null => out.push_str("null"),
233 }
234 Ok(())
235}
236
237fn push_indent(out: &mut String, indent: usize) {
238 for _ in 0..indent {
239 out.push_str(" ");
240 }
241}
242
243fn write_json_string(out: &mut String, value: &str) {
244 out.push('"');
245 for ch in value.chars() {
246 match ch {
247 '"' => out.push_str("\\\""),
248 '\\' => out.push_str("\\\\"),
249 '\n' => out.push_str("\\n"),
250 '\r' => out.push_str("\\r"),
251 '\t' => out.push_str("\\t"),
252 control if (control as u32) < 0x20 => {
253 out.push_str(&format!("\\u{:04x}", control as u32));
254 }
255 other => out.push(other),
256 }
257 }
258 out.push('"');
259}
260
261fn convert_value(
262 source: &Source,
263 text: &str,
264 single_line: bool,
265 value: JsonValue,
266 _start: &Position,
267 location: Location,
268) -> Result<LocatedValue, Error> {
269 match value {
270 JsonValue::Null => Ok(LocatedValue::new(Value::Null, location)),
271 JsonValue::Bool(value) => Ok(LocatedValue::new(Value::Bool(value), location)),
272 JsonValue::Number(number) => match number {
273 spanned_json_parser::value::Number::PosInt(value) => {
274 Ok(LocatedValue::new(Value::Int(value as isize), location))
275 }
276 spanned_json_parser::value::Number::NegInt(value) => {
277 Ok(LocatedValue::new(Value::Int(value as isize), location))
278 }
279 spanned_json_parser::value::Number::Float(value) => {
280 Ok(LocatedValue::new(Value::Float(value), location))
281 }
282 },
283 JsonValue::String(value) => Ok(LocatedValue::new(Value::String(value), location)),
284 JsonValue::Array(values) => {
285 let mut list = Vec::new();
286 for item in &values {
287 let item_location =
288 location_from_position(source, text, single_line, &item.start, Some(&item.end));
289 let converted = convert_value(
290 source,
291 text,
292 single_line,
293 item.value.clone(),
294 &item.start,
295 item_location,
296 )?;
297 list.push(converted);
298 }
299 Ok(LocatedValue::new(Value::List(list), location))
300 }
301 JsonValue::Object(values) => {
302 let mut map = Map::new();
303 for (key, item) in values {
304 let item_location =
305 location_from_position(source, text, single_line, &item.start, Some(&item.end));
306 let converted = convert_value(
307 source,
308 text,
309 single_line,
310 item.value.clone(),
311 &item.start,
312 item_location,
313 )?;
314 map.insert(key, converted);
315 }
316 Ok(LocatedValue::new(Value::Map(map), location))
317 }
318 }
319}
320
321fn location_from_position(
322 source: &Source,
323 text: &str,
324 single_line: bool,
325 start: &Position,
326 end: Option<&Position>,
327) -> Location {
328 if single_line {
329 return Location::in_source(source.clone(), None, None, None);
330 }
331 let mut length = None;
332 if let Some(end) = end
333 && start.line == end.line
334 && end.col >= start.col
335 {
336 length = Some(end.col - start.col + 1);
337 }
338 Location::in_text(
339 source.clone(),
340 text,
341 Some(start.line),
342 Some(start.col),
343 length,
344 )
345}