Skip to main content

helm_schema_ir/symbolic/
mod.rs

1use std::collections::BTreeMap;
2use std::rc::Rc;
3
4use helm_schema_ast::DefineIndex;
5
6use crate::analysis_db::IrAnalysisDb;
7use crate::contract::ContractIr;
8
9/// Reusable state for generating symbolic IR across many templates that
10/// share one [`DefineIndex`].
11///
12/// The context owns exact parse/helper-analysis caches. Reusing it across
13/// templates avoids recomputing helper bodies without changing analysis
14/// semantics; a cache miss and cache hit return the same structural facts.
15#[derive(Clone)]
16pub struct SymbolicIrContext {
17    inner: Rc<SymbolicIrContextInner>,
18}
19
20struct SymbolicIrContextInner {
21    analysis_db: IrAnalysisDb,
22}
23
24impl SymbolicIrContext {
25    /// Creates a reusable symbolic-analysis context for indexed chart sources.
26    #[tracing::instrument(skip_all)]
27    pub fn new(defines: &DefineIndex) -> Self {
28        Self {
29            inner: Rc::new(SymbolicIrContextInner {
30                analysis_db: IrAnalysisDb::new(defines),
31            }),
32        }
33    }
34
35    /// Build a context that can execute chart-authored string defaults when
36    /// a `tpl` call selects them. The strings are not general inference
37    /// evidence; they are consulted only at that explicit execution boundary.
38    #[must_use]
39    pub fn with_chart_default_strings(
40        defines: &DefineIndex,
41        chart_default_strings: BTreeMap<String, String>,
42    ) -> Self {
43        Self::with_policy(defines, chart_default_strings, None)
44    }
45
46    /// Build a context carrying analysis policy inputs: the chart-authored
47    /// string defaults plus the normalized Kubernetes version
48    /// (`.Capabilities.KubeVersion` conditions evaluate against it; `None`
49    /// abstains them).
50    #[must_use]
51    pub fn with_policy(
52        defines: &DefineIndex,
53        chart_default_strings: BTreeMap<String, String>,
54        kubernetes_version: Option<String>,
55    ) -> Self {
56        Self {
57            inner: Rc::new(SymbolicIrContextInner {
58                analysis_db: IrAnalysisDb::with_policy(
59                    defines,
60                    chart_default_strings,
61                    kubernetes_version,
62                ),
63            }),
64        }
65    }
66
67    /// Generate the opaque contract graph without finalizing it.
68    ///
69    /// Callers that need to combine, scope, or otherwise transform chart-local
70    /// contracts should use this method and derive schema facts with
71    /// [`ContractIr::finalize`]. Inspection output can finalize the graph once
72    /// and ask the resulting contract for its stable document.
73    #[must_use]
74    pub fn generate_contract_ir(&self, src: &str) -> ContractIr {
75        self.generate_contract_ir_with_provenance(src, None)
76    }
77
78    /// Builds contract IR while attaching a logical template source path.
79    #[must_use]
80    pub fn generate_contract_ir_for_source(&self, src: &str, source_path: &str) -> ContractIr {
81        self.generate_contract_ir_with_provenance(src, Some(source_path))
82    }
83
84    /// Evaluate a template into the abstract fragment domain, reusing this
85    /// context's memoized helper analyses.
86    #[must_use]
87    pub fn eval_document_fragment(&self, src: &str) -> crate::fragment_eval::EvaluatedDocument {
88        crate::fragment_eval::eval_document(src, None, &self.inner.analysis_db)
89    }
90
91    fn generate_contract_ir_with_provenance(
92        &self,
93        src: &str,
94        source_path: Option<&str>,
95    ) -> ContractIr {
96        let document =
97            crate::fragment_eval::eval_document(src, source_path, &self.inner.analysis_db);
98        let mut contract = crate::fragment_eval::contract_ir_from_document(&document);
99        for name in &document.values_root_helper_includes {
100            contract.extend_values_program_wrappers(
101                self.inner
102                    .analysis_db
103                    .program_wrapper_sentinels(name)
104                    .into_iter()
105                    .map(|(key, spread)| helm_schema_core::ValuesProgramWrapper {
106                        scope_path: String::new(),
107                        key,
108                        spread,
109                    }),
110            );
111        }
112        contract.extend_values_program_wrapper_exclusions(
113            document.pre_rewrite_strict_paths.iter().cloned(),
114        );
115        contract
116    }
117}