Skip to main content

cool_diff/
config.rs

1use std::collections::HashMap;
2
3/// Top-level configuration for the diff algorithm.
4pub struct DiffConfig {
5    /// Controls how arrays are matched at each path.
6    match_config: MatchConfig,
7
8    /// Default array match mode, used when a path does not specify its own.
9    default_array_mode: ArrayMatchMode,
10
11    /// Default ambiguity strategy, used when a path does not specify its own.
12    default_ambiguous_strategy: AmbiguousMatchStrategy,
13}
14
15impl DiffConfig {
16    /// Creates a new config with default settings.
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    /// Sets the match config for array path lookups.
22    pub fn with_match_config(mut self, match_config: MatchConfig) -> Self {
23        self.match_config = match_config;
24        self
25    }
26
27    /// Sets the fallback array match mode for paths without explicit config.
28    pub fn with_fallback_array_mode(mut self, mode: ArrayMatchMode) -> Self {
29        self.default_array_mode = mode;
30        self
31    }
32
33    /// Sets the fallback ambiguity strategy for paths without explicit config.
34    pub fn with_fallback_ambiguous_strategy(mut self, strategy: AmbiguousMatchStrategy) -> Self {
35        self.default_ambiguous_strategy = strategy;
36        self
37    }
38
39    /// Returns the match config.
40    pub fn match_config(&self) -> &MatchConfig {
41        &self.match_config
42    }
43
44    /// Returns the default array match mode.
45    pub fn default_array_mode(&self) -> &ArrayMatchMode {
46        &self.default_array_mode
47    }
48
49    /// Returns the default ambiguity strategy.
50    pub fn default_ambiguous_strategy(&self) -> &AmbiguousMatchStrategy {
51        &self.default_ambiguous_strategy
52    }
53}
54
55impl Default for DiffConfig {
56    fn default() -> Self {
57        Self {
58            match_config: MatchConfig::default(),
59            default_array_mode: ArrayMatchMode::Index,
60            default_ambiguous_strategy: AmbiguousMatchStrategy::default(),
61        }
62    }
63}
64
65/// Configures how array elements are matched between actual and expected values.
66///
67/// By default, arrays are matched by index. Each path can be configured with
68/// a different matching mode and ambiguity strategy via [`ArrayMatchConfig`].
69pub struct MatchConfig {
70    /// Map from dot-separated path (e.g. `spec.containers`) to the array
71    /// match configuration for that path.
72    paths: HashMap<String, ArrayMatchConfig>,
73}
74
75impl MatchConfig {
76    /// Creates an empty config (index-based matching for all arrays).
77    pub fn new() -> Self {
78        Self {
79            paths: HashMap::new(),
80        }
81    }
82
83    /// Sets the array match configuration for the given dot-separated path.
84    pub fn with_config_at(mut self, path: &str, config: ArrayMatchConfig) -> Self {
85        self.paths.insert(path.to_owned(), config);
86        self
87    }
88
89    /// Returns the array match configuration for the given path, if configured.
90    pub fn config_at(&self, path: &str) -> Option<&ArrayMatchConfig> {
91        self.paths.get(path)
92    }
93}
94
95impl Default for MatchConfig {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101/// Per-path configuration for array element matching.
102pub struct ArrayMatchConfig {
103    /// How elements are matched at this path.
104    mode: ArrayMatchMode,
105
106    /// Optional override for the ambiguity strategy at this path.
107    ///
108    /// Falls back to [`DiffConfig::default_ambiguous_strategy`] if `None`.
109    ambiguous_strategy: Option<AmbiguousMatchStrategy>,
110}
111
112impl ArrayMatchConfig {
113    /// Creates a config with the given mode and no ambiguity override.
114    pub fn new(mode: ArrayMatchMode) -> Self {
115        Self {
116            mode,
117            ambiguous_strategy: None,
118        }
119    }
120
121    /// Sets the ambiguity strategy override for this path.
122    pub fn with_ambiguous_strategy(mut self, strategy: AmbiguousMatchStrategy) -> Self {
123        self.ambiguous_strategy = Some(strategy);
124        self
125    }
126
127    /// Returns the array match mode.
128    pub fn mode(&self) -> &ArrayMatchMode {
129        &self.mode
130    }
131
132    /// Returns the ambiguity strategy override, if set.
133    pub fn ambiguous_strategy(&self) -> Option<&AmbiguousMatchStrategy> {
134        self.ambiguous_strategy.as_ref()
135    }
136}
137
138/// How array elements are matched between actual and expected values at a
139/// given path.
140pub enum ArrayMatchMode {
141    /// Match by position (default). Element 0 compares to element 0, etc.
142    Index,
143
144    /// Match by a distinguished key field (e.g. `name`). Scans the actual
145    /// array for an element with a matching key value.
146    Key(String),
147
148    /// Find a matching element anywhere in the actual array. Uses exact
149    /// value comparison for scalars, recursive subset matching for objects.
150    Contains,
151}
152
153/// Controls behavior when multiple actual array elements could match a single
154/// expected element.
155#[derive(Default)]
156pub enum AmbiguousMatchStrategy {
157    /// Fail if more than one candidate exists.
158    #[default]
159    Strict,
160
161    /// Pick the candidate with the fewest diffs, with a warning comment.
162    BestMatch,
163
164    /// Pick the candidate with the fewest diffs, without a warning comment.
165    Silent,
166}