#lib #derive #macro #quoted #pretty-print #derive-macro #recursion #enums #import #struct

ast2str-lib

A crate for pretty-printing ASTs and other recursive data structures

4 stable releases

1.3.0 Aug 23, 2022
1.2.1 Aug 2, 2022
1.2.0 Jul 31, 2022
1.1.0 Sep 4, 2021
Download history 67/week @ 2022-07-31 16/week @ 2022-08-07 7/week @ 2022-08-14 32/week @ 2022-08-21 5/week @ 2022-08-28 22/week @ 2022-09-04 8/week @ 2022-09-11 8/week @ 2022-09-18 6/week @ 2022-09-25 13/week @ 2022-10-02 11/week @ 2022-10-09 2/week @ 2022-10-16 8/week @ 2022-10-23 5/week @ 2022-10-30 19/week @ 2022-11-06 20/week @ 2022-11-13

52 downloads per month
Used in ast2str

MIT license

35KB
621 lines

ast2str

crates.io Documentation Build Status

A proc macro for pretty-printing ASTs and other recursive data structures.

Basic Usage

Refer to the snippet below for a basic usage example, and see the integration test and these two repos (#1.1 + #1.2, #2.1) for larger samples.

// Import the macro
use ast2str::AstToStr;

type Span = std::ops::Range<usize>;

// Annotate some structs and enums as desired
#[derive(AstToStr)]
struct Label {
   #[quoted]
   name: &'static str,
   #[default = "Unresolved"]
   location: Option<usize>,
}

#[derive(AstToStr)]
enum Expr {
    Binary {
        left: Box<Expr>,
        #[quoted]
        operator: &'static str,
        right: Box<Expr>
    },
    Literal(#[rename = "value"] i32, #[skip] Span),
    List { items: Vec<Expr> },
    Label(#[forward] Label),
    Optional {
        #[skip_if = "Option::is_none"]
        value: Option<&'static str>
    }
}

let expr = Expr::Binary {
    left: Box::new(Expr::Literal(5, Span::default())),
    operator: "+",
    right: Box::new(Expr::List { items: vec![
       Expr::Label(Label { name: "x", location: Some(0) }),
       Expr::Label(Label { name: "y", location: Some(1) }),
       Expr::Label(Label { name: "z", location: None }),
       Expr::Optional { value: None },
       Expr::Optional { value: Some("a string") },
    ]})
};
assert_eq!(expr.ast_to_str(), r#"
Expr::Binary
├─left: Expr::Literal
│ ╰─value: 5
├─operator: `+`
╰─right: Expr::List
  ╰─items=↓
    ├─Label
    │ ├─name: `x`
    │ ╰─location: 0
    ├─Label
    │ ├─name: `y`
    │ ╰─location: 1
    ├─Label
    │ ├─name: `z`
    │ ╰─location: Unresolved
    ├─Expr::Optional
    ╰─Expr::Optional
      ╰─value: "a string"
"#.trim());

// The symbols used to draw the tree can be configured using the [`Symbols`] trait:
assert_eq!(expr.ast_to_str_impl(&ast2str::TestSymbols), r#"
Expr::Binary
  left: Expr::Literal
    value: 5
  operator: `+`
  right: Expr::List
    items=
      Label
        name: `x`
        location: 0
      Label
        name: `y`
        location: 1
      Label
        name: `z`
        location: Unresolved
      Expr::Optional
      Expr::Optional
        value: "a string"
"#.trim());

Available Attributes

Attribute
None Format the value with [AstToStr]
#[forward] Skip all other fields and return the [AstToStr] of the annotated field
#[skip] Skip the annotated field
#[display] Format the annotated field with [Display] instead of [AstToStr]
#[debug] Format the annotated field with [Debug] instead of [AstToStr]
#[quoted] Like #[display] but also wraps the value with backticks
#[list] Format the annotated field by executing AstToStr on every element of (&field).into_iter()
#[list(name_or_closure) Format the annotated field by applying the callback on every element of (&field).into_iter()
#[callback(name_or_closure)] Apply the given function or closure to &field and return the result
#[delegate = "getter"] Call self.getter() and format the result as a field
#[default = "value"] Only applies to Option types. If the value is Some(T), format &T with AstToStr. Otherwise, return the value of default
#[skip_if = "my_condition_fn"] Skip the annotated field if the specified function returns true

Dependencies

~155KB