Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/Eolu/const-gen

A crate for generating (relatively) complex compile-time constants in rust
https://github.com/Eolu/const-gen

Last synced: about 8 hours ago
JSON representation

A crate for generating (relatively) complex compile-time constants in rust

Awesome Lists containing this project

README

        

# const-gen

This is a crate for generating compile-time constants in your `build.rs` file. This crate supports converting types that are typically heap-allocated into fixed-size constants. It includes support for primitives, strings, vectors, maps, sets, and comes with a derive macro to allow implementation with structs and enums.

See this example:
```rust,ignore
// build.rs

use const_gen::*;
use std::{env, fs, path::Path};

// First, let's dummy up some structs. Enabling the "derive"
// feature allows us to do this simply, but implementing the
// CompileConst trait by hand is straightforward.

#[derive(CompileConst)]
#[inherit_doc]
/// Example inherited documentation
struct TestStruct
{
test_u8: u8,
/// Example uninherited field documentation
test_vec: Vec,
}

#[derive(CompileConst)]
#[inherit_docs]
/// Example inherited documentation
enum TestEnum
{
Variant1,
Variant2(u8),
#[inherit_doc]
/// Example inherited variant documentation
Variant3 { named: u8 }
}

#[derive(CompileConst)]
struct TestTup(u8, u16);

fn main()
{
// Use the OUT_DIR environment variable to get an
// appropriate path.
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("const_gen.rs");

// Now let's dummy up some data to use in our const
// generation
let test_vec: Vec = vec!(1,2,3,4,5,10,4);
let test_struct = TestStruct
{
test_u8: 12,
test_vec: vec!(String::from("Hello there."))
};
let test_tup_struct = TestTup(4, 55,);
let test_enum = TestEnum::Variant1;
let test_enum_tup = TestEnum::Variant2(23);
let test_enum_structlike = TestEnum::Variant3{ named: 78 };

// Now we'll generate the const declarations. We're also
// going to test with some primitive types.
let const_declarations = vec!
{
// Here are type definitions for our enums and structs
// above. Attributes from build.rs will not be preserved,
// so we need to pass any we want in.
const_definition!(#[derive(Debug)] pub TestStruct),
const_definition!(#[derive(Debug)] TestTup),
const_definition!(#[derive(Debug)] TestEnum),

// And here are constant definitions for particular
// values.
const_declaration!(TEST_U8 = 27u8),
const_declaration!(#[doc = "Example inherited documentation"] TEST_F32 = 33.5f32),
const_declaration!(TEST_VEC = test_vec),
const_declaration!(TEST_STRING = "I'm a string!"),
const_declaration!(TEST_COW =
std::borrow::Cow::from("Cow!")),
const_declaration!(pub TEST_STRUCT = test_struct),
const_declaration!(TEST_TUP_STRUCT = test_tup_struct),
const_declaration!(TEST_ENUM = test_enum),
const_declaration!(TEST_ENUM_TUP = test_enum_tup),
const_declaration!(TEST_ENUM_STRUCTLIKE =
test_enum_structlike),

// or generate statics with the static_declaration macro
static_declaration!(pub static TEST_U8_STATIC = 27u8),
}.join("\n");

// Note: The `const_definition!` and `const_declaration!`
// macros above are just simple wrappers for CompileConst
// trait methods of the same name. Using those methods
// would entail the following sytax:
// TestStruct::const_definition("#[derive(Debug)]")
// test_struct.const_declaration("TEST_STRUCT")
// These may be preferable in cases where const names
// or type attributes have been procedurally generated
// somehow and need to be treated as strings.

// If the "phf" feature is enabled, this crate will also
// support converting HashMap and HashSet types into
// compile-time constant phf map and set types respectively.

// Lastly, output to the destination file.
fs::write(&dest_path, const_declarations).unwrap();
}

```

Now, in our `main.rs` file we can do something like this:

```rust,ignore

// Include our constants
include!(concat!(env!("OUT_DIR"), "/const_gen.rs"));

// And that's it, we can access all of the const values below.
// It plays quite well with rust-analyzer, etc
fn main()
{
println!("{}", TEST_U8);
println!("{}", TEST_F32);
println!("{:?}", TEST_VEC);
println!("{}", TEST_STRING);
println!("{}", TEST_COW);
println!("{:?}", TEST_STRUCT);
println!("{:?}", TEST_TUP_STRUCT);
println!("{:?}", TEST_ENUM);
println!("{:?}", TEST_ENUM_TUP);
println!("{:?}", TEST_ENUM_STRUCTLIKE);
}
```

The actual generated output looks like (an unformatted version of) this:
```rust,ignore
#[derive(Debug)]
struct TestStruct
{
test_u8: u8,
test_vec: &'static [&'static str],
}
#[derive(Debug)]
struct TestTup(u8, u16);
#[derive(Debug)]
/// Example inherited documentation
enum TestEnum
{
Variant1,
Variant2(u8),
/// Example inherited variant documentation
Variant3 { named: u8 },
}
const TEST_U8: u8 = 27u8;
/// Example inherited documentation
const TEST_F32: f32 = 33.5f32;
const TEST_VEC: &'static [u8] =
&[1u8, 2u8, 3u8, 4u8, 5u8, 10u8, 4u8];
const TEST_STRING: &'static str = "I'm a string!";
const TEST_COW: &'static str = "Cow!";
const TEST_STRUCT: TestStruct = TestStruct
{
test_u8: 12u8,
test_vec: &["Hello there."],
};
const TEST_TUP_STRUCT: TestTup = TestTup(4u8, 55u16);
const TEST_ENUM: TestEnum = TestEnum::Variant1;
const TEST_ENUM_TUP: TestEnum = TestEnum::Variant2(23u8);
const TEST_ENUM_STRUCTLIKE: TestEnum = TestEnum::Variant3
{
named: 78u8
};
static TEST_U8_STATIC: u8 = 27u8;
```

## Out-of-the-box Implementations

The following table shows what types have implementations of the CompileConst trait already defined

|Type|Const Representation|
--- | ---
|\|no conversion|
|bool|no conversion|
|String, &str, str|&'static str|
|Vec\, &[T]|&'static [T]|
|[T; N where N is 0-256]|[T's CompileConst representation; N]|
|Box\, Cow\, Rc\, Arc\|T's CompileConst representation|
|Option\|Option\|
|HashMap|phf::Map\, with K and V's CompileConst representation|
|HashSet\|phf::Set\, with E's CompileConst representation|
|()|no conversion|
|\|A tuple with the CompileConst representation of each variant|

There is also a CompileConstArray trait which generates fixed-size arrays rather than static slices for the following

|Type|Const Array Representation|
--- | ---
|Vec\, &[T]|[T; N]|
|String, &str, str|[char; N]|
|Box\, Cow\, Rc\, Arc\|T's CompileConstArray representation|
|()|no conversion|
|\|A tuple with the CompileConstArray representation of each variant. Only supported if each variant implements CompileConstArray.|

## Attributes

#[inherit_doc]
The generated definition will inherit the documentation of the source item.

#[inherit_docs]
The generated definition will inherit the documentation of the source item, as well as internal items (fields and variants).

## Limitations

This crate will use the endianness, pointer widths, etc of the host machine rather than the target. Eg, doing things like calling `to_ne_bytes` on an integer and storing the results in a const will result in a byte representation that may not be equivalent to that same integer on the target machine.

## Features

At the current time, all features are default.

### phf
The `phf` feature implements the CompileConst trait for HashMaps and HashSets. It will generate a `phf::Map` for HashMap types and a `phf::Set` for HashSet types. Note that `phf` does NOT need to be included in your build dependencies, but it ought to be included in your runtime dependencies in order to use the constants.

### derive
The `derive` feature adds `#[derive(CompileConst)]` for structs and enums. The requirement is that all members implement `CompileConst` as well. The #[inherit_docs] attribute may be added to cause generated definition to inherit rustdocs.

### std
The `std` default feature can be disabled to use the `alloc` crate in place of `std` constructs.