Crate syntaxfmt

Crate syntaxfmt 

Source
Expand description

A derive macro based library for flexible syntax tree formatting with pretty printing support.

syntaxfmt provides a trait based approach to formatting syntax trees with both compact and pretty-printed output modes. It’s designed for compiler frontends, code generators, and any application that needs to format structured data as text with optional formatting.

§Features

  • Derive macro - for automatic implementation of formatting logic
  • Dual formatting modes - compact and pretty-printed
  • Collection support - automatic formatting for Vec<T>, &[T], and [T; N] types
  • Boolean and Option support - conditional formatting for bool and Option<T> types
  • Stateful formatting - pass user defined context through the formatting process
  • Custom formatters - override default behavior with custom functions or by explicitly implementing SyntaxFmt
  • Flexible attributes - control delimiters, indentation, and format strings

§Cargo Features

  • derive - enables SyntaxFmt derive macro (on by default)

§Quick Start

use syntaxfmt::{SyntaxFmt, syntax_fmt};

#[derive(SyntaxFmt)]
struct FunctionCall<'src> {
    name: &'src str,
    #[syntax(format = "({content})", pretty_format = "( {content} )")]
    args: &'src str,
}

let call = FunctionCall {
    name: "println",
    args: "\"Hello, world!\"",
};

// Compact formatting
assert_eq!(format!("{}", syntax_fmt(&call)), "println(\"Hello, world!\")");

// Pretty formatting with .pretty()
assert_eq!(format!("{}", syntax_fmt(&call).pretty()), "println( \"Hello, world!\" )");

§Derive Macro Attributes

§Type-level attributes

  • #[syntax(delim = ", ")] - Delimiter between items of this type, used by Vec and slice implementations (default: ",")
  • #[syntax(pretty_delim = ",\n")] - Delimiter in pretty mode (default: ", ")
  • #[syntax(format = "prefix{content}suffix")] - For prefixes and suffixes around the whole type (default: "{content}")
  • #[syntax(pretty_format = "prefix{content}suffix")] - For pretty prefixes and suffixes around the whole type (default: "{content}")
  • #[syntax(state_bound = "MyTrait")] - Add trait bound for exposing functionality to custom formatter functions

§Field-level attributes

  • #[syntax(format = "prefix{content}suffix")] - For prefixes and suffixes around the field (default: "{content}")
  • #[syntax(pretty_format = "prefix{content}suffix")] - For pretty prefixes and suffixes around the field (default: "{content}")
  • #[syntax(content = my_formatter)] - Custom content formatter function
  • #[syntax(empty_suffix = ";")] - Early out with this string when field is empty (for types which implement is_empty() function)
  • #[syntax(indent)] - Write indentation before this field (pretty mode only)
  • #[syntax(indent_region)] - Increase indent level for this field’s content
  • #[syntax(skip)] - Skip this field during formatting

§Examples

§Basic struct formatting

Note that syntax attribute may be used at the type level or at the field level.

use syntaxfmt::{SyntaxFmt, syntax_fmt};

#[derive(SyntaxFmt)]
#[syntax(format = "let {content};")]
struct LetStatement<'src> {
    name: &'src str,
    #[syntax(format = " = {content}")]
    value: &'src str,
}

let stmt = LetStatement {
    name: "x",
    value: "42",
};

assert_eq!(format!("{}", syntax_fmt(&stmt)), "let x = 42;");

§Optional and boolean fields

use syntaxfmt::{SyntaxFmt, syntax_fmt};

#[derive(SyntaxFmt)]
#[syntax(format = "{content};")]
struct ConstStatement<'src> {
    #[syntax(format = "pub ")]
    is_pub: bool,
    #[syntax(format = "const {content}: i32")]
    name: &'src str,
    #[syntax(format = " = {content}")]
    value: Option<i32>,
}

let stmt = ConstStatement {
    is_pub: false,
    name: "X",
    value: None,
};

assert_eq!(format!("{}", syntax_fmt(&stmt)), "const X: i32;");

let pub_stmt = ConstStatement {
    is_pub: true,
    name: "X",
    value: None,
};

assert_eq!(format!("{}", syntax_fmt(&pub_stmt)), "pub const X: i32;");

let value_stmt = ConstStatement {
    is_pub: true,
    name: "X",
    value: Some(42),
};

assert_eq!(format!("{}", syntax_fmt(&value_stmt)), "pub const X: i32 = 42;");

§Pretty printing with indentation

The indent_region attribute increases the indent level for a field’s content, and indent writes indentation before formatting a field in pretty mode. The default indentation is four spaces, which can be customized using the .indent() builder method.

use syntaxfmt::{SyntaxFmt, syntax_fmt};

#[derive(SyntaxFmt)]
struct Statement<'src> {
    #[syntax(format = "{content};", indent)]
    code: &'src str,
}

#[derive(SyntaxFmt)]
struct Block<'src> {
    #[syntax(
        format = "{{{content}}}",
        pretty_format = "{{\n{content}\n}}",
        indent_region
    )]
    body: Statement<'src>,
}

let block = Block { body: Statement { code: "return 42" } };

assert_eq!(format!("{}", syntax_fmt(&block)), "{return 42;}");
assert_eq!(format!("{}", syntax_fmt(&block).pretty()), "{\n    return 42;\n}");

assert_eq!(format!("{}", syntax_fmt(&block).pretty().indent("\t")), "{\n\treturn 42;\n}");

§Using empty_suffix for empty collections

The empty_suffix attribute provides early-out formatting for empty collection fields. When the field’s is_empty() function returns true, only the suffix is output instead of the remainder of the struct fields. This is useful for syntax like mod name; vs mod name { items }.

use syntaxfmt::{SyntaxFmt, syntax_fmt};

#[derive(SyntaxFmt)]
#[syntax(delim = " ", pretty_delim = " ")]
struct Statement<'src>(&'src str);

#[derive(SyntaxFmt)]
#[syntax(format = "mod {content}")]
struct Module<'src> {
    name: &'src str,
    #[syntax(format = " {{{content}}}", empty_suffix = ";")]
    items: Vec<Statement<'src>>,
}

let empty = Module {
    name: "empty",
    items: vec![],
};
assert_eq!(format!("{}", syntax_fmt(&empty)), "mod empty;");

let with_items = Module {
    name: "lib",
    items: vec![Statement("fn main()")],
};
assert_eq!(format!("{}", syntax_fmt(&with_items)), "mod lib {fn main()}");

§Collection formatting

Collections (Vec<T>, &[T], [T; N]) are automatically formatted by iterating over their elements and using the element type’s delimiter configuration.

use syntaxfmt::{SyntaxFmt, syntax_fmt};

#[derive(SyntaxFmt)]
#[syntax(delim = "::", pretty_delim = " :: ")]
struct Segment<'src>(&'src str);

#[derive(SyntaxFmt)]
struct Path<'src> {
    segments: Vec<Segment<'src>>,
}

let path = Path {
    segments: vec![Segment("std"), Segment("collections"), Segment("HashMap")],
};

assert_eq!(
    format!("{}", syntax_fmt(&path)),
    "std::collections::HashMap"
);
assert_eq!(
    format!("{}", syntax_fmt(&path).pretty()),
    "std :: collections :: HashMap"
);

§Custom formatters

The content attribute allows you to specify a custom formatting function for a field.

use syntaxfmt::{SyntaxFmt, SyntaxFormatter, syntax_fmt};

fn quote_formatter<S>(value: &str, ctx: &mut SyntaxFormatter<S>) -> std::fmt::Result {
    write!(ctx, "\"{}\"", value)
}

#[derive(SyntaxFmt)]
struct StringLiteral<'src> {
    #[syntax(content = quote_formatter)]
    value: &'src str,
}

let lit = StringLiteral { value: "hello" };
assert_eq!(format!("{}", syntax_fmt(&lit)), "\"hello\"");

§Stateful formatting with mutable state

You can manually implement SyntaxFmt or use custom formatter functions to access and modify user-provided state during formatting.

use syntaxfmt::{SyntaxFmt, SyntaxFormatter, syntax_fmt};

// State that tracks variable assignments
struct VarTracker {
    next_id: usize,
}

impl VarTracker {
    fn allocate(&mut self) -> usize {
        let id = self.next_id;
        self.next_id += 1;
        id
    }
}

// A variable declaration that gets a unique ID
struct VarDecl<'src> {
    name: &'src str,
}

impl<'src> SyntaxFmt<VarTracker> for VarDecl<'src> {
    fn syntax_fmt(&self, ctx: &mut SyntaxFormatter<VarTracker>) -> std::fmt::Result {
        let id = ctx.state_mut().allocate();
        write!(ctx, "let {}_{} = ", self.name, id)
    }
}

let mut tracker = VarTracker { next_id: 0 };
let decl_0 = VarDecl { name: "x" };
let decl_1 = VarDecl { name: "x" };
assert_eq!(format!("{}", syntax_fmt(&decl_0).state_mut(&mut tracker)), "let x_0 = ");
assert_eq!(format!("{}", syntax_fmt(&decl_1).state_mut(&mut tracker)), "let x_1 = ");
assert_eq!(tracker.next_id, 2);

Structs§

SyntaxDisplay
A wrapper that implements Display for types implementing SyntaxFmt.
SyntaxFormatter
Context passed to formatting implementations, containing the formatter and formatting state.

Traits§

SyntaxFmt
Trait for types that can be formatted as syntax.

Functions§

syntax_fmt
Formats a syntax tree.

Derive Macros§

SyntaxFmt