1use crate::{CommentedValue, Comments, Error, IndentStyle, QuoteStyle, Result, Value};
4use std::collections::HashMap;
5use std::io::Write;
6
7pub trait Emitter {
9 fn emit<W: Write>(&mut self, value: &Value, writer: W) -> Result<()>;
11
12 fn emit_commented<W: Write>(&mut self, value: &CommentedValue, writer: W) -> Result<()>;
14
15 fn emit_with_style<W: Write>(
17 &mut self,
18 value: &CommentedValue,
19 indent_style: &IndentStyle,
20 writer: W,
21 ) -> Result<()>;
22
23 fn reset(&mut self);
25}
26
27#[derive(Debug, Clone)]
29struct ValueInfo {
30 anchor_name: String,
31 first_occurrence: bool,
32}
33
34#[derive(Debug)]
36pub struct BasicEmitter {
37 indent: usize,
38 current_indent: usize,
39 shared_values: HashMap<Value, ValueInfo>,
40 anchor_counter: usize,
41 indent_style: IndentStyle,
42 yaml_version: Option<(u8, u8)>,
43 tag_directives: Vec<(String, String)>,
44}
45
46#[allow(dead_code)]
47impl BasicEmitter {
48 pub fn new() -> Self {
50 Self {
51 indent: 2,
52 current_indent: 0,
53 shared_values: HashMap::new(),
54 anchor_counter: 0,
55 indent_style: IndentStyle::default(),
56 yaml_version: None,
57 tag_directives: Vec::new(),
58 }
59 }
60
61 pub fn with_indent(indent: usize) -> Self {
63 Self {
64 indent,
65 current_indent: 0,
66 shared_values: HashMap::new(),
67 anchor_counter: 0,
68 indent_style: IndentStyle::Spaces(indent),
69 yaml_version: None,
70 tag_directives: Vec::new(),
71 }
72 }
73
74 pub fn with_indent_style(indent_style: IndentStyle) -> Self {
76 let indent = match &indent_style {
77 IndentStyle::Spaces(width) => *width,
78 IndentStyle::Tabs => 1, };
80 Self {
81 indent,
82 current_indent: 0,
83 shared_values: HashMap::new(),
84 anchor_counter: 0,
85 indent_style,
86 yaml_version: None,
87 tag_directives: Vec::new(),
88 }
89 }
90
91 pub fn set_yaml_version(&mut self, major: u8, minor: u8) {
93 self.yaml_version = Some((major, minor));
94 }
95
96 pub fn add_tag_directive(&mut self, handle: String, prefix: String) {
98 self.tag_directives.push((handle, prefix));
99 }
100
101 pub fn clear_directives(&mut self) {
103 self.yaml_version = None;
104 self.tag_directives.clear();
105 }
106
107 fn emit_directives<W: Write>(&self, writer: &mut W) -> Result<()> {
109 if let Some((major, minor)) = self.yaml_version {
111 writeln!(writer, "%YAML {}.{}", major, minor).map_err(|e| Error::Emission {
112 message: format!("Failed to write YAML directive: {}", e),
113 })?;
114 }
115
116 for (handle, prefix) in &self.tag_directives {
118 writeln!(writer, "%TAG {} {}", handle, prefix).map_err(|e| Error::Emission {
119 message: format!("Failed to write TAG directive: {}", e),
120 })?;
121 }
122
123 if self.yaml_version.is_some() || !self.tag_directives.is_empty() {
125 writeln!(writer, "---").map_err(|e| Error::Emission {
126 message: format!("Failed to write document start marker: {}", e),
127 })?;
128 }
129
130 Ok(())
131 }
132
133 fn analyze_shared_values(&mut self, value: &Value) {
135 let mut value_counts = HashMap::new();
136 self.count_value_occurrences(value, &mut value_counts);
137
138 for (val, count) in value_counts {
140 if count > 1 && self.is_complex_value(&val) {
141 let anchor_name = format!("anchor{}", self.anchor_counter);
142 self.anchor_counter += 1;
143 self.shared_values.insert(
144 val,
145 ValueInfo {
146 anchor_name,
147 first_occurrence: true,
148 },
149 );
150 }
151 }
152 }
153
154 fn count_value_occurrences(&self, value: &Value, counts: &mut HashMap<Value, usize>) {
156 if self.is_complex_value(value) {
158 *counts.entry(value.clone()).or_insert(0) += 1;
159 }
160
161 match value {
163 Value::Sequence(seq) => {
164 for item in seq {
165 self.count_value_occurrences(item, counts);
166 }
167 }
168 Value::Mapping(map) => {
169 for (key, val) in map {
170 self.count_value_occurrences(key, counts);
171 self.count_value_occurrences(val, counts);
172 }
173 }
174 _ => {}
175 }
176 }
177
178 const fn is_complex_value(&self, value: &Value) -> bool {
180 matches!(value, Value::Sequence(_) | Value::Mapping(_))
181 }
182
183 fn next_anchor_name(&mut self) -> String {
185 let name = format!("anchor{}", self.anchor_counter);
186 self.anchor_counter += 1;
187 name
188 }
189
190 pub const fn set_indent_style(&mut self, indent_style: IndentStyle) {
192 self.indent = match &indent_style {
193 IndentStyle::Spaces(width) => *width,
194 IndentStyle::Tabs => 1,
195 };
196 self.indent_style = indent_style;
197 }
198
199 fn write_indent<W: Write>(&self, writer: &mut W) -> Result<()> {
201 match &self.indent_style {
202 IndentStyle::Spaces(_width) => {
203 let total_spaces = self.current_indent;
204 for _ in 0..total_spaces {
205 write!(writer, " ")?;
206 }
207 }
208 IndentStyle::Tabs => {
209 let indent_levels = self.current_indent / self.indent;
210 for _ in 0..indent_levels {
211 write!(writer, "\t")?;
212 }
213 }
214 }
215 Ok(())
216 }
217
218 fn emit_leading_comments<W: Write>(&self, comments: &[String], writer: &mut W) -> Result<()> {
220 for comment in comments {
221 self.write_indent(writer)?;
222 writeln!(writer, "# {}", comment)?;
223 }
224 Ok(())
225 }
226
227 fn emit_trailing_comment<W: Write>(&self, comment: &str, writer: &mut W) -> Result<()> {
229 write!(writer, " # {}", comment)?;
230 Ok(())
231 }
232
233 fn emit_inner_comments<W: Write>(&self, comments: &[String], writer: &mut W) -> Result<()> {
235 for comment in comments {
236 writeln!(writer)?;
237 self.write_indent(writer)?;
238 writeln!(writer, "# {}", comment)?;
239 }
240 Ok(())
241 }
242
243 fn emit_scalar<W: Write>(&self, value: &Value, writer: &mut W) -> Result<()> {
245 self.emit_scalar_with_comments(value, None, writer)
246 }
247
248 fn emit_scalar_with_comments<W: Write>(
250 &self,
251 value: &Value,
252 comments: Option<&Comments>,
253 writer: &mut W,
254 ) -> Result<()> {
255 self.emit_scalar_with_comments_and_style(value, comments, None, writer)
256 }
257
258 fn emit_scalar_with_comments_and_style<W: Write>(
260 &self,
261 value: &Value,
262 comments: Option<&Comments>,
263 quote_style: Option<&QuoteStyle>,
264 writer: &mut W,
265 ) -> Result<()> {
266 if let Some(comments) = comments {
268 self.emit_leading_comments(&comments.leading, writer)?;
269 }
270
271 match value {
273 Value::Null => write!(writer, "null")?,
274 Value::Bool(b) => write!(writer, "{}", b)?,
275 Value::Int(i) => write!(writer, "{}", i)?,
276 Value::Float(f) => {
277 if f.is_nan() {
279 write!(writer, ".nan")?;
280 } else if f.is_infinite() {
281 if f.is_sign_positive() {
282 write!(writer, ".inf")?;
283 } else {
284 write!(writer, "-.inf")?;
285 }
286 } else {
287 if f.fract() == 0.0 {
289 write!(writer, "{:.1}", f)?;
290 } else {
291 write!(writer, "{}", f)?;
292 }
293 }
294 }
295 Value::String(s) => {
296 self.emit_string_with_style(s, quote_style, writer)?;
297 }
298 _ => return Err(Error::emission("Non-scalar passed to emit_scalar")),
299 }
300
301 if let Some(comments) = comments {
303 if let Some(ref trailing) = comments.trailing {
304 self.emit_trailing_comment(trailing, writer)?;
305 }
306 }
307
308 Ok(())
309 }
310
311 fn emit_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
313 self.emit_string_with_style(s, None, writer)
314 }
315
316 fn emit_string_with_style<W: Write>(
318 &self,
319 s: &str,
320 preferred_style: Option<&QuoteStyle>,
321 writer: &mut W,
322 ) -> Result<()> {
323 match preferred_style {
324 Some(QuoteStyle::Single) => self.emit_single_quoted_string(s, writer),
325 Some(QuoteStyle::Double) => self.emit_double_quoted_string(s, writer),
326 Some(QuoteStyle::Plain) | None => {
327 if self.needs_quoting(s) {
329 self.emit_double_quoted_string(s, writer)
331 } else {
332 write!(writer, "{}", s)?;
333 Ok(())
334 }
335 }
336 }
337 }
338
339 fn needs_quoting(&self, s: &str) -> bool {
341 if s.is_empty() {
342 return true;
343 }
344
345 if s == "null"
347 || s == "~"
348 || s == "true"
349 || s == "false"
350 || s == "yes"
351 || s == "no"
352 || s == "on"
353 || s == "off"
354 || s.parse::<i64>().is_ok()
355 || s.parse::<f64>().is_ok()
356 {
357 return true;
358 }
359
360 if s.chars().any(|c| c == '.') && s.chars().any(|c| c.is_ascii_digit()) {
363 return true;
366 }
367
368 if s.contains('\n')
370 || s.contains('\r')
371 || s.contains('\t')
372 || s.contains('"')
373 || s.contains('\'')
374 || s.contains(':')
375 || s.contains('#')
376 || s.contains('-')
377 || s.contains('[')
378 || s.contains(']')
379 || s.contains('{')
380 || s.contains('}')
381 || s.starts_with(' ')
382 || s.ends_with(' ')
383 {
384 return true;
385 }
386
387 false
388 }
389
390 fn emit_double_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
392 write!(writer, "\"")?;
393 for ch in s.chars() {
394 match ch {
395 '"' => write!(writer, "\\\"")?,
396 '\\' => write!(writer, "\\\\")?,
397 '\n' => write!(writer, "\\n")?,
398 '\r' => write!(writer, "\\r")?,
399 '\t' => write!(writer, "\\t")?,
400 c if c.is_control() => write!(writer, "\\u{:04x}", c as u32)?,
401 c => write!(writer, "{}", c)?,
402 }
403 }
404 write!(writer, "\"")?;
405 Ok(())
406 }
407
408 fn emit_single_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
410 write!(writer, "'")?;
411 for ch in s.chars() {
412 match ch {
413 '\'' => write!(writer, "''")?, c => write!(writer, "{}", c)?,
415 }
416 }
417 write!(writer, "'")?;
418 Ok(())
419 }
420
421 fn emit_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
423 self.emit_double_quoted_string(s, writer)
424 }
425
426 fn emit_sequence<W: Write>(&mut self, seq: &[Value], writer: &mut W) -> Result<()> {
428 if seq.is_empty() {
429 write!(writer, "[]")?;
430 return Ok(());
431 }
432
433 for (index, item) in seq.iter().enumerate() {
434 if index > 0 {
435 writeln!(writer)?;
436 }
437 self.write_indent(writer)?;
438 write!(writer, "- ")?;
439
440 match item {
441 Value::Sequence(_) | Value::Mapping(_) => {
442 writeln!(writer)?; self.current_indent += self.indent;
444 self.emit_value(item, writer)?;
445 self.current_indent -= self.indent;
446 }
447 _ => {
448 self.emit_scalar(item, writer)?;
449 }
450 }
451 }
452
453 Ok(())
454 }
455
456 fn emit_mapping_with_anchor<W: Write>(
458 &mut self,
459 map: &indexmap::IndexMap<Value, Value>,
460 anchor: &str,
461 writer: &mut W,
462 ) -> Result<()> {
463 if map.is_empty() {
464 write!(writer, "&{} {{}}", anchor)?;
465 return Ok(());
466 }
467
468 writeln!(writer, "&{}", anchor)?;
473
474 let mut first = true;
475 for (key, value) in map {
476 if !first {
477 writeln!(writer)?;
478 }
479 first = false;
480
481 self.write_indent(writer)?;
482
483 let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
485
486 if is_complex_key {
487 write!(writer, "? ")?;
489 self.emit_value(key, writer)?;
490 writeln!(writer)?;
491 self.write_indent(writer)?;
492 } else {
493 self.emit_scalar(key, writer)?;
495 }
496
497 write!(writer, ": ")?;
498
499 match value {
500 Value::Sequence(_) | Value::Mapping(_) => {
501 writeln!(writer)?; self.current_indent += self.indent;
503 self.emit_value(value, writer)?;
504 self.current_indent -= self.indent;
505 }
506 _ => {
507 self.emit_scalar(value, writer)?;
508 }
509 }
510 }
511
512 Ok(())
513 }
514
515 fn emit_mapping<W: Write>(
517 &mut self,
518 map: &indexmap::IndexMap<Value, Value>,
519 writer: &mut W,
520 ) -> Result<()> {
521 if map.is_empty() {
522 write!(writer, "{{}}")?;
523 return Ok(());
524 }
525
526 let mut first = true;
527 for (key, value) in map {
528 if !first {
529 writeln!(writer)?;
530 }
531 first = false;
532
533 self.write_indent(writer)?;
534
535 let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
537
538 if is_complex_key {
539 write!(writer, "? ")?;
541 match key {
542 Value::Mapping(map) => {
543 self.emit_mapping_flow_style(map, writer)?;
545 }
546 Value::Sequence(seq) => {
547 self.emit_sequence_flow_style(seq, writer)?;
549 }
550 _ => {
551 self.emit_value(key, writer)?;
552 }
553 }
554 writeln!(writer)?;
555 self.write_indent(writer)?;
556 } else {
557 self.emit_scalar(key, writer)?;
559 }
560
561 write!(writer, ": ")?;
562
563 match value {
564 Value::Sequence(_) | Value::Mapping(_) => {
565 writeln!(writer)?; self.current_indent += self.indent;
567 self.emit_value(value, writer)?;
568 self.current_indent -= self.indent;
569 }
570 _ => {
571 self.emit_scalar(value, writer)?;
572 }
573 }
574 }
575
576 Ok(())
577 }
578
579 fn emit_mapping_flow_style<W: Write>(
581 &self,
582 map: &indexmap::IndexMap<Value, Value>,
583 writer: &mut W,
584 ) -> Result<()> {
585 write!(writer, "{{")?;
586 let mut first = true;
587 for (key, value) in map {
588 if !first {
589 write!(writer, ", ")?;
590 }
591 first = false;
592
593 match key {
595 Value::Mapping(nested_map) => {
596 self.emit_mapping_flow_style(nested_map, writer)?;
597 }
598 Value::Sequence(nested_seq) => {
599 self.emit_sequence_flow_style(nested_seq, writer)?;
600 }
601 _ => {
602 self.emit_scalar(key, writer)?;
603 }
604 }
605
606 write!(writer, ": ")?;
607
608 match value {
610 Value::Mapping(nested_map) => {
611 self.emit_mapping_flow_style(nested_map, writer)?;
612 }
613 Value::Sequence(nested_seq) => {
614 self.emit_sequence_flow_style(nested_seq, writer)?;
615 }
616 _ => {
617 self.emit_scalar(value, writer)?;
618 }
619 }
620 }
621 write!(writer, "}}")?;
622 Ok(())
623 }
624
625 fn emit_sequence_flow_style<W: Write>(&self, seq: &[Value], writer: &mut W) -> Result<()> {
627 write!(writer, "[")?;
628 let mut first = true;
629 for item in seq {
630 if !first {
631 write!(writer, ", ")?;
632 }
633 first = false;
634 match item {
636 Value::Mapping(nested_map) => {
637 self.emit_mapping_flow_style(nested_map, writer)?;
638 }
639 Value::Sequence(nested_seq) => {
640 self.emit_sequence_flow_style(nested_seq, writer)?;
641 }
642 _ => {
643 self.emit_scalar(item, writer)?;
644 }
645 }
646 }
647 write!(writer, "]")?;
648 Ok(())
649 }
650
651 fn emit_value<W: Write>(&mut self, value: &Value, writer: &mut W) -> Result<()> {
653 if let Some(info) = self.shared_values.get(value).cloned() {
655 if info.first_occurrence {
656 match value {
658 Value::Sequence(seq) => {
659 write!(writer, "&{} ", info.anchor_name)?;
660 self.emit_sequence(seq, writer)?;
661 }
662 Value::Mapping(map) => {
663 self.emit_mapping_with_anchor(map, &info.anchor_name, writer)?;
664 }
665 _ => self.emit_scalar(value, writer)?, }
667 if let Some(info_mut) = self.shared_values.get_mut(value) {
669 info_mut.first_occurrence = false;
670 }
671 } else {
672 write!(writer, "*{}", info.anchor_name)?;
674 }
675 } else {
676 match value {
678 Value::Sequence(seq) => self.emit_sequence(seq, writer),
679 Value::Mapping(map) => self.emit_mapping(map, writer),
680 _ => self.emit_scalar(value, writer),
681 }?;
682 }
683 Ok(())
684 }
685
686 fn emit_value_simple<W: Write>(&mut self, value: &Value, writer: &mut W) -> Result<()> {
688 match value {
689 Value::Sequence(seq) => self.emit_sequence(seq, writer),
690 Value::Mapping(map) => self.emit_mapping(map, writer),
691 _ => self.emit_scalar(value, writer),
692 }
693 }
694
695 fn emit_commented_value<W: Write>(
697 &mut self,
698 commented: &CommentedValue,
699 writer: &mut W,
700 ) -> Result<()> {
701 let comments = if commented.has_comments() {
702 Some(&commented.comments)
703 } else {
704 None
705 };
706
707 let quote_style = commented.quote_style();
708
709 match &commented.value {
710 Value::Sequence(_) | Value::Mapping(_) => {
711 if let Some(comments) = comments {
713 self.emit_leading_comments(&comments.leading, writer)?;
714 }
715 self.emit_value(&commented.value, writer)?;
716
717 if let Some(comments) = comments {
719 if let Some(ref trailing) = comments.trailing {
720 writeln!(writer)?;
721 self.write_indent(writer)?;
722 writeln!(writer, "# {}", trailing)?;
723 }
724 }
725 }
726 _ => {
727 self.emit_scalar_with_comments_and_style(
729 &commented.value,
730 comments,
731 quote_style,
732 writer,
733 )?;
734 }
735 }
736
737 Ok(())
738 }
739}
740
741impl Default for BasicEmitter {
742 fn default() -> Self {
743 Self::new()
744 }
745}
746
747impl Emitter for BasicEmitter {
748 fn emit<W: Write>(&mut self, value: &Value, mut writer: W) -> Result<()> {
749 self.current_indent = 0;
751 self.shared_values.clear();
752 self.anchor_counter = 0;
753
754 self.emit_directives(&mut writer)?;
756
757 self.analyze_shared_values(value);
759
760 if matches!(value, Value::Sequence(_)) {
762 writeln!(writer)?;
763 }
764
765 self.emit_value(value, &mut writer)?;
767 writeln!(writer)?; Ok(())
769 }
770
771 fn emit_commented<W: Write>(&mut self, value: &CommentedValue, mut writer: W) -> Result<()> {
772 self.current_indent = 0;
774 self.shared_values.clear();
775 self.anchor_counter = 0;
776
777 self.emit_directives(&mut writer)?;
779
780 self.analyze_shared_values(&value.value);
782
783 self.emit_commented_value(value, &mut writer)?;
785 writeln!(writer)?; Ok(())
787 }
788
789 fn emit_with_style<W: Write>(
790 &mut self,
791 value: &CommentedValue,
792 indent_style: &IndentStyle,
793 mut writer: W,
794 ) -> Result<()> {
795 let original_style = self.indent_style.clone();
797 self.set_indent_style(indent_style.clone());
798
799 self.current_indent = 0;
801 self.shared_values.clear();
802 self.anchor_counter = 0;
803
804 self.emit_directives(&mut writer)?;
806
807 self.analyze_shared_values(&value.value);
809
810 let result = self.emit_commented_value(value, &mut writer);
812 if result.is_ok() {
813 writeln!(writer)?; }
815
816 self.set_indent_style(original_style);
818
819 result
820 }
821
822 fn reset(&mut self) {
823 self.current_indent = 0;
824 self.shared_values.clear();
825 self.anchor_counter = 0;
826 }
828}
829
830#[cfg(test)]
831mod tests {
832 use super::*;
833 use indexmap::IndexMap;
834
835 #[test]
836 fn test_emit_scalar() {
837 let mut emitter = BasicEmitter::new();
838 let mut output = Vec::new();
839
840 emitter.emit(&Value::Int(42), &mut output).unwrap();
841 assert_eq!(String::from_utf8(output).unwrap(), "42\n");
842 }
843
844 #[test]
845 fn test_emit_string() {
846 let mut emitter = BasicEmitter::new();
847 let mut output = Vec::new();
848
849 emitter
850 .emit(&Value::String("hello".to_string()), &mut output)
851 .unwrap();
852 assert_eq!(String::from_utf8(output).unwrap(), "hello\n");
853 }
854
855 #[test]
856 fn test_emit_quoted_string() {
857 let mut emitter = BasicEmitter::new();
858 let mut output = Vec::new();
859
860 emitter
861 .emit(&Value::String("true".to_string()), &mut output)
862 .unwrap();
863 assert_eq!(String::from_utf8(output).unwrap(), "\"true\"\n");
864 }
865
866 #[test]
867 fn test_emit_sequence() {
868 let mut emitter = BasicEmitter::new();
869 let mut output = Vec::new();
870
871 let seq = Value::Sequence(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
872
873 emitter.emit(&seq, &mut output).unwrap();
874 let result = String::from_utf8(output).unwrap();
875 let expected = "\n- 1\n- 2\n- 3\n";
876 assert_eq!(result, expected);
877 }
878
879 #[test]
880 fn test_emit_mapping() {
881 let mut emitter = BasicEmitter::new();
882 let mut output = Vec::new();
883
884 let mut map = IndexMap::new();
885 map.insert(
886 Value::String("key".to_string()),
887 Value::String("value".to_string()),
888 );
889 map.insert(Value::String("number".to_string()), Value::Int(42));
890
891 emitter.emit(&Value::Mapping(map), &mut output).unwrap();
892 let result = String::from_utf8(output).unwrap();
893
894 assert!(result.contains("key: value"));
896 assert!(result.contains("number: 42"));
897 }
898
899 #[test]
900 fn test_emit_nested_structure() {
901 let mut emitter = BasicEmitter::new();
902 let mut output = Vec::new();
903
904 let inner_seq = Value::Sequence(vec![Value::Int(1), Value::Int(2)]);
905 let mut outer_map = IndexMap::new();
906 outer_map.insert(Value::String("items".to_string()), inner_seq);
907
908 emitter
909 .emit(&Value::Mapping(outer_map), &mut output)
910 .unwrap();
911 let result = String::from_utf8(output).unwrap();
912
913 assert!(result.contains("items:"));
914 assert!(result.contains("- 1"));
915 assert!(result.contains("- 2"));
916 }
917}