config_meta.rs
1 // SPDX-License-Identifier: MIT 2 // Copyright (c) 2025 kingananas20 3 4 //! Enhanced config metadata with field requirement analysis. 5 6 use serde_json::Value; 7 use std::collections::HashSet; 8 9 /// Metadata about configuration fields 10 pub trait ConfigMeta { 11 /// Gets the config metadata from the types of each field 12 fn config_metadata() -> Vec<FieldMeta>; 13 14 /// Corrects the full path for every field 15 #[must_use] 16 fn correct_paths(fields: Vec<FieldMeta>, parent: &str) -> impl Iterator<Item = FieldMeta> { 17 fields.into_iter().map(move |mut field| { 18 field.path = format!("{parent}.{}", field.path); 19 field 20 }) 21 } 22 23 /// Finds the missing required fields 24 #[must_use] 25 fn find_missing_required_fields(config: &Value) -> HashSet<String> { 26 let metadata = Self::config_metadata(); 27 let mut missing = HashSet::new(); 28 29 for field in metadata { 30 if field.skip { 31 continue; 32 } 33 34 let existing = Self::get_nested_value(config, &field.path); 35 36 if field.required && !field.has_default && existing.is_none_or(Value::is_null) { 37 missing.insert(field.path.to_string()); 38 } 39 } 40 41 missing 42 } 43 44 /// Gets the nested values of a JSON `Value` 45 #[must_use] 46 fn get_nested_value<'a>(value: &'a Value, path: &str) -> Option<&'a Value> { 47 let mut current = value; 48 for key in path.split('.') { 49 match current { 50 Value::Object(map) => current = map.get(key)?, 51 _ => return None, 52 } 53 } 54 Some(current) 55 } 56 } 57 58 /// Field metadata with enhanced requirement detection 59 #[expect(clippy::struct_excessive_bools)] 60 #[derive(Debug, Clone)] 61 pub struct FieldMeta { 62 /// Name of the field 63 pub name: &'static str, 64 /// Path to the field 65 pub path: String, 66 /// Type of the field 67 pub ty: &'static str, 68 /// If the field is required (non-optional) 69 pub required: bool, 70 /// If the field has `#[serde(skip)]` 71 pub skip: bool, 72 /// If the field has `#[serde(default)]` 73 pub has_default: bool, 74 /// If it's a nested type 75 pub nested: bool, 76 }