A simple quote-based code generator for Rust

Related tags

Utilities flexgen
Overview

flexgen

Crate Docs

A flexible, yet simple quote-based code generator for creating beautiful Rust code

Why?

Rust has two types of macros, and they are both very popular, however, they are not always the optimal choice. They can impact build performance and make the source code more obfuscated to read and study. Regular macros make it difficult to do much more than simple variable substitution and using quote via proc-macro doesn't allow variable interpolation in doc blocks (see quote-doctest for a solution).

Code generation isn't perfect either. It creates excess code which is likely to be highly duplicated and thus create "noise". However, it can also be nice to have a complete set of source code available and easily reachable via the docs. Since we can generate it ahead of time, its impact on performance is the same as regular Rust code.

The right solution likely depends on the use case. I personally think macros tend to be better for writing either very simple duplication or very fancy things that are hard or impossible without them. Code generation is more niche but works well for generating bulk wrapper code esp. for code that is slightly different per type and requires more logic to handle (esp. in doctests).

Example

It is probably easiest to look at the "fibonacci" example: directory

  • fib.rs - the generated file
  • flexgen.toml - the configuration file
  • main.rs - the source file that generates fib.rs

To run yourself:

  1. Change into the examples/basic directory
  2. Delete the existing fib.rs file
  3. Run: cargo run --example basic
  4. Compile the new fib.rs file: rustc fib.rs -C opt-level=3
  5. Run it: ./fib

Usage

  1. Create a new binary crate (flexgen is a library, not a binary crate)

  2. Edit Cargo.toml with any needed dependencies (at minimum, flexgen, but you will likely want quote and possibly quote-doctest as well)

[dependencies]
flexgen = "0.4"
  1. Edit your main.rs and add in one or more code fragments implementing CodeFragment. How much code a fragment contains is a process of trial and error, but typically it would be "one thing" (ie. one function). See the example above for more details.
// main.rs

use flexgen::var::TokenVars;
use flexgen::{import_vars, CodeFragment, Error};
use quote::quote;

struct HelloWorld;

impl CodeFragment for HelloWorld {
    fn generate(&self, vars: &TokenVars) -> Result<TokenStream, Error> {
        import_vars! { vars => hello };

        Ok(quote! {
            fn main() {
                println!("{hello} world!");
            }
        })
    }
}
  1. Create and edit flexgen.toml

NOTE: All the possible options can be found in the test code here

# flexgen.toml

[fragment_lists]
hello = [ "hello_world" ]

[files.hello]
path = "hello.rs"
fragment_list = "hello"

[files.hello.vars]
hello = "Hello"
  1. Add a main function to your main.rs file
// main.rs

use flexgen::config::Config;
use flexgen::{register_fragments, Error, CodeGenerator};

fn main() -> Result<(), Error> {
    // Register all your code fragments
    let fragments = register_fragments!(HelloWorld);
    // Read in the configuration from our flexgen.toml file
    let config = Config::from_default_toml_file()?;
    // Create a new code generator from our fragments and config
    let gen = CodeGenerator::new(fragments, config)?;
    // Generate our 'hello.rs' file
    gen.generate_files()
}
  1. Execute your binary to generate the code
cargo run

License

This project is licensed optionally under either:

You might also like...
Quad-rand implements pseudo-random generator

quad-rand quad-rand implements pseudo-random generator based on rust atomics. Compatible with wasm and any oth

⚙️ A cute .gitignore generator with support for custom templates
⚙️ A cute .gitignore generator with support for custom templates

Gign A Gitignore Generator Table of Contents Examples Install Custom templates Help Examples # This is how you going to use it probably most of the ti

 A dynamic image generator.
A dynamic image generator.

dynimgen A dynamic image generator. How to use step 1: The designers export the design drawing as an svg file svg rect / image src="img.png"

waveplot is a VCD waveform generator for the terminal.

waveplot waveplot is a VCD waveform generator for the terminal. Installation (dev) Install the Rust compiler 'rustc' using rustup. curl --proto '=http

Easy to use Rust i18n library based on code generation

rosetta-i18n rosetta-i18n is an easy-to-use and opinionated Rust internationalization (i18n) library powered by code generation. rosetta_i18n::include

simple epoch-based reclamation

ebr a simple epoch-based reclamation (EBR) library with low cacheline ping-pong. use ebr::Ebr; let mut ebr: EbrBoxu64 = Ebr::default(); let mut

Experimental Rust tool for generating FFI definitions allowing many other languages to call Rust code

Diplomat is an experimental Rust tool for generating FFI definitions allowing many other languages to call Rust code. With Diplomat, you can simply define Rust APIs to be exposed over FFI and get high-level C, C++, and JavaScript bindings automatically!

Migrate C code to Rust
Migrate C code to Rust

C2Rust helps you migrate C99-compliant code to Rust. The translator (or transpiler) produces unsafe Rust code that closely mirrors the input C code. T

Minimal, flexible framework for implementing solutions to Advent of Code in Rust

This is advent_of_code_traits, a minimal, flexible framework for implementing solutions to Advent of Code in Rust.

Comments
  • Raw strings not supported inside doc tests or comments

    Raw strings not supported inside doc tests or comments

    By looking at how quote encodes these when using /// we can see that it looks at the raw deliminator and adds one more than the longest delim inside the inner string. It is possible to add this of course, but I don't need it, and I'm not sure it is worth the effort. It would be nice to have raw strings inside a doc test/comment, but not sure I've ever needed one. Will likely wait to fix this until I do.

    The output from quote when given "inner raw string" surrounded by many # chars

    ---- tests::rustfmt_inner_string stdout ----
    thread 'tests::rustfmt_inner_string' panicked at 'assertion failed: `(left == right)`
      left: `"# [doc = r\" ```\"] # [doc = r#\" println!(\"inner string\");\"#] # [doc = r#\" println!(\"inner \\\"quote\");\"#] # [doc = r#######\" println!(r######\"inner raw string\"######)\"#######] # [doc = r\" ```\"]"`,
    
    bug 
    opened by nu11ptr 1
  • rust-format: post-processing for module-level rustdoc attributes

    rust-format: post-processing for module-level rustdoc attributes

    Module level rustdoc has different syntax than regular rustdoc:

    //! my documentation
    or
    #![doc="my documantation"]
    

    Right now this case is not handled by the post-processor and module level documentation attributes are not converted to their comment counterparts when doc post-processing is enabled.

    opened by tomkris 3
Owner
Scott Meeuwsen
Scott Meeuwsen
A simple code boilerplate generator written in Rust.

?? Cgen What is Cgen? A modern, cross-platform, multi-language boilerplate generator aimed to make your code generation less hectic! If you wish to su

Rithul Kamesh 1 Dec 25, 2021
A code generator to reduce repetitive tasks and build high-quality Rust libraries. 🦀

LibMake A code generator to reduce repetitive tasks and build high-quality Rust libraries Welcome to libmake ?? Website • Documentation • Report Bug •

Sebastien Rousseau 27 Mar 12, 2023
🦀 Rust-based implementation of a Snowflake Generator which communicates using gRPC

Clawflake Clawflake is a Rust application which implements Twitter Snowflakes and communicates using gRPC. Snowflake ID numbers are 63 bits integers s

n1c00o 5 Oct 31, 2022
Dead simple, minimal SPDX License generator library written in Rust.

lice Dead simple, minimal SPDX License generator library written in Rust. Lice is in beta Install | User Docs | Crate Docs | Reference | Contributing

refcell.eth 9 Oct 22, 2023
A simple, fast, easy README generator

Welcome to Readme Generator A simple, fast, easy README generator. Never worry about READMEs again! What it does: Run the command in your project's di

Dhravya Shah 41 Nov 13, 2022
Universal changelog generator using conventional commit+ with monorepo support. Written in Rust.

chlog Universal changelog generator using conventional commit+ with monorepo support. chlog can generate the changelog from the conventional commits w

Jeff Yang 3 Nov 27, 2022
Nexmark event generator in Rust.

Nexmark-rs The Nexmark benchmark data generator in Rust. Installation cargo install nexmark --features bin Usage Generate nexmark events. Print one pe

RisingWave Labs 11 Nov 27, 2022
Pure rust implementation of python's random module with compatible generator behaviour.

pyrand Pure rust implementation of (parts of) python's random module with compatible PRNG behaviour: seeding with equivalent values will yield identic

Stefan V. 4 Feb 10, 2024
Boot tools: loader, image generator, etc as a library crate

ArcBoot v0 A uefi bootloader for riscv, arm and x86. Comes in the form of a single executable. the source code contains a single executable target and

Spectral Project 3 Oct 3, 2022
Flexible snowflake generator, reference snoyflake and leaf.

Flexible snowflake generator, reference snoyflake and leaf.

Egccri 2 May 6, 2022