From d7eae19a70a2850c8a9ccab6ca97d090ccf2ac89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Wed, 19 Mar 2025 13:36:17 +0100 Subject: [PATCH] Provide config information in machine readable format (#3275) * Provide config information in machine readable format * Make tests not fail * Address review issues --- esp-config/Cargo.toml | 4 ++- esp-config/src/generate.rs | 66 +++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/esp-config/Cargo.toml b/esp-config/Cargo.toml index c03b0fd5b..5fbf9825f 100644 --- a/esp-config/Cargo.toml +++ b/esp-config/Cargo.toml @@ -10,10 +10,12 @@ license = "MIT OR Apache-2.0" [dependencies] document-features = "0.2.10" +serde = { version = "1.0.197", features = ["derive"], optional = true } +serde_json = { version = "1.0.0", optional = true } [dev-dependencies] temp-env = "0.3.6" [features] ## Enable the generation and parsing of a config -build = [] +build = ["dep:serde","dep:serde_json"] diff --git a/esp-config/src/generate.rs b/esp-config/src/generate.rs index 3088f0861..462082ce4 100644 --- a/esp-config/src/generate.rs +++ b/esp-config/src/generate.rs @@ -8,6 +8,8 @@ use std::{ path::PathBuf, }; +use serde::Serialize; + const DOC_TABLE_HEADER: &str = r#" | Name | Description | Default value | Allowed value | |------|-------------|---------------|---------------| @@ -55,7 +57,7 @@ impl fmt::Display for Error { } /// Supported configuration value types. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum Value { /// Booleans. Bool(bool), @@ -146,6 +148,7 @@ impl fmt::Display for Value { } /// Configuration value validation functions. +#[derive(Serialize)] pub enum Validator { /// Only allow negative integers, i.e. any values less than 0. NegativeInteger, @@ -159,9 +162,21 @@ pub enum Validator { /// String-Enumeration. Only allows one of the given Strings. Enumeration(Vec), /// A custom validation function to run against any supported value type. + #[serde(serialize_with = "serialize_custom")] + #[serde(untagged)] Custom(Box Result<(), Error>>), } +fn serialize_custom( + _: &Box Result<(), Error>>, + serializer: S, +) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str("Custom") +} + impl Validator { fn validate(&self, value: &Value) -> Result<(), Error> { match self { @@ -363,6 +378,8 @@ pub fn generate_config_internal( emit_configuration(stdout, &prefix, &configs, &validators, &mut selected_config); + write_config_json(snake_case(crate_name), config, &configs); + if emit_md_tables { let file_name = snake_case(crate_name); write_config_tables(&file_name, doc_table, selected_config); @@ -371,6 +388,53 @@ pub fn generate_config_internal( configs } +fn write_config_json( + crate_name: String, + config: &[(&str, &str, Value, Option)], + selected_config: &HashMap, +) { + #[derive(Serialize)] + struct Item<'a> { + name: String, + description: String, + default_value: Value, + actual_value: Value, + constraint: &'a Option, + } + + let mut to_write: Vec> = Vec::new(); + for item in config.iter() { + let option_name = format!( + "{}_CONFIG_{}", + screaming_snake_case(&crate_name), + screaming_snake_case(&item.0) + ); + let val = match selected_config.get(&option_name) { + Some(val) => val.clone(), + None => item.2.clone(), + }; + + to_write.push(Item { + name: item.0.to_string(), + description: item.1.to_string(), + default_value: item.2.clone(), + actual_value: val, + constraint: &item.3, + }) + } + + #[cfg(not(test))] + { + let json = serde_json::to_string(&to_write).unwrap(); + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file = out_dir + .join(format!("{crate_name}_config_data.json")) + .display() + .to_string(); + fs::write(out_file, json).unwrap(); + } +} + // A work-around for https://github.com/rust-lang/cargo/issues/10358 // This can be removed when https://github.com/rust-lang/cargo/pull/14058 is merged. // Unlikely to work on projects in workspaces