Crates.io | flexgen |
lib.rs | flexgen |
version | 0.4.5 |
source | src |
created_at | 2022-03-30 22:13:59.263441 |
updated_at | 2022-04-22 01:47:12.3362 |
description | A flexible, yet simple quote-based code generator for creating beautiful Rust code |
homepage | |
repository | https://github.com/nu11ptr/flexgen |
max_upload_size | |
id | 559422 |
size | 51,144 |
A flexible, yet simple quote-based code generator for creating beautiful Rust code
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).
It is probably easiest to look at the "fibonacci" example: directory
fib.rs
- the generated fileflexgen.toml
- the configuration filemain.rs
- the source file that generates fib.rs
To run yourself:
examples/basic
directoryfib.rs
filecargo run --example basic
rustc fib.rs -C opt-level=3
./fib
Create a new binary crate (flexgen
is a library, not a binary crate)
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"
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!");
}
})
}
}
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"
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()
}
cargo run
This project is licensed optionally under either: