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}