Provide config information in machine readable format (#3275)

* Provide config information in machine readable format

* Make tests not fail

* Address review issues
This commit is contained in:
Björn Quentin 2025-03-19 13:36:17 +01:00 committed by GitHub
parent dc55ef008c
commit d7eae19a70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 68 additions and 2 deletions

View File

@ -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"]

View File

@ -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<String>),
/// A custom validation function to run against any supported value type.
#[serde(serialize_with = "serialize_custom")]
#[serde(untagged)]
Custom(Box<dyn Fn(&Value) -> Result<(), Error>>),
}
fn serialize_custom<S>(
_: &Box<dyn Fn(&Value) -> Result<(), Error>>,
serializer: S,
) -> Result<S::Ok, S::Error>
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<W: Write>(
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<W: Write>(
configs
}
fn write_config_json(
crate_name: String,
config: &[(&str, &str, Value, Option<Validator>)],
selected_config: &HashMap<String, Value>,
) {
#[derive(Serialize)]
struct Item<'a> {
name: String,
description: String,
default_value: Value,
actual_value: Value,
constraint: &'a Option<Validator>,
}
let mut to_write: Vec<Item<'_>> = 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