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