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(), 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 newline_at_indent(buf, depth + 1, config);
145 format_value(field, config, depth + 1, buf);
146 }
147 newline_at_indent(buf, depth, config);
148 } else {
149 for field in v.fields.iter() {
150 buf.push(' ');
151 format_value(field, config, depth, buf);
152 }
153 }
154
155 buf.push_str(&format!(" wrap-{}", field_count));
156 }
157}
158
159fn format_list(fields: &[Value], config: &SonConfig, depth: usize, buf: &mut String) {
161 buf.push_str("list-of");
162
163 if fields.is_empty() {
164 return;
165 }
166
167 if config.pretty {
168 for field in fields.iter() {
169 newline_at_indent(buf, depth + 1, config);
170 format_value(field, config, depth + 1, buf);
171 buf.push_str(" lv");
172 }
173 } else {
174 for field in fields.iter() {
175 buf.push(' ');
176 format_value(field, config, depth, buf);
177 buf.push_str(" lv");
178 }
179 }
180}
181
182fn format_map(map: &HashMap<MapKey, Value>, config: &SonConfig, depth: usize, buf: &mut String) {
184 buf.push_str("map-of");
185
186 if map.is_empty() {
187 return;
188 }
189
190 let mut entries: Vec<_> = map.iter().collect();
192 entries.sort_by(|(k1, _), (k2, _)| {
193 let s1 = map_key_sort_string(k1);
194 let s2 = map_key_sort_string(k2);
195 s1.cmp(&s2)
196 });
197
198 if config.pretty {
199 for (key, value) in entries {
200 newline_at_indent(buf, depth + 1, config);
201 format_map_key(key, buf);
202 buf.push(' ');
203 format_value(value, config, depth + 1, buf);
204 buf.push_str(" kv");
205 }
206 } else {
207 for (key, value) in entries {
208 buf.push(' ');
209 format_map_key(key, buf);
210 buf.push(' ');
211 format_value(value, config, depth, buf);
212 buf.push_str(" kv");
213 }
214 }
215}
216
217fn map_key_sort_string(key: &MapKey) -> String {
219 match key {
220 MapKey::Int(n) => format!("0_{:020}", n), MapKey::Bool(b) => format!("1_{}", b), MapKey::String(s) => format!("2_{}", s.as_str()), }
224}
225
226fn format_map_key(key: &MapKey, buf: &mut String) {
228 match key {
229 MapKey::Int(n) => buf.push_str(&n.to_string()),
230 MapKey::Bool(b) => buf.push_str(if *b { "true" } else { "false" }),
231 MapKey::String(s) => format_string(s.as_str(), buf),
232 }
233}
234
235fn push_indent(buf: &mut String, depth: usize, indent_size: usize) {
237 for _ in 0..(depth * indent_size) {
238 buf.push(' ');
239 }
240}
241
242fn newline_at_indent(buf: &mut String, depth: usize, config: &SonConfig) {
244 buf.push('\n');
245 push_indent(buf, depth, config.indent);
246}
247
248#[unsafe(no_mangle)]
259pub unsafe extern "C" fn patch_seq_son_dump(stack: Stack) -> Stack {
260 unsafe { son_dump_impl(stack, false) }
261}
262
263#[unsafe(no_mangle)]
270pub unsafe extern "C" fn patch_seq_son_dump_pretty(stack: Stack) -> Stack {
271 unsafe { son_dump_impl(stack, true) }
272}
273
274unsafe fn son_dump_impl(stack: Stack, pretty: bool) -> Stack {
276 let (rest, value) = unsafe { pop(stack) };
277
278 let config = if pretty {
279 SonConfig::pretty()
280 } else {
281 SonConfig::compact()
282 };
283
284 let result = value_to_son(&value, &config);
285 let result_str = SeqString::from(result);
286
287 unsafe { push(rest, Value::String(result_str)) }
288}
289
290#[cfg(test)]
295mod tests {
296 use super::*;
297 use crate::seqstring::global_string;
298 use std::sync::Arc;
299
300 #[test]
301 fn test_int() {
302 let v = Value::Int(42);
303 assert_eq!(value_to_son(&v, &SonConfig::default()), "42");
304 }
305
306 #[test]
307 fn test_negative_int() {
308 let v = Value::Int(-123);
309 assert_eq!(value_to_son(&v, &SonConfig::default()), "-123");
310 }
311
312 #[test]
313 fn test_float() {
314 let v = Value::Float(2.5);
315 assert_eq!(value_to_son(&v, &SonConfig::default()), "2.5");
316 }
317
318 #[test]
319 fn test_float_whole_number() {
320 let v = Value::Float(42.0);
321 let s = value_to_son(&v, &SonConfig::default());
322 assert!(s.contains('.'), "Float should contain decimal point: {}", s);
323 }
324
325 #[test]
326 fn test_bool_true() {
327 let v = Value::Bool(true);
328 assert_eq!(value_to_son(&v, &SonConfig::default()), "true");
329 }
330
331 #[test]
332 fn test_bool_false() {
333 let v = Value::Bool(false);
334 assert_eq!(value_to_son(&v, &SonConfig::default()), "false");
335 }
336
337 #[test]
338 fn test_string_simple() {
339 let v = Value::String(global_string("hello".to_string()));
340 assert_eq!(value_to_son(&v, &SonConfig::default()), r#""hello""#);
341 }
342
343 #[test]
344 fn test_string_escaping() {
345 let v = Value::String(global_string("hello\nworld".to_string()));
346 assert_eq!(value_to_son(&v, &SonConfig::default()), r#""hello\nworld""#);
347 }
348
349 #[test]
350 fn test_string_quotes() {
351 let v = Value::String(global_string(r#"say "hi""#.to_string()));
352 assert_eq!(value_to_son(&v, &SonConfig::default()), r#""say \"hi\"""#);
353 }
354
355 #[test]
356 fn test_symbol() {
357 let v = Value::Symbol(global_string("my-symbol".to_string()));
358 assert_eq!(value_to_son(&v, &SonConfig::default()), ":my-symbol");
359 }
360
361 #[test]
362 fn test_empty_list() {
363 let list = Value::Variant(Arc::new(VariantData::new(
364 global_string("List".to_string()),
365 vec![],
366 )));
367 assert_eq!(value_to_son(&list, &SonConfig::default()), "list-of");
368 }
369
370 #[test]
371 fn test_list() {
372 let list = Value::Variant(Arc::new(VariantData::new(
373 global_string("List".to_string()),
374 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
375 )));
376 assert_eq!(
377 value_to_son(&list, &SonConfig::default()),
378 "list-of 1 lv 2 lv 3 lv"
379 );
380 }
381
382 #[test]
383 fn test_list_pretty() {
384 let list = Value::Variant(Arc::new(VariantData::new(
385 global_string("List".to_string()),
386 vec![Value::Int(1), Value::Int(2)],
387 )));
388 let expected = "list-of\n 1 lv\n 2 lv";
389 assert_eq!(value_to_son(&list, &SonConfig::pretty()), expected);
390 }
391
392 #[test]
393 fn test_empty_map() {
394 let m: HashMap<MapKey, Value> = HashMap::new();
395 let v = Value::Map(Box::new(m));
396 assert_eq!(value_to_son(&v, &SonConfig::default()), "map-of");
397 }
398
399 #[test]
400 fn test_map() {
401 let mut m = HashMap::new();
402 m.insert(
403 MapKey::String(global_string("key".to_string())),
404 Value::Int(42),
405 );
406 let v = Value::Map(Box::new(m));
407 assert_eq!(
408 value_to_son(&v, &SonConfig::default()),
409 r#"map-of "key" 42 kv"#
410 );
411 }
412
413 #[test]
414 fn test_variant_no_fields() {
415 let v = Value::Variant(Arc::new(VariantData::new(
416 global_string("None".to_string()),
417 vec![],
418 )));
419 assert_eq!(value_to_son(&v, &SonConfig::default()), ":None wrap-0");
420 }
421
422 #[test]
423 fn test_variant_with_fields() {
424 let v = Value::Variant(Arc::new(VariantData::new(
425 global_string("Point".to_string()),
426 vec![Value::Int(10), Value::Int(20)],
427 )));
428 assert_eq!(
429 value_to_son(&v, &SonConfig::default()),
430 ":Point 10 20 wrap-2"
431 );
432 }
433
434 #[test]
435 fn test_variant_pretty() {
436 let v = Value::Variant(Arc::new(VariantData::new(
437 global_string("Point".to_string()),
438 vec![Value::Int(10), Value::Int(20)],
439 )));
440 let expected = ":Point\n 10\n 20\n wrap-2";
441 assert_eq!(value_to_son(&v, &SonConfig::pretty()), expected);
442 }
443
444 #[test]
445 fn test_nested_list_in_map() {
446 let list = Value::Variant(Arc::new(VariantData::new(
447 global_string("List".to_string()),
448 vec![Value::Int(1), Value::Int(2)],
449 )));
450 let mut m = HashMap::new();
451 m.insert(MapKey::String(global_string("items".to_string())), list);
452 let v = Value::Map(Box::new(m));
453 assert_eq!(
454 value_to_son(&v, &SonConfig::default()),
455 r#"map-of "items" list-of 1 lv 2 lv kv"#
456 );
457 }
458
459 #[test]
460 fn test_quotation() {
461 let v = Value::Quotation {
462 wrapper: 0,
463 impl_: 0,
464 };
465 assert_eq!(value_to_son(&v, &SonConfig::default()), "<quotation>");
466 }
467
468 #[test]
469 fn test_closure() {
470 let v = Value::Closure {
471 fn_ptr: 0,
472 env: Arc::new([]),
473 };
474 assert_eq!(value_to_son(&v, &SonConfig::default()), "<closure>");
475 }
476}