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 emit_anchors: bool,
46 sequence_indent: Option<usize>,
48}
49
50#[allow(dead_code)]
51impl BasicEmitter {
52 pub fn new() -> Self {
54 Self {
55 indent: 2,
56 current_indent: 0,
57 shared_values: HashMap::new(),
58 anchor_counter: 0,
59 indent_style: IndentStyle::default(),
60 yaml_version: None,
61 tag_directives: Vec::new(),
62 emit_anchors: true,
63 sequence_indent: None,
64 }
65 }
66
67 pub fn with_indent(indent: usize) -> Self {
69 Self {
70 indent,
71 current_indent: 0,
72 shared_values: HashMap::new(),
73 anchor_counter: 0,
74 indent_style: IndentStyle::Spaces(indent),
75 yaml_version: None,
76 tag_directives: Vec::new(),
77 emit_anchors: true,
78 sequence_indent: None,
79 }
80 }
81
82 pub fn with_indent_style(indent_style: IndentStyle) -> Self {
84 let indent = match &indent_style {
85 IndentStyle::Spaces(width) => *width,
86 IndentStyle::Tabs => 1, };
88 Self {
89 indent,
90 current_indent: 0,
91 shared_values: HashMap::new(),
92 anchor_counter: 0,
93 indent_style,
94 yaml_version: None,
95 tag_directives: Vec::new(),
96 emit_anchors: true,
97 sequence_indent: None,
98 }
99 }
100
101 pub fn set_emit_anchors(&mut self, enabled: bool) {
103 self.emit_anchors = enabled;
104 }
105
106 pub fn set_sequence_indent(&mut self, indent: Option<usize>) {
108 self.sequence_indent = indent;
109 }
110
111 fn effective_sequence_indent(&self) -> usize {
113 self.sequence_indent.unwrap_or(self.indent)
114 }
115
116 pub fn set_yaml_version(&mut self, major: u8, minor: u8) {
118 self.yaml_version = Some((major, minor));
119 }
120
121 pub fn add_tag_directive(&mut self, handle: String, prefix: String) {
123 self.tag_directives.push((handle, prefix));
124 }
125
126 pub fn clear_directives(&mut self) {
128 self.yaml_version = None;
129 self.tag_directives.clear();
130 }
131
132 fn emit_directives<W: Write>(&self, writer: &mut W) -> Result<()> {
134 if let Some((major, minor)) = self.yaml_version {
136 writeln!(writer, "%YAML {}.{}", major, minor).map_err(|e| Error::Emission {
137 message: format!("Failed to write YAML directive: {}", e),
138 })?;
139 }
140
141 for (handle, prefix) in &self.tag_directives {
143 writeln!(writer, "%TAG {} {}", handle, prefix).map_err(|e| Error::Emission {
144 message: format!("Failed to write TAG directive: {}", e),
145 })?;
146 }
147
148 if self.yaml_version.is_some() || !self.tag_directives.is_empty() {
150 writeln!(writer, "---").map_err(|e| Error::Emission {
151 message: format!("Failed to write document start marker: {}", e),
152 })?;
153 }
154
155 Ok(())
156 }
157
158 fn analyze_shared_values(&mut self, value: &Value) {
160 let mut value_counts = HashMap::new();
161 self.count_value_occurrences(value, &mut value_counts);
162
163 for (val, count) in value_counts {
165 if count > 1 && self.is_complex_value(&val) {
166 let anchor_name = format!("anchor{}", self.anchor_counter);
167 self.anchor_counter += 1;
168 self.shared_values.insert(
169 val,
170 ValueInfo {
171 anchor_name,
172 first_occurrence: true,
173 },
174 );
175 }
176 }
177 }
178
179 fn count_value_occurrences(&self, value: &Value, counts: &mut HashMap<Value, usize>) {
181 if self.is_complex_value(value) {
183 *counts.entry(value.clone()).or_insert(0) += 1;
184 }
185
186 match value {
188 Value::Sequence(seq) => {
189 for item in seq {
190 self.count_value_occurrences(item, counts);
191 }
192 }
193 Value::Mapping(map) => {
194 for (key, val) in map {
195 self.count_value_occurrences(key, counts);
196 self.count_value_occurrences(val, counts);
197 }
198 }
199 _ => {}
200 }
201 }
202
203 const fn is_complex_value(&self, value: &Value) -> bool {
205 matches!(value, Value::Sequence(_) | Value::Mapping(_))
206 }
207
208 fn next_anchor_name(&mut self) -> String {
210 let name = format!("anchor{}", self.anchor_counter);
211 self.anchor_counter += 1;
212 name
213 }
214
215 pub const fn set_indent_style(&mut self, indent_style: IndentStyle) {
217 self.indent = match &indent_style {
218 IndentStyle::Spaces(width) => *width,
219 IndentStyle::Tabs => 1,
220 };
221 self.indent_style = indent_style;
222 }
223
224 fn write_indent<W: Write>(&self, writer: &mut W) -> Result<()> {
226 match &self.indent_style {
227 IndentStyle::Spaces(_width) => {
228 let total_spaces = self.current_indent;
229 for _ in 0..total_spaces {
230 write!(writer, " ")?;
231 }
232 }
233 IndentStyle::Tabs => {
234 let indent_levels = self.current_indent / self.indent;
235 for _ in 0..indent_levels {
236 write!(writer, "\t")?;
237 }
238 }
239 }
240 Ok(())
241 }
242
243 fn emit_leading_comments<W: Write>(&self, comments: &[String], writer: &mut W) -> Result<()> {
245 for comment in comments {
246 self.write_indent(writer)?;
247 writeln!(writer, "# {}", comment)?;
248 }
249 Ok(())
250 }
251
252 fn emit_trailing_comment<W: Write>(&self, comment: &str, writer: &mut W) -> Result<()> {
254 write!(writer, " # {}", comment)?;
255 Ok(())
256 }
257
258 fn emit_inner_comments<W: Write>(&self, comments: &[String], writer: &mut W) -> Result<()> {
260 for comment in comments {
261 writeln!(writer)?;
262 self.write_indent(writer)?;
263 writeln!(writer, "# {}", comment)?;
264 }
265 Ok(())
266 }
267
268 fn emit_scalar<W: Write>(&self, value: &Value, writer: &mut W) -> Result<()> {
270 self.emit_scalar_with_comments(value, None, writer)
271 }
272
273 fn emit_scalar_with_comments<W: Write>(
275 &self,
276 value: &Value,
277 comments: Option<&Comments>,
278 writer: &mut W,
279 ) -> Result<()> {
280 self.emit_scalar_with_comments_and_style(value, comments, None, writer)
281 }
282
283 fn emit_scalar_with_comments_and_style<W: Write>(
285 &self,
286 value: &Value,
287 comments: Option<&Comments>,
288 quote_style: Option<&QuoteStyle>,
289 writer: &mut W,
290 ) -> Result<()> {
291 if let Some(comments) = comments {
293 self.emit_leading_comments(&comments.leading, writer)?;
294 }
295
296 match value {
298 Value::Null => write!(writer, "null")?,
299 Value::Bool(b) => write!(writer, "{}", b)?,
300 Value::Int(i) => write!(writer, "{}", i)?,
301 Value::Float(f) => {
302 if f.is_nan() {
304 write!(writer, ".nan")?;
305 } else if f.is_infinite() {
306 if f.is_sign_positive() {
307 write!(writer, ".inf")?;
308 } else {
309 write!(writer, "-.inf")?;
310 }
311 } else {
312 if f.fract() == 0.0 {
314 write!(writer, "{:.1}", f)?;
315 } else {
316 write!(writer, "{}", f)?;
317 }
318 }
319 }
320 Value::String(s) => {
321 self.emit_string_with_style(s, quote_style, writer)?;
322 }
323 _ => return Err(Error::emission("Non-scalar passed to emit_scalar")),
324 }
325
326 if let Some(comments) = comments {
328 if let Some(ref trailing) = comments.trailing {
329 self.emit_trailing_comment(trailing, writer)?;
330 }
331 }
332
333 Ok(())
334 }
335
336 fn emit_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
338 self.emit_string_with_style(s, None, writer)
339 }
340
341 fn emit_string_with_style<W: Write>(
343 &self,
344 s: &str,
345 preferred_style: Option<&QuoteStyle>,
346 writer: &mut W,
347 ) -> Result<()> {
348 match preferred_style {
349 Some(QuoteStyle::Single) => self.emit_single_quoted_string(s, writer),
350 Some(QuoteStyle::Double) => self.emit_double_quoted_string(s, writer),
351 Some(QuoteStyle::Plain) | None => {
352 if self.needs_quoting(s) {
354 self.emit_double_quoted_string(s, writer)
356 } else {
357 write!(writer, "{}", s)?;
358 Ok(())
359 }
360 }
361 }
362 }
363
364 fn needs_quoting(&self, s: &str) -> bool {
366 if s.is_empty() {
367 return true;
368 }
369
370 let lower = s.to_ascii_lowercase();
372 if lower == "null"
373 || lower == "~"
374 || lower == "true"
375 || lower == "false"
376 || lower == "yes"
377 || lower == "no"
378 || lower == "on"
379 || lower == "off"
380 || s.parse::<i64>().is_ok()
381 || s.parse::<f64>().is_ok()
382 {
383 return true;
384 }
385
386 if s.starts_with(' ') || s.ends_with(' ') {
388 return true;
389 }
390
391 if s.contains('\n') || s.contains('\r') || s.contains('\t') {
393 return true;
394 }
395
396 let first = s.as_bytes()[0];
399 if matches!(
400 first,
401 b'[' | b']'
402 | b'{'
403 | b'}'
404 | b'"'
405 | b'\''
406 | b'|'
407 | b'>'
408 | b'!'
409 | b'&'
410 | b'*'
411 | b'?'
412 | b'%'
413 | b','
414 | b'@'
415 | b'`'
416 | b'#'
417 ) {
418 return true;
419 }
420
421 if first == b'-' && (s.len() == 1 || s.as_bytes()[1] == b' ') {
423 return true;
424 }
425
426 if s.contains(": ") || s.ends_with(':') {
428 return true;
429 }
430
431 if s.contains(" #") {
433 return true;
434 }
435
436 false
437 }
438
439 fn emit_double_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
441 write!(writer, "\"")?;
442 for ch in s.chars() {
443 match ch {
444 '"' => write!(writer, "\\\"")?,
445 '\\' => write!(writer, "\\\\")?,
446 '\n' => write!(writer, "\\n")?,
447 '\r' => write!(writer, "\\r")?,
448 '\t' => write!(writer, "\\t")?,
449 c if c.is_control() => write!(writer, "\\u{:04x}", c as u32)?,
450 c => write!(writer, "{}", c)?,
451 }
452 }
453 write!(writer, "\"")?;
454 Ok(())
455 }
456
457 fn emit_single_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
459 write!(writer, "'")?;
460 for ch in s.chars() {
461 match ch {
462 '\'' => write!(writer, "''")?, c => write!(writer, "{}", c)?,
464 }
465 }
466 write!(writer, "'")?;
467 Ok(())
468 }
469
470 fn emit_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
472 self.emit_double_quoted_string(s, writer)
473 }
474
475 fn emit_sequence<W: Write>(&mut self, seq: &[Value], writer: &mut W) -> Result<()> {
477 if seq.is_empty() {
478 write!(writer, "[]")?;
479 return Ok(());
480 }
481
482 for (index, item) in seq.iter().enumerate() {
483 if index > 0 {
484 writeln!(writer)?;
485 }
486 self.write_indent(writer)?;
487 write!(writer, "- ")?;
488 self.emit_sequence_item_body(item, writer)?;
489 }
490
491 Ok(())
492 }
493
494 fn emit_sequence_item_body<W: Write>(&mut self, item: &Value, writer: &mut W) -> Result<()> {
500 match item {
501 Value::Sequence(seq) if seq.is_empty() => {
502 write!(writer, "[]")?;
503 return Ok(());
504 }
505 Value::Mapping(map) if map.is_empty() => {
506 write!(writer, "{{}}")?;
507 return Ok(());
508 }
509 _ => {}
510 }
511
512 if matches!(item, Value::Sequence(_) | Value::Mapping(_)) {
513 if let Some(info) = self.shared_values.get(item).cloned() {
514 if !info.first_occurrence {
515 write!(writer, "*{}", info.anchor_name)?;
516 return Ok(());
517 }
518 writeln!(writer, "&{}", info.anchor_name)?;
519 if let Some(info_mut) = self.shared_values.get_mut(item) {
520 info_mut.first_occurrence = false;
521 }
522 self.current_indent += self.indent;
523 match item {
524 Value::Sequence(s) => self.emit_sequence(s, writer)?,
525 Value::Mapping(m) => self.emit_mapping(m, writer)?,
526 _ => unreachable!(),
527 }
528 self.current_indent -= self.indent;
529 return Ok(());
530 }
531 }
532
533 match item {
534 Value::Mapping(map) => {
535 self.current_indent += self.indent;
536 self.emit_mapping_inner(map, writer, true)?;
537 self.current_indent -= self.indent;
538 }
539 Value::Sequence(_) => {
540 writeln!(writer)?;
541 self.current_indent += self.indent;
542 self.emit_value(item, writer)?;
543 self.current_indent -= self.indent;
544 }
545 _ => {
546 self.emit_scalar(item, writer)?;
547 }
548 }
549 Ok(())
550 }
551
552 fn emit_pair_value<W: Write>(&mut self, value: &Value, writer: &mut W) -> Result<()> {
558 match value {
559 Value::Sequence(seq) if seq.is_empty() => {
560 write!(writer, ": []")?;
561 return Ok(());
562 }
563 Value::Mapping(map) if map.is_empty() => {
564 write!(writer, ": {{}}")?;
565 return Ok(());
566 }
567 _ => {}
568 }
569
570 if matches!(value, Value::Sequence(_) | Value::Mapping(_)) {
571 if let Some(info) = self.shared_values.get(value).cloned() {
572 if !info.first_occurrence {
573 write!(writer, ": *{}", info.anchor_name)?;
574 return Ok(());
575 }
576 writeln!(writer, ": &{}", info.anchor_name)?;
577 if let Some(info_mut) = self.shared_values.get_mut(value) {
578 info_mut.first_occurrence = false;
579 }
580 let bump = match value {
581 Value::Sequence(_) => self.effective_sequence_indent(),
582 _ => self.indent,
583 };
584 self.current_indent += bump;
585 match value {
586 Value::Sequence(s) => self.emit_sequence(s, writer)?,
587 Value::Mapping(m) => self.emit_mapping(m, writer)?,
588 _ => unreachable!(),
589 }
590 self.current_indent -= bump;
591 return Ok(());
592 }
593 }
594
595 match value {
596 Value::Sequence(_) => {
597 writeln!(writer, ":")?;
598 let bump = self.effective_sequence_indent();
599 self.current_indent += bump;
600 self.emit_value(value, writer)?;
601 self.current_indent -= bump;
602 }
603 Value::Mapping(_) => {
604 writeln!(writer, ":")?;
605 self.current_indent += self.indent;
606 self.emit_value(value, writer)?;
607 self.current_indent -= self.indent;
608 }
609 _ => {
610 write!(writer, ": ")?;
611 self.emit_scalar(value, writer)?;
612 }
613 }
614 Ok(())
615 }
616
617 fn emit_mapping_with_anchor<W: Write>(
619 &mut self,
620 map: &indexmap::IndexMap<Value, Value>,
621 anchor: &str,
622 writer: &mut W,
623 ) -> Result<()> {
624 if map.is_empty() {
625 write!(writer, "&{} {{}}", anchor)?;
626 return Ok(());
627 }
628
629 writeln!(writer, "&{}", anchor)?;
634
635 let mut first = true;
636 for (key, value) in map {
637 if !first {
638 writeln!(writer)?;
639 }
640 first = false;
641
642 self.write_indent(writer)?;
643
644 let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
646
647 if is_complex_key {
648 write!(writer, "? ")?;
650 self.emit_value(key, writer)?;
651 writeln!(writer)?;
652 self.write_indent(writer)?;
653 } else {
654 self.emit_scalar(key, writer)?;
656 }
657
658 self.emit_pair_value(value, writer)?;
659 }
660
661 Ok(())
662 }
663
664 fn emit_mapping<W: Write>(
666 &mut self,
667 map: &indexmap::IndexMap<Value, Value>,
668 writer: &mut W,
669 ) -> Result<()> {
670 self.emit_mapping_inner(map, writer, false)
671 }
672
673 fn emit_mapping_inner<W: Write>(
676 &mut self,
677 map: &indexmap::IndexMap<Value, Value>,
678 writer: &mut W,
679 skip_first_indent: bool,
680 ) -> Result<()> {
681 if map.is_empty() {
682 write!(writer, "{{}}")?;
683 return Ok(());
684 }
685
686 let mut first = true;
687 for (key, value) in map {
688 if !first {
689 writeln!(writer)?;
690 }
691
692 if !first || !skip_first_indent {
693 self.write_indent(writer)?;
694 }
695 first = false;
696
697 let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
699
700 if is_complex_key {
701 write!(writer, "? ")?;
703 match key {
704 Value::Mapping(map) => {
705 self.emit_mapping_flow_style(map, writer)?;
706 }
707 Value::Sequence(seq) => {
708 self.emit_sequence_flow_style(seq, writer)?;
709 }
710 _ => {
711 self.emit_value(key, writer)?;
712 }
713 }
714 writeln!(writer)?;
715 self.write_indent(writer)?;
716 } else {
717 self.emit_scalar(key, writer)?;
719 }
720
721 self.emit_pair_value(value, writer)?;
722 }
723
724 Ok(())
725 }
726
727 fn emit_mapping_flow_style<W: Write>(
729 &self,
730 map: &indexmap::IndexMap<Value, Value>,
731 writer: &mut W,
732 ) -> Result<()> {
733 write!(writer, "{{")?;
734 let mut first = true;
735 for (key, value) in map {
736 if !first {
737 write!(writer, ", ")?;
738 }
739 first = false;
740
741 match key {
743 Value::Mapping(nested_map) => {
744 self.emit_mapping_flow_style(nested_map, writer)?;
745 }
746 Value::Sequence(nested_seq) => {
747 self.emit_sequence_flow_style(nested_seq, writer)?;
748 }
749 _ => {
750 self.emit_scalar(key, writer)?;
751 }
752 }
753
754 write!(writer, ": ")?;
755
756 match value {
758 Value::Mapping(nested_map) => {
759 self.emit_mapping_flow_style(nested_map, writer)?;
760 }
761 Value::Sequence(nested_seq) => {
762 self.emit_sequence_flow_style(nested_seq, writer)?;
763 }
764 _ => {
765 self.emit_scalar(value, writer)?;
766 }
767 }
768 }
769 write!(writer, "}}")?;
770 Ok(())
771 }
772
773 fn emit_sequence_flow_style<W: Write>(&self, seq: &[Value], writer: &mut W) -> Result<()> {
775 write!(writer, "[")?;
776 let mut first = true;
777 for item in seq {
778 if !first {
779 write!(writer, ", ")?;
780 }
781 first = false;
782 match item {
784 Value::Mapping(nested_map) => {
785 self.emit_mapping_flow_style(nested_map, writer)?;
786 }
787 Value::Sequence(nested_seq) => {
788 self.emit_sequence_flow_style(nested_seq, writer)?;
789 }
790 _ => {
791 self.emit_scalar(item, writer)?;
792 }
793 }
794 }
795 write!(writer, "]")?;
796 Ok(())
797 }
798
799 fn emit_value<W: Write>(&mut self, value: &Value, writer: &mut W) -> Result<()> {
801 if let Some(info) = self.shared_values.get(value).cloned() {
803 if info.first_occurrence {
804 match value {
808 Value::Sequence(seq) => {
809 writeln!(writer, "&{}", info.anchor_name)?;
810 self.write_indent(writer)?;
811 self.emit_sequence(seq, writer)?;
812 }
813 Value::Mapping(map) => {
814 self.emit_mapping_with_anchor(map, &info.anchor_name, writer)?;
815 }
816 _ => self.emit_scalar(value, writer)?, }
818 if let Some(info_mut) = self.shared_values.get_mut(value) {
820 info_mut.first_occurrence = false;
821 }
822 } else {
823 write!(writer, "*{}", info.anchor_name)?;
825 }
826 } else {
827 match value {
829 Value::Sequence(seq) => self.emit_sequence(seq, writer),
830 Value::Mapping(map) => self.emit_mapping(map, writer),
831 _ => self.emit_scalar(value, writer),
832 }?;
833 }
834 Ok(())
835 }
836
837 fn emit_value_simple<W: Write>(&mut self, value: &Value, writer: &mut W) -> Result<()> {
839 match value {
840 Value::Sequence(seq) => self.emit_sequence(seq, writer),
841 Value::Mapping(map) => self.emit_mapping(map, writer),
842 _ => self.emit_scalar(value, writer),
843 }
844 }
845
846 fn emit_commented_value<W: Write>(
848 &mut self,
849 commented: &CommentedValue,
850 writer: &mut W,
851 ) -> Result<()> {
852 let comments = if commented.has_comments() {
853 Some(&commented.comments)
854 } else {
855 None
856 };
857
858 let quote_style = commented.quote_style();
859
860 match &commented.value {
861 Value::Sequence(_) | Value::Mapping(_) => {
862 if let Some(comments) = comments {
864 self.emit_leading_comments(&comments.leading, writer)?;
865 }
866 self.emit_value(&commented.value, writer)?;
867
868 if let Some(comments) = comments {
870 if !comments.inner.is_empty() {
871 self.emit_inner_comments(&comments.inner, writer)?;
872 }
873 }
874
875 if let Some(comments) = comments {
877 if let Some(ref trailing) = comments.trailing {
878 writeln!(writer)?;
879 self.write_indent(writer)?;
880 writeln!(writer, "# {}", trailing)?;
881 }
882 }
883 }
884 _ => {
885 self.emit_scalar_with_comments_and_style(
887 &commented.value,
888 comments,
889 quote_style,
890 writer,
891 )?;
892 }
893 }
894
895 Ok(())
896 }
897
898 pub fn emit_commented_value_public<W: Write>(
900 &mut self,
901 commented: &CommentedValue,
902 writer: W,
903 ) -> Result<()> {
904 let mut writer = writer;
905 self.emit_commented_value(commented, &mut writer)
906 }
907}
908
909impl Default for BasicEmitter {
910 fn default() -> Self {
911 Self::new()
912 }
913}
914
915impl Emitter for BasicEmitter {
916 fn emit<W: Write>(&mut self, value: &Value, mut writer: W) -> Result<()> {
917 self.current_indent = 0;
919 self.shared_values.clear();
920 self.anchor_counter = 0;
921
922 self.emit_directives(&mut writer)?;
924
925 if self.emit_anchors {
927 self.analyze_shared_values(value);
928 }
929
930 if matches!(value, Value::Sequence(_)) {
932 writeln!(writer)?;
933 }
934
935 self.emit_value(value, &mut writer)?;
937 writeln!(writer)?; Ok(())
939 }
940
941 fn emit_commented<W: Write>(&mut self, value: &CommentedValue, mut writer: W) -> Result<()> {
942 self.current_indent = 0;
944 self.shared_values.clear();
945 self.anchor_counter = 0;
946
947 self.emit_directives(&mut writer)?;
949
950 if self.emit_anchors {
952 self.analyze_shared_values(&value.value);
953 }
954
955 self.emit_commented_value(value, &mut writer)?;
957 writeln!(writer)?; Ok(())
959 }
960
961 fn emit_with_style<W: Write>(
962 &mut self,
963 value: &CommentedValue,
964 indent_style: &IndentStyle,
965 mut writer: W,
966 ) -> Result<()> {
967 let original_style = self.indent_style.clone();
969 self.set_indent_style(indent_style.clone());
970
971 self.current_indent = 0;
973 self.shared_values.clear();
974 self.anchor_counter = 0;
975
976 self.emit_directives(&mut writer)?;
978
979 if self.emit_anchors {
981 self.analyze_shared_values(&value.value);
982 }
983
984 let result = self.emit_commented_value(value, &mut writer);
986 if result.is_ok() {
987 writeln!(writer)?; }
989
990 self.set_indent_style(original_style);
992
993 result
994 }
995
996 fn reset(&mut self) {
997 self.current_indent = 0;
998 self.shared_values.clear();
999 self.anchor_counter = 0;
1000 }
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006 use super::*;
1007 use indexmap::IndexMap;
1008
1009 #[test]
1010 fn test_emit_scalar() {
1011 let mut emitter = BasicEmitter::new();
1012 let mut output = Vec::new();
1013
1014 emitter.emit(&Value::Int(42), &mut output).unwrap();
1015 assert_eq!(String::from_utf8(output).unwrap(), "42\n");
1016 }
1017
1018 #[test]
1019 fn test_emit_string() {
1020 let mut emitter = BasicEmitter::new();
1021 let mut output = Vec::new();
1022
1023 emitter
1024 .emit(&Value::String("hello".to_string()), &mut output)
1025 .unwrap();
1026 assert_eq!(String::from_utf8(output).unwrap(), "hello\n");
1027 }
1028
1029 #[test]
1030 fn test_emit_quoted_string() {
1031 let mut emitter = BasicEmitter::new();
1032 let mut output = Vec::new();
1033
1034 emitter
1035 .emit(&Value::String("true".to_string()), &mut output)
1036 .unwrap();
1037 assert_eq!(String::from_utf8(output).unwrap(), "\"true\"\n");
1038 }
1039
1040 #[test]
1041 fn test_emit_sequence() {
1042 let mut emitter = BasicEmitter::new();
1043 let mut output = Vec::new();
1044
1045 let seq = Value::Sequence(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
1046
1047 emitter.emit(&seq, &mut output).unwrap();
1048 let result = String::from_utf8(output).unwrap();
1049 let expected = "\n- 1\n- 2\n- 3\n";
1050 assert_eq!(result, expected);
1051 }
1052
1053 #[test]
1054 fn test_emit_mapping() {
1055 let mut emitter = BasicEmitter::new();
1056 let mut output = Vec::new();
1057
1058 let mut map = IndexMap::new();
1059 map.insert(
1060 Value::String("key".to_string()),
1061 Value::String("value".to_string()),
1062 );
1063 map.insert(Value::String("number".to_string()), Value::Int(42));
1064
1065 emitter.emit(&Value::Mapping(map), &mut output).unwrap();
1066 let result = String::from_utf8(output).unwrap();
1067
1068 assert!(result.contains("key: value"));
1070 assert!(result.contains("number: 42"));
1071 }
1072
1073 #[test]
1074 fn test_emit_nested_structure() {
1075 let mut emitter = BasicEmitter::new();
1076 let mut output = Vec::new();
1077
1078 let inner_seq = Value::Sequence(vec![Value::Int(1), Value::Int(2)]);
1079 let mut outer_map = IndexMap::new();
1080 outer_map.insert(Value::String("items".to_string()), inner_seq);
1081
1082 emitter
1083 .emit(&Value::Mapping(outer_map), &mut output)
1084 .unwrap();
1085 let result = String::from_utf8(output).unwrap();
1086
1087 assert!(result.contains("items:"));
1088 assert!(result.contains("- 1"));
1089 assert!(result.contains("- 2"));
1090 }
1091}