Skip to main content

helm_schema/
provider_builder.rs

1use std::path::PathBuf;
2use std::sync::Arc;
3
4use helm_schema_k8s::{
5    Chain, ChartLocalCrdSchemaProvider, CrdsCatalogSchemaProvider, DiagnosticSink,
6    K8sSchemaProvider, K8sVersionChain, KubernetesJsonSchemaProvider, LayoutChecker,
7    LocalSchemaProvider, LocalSchemaUniverse, NegativeCache,
8};
9use tracing::instrument;
10
11/// Options consumed by `build_provider`. Comes from the CLI surface or
12/// from library callers.
13#[derive(Debug, Clone, Default)]
14pub struct ProviderOptions {
15    /// User-ordered K8s version list; the first entry is the primary.
16    pub k8s_versions: Vec<String>,
17    /// Auto-fallback window of older K8s minors. `None` = disabled.
18    pub k8s_version_fallback_window: Option<u32>,
19    /// Additional K8s schema mirror URLs.
20    pub k8s_schema_mirrors: Vec<String>,
21    /// Managed K8s cache root.
22    pub k8s_schema_cache_dir: Option<PathBuf>,
23    /// Bypass persistent K8s cache reads while refreshing cache writes.
24    pub no_cache: bool,
25
26    /// Whether provider cache misses may access upstream sources.
27    pub allow_net: bool,
28    /// Whether versioned `Kubernetes OpenAPI` providers are omitted.
29    pub disable_k8s_schemas: bool,
30
31    /// `crd_lookup_loose=true` activates the cross-version CRD cache
32    /// scan + `CrdVersionAvailableAtOtherVersions` hint.
33    pub crd_lookup_loose: bool,
34    /// Additional CRD catalog mirror URLs.
35    pub crd_catalog_mirrors: Vec<String>,
36    /// Managed CRD cache root.
37    pub crd_catalog_cache_dir: Option<PathBuf>,
38    /// Hand-maintained CRD override root.
39    pub crd_override_dir: Option<PathBuf>,
40    /// Chart-local schema universe built from sources such as static `crds/`.
41    pub local_schema_universe: LocalSchemaUniverse,
42    /// Write `.meta` sidecars next to CRD cache entries.
43    pub crd_cache_record_source: bool,
44
45    /// Enable Feature D apiVersion inference.
46    pub api_version_guess: bool,
47}
48
49#[instrument(skip_all)]
50pub fn build_provider(opts: &ProviderOptions, diagnostic_sink: Option<&DiagnosticSink>) -> Chain {
51    let mut providers: Vec<Box<dyn K8sSchemaProvider>> = Vec::new();
52    let negative_cache = Arc::new(NegativeCache::new());
53    let layout_checker = Arc::new(LayoutChecker::new());
54
55    if let Some(dir) = &opts.crd_override_dir {
56        providers.push(Box::new(
57            LocalSchemaProvider::new(dir.clone()).with_api_version_guess(opts.api_version_guess),
58        ));
59    }
60
61    let chart_local_provider = ChartLocalCrdSchemaProvider::new(opts.local_schema_universe.clone())
62        .with_api_version_guess(opts.api_version_guess);
63    if !chart_local_provider.is_empty() {
64        providers.push(Box::new(chart_local_provider));
65    }
66
67    let mut crds_catalog = CrdsCatalogSchemaProvider::new()
68        .with_allow_download(opts.allow_net)
69        .with_mirrors(opts.crd_catalog_mirrors.clone())
70        .with_loose(opts.crd_lookup_loose)
71        .with_api_version_guess(opts.api_version_guess)
72        .with_negative_cache(Arc::clone(&negative_cache))
73        .with_layout_checker(Arc::clone(&layout_checker))
74        .with_record_source(opts.crd_cache_record_source);
75    if let Some(dir) = &opts.crd_catalog_cache_dir {
76        crds_catalog = crds_catalog.with_cache_dir(dir.clone());
77    }
78    if let Some(sink) = diagnostic_sink {
79        crds_catalog = crds_catalog.with_diagnostic_sink(sink.clone());
80    }
81    providers.push(Box::new(crds_catalog));
82
83    if !opts.disable_k8s_schemas {
84        let versions =
85            K8sVersionChain::new(opts.k8s_versions.clone(), opts.k8s_version_fallback_window);
86        let mut k8s = KubernetesJsonSchemaProvider::with_versions(versions)
87            .with_allow_download(opts.allow_net)
88            .with_use_cache(!opts.no_cache)
89            .with_mirrors(opts.k8s_schema_mirrors.clone())
90            .with_api_version_guess(opts.api_version_guess)
91            .with_negative_cache(Arc::clone(&negative_cache))
92            .with_layout_checker(Arc::clone(&layout_checker));
93        if let Some(dir) = &opts.k8s_schema_cache_dir {
94            k8s = k8s.with_cache_dir(dir.clone());
95        }
96        if let Some(sink) = diagnostic_sink {
97            k8s = k8s.with_diagnostic_sink(sink.clone());
98        }
99        providers.push(Box::new(k8s));
100    }
101
102    let mut chain = Chain::new(providers).with_inference_enabled(opts.api_version_guess);
103    if let Some(sink) = diagnostic_sink {
104        chain = chain.with_diagnostic_sink(sink.clone());
105    }
106    chain
107}