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}
45
46#[allow(dead_code)]
47impl BasicEmitter {
48    /// Create a new emitter with default settings
49    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    /// Create an emitter with custom indent
62    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    /// Create an emitter with specific indent style
75    pub fn with_indent_style(indent_style: IndentStyle) -> Self {
76        let indent = match &indent_style {
77            IndentStyle::Spaces(width) => *width,
78            IndentStyle::Tabs => 1, // Tabs count as 1 indent level
79        };
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    /// Set the YAML version directive
92    pub fn set_yaml_version(&mut self, major: u8, minor: u8) {
93        self.yaml_version = Some((major, minor));
94    }
95
96    /// Add a TAG directive
97    pub fn add_tag_directive(&mut self, handle: String, prefix: String) {
98        self.tag_directives.push((handle, prefix));
99    }
100
101    /// Clear all directives
102    pub fn clear_directives(&mut self) {
103        self.yaml_version = None;
104        self.tag_directives.clear();
105    }
106
107    /// Emit directives to the writer
108    fn emit_directives<W: Write>(&self, writer: &mut W) -> Result<()> {
109        // Emit YAML version directive if set
110        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        // Emit TAG directives
117        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 we emitted any directives, emit document start marker
124        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    /// Analyze the value tree to identify shared values that need anchors
134    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        // Generate anchors for values that occur more than once and are complex
139        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    /// Recursively count occurrences of each value
155    fn count_value_occurrences(&self, value: &Value, counts: &mut HashMap<Value, usize>) {
156        // Only track complex values (sequences and mappings)
157        if self.is_complex_value(value) {
158            *counts.entry(value.clone()).or_insert(0) += 1;
159        }
160
161        // Recurse into child values
162        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    /// Check if a value is complex enough to warrant anchor/alias handling
179    const fn is_complex_value(&self, value: &Value) -> bool {
180        matches!(value, Value::Sequence(_) | Value::Mapping(_))
181    }
182
183    /// Generate next anchor name
184    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    /// Update the indent style (useful for round-trip preservation)
191    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    /// Write indentation to the output
200    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    /// Write leading comments to the output
219    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    /// Write a trailing comment on the same line
228    fn emit_trailing_comment<W: Write>(&self, comment: &str, writer: &mut W) -> Result<()> {
229        write!(writer, " # {}", comment)?;
230        Ok(())
231    }
232
233    /// Write inner comments (between collection items)
234    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    /// Emit a scalar value
244    fn emit_scalar<W: Write>(&self, value: &Value, writer: &mut W) -> Result<()> {
245        self.emit_scalar_with_comments(value, None, writer)
246    }
247
248    /// Emit a scalar value with optional comments and style
249    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    /// Emit a scalar value with optional comments and style information
259    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        // Emit leading comments
267        if let Some(comments) = comments {
268            self.emit_leading_comments(&comments.leading, writer)?;
269        }
270
271        // Emit the scalar value
272        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                // Handle special float values
278                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                    // Ensure the float is written with decimal point to preserve type
288                    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        // Emit trailing comment
302        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    /// Emit a string, choosing appropriate quoting style
312    fn emit_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
313        self.emit_string_with_style(s, None, writer)
314    }
315
316    /// Emit a string with specific quote style
317    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                // Check if string needs quoting
328                if self.needs_quoting(s) {
329                    // Default to double quotes when quoting is needed
330                    self.emit_double_quoted_string(s, writer)
331                } else {
332                    write!(writer, "{}", s)?;
333                    Ok(())
334                }
335            }
336        }
337    }
338
339    /// Check if a string needs to be quoted
340    fn needs_quoting(&self, s: &str) -> bool {
341        if s.is_empty() {
342            return true;
343        }
344
345        // String needs quoting if it could be interpreted as another type
346        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        // Check for version-like strings that could be ambiguous
361        // e.g., "2.1.0", "1.2.3", etc. - these contain dots and could be misinterpreted
362        if s.chars().any(|c| c == '.') && s.chars().any(|c| c.is_ascii_digit()) {
363            // If it looks like a version number (contains dots and digits), quote it
364            // to prevent parsing ambiguity
365            return true;
366        }
367
368        // Check for special characters
369        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    /// Emit a double-quoted string
391    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    /// Emit a single-quoted string
409    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, "''")?, // In YAML, single quotes are escaped by doubling
414                c => write!(writer, "{}", c)?,
415            }
416        }
417        write!(writer, "'")?;
418        Ok(())
419    }
420
421    /// Emit a quoted string (legacy method)
422    fn emit_quoted_string<W: Write>(&self, s: &str, writer: &mut W) -> Result<()> {
423        self.emit_double_quoted_string(s, writer)
424    }
425
426    /// Emit a sequence (array/list)
427    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)?; // Add newline before nested structure
443                    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    /// Emit a mapping with an anchor
457    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        // For anchored mappings, we need to format it as:
469        // &anchor
470        // key1: value1
471        // key2: value2
472        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            // Handle both simple and complex keys
484            let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
485
486            if is_complex_key {
487                // Complex key - emit with explicit key marker
488                write!(writer, "? ")?;
489                self.emit_value(key, writer)?;
490                writeln!(writer)?;
491                self.write_indent(writer)?;
492            } else {
493                // Simple key
494                self.emit_scalar(key, writer)?;
495            }
496
497            write!(writer, ": ")?;
498
499            match value {
500                Value::Sequence(_) | Value::Mapping(_) => {
501                    writeln!(writer)?; // Add newline before nested structure
502                    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    /// Emit a mapping (dictionary/object)
516    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            // Handle both simple and complex keys
536            let is_complex_key = matches!(key, Value::Sequence(_) | Value::Mapping(_));
537
538            if is_complex_key {
539                // Complex key - emit with explicit key marker and flow style
540                write!(writer, "? ")?;
541                match key {
542                    Value::Mapping(map) => {
543                        // Emit mapping in flow style to avoid ambiguity
544                        self.emit_mapping_flow_style(map, writer)?;
545                    }
546                    Value::Sequence(seq) => {
547                        // Emit sequence in flow style
548                        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                // Simple key
558                self.emit_scalar(key, writer)?;
559            }
560
561            write!(writer, ": ")?;
562
563            match value {
564                Value::Sequence(_) | Value::Mapping(_) => {
565                    writeln!(writer)?; // Add newline before nested structure
566                    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    /// Emit a mapping in flow style for complex keys
580    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            // Emit key (handle nested complex values)
594            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            // Emit value (handle nested complex values)
609            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    /// Emit a sequence in flow style for complex keys
626    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            // Handle nested complex values
635            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    /// Emit any value, dispatching to the appropriate method with anchor/alias support
652    fn emit_value<W: Write>(&mut self, value: &Value, writer: &mut W) -> Result<()> {
653        // Check if this value has an anchor/alias
654        if let Some(info) = self.shared_values.get(value).cloned() {
655            if info.first_occurrence {
656                // First occurrence: emit with anchor
657                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)?, // Scalars shouldn't be shared
666                }
667                // Mark as no longer first occurrence
668                if let Some(info_mut) = self.shared_values.get_mut(value) {
669                    info_mut.first_occurrence = false;
670                }
671            } else {
672                // Subsequent occurrence: emit alias
673                write!(writer, "*{}", info.anchor_name)?;
674            }
675        } else {
676            // Regular value without sharing
677            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    /// Emit any value (old method for backwards compatibility)
687    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    /// Emit a commented value with full comment and style support
696    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                // For collections, emit leading comments first
712                if let Some(comments) = comments {
713                    self.emit_leading_comments(&comments.leading, writer)?;
714                }
715                self.emit_value(&commented.value, writer)?;
716
717                // Emit inner comments for collections
718                if let Some(comments) = comments {
719                    if !comments.inner.is_empty() {
720                        self.emit_inner_comments(&comments.inner, writer)?;
721                    }
722                }
723
724                // Trailing comments for collections go on a new line
725                if let Some(comments) = comments {
726                    if let Some(ref trailing) = comments.trailing {
727                        writeln!(writer)?;
728                        self.write_indent(writer)?;
729                        writeln!(writer, "# {}", trailing)?;
730                    }
731                }
732            }
733            _ => {
734                // For scalars, use the scalar comment and style method
735                self.emit_scalar_with_comments_and_style(
736                    &commented.value,
737                    comments,
738                    quote_style,
739                    writer,
740                )?;
741            }
742        }
743
744        Ok(())
745    }
746
747    /// Emit a CommentedValue with comment preservation (public API)
748    pub fn emit_commented_value_public<W: Write>(
749        &mut self,
750        commented: &CommentedValue,
751        writer: W,
752    ) -> Result<()> {
753        let mut writer = writer;
754        self.emit_commented_value(commented, &mut writer)
755    }
756}
757
758impl Default for BasicEmitter {
759    fn default() -> Self {
760        Self::new()
761    }
762}
763
764impl Emitter for BasicEmitter {
765    fn emit<W: Write>(&mut self, value: &Value, mut writer: W) -> Result<()> {
766        // Reset state
767        self.current_indent = 0;
768        self.shared_values.clear();
769        self.anchor_counter = 0;
770
771        // Emit directives if any
772        self.emit_directives(&mut writer)?;
773
774        // Analyze for shared values first
775        self.analyze_shared_values(value);
776
777        // For top-level sequences, add a leading newline for proper formatting
778        if matches!(value, Value::Sequence(_)) {
779            writeln!(writer)?;
780        }
781
782        // Emit the value
783        self.emit_value(value, &mut writer)?;
784        writeln!(writer)?; // Add final newline
785        Ok(())
786    }
787
788    fn emit_commented<W: Write>(&mut self, value: &CommentedValue, mut writer: W) -> Result<()> {
789        // Reset state
790        self.current_indent = 0;
791        self.shared_values.clear();
792        self.anchor_counter = 0;
793
794        // Emit directives if any
795        self.emit_directives(&mut writer)?;
796
797        // Analyze for shared values first
798        self.analyze_shared_values(&value.value);
799
800        // Emit the commented value
801        self.emit_commented_value(value, &mut writer)?;
802        writeln!(writer)?; // Add final newline
803        Ok(())
804    }
805
806    fn emit_with_style<W: Write>(
807        &mut self,
808        value: &CommentedValue,
809        indent_style: &IndentStyle,
810        mut writer: W,
811    ) -> Result<()> {
812        // Store current style and temporarily update
813        let original_style = self.indent_style.clone();
814        self.set_indent_style(indent_style.clone());
815
816        // Reset state
817        self.current_indent = 0;
818        self.shared_values.clear();
819        self.anchor_counter = 0;
820
821        // Emit directives if any
822        self.emit_directives(&mut writer)?;
823
824        // Analyze for shared values first
825        self.analyze_shared_values(&value.value);
826
827        // Emit the commented value with the specified style
828        let result = self.emit_commented_value(value, &mut writer);
829        if result.is_ok() {
830            writeln!(writer)?; // Add final newline
831        }
832
833        // Restore original style
834        self.set_indent_style(original_style);
835
836        result
837    }
838
839    fn reset(&mut self) {
840        self.current_indent = 0;
841        self.shared_values.clear();
842        self.anchor_counter = 0;
843        // Note: We don't reset directives here as they might need to persist
844    }
845}
846
847#[cfg(test)]
848mod tests {
849    use super::*;
850    use indexmap::IndexMap;
851
852    #[test]
853    fn test_emit_scalar() {
854        let mut emitter = BasicEmitter::new();
855        let mut output = Vec::new();
856
857        emitter.emit(&Value::Int(42), &mut output).unwrap();
858        assert_eq!(String::from_utf8(output).unwrap(), "42\n");
859    }
860
861    #[test]
862    fn test_emit_string() {
863        let mut emitter = BasicEmitter::new();
864        let mut output = Vec::new();
865
866        emitter
867            .emit(&Value::String("hello".to_string()), &mut output)
868            .unwrap();
869        assert_eq!(String::from_utf8(output).unwrap(), "hello\n");
870    }
871
872    #[test]
873    fn test_emit_quoted_string() {
874        let mut emitter = BasicEmitter::new();
875        let mut output = Vec::new();
876
877        emitter
878            .emit(&Value::String("true".to_string()), &mut output)
879            .unwrap();
880        assert_eq!(String::from_utf8(output).unwrap(), "\"true\"\n");
881    }
882
883    #[test]
884    fn test_emit_sequence() {
885        let mut emitter = BasicEmitter::new();
886        let mut output = Vec::new();
887
888        let seq = Value::Sequence(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
889
890        emitter.emit(&seq, &mut output).unwrap();
891        let result = String::from_utf8(output).unwrap();
892        let expected = "\n- 1\n- 2\n- 3\n";
893        assert_eq!(result, expected);
894    }
895
896    #[test]
897    fn test_emit_mapping() {
898        let mut emitter = BasicEmitter::new();
899        let mut output = Vec::new();
900
901        let mut map = IndexMap::new();
902        map.insert(
903            Value::String("key".to_string()),
904            Value::String("value".to_string()),
905        );
906        map.insert(Value::String("number".to_string()), Value::Int(42));
907
908        emitter.emit(&Value::Mapping(map), &mut output).unwrap();
909        let result = String::from_utf8(output).unwrap();
910
911        // Should contain the key-value pairs
912        assert!(result.contains("key: value"));
913        assert!(result.contains("number: 42"));
914    }
915
916    #[test]
917    fn test_emit_nested_structure() {
918        let mut emitter = BasicEmitter::new();
919        let mut output = Vec::new();
920
921        let inner_seq = Value::Sequence(vec![Value::Int(1), Value::Int(2)]);
922        let mut outer_map = IndexMap::new();
923        outer_map.insert(Value::String("items".to_string()), inner_seq);
924
925        emitter
926            .emit(&Value::Mapping(outer_map), &mut output)
927            .unwrap();
928        let result = String::from_utf8(output).unwrap();
929
930        assert!(result.contains("items:"));
931        assert!(result.contains("- 1"));
932        assert!(result.contains("- 2"));
933    }
934}