Skip to main content

helm_schema_k8s/kubernetes_openapi/
version_chain.rs

1/// Configuration for the in-provider K8s version chain.
2#[derive(Debug, Clone)]
3pub struct K8sVersionChain {
4    /// User-supplied versions in their literal CLI order. The first is
5    /// the primary, the rest are explicit fallbacks.
6    pub explicit: Vec<String>,
7    /// Auto-extension policy: `None` = no auto-fallback;
8    /// `Some(n)` = append `n` minors below the smallest
9    /// explicit version, monotonically descending.
10    pub auto_fallback_window: Option<u32>,
11}
12
13impl K8sVersionChain {
14    /// Build a chain from explicit user-ordered versions and an
15    /// optional auto-fallback window. The two are combined as:
16    ///   `[explicit..., auto_fallback...]`
17    /// where the auto-fallback list is a descending window of `n` minors
18    /// below the smallest explicit version. Auto-extension is only
19    /// valid when `explicit.len() == 1` — the chain falls back to
20    /// "explicit only" for any other shape.
21    #[must_use]
22    pub fn new(explicit: Vec<String>, auto_fallback_window: Option<u32>) -> Self {
23        Self {
24            explicit,
25            auto_fallback_window,
26        }
27    }
28
29    /// Materialise the ordered list of `version_dirs` to probe.
30    #[must_use]
31    pub fn ordered(&self) -> Vec<String> {
32        let mut out: Vec<String> = self.explicit.clone();
33        if let Some(window) = self.auto_fallback_window
34            && self.explicit.len() == 1
35            && let Some(primary) = self.explicit.first().and_then(|v| parse_minor(v))
36        {
37            for offset in 1..=window {
38                let Some(next_minor) = primary.1.checked_sub(offset) else {
39                    break;
40                };
41                out.push(format!("v{}.{next_minor}.0", primary.0));
42            }
43        }
44        out
45    }
46
47    /// The primary (first explicit) version, if any.
48    #[must_use]
49    pub fn primary(&self) -> Option<&str> {
50        self.explicit.first().map(String::as_str)
51    }
52
53    /// Versions that should participate in apiVersion-inference cache
54    /// scanning. This is `explicit` only — auto-fallback versions are
55    /// escape valves for legacy resources whose schemas exist only in
56    /// older K8s minors; they do NOT represent "what the user intends
57    /// to target". Including them in inference would surface
58    /// historical apiVersions (`policy/v1beta1`, `extensions/v1beta1`,
59    /// …) for kinds whose modern version lives at the primary
60    /// version dir, producing spurious `AmbiguousApiVersion`
61    /// diagnostics.
62    #[must_use]
63    pub fn inference_scan_versions(&self) -> Vec<String> {
64        self.explicit.clone()
65    }
66}
67
68fn parse_minor(version_dir: &str) -> Option<(u32, u32)> {
69    let trimmed = version_dir.trim().trim_start_matches('v');
70    let trimmed = trimmed.split('-').next().unwrap_or(trimmed);
71    let mut parts = trimmed.split('.');
72    let major: u32 = parts.next()?.parse().ok()?;
73    let minor: u32 = parts.next()?.parse().ok()?;
74    Some((major, minor))
75}
76
77#[cfg(test)]
78#[path = "tests/version_chain.rs"]
79mod tests;