Output Rust code using `syn`

finchie
Oct 26, 2023, 8:14 AM
BA5Y6VSEHJQBOYBS6R6FE6IZDRNAPNIN5ITJXWK7L46RJVHNI7JAC

Dependencies

  • [2] 2N3KOCP7 Create MVP Pandoc->Rust compiler
  • [*] C73UJ7ZY Create simple `xilem_html` demo

Change contents

  • edit in Cargo.lock at line 422
    [2.1000]
    [2.1000]
    "proc-macro2",
    "quote",
    "syn",
  • edit in crates/typst_rust_gen/src/lib.rs at line 2
    [2.1232]
    [2.1232]
    use pandoc::PandocOutput;
    use quote::quote;
    use syn::punctuated::Punctuated;
  • replacement in crates/typst_rust_gen/src/lib.rs at line 7
    [2.1233][2.1233:1308]()
    use pandoc::{OutputFormat, PandocOutput};
    use pandoc_ast::{Block, Inline};
    [2.1233]
    [2.1308]
    // Safely wrap i64 header levels in an enum
    enum HeaderLevel {
    H1,
    H2,
    H3,
    H4,
    H5,
    H6,
    }
    // This is not TryFrom as there's not really much we can do to handle the error?
    impl From<i64> for HeaderLevel {
    fn from(value: i64) -> Self {
    match value {
    1 => HeaderLevel::H1,
    2 => HeaderLevel::H2,
    3 => HeaderLevel::H3,
    4 => HeaderLevel::H4,
    5 => HeaderLevel::H5,
    6 => HeaderLevel::H6,
    _ => panic!(
    "Unexpected header level! Headers must be in range of 1 to 6 (inclusive), got: {}",
    value
    ),
    }
    }
    }
    impl From<HeaderLevel> for &str {
    fn from(value: HeaderLevel) -> Self {
    match value {
    HeaderLevel::H1 => "h1",
    HeaderLevel::H2 => "h2",
    HeaderLevel::H3 => "h3",
    HeaderLevel::H4 => "h4",
    HeaderLevel::H5 => "h5",
    HeaderLevel::H6 => "h6",
    }
    }
    }
    struct ElementBlock<'a> {
    name: &'a str,
    expression: syn::Expr,
    }
    impl ElementBlock<'_> {
    fn method_call(self) -> syn::Expr {
    let mut args: Punctuated<syn::Expr, syn::token::Comma> = Punctuated::new();
    args.push(self.expression);
  • edit in crates/typst_rust_gen/src/lib.rs at line 58
    [2.1309]
    [2.1309]
    let expr_call = syn::ExprCall {
    attrs: Vec::new(),
    func: Box::new(expr_path(self.name)),
    paren_token: syn::token::Paren::default(),
    args,
    };
    syn::Expr::Call(expr_call)
    }
    }
  • replacement in crates/typst_rust_gen/src/lib.rs at line 72
    [2.1423][2.1423:1485]()
    pandoc.set_output_format(OutputFormat::Json, Vec::new());
    [2.1423]
    [2.1485]
    pandoc.set_output_format(pandoc::OutputFormat::Json, Vec::new());
  • edit in crates/typst_rust_gen/src/lib.rs at line 82
    [2.1781]
    [2.1781]
    let blocks: Vec<syn::Expr> = ast.blocks.iter().map(transform_block).collect();
    dbg!(&blocks, blocks.len());
  • replacement in crates/typst_rust_gen/src/lib.rs at line 86
    [2.1782][2.1782:1947]()
    let blocks: String = ast
    .blocks
    .iter()
    .map(transform_block)
    .collect::<Vec<String>>()
    .join(", ");
    dbg!(&blocks);
    [2.1782]
    [2.1947]
    let file = syn::File {
    shebang: None,
    attrs: Vec::new(),
    items: vec![syn::Item::Fn(syn::ItemFn {
    attrs: Vec::new(),
    vis: syn::Visibility::Public(syn::token::Pub::default()),
    sig: syn::Signature {
    constness: None,
    asyncness: None,
    unsafety: None,
    abi: None,
    fn_token: syn::token::Fn::default(),
    ident: syn::Ident::new("test", proc_macro2::Span::call_site()),
    generics: syn::Generics::default(),
    paren_token: syn::token::Paren::default(),
    inputs: Punctuated::new(),
    variadic: None,
    output: syn::ReturnType::Default,
    },
    block: Box::new(syn::Block {
    brace_token: syn::token::Brace::default(),
    stmts: vec![syn::Stmt::Expr(
    syn::Expr::Tuple(syn::ExprTuple {
    attrs: Vec::new(),
    paren_token: syn::token::Paren::default(),
    elems: Punctuated::from_iter(blocks.into_iter()),
    }),
    None,
    )],
    }),
    })],
    };
    quote!(#file).to_string()
    }
    fn path_segment(segment: &str) -> syn::PathSegment {
    syn::PathSegment {
    ident: syn::Ident::new(segment, proc_macro2::Span::call_site()),
    arguments: syn::PathArguments::None,
    }
    }
    fn expr_path(name: &str) -> syn::Expr {
    let mut segments: Punctuated<syn::PathSegment, syn::token::PathSep> = Punctuated::new();
  • replacement in crates/typst_rust_gen/src/lib.rs at line 132
    [2.1948][2.1948:1959]()
    blocks
    [2.1948]
    [2.1959]
    // The path should be el::name
    // e.g. el::h2
    segments.push(path_segment("el"));
    segments.push(path_segment(name));
    let expr_path = syn::ExprPath {
    attrs: Vec::new(),
    qself: None,
    path: syn::Path {
    leading_colon: None,
    segments,
    },
    };
    syn::Expr::Path(expr_path)
  • replacement in crates/typst_rust_gen/src/lib.rs at line 149
    [2.1962][2.1962:2076]()
    fn transform_block(block: &Block) -> String {
    match block {
    Block::Header(level, _attr, inlines) => {
    [2.1962]
    [2.2076]
    fn transform_block(block: &pandoc_ast::Block) -> syn::Expr {
    let block = match block {
    pandoc_ast::Block::Header(level, _attr, inlines) => {
  • edit in crates/typst_rust_gen/src/lib.rs at line 153
    [2.2142]
    [2.2142]
    let header_level = HeaderLevel::from(*level);
  • replacement in crates/typst_rust_gen/src/lib.rs at line 155
    [2.2143][2.2143:2211]()
    format!(r#"el::h{}("{}")"#, level, transformed_inlines)
    [2.2143]
    [2.2211]
    ElementBlock {
    name: header_level.into(),
    expression: transformed_inlines,
    }
  • replacement in crates/typst_rust_gen/src/lib.rs at line 160
    [2.2221][2.2221:2255]()
    Block::Para(inlines) => {
    [2.2221]
    [2.2255]
    pandoc_ast::Block::Para(inlines) => {
  • replacement in crates/typst_rust_gen/src/lib.rs at line 162
    [2.2321][2.2321:2380]()
    format!(r#"el::p("{}")"#, transformed_inlines)
    [2.2321]
    [2.2380]
    ElementBlock {
    name: "p",
    expression: transformed_inlines,
    }
  • replacement in crates/typst_rust_gen/src/lib.rs at line 168
    [2.2412][2.2412:2418]()
    }
    [2.2412]
    [2.2418]
    };
    block.method_call()
  • replacement in crates/typst_rust_gen/src/lib.rs at line 173
    [2.2421][2.2421:2470]()
    fn transform_inline(inline: &Inline) -> String {
    [2.2421]
    [2.2470]
    fn transform_inline(inline: &pandoc_ast::Inline) -> &str {
  • replacement in crates/typst_rust_gen/src/lib.rs at line 175
    [2.2489][2.2489:2579]()
    Inline::Str(text) => text.to_owned(),
    Inline::Space => String::from(" "),
    [2.2489]
    [2.2579]
    pandoc_ast::Inline::Str(text) => text,
    pandoc_ast::Inline::Space => " ",
  • replacement in crates/typst_rust_gen/src/lib.rs at line 181
    [2.2610][2.2610:2784]()
    fn transform_inlines(inlines: &Vec<Inline>) -> String {
    let transformed: String = inlines.iter().map(transform_inline).collect();
    dbg!(&transformed);
    transformed
    [2.2610]
    [2.2784]
    fn transform_inlines(inlines: &Vec<pandoc_ast::Inline>) -> syn::Expr {
    let transformed: Vec<&str> = inlines.iter().map(transform_inline).collect();
    let full_literal: String = transformed.join("");
    dbg!(&full_literal);
    // TODO: figure out what the span actually should be
    let lit_str = syn::LitStr::new(&full_literal, proc_macro2::Span::call_site());
    syn::Expr::Lit(syn::ExprLit {
    attrs: Vec::new(),
    lit: syn::Lit::Str(lit_str),
    })
  • edit in crates/typst_rust_gen/Cargo.toml at line 9
    [2.3134]
    proc-macro2 = "1.0.69"
    quote = "1.0.33"
    syn = { version = "2.0.38", features = ["full", "extra-traits"] }