1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
5#[serde(transparent)]
6pub struct ConfigVersion(pub u32);
7
8impl ConfigVersion {
9 pub const V1: Self = Self(1);
10 pub const CURRENT: Self = Self(crate::CONFIG_CURRENT_VERSION);
11
12 pub fn is_compatible(&self, other: &Self) -> bool {
13 self.0 <= other.0
14 }
15
16 pub fn needs_migration(&self, target: &Self) -> bool {
17 self.0 < target.0
18 }
19}
20
21impl fmt::Display for ConfigVersion {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(f, "v{}", self.0)
24 }
25}
26
27impl Default for ConfigVersion {
28 fn default() -> Self {
29 Self::CURRENT
30 }
31}
32
33#[derive(Debug, Clone)]
34pub struct ConfigSchema {
35 pub version: ConfigVersion,
36 pub required_fields: Vec<String>,
37 pub optional_fields: Vec<String>,
38 pub deprecated_fields: Vec<String>,
39}
40
41impl ConfigSchema {
42 pub fn v1() -> Self {
43 Self {
44 version: ConfigVersion::V1,
45 required_fields: vec![
46 "version".to_string(),
47 "enabled".to_string(),
48 "providers".to_string(),
49 ],
50 optional_fields: vec![
51 "cache_dir".to_string(),
52 "cache_ttl".to_string(),
53 "parallel_execution".to_string(),
54 "max_concurrent_jobs".to_string(),
55 "providers_config".to_string(),
56 "filters".to_string(),
57 "ui".to_string(),
58 "commands".to_string(),
59 "overrides".to_string(),
60 ],
61 deprecated_fields: vec![],
62 }
63 }
64
65 pub fn for_version(version: ConfigVersion) -> Self {
66 match version.0 {
67 1 => Self::v1(),
68 _ => Self::v1(), }
70 }
71
72 pub fn validate_fields(&self, fields: &[String]) -> Result<(), Vec<String>> {
73 let mut missing = Vec::new();
74
75 for required in &self.required_fields {
76 if !fields.contains(required) {
77 missing.push(required.clone());
78 }
79 }
80
81 if missing.is_empty() {
82 Ok(())
83 } else {
84 Err(missing)
85 }
86 }
87
88 pub fn get_deprecated_fields(&self, fields: &[String]) -> Vec<String> {
89 fields
90 .iter()
91 .filter(|f| self.deprecated_fields.contains(f))
92 .cloned()
93 .collect()
94 }
95}