build toml_const

Compile-time constants from TOML

2 releases

0.1.1 Nov 28, 2023
0.1.0 Nov 27, 2023

#314 in Build Utils


Used in toml_const_cli

MIT license

25KB
374 lines

toml_const

TOML compile-time constants

Getting started

Install the CLI. This generates some boilerplate files and code:

cargo install toml_const_cli

# use toml_const_cli init --help to view more options
toml_const_cli init <MANIFEST_PATH> # path to Cargo.toml

Your package should now look like this:

package
├── .cargo          # generated/modified by CLI
│   └── config.toml
├── .config         # generated by CLI
│   ├── .gitignore
│   ├── package.debug.toml      # debug variant
│   ├── package.deploy.toml     # deploy/release variant
│   └── package.template.toml   # defaults
├── src
│   └── main.rs
├── .gitignore
├── build.rs        # generated by CLI
├── generated.rs    # generated by build.rs
└── Cargo.toml

If you have an existing build script, add the following inside:

// inside build.rs
fn main() {
    toml_const::run();

    // ... rest of your build script
}

generated.rs can now be included into your code:

include!("../generated.rs") // included in main, for example

It is recommended to include this file in a separate module:

// inside main.rs / lib.rs
mod consts;

// inside consts.rs
#![allow(unused)]
include!("../generated.rs")

And then use it:

let this: bool = consts::USE; // the USE const must always be defined

Explanation

All valid TOML datatypes are generated as compile-time constants, except for tables/arrays that contain inner tables/arrays.

Arrays and tables are defined inside a lazy_static! wrapper.

All tables are destructured, when possible, to keys that point to their TOML values. Namespaces are separated with an underscore "_".

Example

[table]
field = "string"
time = 11:20:00
integer = 3

[table.inner]
field = "another string"
datetime = 1979-05-27 07:32:00Z
one_billion = 1e09

[table.other]
normal_array = [1, 2, 3, 4, 5, 6]

[[arr_of_tables]]
this = "foo"
that = "bar"

[[arr_of_tables]]
this = "fizz"
that = "buzz"

[[arr_of_tables]]
this = "pee"
that = "poo"

Destructured variables

// destructured first table
let f: &str = TABLE_FIELD;
let t: toml::value::Datetime = TABLE_TIME;
let i: i64 = TABLE_INTEGER;

// destructured second table
let tif: &str = TABLE_INNER_FIELD;
let tidt: toml::value::Datetime = TABLE_INNER_DATETIME;
let tib: i64 = TABLE_INNER_ONE_BILLION;

Tables

Last-level tables, or tables that do not contain inner tables or arrays, also have their key-value pairs stored as a HashMap<&'static str, String>:

/// `table` does not have an associated hashmap, as it contains
/// an inner table `table.inner`.
///
/// The associatd hashmap for `table.inner` is TABLE_INNER.
let ht: &HashMap<&'static str, String> = &*TABLE_INNER;

// hashmaps lose type-specific information
let tif_string: &str = TABLE_INNER.get("FIELD").unwrap();
let tidt_string: &str = TABLE_INNER.get("DATETIME").unwrap();
let tib_string: &str = TABLE_INNER.get("ONE_BILLION").unwrap();

// but you can iterate over them at runtime
for key in TABLE_INNER.keys() {
    // ...
}

Arrays

Arrays can also be used. The type for all elements is inferred from the first element.

// iterate over array elements
for item: &i64 in TABLE_OTHER_NORMAL_ARRAY.iter() {
    // ...
}

// iterate over array of tables
for subtable: &HashMap<&'static str, String> in ARR_OF_TABLES.iter() {
    // ...
}
Generated code
// ...imports excluded

/// type: &'static str
pub const TABLE_INNER_FIELD: &'static str = "another string";
/// type: f64
pub const TABLE_INNER_ONE_BILLION: f64 = (1000000000_f64);
/// type: i64
pub const TABLE_INTEGER: i64 = (3_i64);
lazy_static::lazy_static! {
/// type: [HashMap<&'static str, String>; 3]
pub static ref ARR_OF_TABLES: [HashMap<&'static str, String>; 3] = [
HashMap::from([
("that", "bar".to_string()),("this", "foo".to_string()),])
,
HashMap::from([
("that", "buzz".to_string()),("this", "fizz".to_string()),])
,
HashMap::from([
("that", "poo".to_string()),("this", "pee".to_string()),])

];
}
/// type: &'static str
pub const TABLE_FIELD: &'static str = "string";
/// type: bool
pub const USE: bool = false;
/// type: Datetime
pub const TABLE_TIME: Datetime = Datetime {
    date: None,
    time: Some(Time {
        hour: 11,
        minute: 20,
        second: 0,
        nanosecond: 0,
    }),
    offset: None,
};
lazy_static::lazy_static! {
/// type: [i64; 6]
pub static ref OTHER_NORMAL_ARRAY: [i64; 6] = [
(1_i64),
(2_i64),
(3_i64),
(4_i64),
(5_i64),
(6_i64)
];
}
/// type: Datetime
pub const TABLE_INNER_DATETIME: Datetime = Datetime {
    date: Some(Date {
        year: 1979,
        month: 5,
        day: 27,
    }),
    time: Some(Time {
        hour: 7,
        minute: 32,
        second: 0,
        nanosecond: 0,
    }),
    offset: Some(Offset::Z),
};
lazy_static::lazy_static! {
/// type: HashMap<&'static str, String>
pub static ref TABLE_INNER: HashMap<&'static str, String> = HashMap::from([
("DATETIME", Datetime { date: Some(Date { year: 1979, month: 5, day: 27 } ), time: Some(Time { hour: 7, minute: 32, second: 0, nanosecond: 0 } ), offset: Some(Offset::Z) }.to_string()),
("FIELD", "another string".to_string()),
("ONE_BILLION", (1000000000_f64).to_string()),
]);
}

Template, debug, deploy

toml_const generates 3 toml files into your root project directory (located in .config/ by default).

The contents from *.template.toml is used as a base, matching keys from *.debug.toml or *.deploy.toml will override the template values. New keys defined in debug or deploy will be added as well.

Setting the top-level key use=true will cause toml_const to generate code from that particular config file.

debug use deploy use file(s) used
false false template: compilation warning
false true template + deploy
true false template + debug
true true template + deploy

Dependencies

~265–510KB
~11K SLoC