aimx/
writer.rs

1//! Buffered writer for serializing AIM data structures.
2//!
3//! This module provides a buffered writer implementation for efficiently
4//! serializing AST and AIM data structures to their text representations.
5//! It includes specialized formatting functions for different data types
6//! and supports various output modes including escaped and quoted formats.
7//!
8//! The Writer uses several optimization techniques:
9//! - Buffered writing with a single String buffer to minimize allocations
10//! - Efficient number formatting using `itoa` and `ryu` crates
11//! - Direct character writing when possible
12//! - Specialized formatters for each data type
13//!
14//! # Examples
15//!
16//! ```rust
17//! use aimx::writer::{Writer, PrintMode, Prefix};
18//! use aimx::{Literal, Value};
19//!
20//! // Writing simple text
21//! let mut writer = Writer::stringizer();
22//! writer.write_str("Hello, world!");
23//! assert_eq!(writer.finish(), "Hello, world!");
24//!
25//! // Writing values with different modes
26//! let value = Value::Literal(Literal::from_text("test".to_string()));
27//!
28//! // String mode - no escaping
29//! let mut writer = Writer::stringizer();
30//! writer.print_value(&Prefix::None, &value);
31//! assert_eq!(writer.finish(), "test");
32//!
33//! // Sanitized mode - escape special characters
34//! let mut writer = Writer::sanitizer();
35//! writer.print_value(&Prefix::None, &value);
36//! assert_eq!(writer.finish(), "test");
37//!
38//! // Formula mode - quote and escape special characters
39//! let mut writer = Writer::formulizer();
40//! writer.print_value(&Prefix::None, &value);
41//! assert_eq!(writer.finish(), "\"test\"");
42//! ```
43//!
44//! # Modes and Formatting
45//!
46//! The Writer supports three main formatting modes through the [`PrintMode`] enum:
47//! - [`PrintMode::None`] - No special formatting (stringizer)
48//! - [`PrintMode::Escape`] - Escape special characters (sanitizer)
49//! - [`PrintMode::QuoteEscape`] - Quote and escape special characters (formulizer)
50//!
51//! Prefix styles for list formatting are available through the [`Prefix`] enum:
52//! - [`Prefix::None`] - No prefix
53//! - [`Prefix::Unordered`] - Unordered list prefix (e.g., "- ")
54//! - [`Prefix::Ordered`] - Ordered list prefix (e.g., "1. ")
55//!
56//! # Implementation Details
57//!
58//! The Writer is designed for performance with several key optimizations:
59//! - Buffered writing with a single String buffer to minimize allocations
60//! - Efficient number formatting using `itoa` and `ryu` crates for integers and floats
61//! - Direct character writing when possible
62//! - Specialized formatters for each data type
63//! - Thread-safe operation for use in concurrent scenarios
64//!
65//! # See Also
66//!
67//! - [`Value`] - Runtime value representation that uses Writer for formatting
68//! - [`Literal`] - Compile-time literal value representation
69//! - [`Reference`] - Variable reference representation
70use jiff::civil::DateTime;
71use crate::{
72    ExpressionLike, Literal, Reference, Value
73};
74
75/// Prefix style for list items.
76#[derive(Debug, PartialEq, Clone, Copy)]
77pub enum Prefix {
78    /// No prefix
79    None,
80    /// Unordered list prefix (e.g., "- ")
81    Unordered,
82    /// Ordered list prefix (e.g., "1. ")
83    Ordered,
84}
85
86/// Output formatting mode for the writer.
87#[derive(Debug, PartialEq, Clone, Copy)]
88pub enum PrintMode {
89    /// No special formatting
90    None,
91    /// Escape special characters
92    Escape,
93    /// Quote and escape special characters
94    QuoteEscape,
95}
96
97/// A buffered writer for serializing AST/AIM data structures.
98/// 
99/// The Writer provides efficient serialization of AST/AIM components to their text representation.
100/// It uses buffered writing with specialized formatting functions for different data types,
101/// providing optimal performance for generating AIM files.
102/// 
103/// # Implementation Details
104/// 
105/// The Writer uses several optimization techniques:
106/// - Buffered writing with a single String buffer to minimize allocations
107/// - Efficient number formatting using `itoa` and `ryu` crates
108/// - Direct character writing when possible
109/// - Specialized formatters for each data type
110pub struct Writer {
111    buffer: String,
112    int: itoa::Buffer,
113    float: ryu::Buffer,
114    mode: PrintMode,
115    prefix: Prefix,
116}
117
118impl Writer {
119    /// Creates a new Writer instance with an empty buffer.
120    /// 
121    /// # Arguments
122    /// 
123    /// * `format` - The PrintMode to use for output formatting
124    /// 
125    /// # Returns
126    /// 
127    /// * `Writer` - A new Writer instance
128    pub fn new(format: PrintMode, prefix: Prefix) -> Self {
129        Self {
130            buffer: String::new(),
131            int: itoa::Buffer::new(),
132            float: ryu::Buffer::new(),
133            mode: format,
134            prefix,
135        }
136    }
137
138    /// Creates a new Writer instance for writing with an empty buffer.
139    /// 
140    /// # Returns
141    /// 
142    /// * `Writer` - A new Writer instance
143    pub fn stringizer() -> Self {
144        Self {
145            buffer: String::new(),
146            int: itoa::Buffer::new(),
147            float: ryu::Buffer::new(),
148            mode: PrintMode::None,
149            prefix: Prefix::Unordered,
150        }
151    }
152
153    pub fn is_stringizer(&self) -> bool {
154        match self.mode {
155            PrintMode::None => true,
156            _ => false,
157        }
158    }
159
160    /// Creates a new Writer instance for sanitized writing with an empty buffer.
161    /// 
162    /// # Returns
163    /// 
164    /// * `Writer` - A new sanitized Writer instance
165    pub fn sanitizer() -> Self {
166        Self {
167            buffer: String::new(),
168            int: itoa::Buffer::new(),
169            float: ryu::Buffer::new(),
170            mode: PrintMode::Escape,
171            prefix: Prefix::Unordered,
172        }
173    }
174
175    pub fn is_sanitizer(&self) -> bool {
176        match self.mode {
177            PrintMode::Escape => true,
178            _ => false,
179        }
180    }
181
182    /// Creates a new Writer instance for writing formulas with an empty buffer.
183    /// 
184    /// # Returns
185    /// 
186    /// * `Writer` - A new formula Writer instance
187    pub fn formulizer() -> Self {
188        Self {
189            buffer: String::new(),
190            int: itoa::Buffer::new(),
191            float: ryu::Buffer::new(),
192            mode: PrintMode::QuoteEscape,
193            prefix: Prefix::None,
194        }
195    }
196
197    pub fn is_formulizer(&self) -> bool {
198        match self.mode {
199            PrintMode::QuoteEscape => true,
200            _ => false,
201        }
202    }
203
204    pub fn to_sanitized(text: &str) -> String {
205        let mut writer = Writer::sanitizer();
206        writer.escape(text);
207        writer.finish()
208    }
209
210    pub fn to_formula(text: &str) -> String {
211        let mut writer = Writer::formulizer();
212        writer.quote_escape(text);
213        writer.finish()
214    }
215
216    pub fn prefix(&self) -> Prefix {
217        self.prefix
218    }
219
220    pub fn print_mode(&self) -> PrintMode {
221        self.mode
222    }
223
224    /// Writes a string slice to the buffer.
225    /// 
226    /// # Arguments
227    /// 
228    /// * `s` - The string slice to write
229    pub fn write_str(&mut self, s: &str) {
230        self.buffer.push_str(s);
231    }
232    
233    /// Writes a single character to the buffer.
234    /// 
235    /// # Arguments
236    /// 
237    /// * `c` - The character to write
238    pub fn write_char(&mut self, c: char) {
239        self.buffer.push(c);
240    }
241
242    /// Writes a line ending (newline character) to the buffer.
243    pub fn write_eol(&mut self) {
244        self.buffer.push('\n');
245    }
246
247    /// Writes a string slice and line ending to the buffer.
248    /// 
249    /// # Arguments
250    /// 
251    /// * `s` - The string slice to write
252    pub fn write_line(&mut self, s: &str) {
253        self.buffer.push_str(s);
254        self.buffer.push('\n');
255    }
256
257    /// Writes a floating-point number to the buffer using efficient formatting.
258    /// 
259    /// This method uses the ryu crate for efficient float-to-string conversion
260    /// and removes unnecessary trailing ".0" for integer values.
261    /// 
262    /// # Arguments
263    /// 
264    /// * `value` - The f64 value to write
265    pub fn write_f64(&mut self, value: f64) {
266        if value.is_normal() {
267            let mut string = self.float.format(value);
268            if let Some(integer) = string.strip_suffix(".0") {
269                string = integer;
270            }
271            self.buffer.push_str(string);
272        } else if value.is_infinite() {
273            if value.is_sign_positive() {
274                self.buffer.push_str("+Inf");
275            }
276            else {
277                self.buffer.push_str("-Inf");
278            }
279        } else if value.is_nan() {
280            self.buffer.push_str("NaN");
281        } else {
282            self.buffer.push('0');
283        }
284    }
285
286    /// Writes an unsigned integer to the buffer using efficient formatting.
287    /// 
288    /// This method uses the itoa crate for efficient integer-to-string conversion.
289    /// 
290    /// # Arguments
291    /// 
292    /// * `value` - The u32 value to write
293    pub fn write_unsigned(&mut self, value: u32) {
294        self.buffer.push_str(self.int.format(value));
295    }
296
297    /// Print text with the current mode's formatting rules.
298    /// 
299    /// # Arguments
300    /// 
301    /// * `text` - The text to print
302    pub fn print(&mut self, text: &str) {
303        match self.mode {
304            PrintMode::None => self.write_str(text),
305            PrintMode::Escape => self.escape(text),
306            PrintMode::QuoteEscape => self.quote_escape(text),
307        }
308    }
309
310    /// Writes a string literal with proper escaping.
311    /// 
312    /// This method properly escapes special characters in string literals:
313    /// - `"` becomes `\"`
314    /// - `\` becomes `\\`
315    /// - Control characters like newline, tab, etc. are escaped appropriately
316    /// 
317    /// # Arguments
318    /// 
319    /// * `text` - The text to escape and write
320    pub fn escape(&mut self, text: &str) {
321        for c in text.chars() {
322            match c {
323                '"' => self.buffer.push_str("\\\""),
324                '\\' => self.buffer.push_str("\\\\"),
325                '\u{08}' => self.buffer.push_str("\\b"),
326                '\u{0C}' => self.buffer.push_str("\\f"),
327                '\n' => self.buffer.push_str("\\n"),
328                '\r' => self.buffer.push_str("\\r"),
329                '\t' => self.buffer.push_str("\\t"),
330                // ignore other control characters
331                c if c < '\u{20}' => {}
332                _ => self.buffer.push(c),
333            }
334        }
335    }
336
337    /// Writes a string literal with proper quoting and escaping.
338    /// 
339    /// This method surrounds the text with double quotes and applies
340    /// proper escaping for special characters.
341    /// 
342    /// # Arguments
343    /// 
344    /// * `text` - The text to quote, escape, and write
345    pub fn quote_escape(&mut self, text: &str) {
346        self.buffer.push('"');
347        self.escape(text);
348        self.buffer.push('"');
349    }
350
351    /// Writes a string literal surrounded by angle brackets with proper escaping.
352    /// 
353    /// This method surrounds the text with angle brackets and applies
354    /// proper escaping for special characters.
355    /// 
356    /// # Arguments
357    /// 
358    /// * `text` - The text to tag, escape, and write
359    pub fn tag_escape(&mut self, text: &str) {
360        self.buffer.push('<');
361        self.escape(text);
362        self.buffer.push('>');
363    }
364
365    /// Writes a date/time value in ISO 8601 format.
366    /// 
367    /// The date is formatted as: `YYYY-MM-DDTHH:MM:SS.sss`
368    /// 
369    /// # Arguments
370    /// 
371    /// * `date` - The DateTime to format and write
372    pub fn write_date(&mut self, date: &DateTime) {
373        // Format as ISO 8601 string: "2023-12-25T10:30:45.000"        
374        // Write year
375        self.buffer.push_str(self.int.format(date.year()));
376        self.buffer.push('-');
377        
378        // Write month (padded to 2 digits)
379        let month = date.month();
380        if month < 10 {
381            self.buffer.push('0');
382        }
383        self.buffer.push_str(self.int.format(month));
384        self.buffer.push('-');
385        
386        // Write day (padded to 2 digits)
387        let day = date.day();
388        if day < 10 {
389            self.buffer.push('0');
390        }
391        self.buffer.push_str(self.int.format(day));
392        self.buffer.push('T');
393        
394        // Write hour (padded to 2 digits)
395        let hour = date.hour();
396        if hour < 10 {
397            self.buffer.push('0');
398        }
399        self.buffer.push_str(self.int.format(hour));
400        self.buffer.push(':');
401        
402        // Write minute (padded to 2 digits)
403        let minute = date.minute();
404        if minute < 10 {
405            self.buffer.push('0');
406        }
407        self.buffer.push_str(self.int.format(minute));
408        self.buffer.push(':');
409        
410        // Write second (padded to 2 digits)
411        let second = date.second();
412        if second < 10 {
413            self.buffer.push('0');
414        }
415        self.buffer.push_str(self.int.format(second));
416
417        let millisecond = date.subsec_nanosecond() / 1000000;
418        if millisecond > 0 {
419            self.buffer.push('.');
420            if millisecond < 100 {
421                self.buffer.push('0');
422            }
423            if millisecond < 10 {
424                self.buffer.push('0');
425            }
426            self.buffer.push_str(self.int.format(millisecond));
427        }
428    }
429
430    /// Writes a boolean value as "true" or "false".
431    /// 
432    /// # Arguments
433    /// 
434    /// * `state` - The boolean value to write
435    pub fn write_bool(&mut self, state: bool) {
436        if state {
437            self.buffer.push_str("true");
438        }
439        else {
440            self.buffer.push_str("false");
441        }
442    }
443
444    /// Print a task with the current mode's formatting rules.
445    /// 
446    /// # Arguments
447    /// 
448    /// * `state` - The task status (Some(true) = completed, Some(false) = failed, None = pending)
449    /// * `text` - The task text
450    pub fn print_task(&mut self, state: &Option<bool>, text: &str) {
451        match self.mode {
452            PrintMode::None => self.write_task(state, text),
453            PrintMode::Escape => self.escape_task(state, text),
454            PrintMode::QuoteEscape => self.quote_escape_task(state, text),
455        }
456    }
457
458    /// Write a task in its raw (non-escaped) textual representation.
459    /// 
460    /// # Arguments
461    /// 
462    /// * `state` - The task status
463    /// * `text` - The task text
464    fn write_task(&mut self, state: &Option<bool>, text: &str) {
465        match state {
466            Some(check) => self.buffer.push_str(if *check { "[x] " } else { "[-] " }),
467            None => self.buffer.push_str("[ ] "),
468        }
469        self.buffer.push_str(text);
470    }
471
472    /// Escape a task in its JSON/C lang textual representation.
473    /// 
474    /// # Arguments
475    /// 
476    /// * `state` - The task status
477    /// * `text` - The task text
478    fn escape_task(&mut self, state: &Option<bool>, text: &str) {
479        match state {
480            Some(check) => self.buffer.push_str(if *check { "[x] " } else { "[-] " }),
481            None => self.buffer.push_str("[ ] "),
482        }
483        self.escape(text);
484    }
485
486    /// Quote escape a task in its AIM textual representation.
487    /// 
488    /// # Arguments
489    /// 
490    /// * `state` - The task status
491    /// * `text` - The task text
492    fn quote_escape_task(&mut self, state: &Option<bool>, text: &str) {
493        match state {
494            Some(check) => self.buffer.push_str(if *check { "[x] " } else { "[-] " }),
495            None => self.buffer.push_str("[ ] "),
496        }
497        self.quote_escape(text);
498    }
499
500    /// Writes a version header in the format `[epoch[:partial]]\n`.
501    /// 
502    /// # Arguments
503    /// 
504    /// * `epoc` - The epoch number
505    /// * `partial` - The partial version number
506    pub fn write_version(&mut self, epoc: u32, partial: u32) {
507        self.buffer.push('[');
508        self.write_unsigned(epoc);
509        if partial > 0 {
510            self.buffer.push(':');
511            self.write_unsigned(partial);
512        }
513        self.buffer.push(']');
514        self.write_eol();
515    }
516
517    /// Print a literal with the current mode's formatting rules.
518    /// 
519    /// # Arguments
520    /// 
521    /// * `literal` - The literal to print
522    pub fn print_literal(&mut self, literal: &Literal) {
523        match self.mode {
524            PrintMode::None => self.write_literal(literal),
525            PrintMode::Escape => self.escape_literal(literal),
526            PrintMode::QuoteEscape => self.quote_escape_literal(literal),
527        }
528    }
529
530    /// Prints a literal value in its raw (non-escaped) textual representation.
531    /// 
532    /// # Arguments
533    /// 
534    /// * `literal` - The literal to write
535    fn write_literal(&mut self, literal: &Literal) {
536        match literal {
537            Literal::Bool(b) => self.write_bool(*b),
538            Literal::Date(d) => self.write_date(d),
539            Literal::Empty => {}
540            Literal::Number(n) => self.write_f64(*n),
541            Literal::Task(s, t) => self.write_task(s, t),
542            Literal::Text(s) => self.buffer.push_str(s),
543        }
544    }
545
546    /// Prints a literal value in its JSON/C land textual representation.
547    /// 
548    /// # Arguments
549    /// 
550    /// * `literal` - The literal to escape and write
551    fn escape_literal(&mut self, literal: &Literal) {
552        match literal {
553            Literal::Bool(b) => self.write_bool(*b),
554            Literal::Date(d) => self.write_date(d),
555            Literal::Empty => {}
556            Literal::Number(n) => self.write_f64(*n),
557            Literal::Task(s, t) => self.escape_task(s, t),
558            Literal::Text(s) => self.escape(s),
559        }
560    }
561
562    /// Writes a literal value in its AIM textual representation.
563    /// 
564    /// # Arguments
565    /// 
566    /// * `literal` - The literal to quote, escape, and write
567    fn quote_escape_literal(&mut self, literal: &Literal) {
568        match literal {
569            Literal::Bool(b) => self.write_bool(*b),
570            Literal::Date(d) => self.write_date(d),
571            Literal::Empty => {}
572            Literal::Number(n) => self.write_f64(*n),
573            Literal::Task(s, t) => self.quote_escape_task(s, t),
574            Literal::Text(s) => self.quote_escape(s),
575        }
576    }
577
578    /// Writes a reference in its textual representation.
579    /// 
580    /// # Arguments
581    /// 
582    /// * `reference` - The reference to write
583    pub fn write_reference(&mut self, reference: &Reference) {
584        match reference {
585            Reference::One(first) => self.buffer.push_str(first),
586            // Relative := identifier.identifier
587            Reference::Two(first, second) => {
588                self.buffer.push_str(first);
589                self.buffer.push('.');
590                self.buffer.push_str(second);
591            }
592            // Relative := identifier.identifier.identifier
593            Reference::Three(first, second, third) => {
594                self.buffer.push_str(first);
595                self.buffer.push('.');
596                self.buffer.push_str(second);
597                self.buffer.push('.');
598                self.buffer.push_str(third);
599            }
600        }
601    }
602
603    /// Print format information with the current mode's formatting rules.
604    /// 
605    /// # Arguments
606    /// 
607    /// * `instruction` - The instruction text
608    /// * `format` - The format text
609    /// * `array` - The array of format parameters
610    pub fn print_format(&mut self, instruction: &str, format: &str, array: &Vec<String>) {
611        match self.mode {
612            PrintMode::None => self.write_format(instruction, format, array),
613            PrintMode::Escape => self.escape_format(instruction, format, array),
614            PrintMode::QuoteEscape => self.quote_escape_format(instruction, format, array),
615        }
616    }
617
618    /// Write format information in raw format.
619    /// 
620    /// # Arguments
621    /// 
622    /// * `instruction` - The instruction text
623    /// * `format` - The format text
624    /// * `array` - The array of format parameters
625    fn write_format(&mut self, instruction: &str, format: &str, array: &Vec<String>) {
626        self.buffer.push_str(instruction);
627        self.buffer.push_str(" <");
628        self.buffer.push_str(format);
629        self.buffer.push_str("> ");
630        for (index, text) in array.iter().enumerate() {
631            if index > 0 {
632                self.buffer.push_str(", ");
633            }
634            self.buffer.push_str(text);
635        }
636    }
637
638    /// Write format information with escaping.
639    /// 
640    /// # Arguments
641    /// 
642    /// * `instruction` - The instruction text
643    /// * `format` - The format text
644    /// * `array` - The array of format parameters
645    fn escape_format(&mut self, instruction: &str, format: &str, array: &Vec<String>) {
646        self.escape(instruction);
647        self.buffer.push(' ');
648        self.tag_escape(format);
649        self.buffer.push(' ');
650        for (index, text) in array.iter().enumerate() {
651            if index > 0 {
652                self.buffer.push_str(", ");
653            }
654            self.escape(text);
655        }
656    }
657
658    /// Write format information with quoting and escaping.
659    /// 
660    /// # Arguments
661    /// 
662    /// * `instruction` - The instruction text
663    /// * `format` - The format text
664    /// * `array` - The array of format parameters
665    fn quote_escape_format(&mut self, instruction: &str, format: &str, array: &Vec<String>) {
666        self.quote_escape(instruction);
667        self.buffer.push(' ');
668        self.tag_escape(format);
669        self.buffer.push(' ');
670        for (index, text) in array.iter().enumerate() {
671            if index > 0 {
672                self.buffer.push_str(", ");
673            }
674            self.quote_escape(text);
675        }
676    }
677
678    /// Writes a list prefix based on the prefix type and index.
679    /// 
680    /// # Arguments
681    /// 
682    /// * `prefix` - The prefix type to write
683    /// * `index` - The index for ordered lists
684    pub fn write_prefix(&mut self, indent: isize, prefix: &Prefix, index: usize) {
685        match prefix {
686            Prefix::Ordered => {
687                for _ in 0..indent {
688                    self.buffer.push('\t');
689                }
690                self.write_unsigned((index + 1) as u32);
691                self.buffer.push_str(". ");
692            }
693            Prefix::Unordered => {
694                for _ in 0..indent {
695                    self.buffer.push('\t');
696                }
697                self.buffer.push_str("- ");
698            }
699            _ => {}
700        }
701    }
702
703    pub fn print_errata(&mut self, reason: &str, formula: &str, location: &str) {
704        match self.mode {
705            PrintMode::None => self.write_errata(reason, formula, location),
706            PrintMode::Escape => self.escape_errata(reason, formula, location),
707            // Print the original expression
708            PrintMode::QuoteEscape => self.quote_escape_errata(reason, formula),
709        }
710    }
711
712    fn write_errata(&mut self, reason: &str, formula: &str, location: &str) {
713        if reason.len() > 0 {
714            self.buffer.push_str(reason);
715            self.buffer.push('\n');
716        }
717        self.buffer.push_str(formula);
718        self.buffer.push('\n');
719        if location.len() > 0 && let Some(offset) = formula.find(location) {
720            for _ in 0..offset {
721                self.buffer.push(' ');
722            }      
723            self.buffer.push('^');     
724            self.buffer.push('\n');
725        }
726    }
727
728    fn escape_errata(&mut self, reason: &str, formula: &str, location: &str) {
729        let formula= Writer::to_sanitized(formula);
730        let location= Writer::to_sanitized(location);
731        if reason.len() > 0 {
732            self.escape(&reason);
733            self.buffer.push('\n');
734        }
735        self.buffer.push_str(&formula);
736        self.buffer.push('\n');
737        if location.len() > 0 && let Some(offset) = formula.find(&location) {
738            for _ in 0..offset {
739                self.buffer.push(' ');
740            }      
741            self.buffer.push('^');                
742            self.buffer.push('\n');
743        }
744    }
745
746    fn quote_escape_errata(&mut self, reason: &str, formula: &str) {
747        // Just print the expression
748        if formula.len() > 0 {
749            self.escape(&formula);
750        }
751        else {
752            self.escape(&reason);
753        }
754    }
755
756    /// Print format information with the current mode's formatting rules.
757    /// 
758    /// # Arguments
759    /// 
760    /// * `prefix` - The instruction text
761    /// * `format` - The format text
762    /// * `array` - The array of format parameters
763    pub fn print_value(&mut self, prefix: &Prefix, value: &Value) {
764        match self.mode {
765            PrintMode::None => self.write_value(-1, prefix, 0, value),
766            PrintMode::Escape => self.escape_value(-1, prefix, 0, value),
767            PrintMode::QuoteEscape => self.quote_escape_value(0, value),
768        }
769    }
770
771    /// Prints a value in its raw (non-escaped) textual representation.
772    fn write_value(&mut self, indent: isize, prefix: &Prefix, index: usize, value: &Value) {
773        match value {
774            Value::Empty | Value::Node(_) => {},
775            Value::Errata(errata) => errata.write(self),
776            Value::Literal(literal) => {
777                if indent >= 0 {
778                    self.write_prefix(indent, prefix, index);
779                }
780                self.write_literal(literal)
781            }
782            Value::Array(values) => {
783                for (index, value) in values.iter().enumerate() {
784                    if index > 0 {
785                        self.buffer.push('\n');
786                    }
787                    self.write_value(indent + 1, prefix, index, value);
788                }
789            }
790            Value::Closure(closure) => closure.write(self),
791            Value::Assignment(reference) => reference.write(self),
792            Value::Format(format)  => format.write(self),
793            Value::Eval(eval) => eval.write(self),
794        }
795    }
796
797    /// Writes a value in its JSON/C lang textual representation.
798    fn escape_value(&mut self, indent: isize, prefix: &Prefix, index: usize, value: &Value) {
799        match value {
800            Value::Empty | Value::Node(_) => {},
801            Value::Errata(errata) => errata.write(self),
802            Value::Literal(literal) => {
803                if indent >= 0 {
804                    self.write_prefix(indent, prefix, index);
805                }
806                self.escape_literal(literal);
807            }
808            Value::Array(values) => {
809                for (index, value) in values.iter().enumerate() {
810                    if index > 0 {
811                        self.buffer.push_str("\\n");
812                    }
813                    self.escape_value(indent + 1, prefix, index, value);
814                }
815            }
816            Value::Closure(closure) => closure.write(self),
817            Value::Assignment(reference) => reference.write(self),
818            Value::Format(format)  => format.write(self),
819            Value::Eval(eval) => eval.write(self),
820       }
821    }
822
823    /// Writes a value in its AIM textual representation.
824    fn quote_escape_value(&mut self, indent: usize, value: &Value) {
825        match value {
826            Value::Empty | Value::Node(_) => {},
827            Value::Errata(errata) => errata.write(self),
828            Value::Literal(literal) => self.quote_escape_literal(literal),
829            Value::Array(values) => {
830                if indent > 0 {
831                    self.buffer.push('(');
832                }
833                for (index, value) in values.iter().enumerate() {
834                    if index > 0 {
835                        self.buffer.push_str(", ");
836                    }
837                    self.quote_escape_value(indent + 1, value);
838                }
839                if indent > 0 {
840                    self.buffer.push(')');
841                }
842            }
843            Value::Closure(closure) => closure.write(self),
844            Value::Assignment(reference) => reference.write(self),
845            Value::Format(format)  => format.write(self),
846            Value::Eval(eval) => eval.write(self),
847        }
848    }
849
850
851    /// Writes a unary operation with the operator and operand.
852    /// 
853    /// # Arguments
854    /// 
855    /// * `op` - The operator string
856    /// * `unary` - The operand expression
857    pub fn write_unary_op(&mut self, op: &str, unary: &dyn ExpressionLike) {
858        self.write_str(op);
859        unary.write(self);
860    }
861
862    /// Writes a binary operation with left operand, operator, and right operand.
863    /// 
864    /// # Arguments
865    /// 
866    /// * `left` - The left operand expression
867    /// * `op` - The operator string
868    /// * `right` - The right operand expression
869    pub fn write_binary_op(&mut self, left: &dyn ExpressionLike, op: &str, right: &dyn ExpressionLike) {
870        left.write(self);
871        self.write_str(op);
872        right.write(self);
873    }
874
875    /// Consumes the writer and returns the final string buffer.
876    /// 
877    /// # Returns
878    /// 
879    /// * `String` - The final accumulated string
880    pub fn finish(self) -> String {
881        self.buffer
882    }
883}