1use crate::seqstring::SeqString;
18use crate::stack::{Stack, pop, push};
19use crate::value::{MapKey, Value, VariantData};
20use std::collections::HashMap;
21
22#[derive(Clone)]
24pub(crate) struct SonConfig {
25 pub(crate) pretty: bool,
27 pub(crate) indent: usize,
29}
30
31impl Default for SonConfig {
32 fn default() -> Self {
33 Self {
34 pretty: false,
35 indent: 2,
36 }
37 }
38}
39
40impl SonConfig {
41 pub(crate) fn compact() -> Self {
43 Self::default()
44 }
45
46 pub(crate) fn pretty() -> Self {
48 Self {
49 pretty: true,
50 indent: 2,
51 }
52 }
53}
54
55pub(crate) fn value_to_son(value: &Value, config: &SonConfig) -> String {
57 let mut buf = String::new();
58 format_value(value, config, 0, &mut buf);
59 buf
60}
61
62fn format_value(value: &Value, config: &SonConfig, depth: usize, buf: &mut String) {
64 match value {
65 Value::Int(n) => {
66 buf.push_str(&n.to_string());
67 }
68 Value::Float(f) => {
69 let s = f.to_string();
70 buf.push_str(&s);
71 if !s.contains('.') && f.is_finite() {
73 buf.push_str(".0");
74 }
75 }
76 Value::Bool(b) => {
77 buf.push_str(if *b { "true" } else { "false" });
78 }
79 Value::String(s) => {
80 format_string(&s.as_str_lossy(), buf);
86 }
87 Value::Symbol(s) => {
88 buf.push(':');
89 buf.push_str(&s.as_str_lossy());
90 }
91 Value::Variant(v) => {
92 format_variant(v, config, depth, buf);
93 }
94 Value::Map(m) => {
95 format_map(m, config, depth, buf);
96 }
97 Value::Quotation { .. } => {
98 buf.push_str("<quotation>");
99 }
100 Value::Closure { .. } => {
101 buf.push_str("<closure>");
102 }
103 Value::Channel(_) => {
104 buf.push_str("<channel>");
105 }
106 Value::WeaveCtx { .. } => {
107 buf.push_str("<weave-ctx>");
108 }
109 }
110}
111
112fn format_string(s: &str, buf: &mut String) {
114 buf.push('"');
115 for c in s.chars() {
116 match c {
117 '"' => buf.push_str("\\\""),
118 '\\' => buf.push_str("\\\\"),
119 '\n' => buf.push_str("\\n"),
120 '\r' => buf.push_str("\\r"),
121 '\t' => buf.push_str("\\t"),
122 '\x08' => buf.push_str("\\b"),
123 '\x0C' => buf.push_str("\\f"),
124 c if c.is_control() => {
125 buf.push_str(&format!("\\u{:04x}", c as u32));
126 }
127 c => buf.push(c),
128 }
129 }
130 buf.push('"');
131}
132
133fn format_variant(v: &VariantData, config: &SonConfig, depth: usize, buf: &mut String) {
135 let is_list = v.tag.as_bytes() == b"List";
139
140 if is_list {
141 format_list(&v.fields, config, depth, buf);
142 } else {
143 buf.push(':');
145 buf.push_str(&v.tag.as_str_lossy());
146
147 let field_count = v.fields.len();
148
149 if config.pretty && !v.fields.is_empty() {
150 for field in v.fields.iter() {
151 newline_at_indent(buf, depth + 1, config);
152 format_value(field, config, depth + 1, buf);
153 }
154 newline_at_indent(buf, depth, config);
155 } else {
156 for field in v.fields.iter() {
157 buf.push(' ');
158 format_value(field, config, depth, buf);
159 }
160 }
161
162 buf.push_str(&format!(" wrap-{}", field_count));
163 }
164}
165
166fn format_list(fields: &[Value], config: &SonConfig, depth: usize, buf: &mut String) {
168 buf.push_str("list-of");
169
170 if fields.is_empty() {
171 return;
172 }
173
174 if config.pretty {
175 for field in fields.iter() {
176 newline_at_indent(buf, depth + 1, config);
177 format_value(field, config, depth + 1, buf);
178 buf.push_str(" lv");
179 }
180 } else {
181 for field in fields.iter() {
182 buf.push(' ');
183 format_value(field, config, depth, buf);
184 buf.push_str(" lv");
185 }
186 }
187}
188
189fn format_map(map: &HashMap<MapKey, Value>, config: &SonConfig, depth: usize, buf: &mut String) {
191 buf.push_str("map-of");
192
193 if map.is_empty() {
194 return;
195 }
196
197 let mut entries: Vec<_> = map.iter().collect();
199 entries.sort_by(|(k1, _), (k2, _)| {
200 let s1 = map_key_sort_string(k1);
201 let s2 = map_key_sort_string(k2);
202 s1.cmp(&s2)
203 });
204
205 if config.pretty {
206 for (key, value) in entries {
207 newline_at_indent(buf, depth + 1, config);
208 format_map_key(key, buf);
209 buf.push(' ');
210 format_value(value, config, depth + 1, buf);
211 buf.push_str(" kv");
212 }
213 } else {
214 for (key, value) in entries {
215 buf.push(' ');
216 format_map_key(key, buf);
217 buf.push(' ');
218 format_value(value, config, depth, buf);
219 buf.push_str(" kv");
220 }
221 }
222}
223
224fn map_key_sort_string(key: &MapKey) -> String {
226 match key {
227 MapKey::Int(n) => format!("0_{:020}", n), MapKey::Bool(b) => format!("1_{}", b), MapKey::String(s) => format!("2_{}", s.as_str_lossy()), }
231}
232
233fn format_map_key(key: &MapKey, buf: &mut String) {
235 match key {
236 MapKey::Int(n) => buf.push_str(&n.to_string()),
237 MapKey::Bool(b) => buf.push_str(if *b { "true" } else { "false" }),
238 MapKey::String(s) => format_string(&s.as_str_lossy(), buf),
239 }
240}
241
242fn push_indent(buf: &mut String, depth: usize, indent_size: usize) {
244 for _ in 0..(depth * indent_size) {
245 buf.push(' ');
246 }
247}
248
249fn newline_at_indent(buf: &mut String, depth: usize, config: &SonConfig) {
251 buf.push('\n');
252 push_indent(buf, depth, config.indent);
253}
254
255#[unsafe(no_mangle)]
266pub unsafe extern "C" fn patch_seq_son_dump(stack: Stack) -> Stack {
267 unsafe { son_dump_impl(stack, false) }
268}
269
270#[unsafe(no_mangle)]
277pub unsafe extern "C" fn patch_seq_son_dump_pretty(stack: Stack) -> Stack {
278 unsafe { son_dump_impl(stack, true) }
279}
280
281unsafe fn son_dump_impl(stack: Stack, pretty: bool) -> Stack {
283 let (rest, value) = unsafe { pop(stack) };
284
285 let config = if pretty {
286 SonConfig::pretty()
287 } else {
288 SonConfig::compact()
289 };
290
291 let result = value_to_son(&value, &config);
292 let result_str = SeqString::from(result);
293
294 unsafe { push(rest, Value::String(result_str)) }
295}
296
297#[cfg(test)]
302mod tests {
303 use super::*;
304 use crate::seqstring::global_string;
305 use std::sync::Arc;
306
307 #[test]
308 fn test_int() {
309 let v = Value::Int(42);
310 assert_eq!(value_to_son(&v, &SonConfig::default()), "42");
311 }
312
313 #[test]
314 fn test_negative_int() {
315 let v = Value::Int(-123);
316 assert_eq!(value_to_son(&v, &SonConfig::default()), "-123");
317 }
318
319 #[test]
320 fn test_float() {
321 let v = Value::Float(2.5);
322 assert_eq!(value_to_son(&v, &SonConfig::default()), "2.5");
323 }
324
325 #[test]
326 fn test_float_whole_number() {
327 let v = Value::Float(42.0);
328 let s = value_to_son(&v, &SonConfig::default());
329 assert!(s.contains('.'), "Float should contain decimal point: {}", s);
330 }
331
332 #[test]
333 fn test_bool_true() {
334 let v = Value::Bool(true);
335 assert_eq!(value_to_son(&v, &SonConfig::default()), "true");
336 }
337
338 #[test]
339 fn test_bool_false() {
340 let v = Value::Bool(false);
341 assert_eq!(value_to_son(&v, &SonConfig::default()), "false");
342 }
343
344 #[test]
345 fn test_string_simple() {
346 let v = Value::String(global_string("hello".to_string()));
347 assert_eq!(value_to_son(&v, &SonConfig::default()), r#""hello""#);
348 }
349
350 #[test]
351 fn test_string_escaping() {
352 let v = Value::String(global_string("hello\nworld".to_string()));
353 assert_eq!(value_to_son(&v, &SonConfig::default()), r#""hello\nworld""#);
354 }
355
356 #[test]
357 fn test_string_quotes() {
358 let v = Value::String(global_string(r#"say "hi""#.to_string()));
359 assert_eq!(value_to_son(&v, &SonConfig::default()), r#""say \"hi\"""#);
360 }
361
362 #[test]
363 fn test_symbol() {
364 let v = Value::Symbol(global_string("my-symbol".to_string()));
365 assert_eq!(value_to_son(&v, &SonConfig::default()), ":my-symbol");
366 }
367
368 #[test]
369 fn test_empty_list() {
370 let list = Value::Variant(Arc::new(VariantData::new(
371 global_string("List".to_string()),
372 vec![],
373 )));
374 assert_eq!(value_to_son(&list, &SonConfig::default()), "list-of");
375 }
376
377 #[test]
378 fn test_list() {
379 let list = Value::Variant(Arc::new(VariantData::new(
380 global_string("List".to_string()),
381 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
382 )));
383 assert_eq!(
384 value_to_son(&list, &SonConfig::default()),
385 "list-of 1 lv 2 lv 3 lv"
386 );
387 }
388
389 #[test]
390 fn test_list_pretty() {
391 let list = Value::Variant(Arc::new(VariantData::new(
392 global_string("List".to_string()),
393 vec![Value::Int(1), Value::Int(2)],
394 )));
395 let expected = "list-of\n 1 lv\n 2 lv";
396 assert_eq!(value_to_son(&list, &SonConfig::pretty()), expected);
397 }
398
399 #[test]
400 fn test_empty_map() {
401 let m: HashMap<MapKey, Value> = HashMap::new();
402 let v = Value::Map(Box::new(m));
403 assert_eq!(value_to_son(&v, &SonConfig::default()), "map-of");
404 }
405
406 #[test]
407 fn test_map() {
408 let mut m = HashMap::new();
409 m.insert(
410 MapKey::String(global_string("key".to_string())),
411 Value::Int(42),
412 );
413 let v = Value::Map(Box::new(m));
414 assert_eq!(
415 value_to_son(&v, &SonConfig::default()),
416 r#"map-of "key" 42 kv"#
417 );
418 }
419
420 #[test]
421 fn test_variant_no_fields() {
422 let v = Value::Variant(Arc::new(VariantData::new(
423 global_string("None".to_string()),
424 vec![],
425 )));
426 assert_eq!(value_to_son(&v, &SonConfig::default()), ":None wrap-0");
427 }
428
429 #[test]
430 fn test_variant_with_fields() {
431 let v = Value::Variant(Arc::new(VariantData::new(
432 global_string("Point".to_string()),
433 vec![Value::Int(10), Value::Int(20)],
434 )));
435 assert_eq!(
436 value_to_son(&v, &SonConfig::default()),
437 ":Point 10 20 wrap-2"
438 );
439 }
440
441 #[test]
442 fn test_variant_pretty() {
443 let v = Value::Variant(Arc::new(VariantData::new(
444 global_string("Point".to_string()),
445 vec![Value::Int(10), Value::Int(20)],
446 )));
447 let expected = ":Point\n 10\n 20\n wrap-2";
448 assert_eq!(value_to_son(&v, &SonConfig::pretty()), expected);
449 }
450
451 #[test]
452 fn test_nested_list_in_map() {
453 let list = Value::Variant(Arc::new(VariantData::new(
454 global_string("List".to_string()),
455 vec![Value::Int(1), Value::Int(2)],
456 )));
457 let mut m = HashMap::new();
458 m.insert(MapKey::String(global_string("items".to_string())), list);
459 let v = Value::Map(Box::new(m));
460 assert_eq!(
461 value_to_son(&v, &SonConfig::default()),
462 r#"map-of "items" list-of 1 lv 2 lv kv"#
463 );
464 }
465
466 #[test]
467 fn test_quotation() {
468 let v = Value::Quotation {
469 wrapper: 0,
470 impl_: 0,
471 };
472 assert_eq!(value_to_son(&v, &SonConfig::default()), "<quotation>");
473 }
474
475 #[test]
476 fn test_closure() {
477 let v = Value::Closure {
478 fn_ptr: 0,
479 env: Arc::new([]),
480 };
481 assert_eq!(value_to_son(&v, &SonConfig::default()), "<closure>");
482 }
483}