use serde_json::Value;
use std::collections::HashSet;
pub trait ConfigMeta {
fn config_metadata() -> Vec<FieldMeta>;
#[must_use]
fn correct_paths(fields: Vec<FieldMeta>, parent: &str) -> impl Iterator<Item = FieldMeta> {
fields.into_iter().map(move |mut field| {
field.path = format!("{parent}.{}", field.path);
field
})
}
#[must_use]
fn find_missing_required_fields(config: &Value) -> HashSet<String> {
let metadata = Self::config_metadata();
let mut missing = HashSet::new();
for field in metadata {
if field.skip {
continue;
}
let existing = Self::get_nested_value(config, &field.path);
if field.required && !field.has_default && existing.is_none_or(Value::is_null) {
missing.insert(field.path.to_string());
}
}
missing
}
#[must_use]
fn get_nested_value<'a>(value: &'a Value, path: &str) -> Option<&'a Value> {
let mut current = value;
for key in path.split('.') {
match current {
Value::Object(map) => current = map.get(key)?,
_ => return None,
}
}
Some(current)
}
}
#[expect(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)]
pub struct FieldMeta {
pub name: &'static str,
pub path: String,
pub ty: &'static str,
pub required: bool,
pub skip: bool,
pub has_default: bool,
pub nested: bool,
}