Skip to content

Instantly share code, notes, and snippets.

@riskable
Last active April 23, 2022 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save riskable/829f3f544a827a56da64957f52d46c1f to your computer and use it in GitHub Desktop.
Save riskable/829f3f544a827a56da64957f52d46c1f to your computer and use it in GitHub Desktop.
A build.rs made for generating a config.rs that's full of const values from a Config.toml (in a no_std project)
//! Reads our Config.toml and generates userconfig.rs by converting the values to constants
//! that end up being placed in `src/config.rs` via:
//! `include!(concat!(env!("OUT_DIR"), "/userconfig.rs"));`
use core::include;
use std::env;
use std::fs;
use std::path::Path;
// So we can use the date for the serial number
use chrono::{DateTime, Utc};
use toml;
use toml::Value;
// A little hack that lets us use our configuration structs without having to duplicate them here:
include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/config_structs.rs"
));
// NOTE: config_structs.rs includes the serde import so we don't need it here
fn main() {
let now: DateTime<Utc> = Utc::now();
// So we can have a build-time firmware serial number in main.rs like so:
// concat!(env!("SERIALNOW"), " v1.0:BEEF")
println!("cargo:rustc-env=SERIALNOW={}", now.timestamp());
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("userconfig.rs");
let mut out = String::new();
let contents = fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/Config.toml")).unwrap();
// Need the toml in Table form so we can extract values by key:
let config_table: Value = toml::from_str(&contents).unwrap();
let config: Config = toml::from_str(&contents[..]).unwrap();
// out.push_str(format!("// config: {:?}", config).as_str());
// TODO: Figure out a way to iterate over configurable items instead of having them hard coded like this
for fname in KeyboardConfig::field_names().iter() {
let meta = &config.keyboard.gen_meta_tuple(fname);
let value = config_table["keyboard"].get(fname);
let mut _const_out = String::new();
if let Some(val) = value {
// out.push_str(format!("// val.type_str(): {:?}\n", val.type_str()).as_str());
if meta.2.ends_with("str") {
_const_out = format!(
"pub const KEYBOARD_{}: {} = {};\n",
meta.1.to_uppercase(),
"&'static str",
val
);
} else {
_const_out = format!(
"pub const KEYBOARD_{}: {} = {};\n",
meta.1.to_uppercase(),
meta.2,
val
);
}
out.push_str(&_const_out);
}
}
for fname in MouseConfig::field_names().iter() {
let meta = &config.mouse.gen_meta_tuple(fname);
let value = config_table["mouse"].get(meta.1);
if let Some(val) = value {
let const_out = format!(
"pub const {}_{}: {} = {};\n",
meta.0.to_uppercase().split("CONFIG").next().unwrap(),
meta.1.to_uppercase(),
meta.2,
val
);
out.push_str(&const_out);
}
}
for fname in LedsConfig::field_names().iter() {
let meta = &config.leds.gen_meta_tuple(fname);
let value = config_table["leds"].get(meta.1);
if let Some(val) = value {
let const_out = format!(
"pub const {}_{}: {} = {};\n",
meta.0.to_uppercase().split("CONFIG").next().unwrap(),
meta.1.to_uppercase(),
meta.2,
val
);
out.push_str(&const_out);
}
}
for fname in EncoderConfig::field_names().iter() {
let meta = &config.encoder.gen_meta_tuple(fname);
let value = config_table["encoder"].get(meta.1);
if let Some(val) = value {
let const_out = format!(
"pub const {}_{}: {} = {};\n",
meta.0.to_uppercase().split("CONFIG").next().unwrap(),
meta.1.to_uppercase(),
meta.2,
val
);
out.push_str(&const_out);
}
}
for fname in DisplayConfig::field_names().iter() {
let meta = &config.display.gen_meta_tuple(fname);
let value = config_table["display"].get(meta.1);
if let Some(val) = value {
let const_out = format!(
"pub const {}_{}: {} = {};\n",
meta.0.to_uppercase().split("CONFIG").next().unwrap(),
meta.1.to_uppercase(),
meta.2,
val
);
out.push_str(&const_out);
}
}
for fname in InfraredConfig::field_names().iter() {
let meta = &config.infrared.gen_meta_tuple(fname);
let value = config_table["infrared"].get(meta.1);
if let Some(val) = value {
let const_out = format!(
"pub const {}_{}: {} = {};\n",
meta.0.to_uppercase().split("CONFIG").next().unwrap(),
meta.1.to_uppercase(),
meta.2,
val
);
out.push_str(&const_out);
}
}
for fname in DevConfig::field_names().iter() {
let meta = &config.dev.gen_meta_tuple(fname);
let value = config_table["dev"].get(meta.1);
if let Some(val) = value {
let const_out = format!(
"pub const {}_{}: {} = {};\n",
meta.0.to_uppercase().split("CONFIG").next().unwrap(),
meta.1.to_uppercase(),
meta.2,
val
);
out.push_str(&const_out);
}
}
fs::write(&dest_path, out).unwrap();
println!("cargo:rerun-if-changed=Config.toml");
println!("cargo:rerun-if-changed=build.rs");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment