/ src / config_meta.rs
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  }