Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Printing

The ToTokens trait enables round-trip formatting: parse source, modify AST, print back.

The ToTokens Trait

pub trait ToTokens {
    fn write(&self, printer: &mut Printer);
}

Implement for each AST node:

impl ToTokens for KeyValue {
    fn write(&self, p: &mut Printer) {
        self.key.value.write(p);
        p.space();
        self.eq.value.write(p);
        p.space();
        self.value.value.write(p);
    }
}

Printer Methods

Basic Output

p.word("text");         // Append literal text
p.token(&tok);          // Append token's string form
p.space();              // Single space
p.newline();            // Line break

Indentation

p.open_block();         // Increase indent, add newline
p.close_block();        // Decrease indent, add newline
p.indent();             // Just increase indent level
p.dedent();             // Just decrease indent level

Separators

// Write items with separator
p.write_separated(&items, ", ");

// Write with custom logic
for (i, item) in items.iter().enumerate() {
    if i > 0 { p.word(", "); }
    item.write(p);
}

Converting to String

// Using the trait method
let output = kv.to_string_formatted();

// Manual printer usage
let mut printer = Printer::new();
kv.write(&mut printer);
let output = printer.finish();

Round-trip Example

// Parse
let mut stream = TokenStream::lex("key = 42")?;
let kv: Spanned<KeyValue> = stream.parse()?;

// Modify
let mut modified = kv.value.clone();
modified.value = Spanned {
    value: Value::Integer(IntegerToken::new(100)),
    span: Span::CallSite,
};

// Print
let output = modified.to_string_formatted();
assert_eq!(output, "key = 100");

Implementation Patterns

Token Structs

Token structs need explicit ToTokens:

impl ToTokens for EqToken {
    fn write(&self, p: &mut Printer) {
        p.token(&self.token());
    }
}

impl ToTokens for BasicStringToken {
    fn write(&self, p: &mut Printer) {
        // Re-add quotes stripped during lexing
        p.word("\"");
        p.word(&self.0);
        p.word("\"");
    }
}

Enum Variants

impl ToTokens for Value {
    fn write(&self, p: &mut Printer) {
        match self {
            Value::String(s) => s.write(p),
            Value::Integer(n) => n.write(p),
            Value::True(t) => t.write(p),
            Value::False(f) => f.write(p),
            Value::Array(a) => a.write(p),
            Value::InlineTable(t) => t.write(p),
        }
    }
}

Collections

impl ToTokens for Array {
    fn write(&self, p: &mut Printer) {
        self.lbracket.value.write(p);
        for (i, item) in self.items.iter().enumerate() {
            if i > 0 { p.word(", "); }
            item.value.value.write(p);
        }
        self.rbracket.value.write(p);
    }
}

Preserving Trivia

For exact round-trip, preserve comments and whitespace:

impl ToTokens for Trivia {
    fn write(&self, p: &mut Printer) {
        match self {
            Trivia::Newline(_) => p.newline(),
            Trivia::Comment(c) => {
                p.token(&c.value.token());
            }
        }
    }
}