Skip to main content

rust_yaml/
emitter.rs

1//! YAML emitter for generating text output
2
3use crate::{CommentedValue, Comments, Error, IndentStyle, QuoteStyle, Result, Value};
4use std::collections::HashMap;
5use std::io::Write;
6
7/// Trait for YAML emitters that generate text output from values
8pub trait Emitter {
9    /// Emit a value to the output
10    fn emit<W: Write>(&mut self, value: &Value, writer: W) -> Result<()>;
11
12    /// Emit a commented value to the output with comment preservation
13    fn emit_commented<W: Write>(&mut self, value: &CommentedValue, writer: W) -> Result<()>;
14
15    /// Emit a commented value with specific indent style
16    fn emit_with_style<W: Write>(
17        &mut self,
18        value: &CommentedValue,
19        indent_style: &IndentStyle,
20        writer: W,
21    ) -> Result<()>;
22
23    /// Reset the emitter state
24    fn reset(&mut self);
25}
26
27/// Information about shared values for anchor/alias emission
28#[derive(Debug, Clone)]
29struct ValueInfo {
30    anchor_name: String,
31    first_occurrence: bool,
32}
33
34/// Basic emitter implementation that generates clean YAML
35#[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    /// Whether to automatically detect shared values and emit anchors/aliases
45    emit_anchors: bool,
46    /// Indentation for sequence items (None = same as indent)
47    sequence_indent: Option<usize>,
48}
49
50#[allow(dead_code)]
51impl BasicEmitter {
52    /// Create a new emitter with default settings
53    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    /// Create an emitter with custom indent
68    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    /// Create an emitter with specific indent style
83    pub fn with_indent_style(indent_style: IndentStyle) -> Self {
84        let indent = match &indent_style {
85            IndentStyle::Spaces(width) => *width,
86            IndentStyle::Tabs => 1, // Tabs count as 1 indent level
87        };
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    /// Enable or disable automatic anchor/alias emission for shared values
102    pub fn set_emit_anchors(&mut self, enabled: bool) {
103        self.emit_anchors = enabled;
104    }
105
106    /// Set the indentation for sequence items (None = same as base indent)
107    pub fn set_sequence_indent(&mut self, indent: Option<usize>) {
108        self.sequence_indent = indent;
109    }
110
111    /// Get the effective sequence indentation
112    fn effective_sequence_indent(&self) -> usize {
113        self.sequence_indent.unwrap_or(self.indent)
114    }
115
116    /// Set the YAML version directive
117    pub fn set_yaml_version(&mut self, major: u8, minor: u8) {
118        self.yaml_version = Some((major, minor));
119    }
120
121    /// Add a TAG directive
122    pub fn add_tag_directive(&mut self, handle: String, prefix: String) {
123        self.tag_directives.push((handle, prefix));
124    }
125
126    /// Clear all directives
127    pub fn clear_directives(&mut self) {
128        self.yaml_version = None;
129        self.tag_directives.clear();
130    }
131
132    /// Emit directives to the writer
133    fn emit_directives<W: Write>(&self, writer: &mut W) -> Result<()> {
134        // Emit YAML version directive if set
135        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        // Emit TAG directives
142        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 we emitted any directives, emit document start marker
149        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    /// Analyze the value tree to identify shared values that need anchors
159    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        // Generate anchors for values that occur more than once and are complex
164        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    /// Recursively count occurrences of each value
180    fn count_value_occurrences(&self, value: &Value, counts: &mut HashMap<Value, usize>) {
181        // Only track complex values (sequences and mappings)
182        if self.is_complex_value(value) {
183            *counts.entry(value.clone()).or_insert(0) += 1;
184        }
185
186        // Recurse into child values
187        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    /// Check if a value is complex enough to warrant anchor/alias handling
204    const fn is_complex_value(&self, value: &Value) -> bool {
205        matches!(value, Value::Sequence(_) | Value::Mapping(_))
206    }
207
208    /// Generate next anchor name
209    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    /// Update the indent style (useful for round-trip preservation)
216    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    /// Write indentation to the output
225    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    /// Write leading comments to the output
244    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    /// Write a trailing comment on the same line
253    fn emit_trailing_comment<W: Write>(&self, comment: &str, writer: &mut W) -> Result<()> {
254        write!(writer, " # {}", comment)?;
255        Ok(())
256    }
257
258    /// Write inner comments (between collection items)
259    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    /// Emit a scalar value
269    fn emit_scalar<W: Write>(&self, value: &Value, writer: &mut W) -> Result<()> {
270        self.emit_scalar_with_comments(value, None, writer)
271    }
272
273    /// Emit a scalar value with optional comments and style
274    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    /// Emit a scalar value with optional comments and style information
284    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        // Emit leading comments
292        if let Some(comments) = comments {
293            self.emit_leading_comments(&comments.leading, writer)?;
294        }
295
296        // Emit the scalar value
297        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                // Handle special float values
303                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                    // Ensure the float is written with decimal point to preserve type
313                    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        // Emit trailing comment
327        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    /// Emit a string, choosing appropriate quoting style
337    fn emit_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
338        self.emit_string_with_style(s, None, writer)
339    }
340
341    /// Emit a string with specific quote style
342    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                // Check if string needs quoting
353                if self.needs_quoting(s) {
354                    // Default to double quotes when quoting is needed
355                    self.emit_double_quoted_string(s, writer)
356                } else {
357                    write!(writer, "{}", s)?;
358                    Ok(())
359                }
360            }
361        }
362    }
363
364    /// Check if a string needs to be quoted
365    fn needs_quoting(&self, s: &str) -> bool {
366        if s.is_empty() {
367            return true;
368        }
369
370        // String needs quoting if it could be interpreted as another type
371        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        // Starts or ends with whitespace
387        if s.starts_with(' ') || s.ends_with(' ') {
388            return true;
389        }
390
391        // Contains literal newlines or tabs
392        if s.contains('\n') || s.contains('\r') || s.contains('\t') {
393            return true;
394        }
395
396        // First character is a YAML indicator that would be consumed by the
397        // scanner before reaching plain-scalar scanning.
398        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        // `- ` or lone `-` at the start would be a block entry indicator
422        if first == b'-' && (s.len() == 1 || s.as_bytes()[1] == b' ') {
423            return true;
424        }
425
426        // `: ` anywhere, or trailing `:`, would be a key-value separator
427        if s.contains(": ") || s.ends_with(':') {
428            return true;
429        }
430
431        // ` #` would start an inline comment
432        if s.contains(" #") {
433            return true;
434        }
435
436        false
437    }
438
439    /// Emit a double-quoted string
440    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    /// Emit a single-quoted string
458    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, "''")?, // In YAML, single quotes are escaped by doubling
463                c => write!(writer, "{}", c)?,
464            }
465        }
466        write!(writer, "'")?;
467        Ok(())
468    }
469
470    /// Emit a quoted string (legacy method)
471    fn emit_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
472        self.emit_double_quoted_string(s, writer)
473    }
474
475    /// Emit a sequence (array/list)
476    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    /// Emit a sequence item after the caller has written `- ` at the proper indent.
495    ///
496    /// Handles anchor/alias placement so anchored block collections render as
497    /// `- &anchor` followed by their body on the next lines (avoiding the
498    /// invalid `&anchor   - item` pattern the scanner rejects).
499    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    /// Emit the value portion of a mapping pair. Caller has already written the key.
553    ///
554    /// Handles anchor/alias placement so anchored block collections render as
555    /// `key: &anchor` followed by their body on the next lines, and aliases as
556    /// `key: *anchor` on the same line.
557    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    /// Emit a mapping with an anchor
618    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        // For anchored mappings, we need to format it as:
630        // &anchor
631        // key1: value1
632        // key2: value2
633        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            // Handle both simple and complex keys
645            let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
646
647            if is_complex_key {
648                // Complex key - emit with explicit key marker
649                write!(writer, "? ")?;
650                self.emit_value(key, writer)?;
651                writeln!(writer)?;
652                self.write_indent(writer)?;
653            } else {
654                // Simple key
655                self.emit_scalar(key, writer)?;
656            }
657
658            self.emit_pair_value(value, writer)?;
659        }
660
661        Ok(())
662    }
663
664    /// Emit a mapping (dictionary/object)
665    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    /// Emit a mapping, optionally skipping indentation on the first entry
674    /// (used when the first entry follows "- " in a sequence)
675    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            // Handle both simple and complex keys
698            let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
699
700            if is_complex_key {
701                // Complex key - emit with explicit key marker and flow style
702                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                // Simple key
718                self.emit_scalar(key, writer)?;
719            }
720
721            self.emit_pair_value(value, writer)?;
722        }
723
724        Ok(())
725    }
726
727    /// Emit a mapping in flow style for complex keys
728    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            // Emit key (handle nested complex values)
742            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            // Emit value (handle nested complex values)
757            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    /// Emit a sequence in flow style for complex keys
774    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            // Handle nested complex values
783            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    /// Emit any value, dispatching to the appropriate method with anchor/alias support
800    fn emit_value<W: Write>(&mut self, value: &Value, writer: &mut W) -> Result<()> {
801        // Check if this value has an anchor/alias
802        if let Some(info) = self.shared_values.get(value).cloned() {
803            if info.first_occurrence {
804                // First occurrence: emit with anchor on its own line so the
805                // following block collection cannot accidentally land on the
806                // same line as the property (which the scanner rejects).
807                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)?, // Scalars shouldn't be shared
817                }
818                // Mark as no longer first occurrence
819                if let Some(info_mut) = self.shared_values.get_mut(value) {
820                    info_mut.first_occurrence = false;
821                }
822            } else {
823                // Subsequent occurrence: emit alias
824                write!(writer, "*{}", info.anchor_name)?;
825            }
826        } else {
827            // Regular value without sharing
828            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    /// Emit any value (old method for backwards compatibility)
838    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    /// Emit a commented value with full comment and style support
847    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                // For collections, emit leading comments first
863                if let Some(comments) = comments {
864                    self.emit_leading_comments(&comments.leading, writer)?;
865                }
866                self.emit_value(&commented.value, writer)?;
867
868                // Emit inner comments for collections
869                if let Some(comments) = comments {
870                    if !comments.inner.is_empty() {
871                        self.emit_inner_comments(&comments.inner, writer)?;
872                    }
873                }
874
875                // Trailing comments for collections go on a new line
876                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                // For scalars, use the scalar comment and style method
886                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    /// Emit a CommentedValue with comment preservation (public API)
899    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        // Reset state
918        self.current_indent = 0;
919        self.shared_values.clear();
920        self.anchor_counter = 0;
921
922        // Emit directives if any
923        self.emit_directives(&mut writer)?;
924
925        // Analyze for shared values only if anchors are enabled
926        if self.emit_anchors {
927            self.analyze_shared_values(value);
928        }
929
930        // For top-level sequences, add a leading newline for proper formatting
931        if matches!(value, Value::Sequence(_)) {
932            writeln!(writer)?;
933        }
934
935        // Emit the value
936        self.emit_value(value, &mut writer)?;
937        writeln!(writer)?; // Add final newline
938        Ok(())
939    }
940
941    fn emit_commented<W: Write>(&mut self, value: &CommentedValue, mut writer: W) -> Result<()> {
942        // Reset state
943        self.current_indent = 0;
944        self.shared_values.clear();
945        self.anchor_counter = 0;
946
947        // Emit directives if any
948        self.emit_directives(&mut writer)?;
949
950        // Analyze for shared values only if anchors are enabled
951        if self.emit_anchors {
952            self.analyze_shared_values(&value.value);
953        }
954
955        // Emit the commented value
956        self.emit_commented_value(value, &mut writer)?;
957        writeln!(writer)?; // Add final newline
958        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        // Store current style and temporarily update
968        let original_style = self.indent_style.clone();
969        self.set_indent_style(indent_style.clone());
970
971        // Reset state
972        self.current_indent = 0;
973        self.shared_values.clear();
974        self.anchor_counter = 0;
975
976        // Emit directives if any
977        self.emit_directives(&mut writer)?;
978
979        // Analyze for shared values only if anchors are enabled
980        if self.emit_anchors {
981            self.analyze_shared_values(&value.value);
982        }
983
984        // Emit the commented value with the specified style
985        let result = self.emit_commented_value(value, &mut writer);
986        if result.is_ok() {
987            writeln!(writer)?; // Add final newline
988        }
989
990        // Restore original style
991        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        // Note: We don't reset directives here as they might need to persist
1001    }
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        // Should contain the key-value pairs
1069        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}