Skip to main content

saf_core/spec/
analyzed.rs

1//! Layered spec registry combining YAML-authored specs with analysis-derived facts.
2
3use std::collections::BTreeMap;
4
5use super::derived::DerivedSpec;
6use super::registry::SpecRegistry;
7use super::types::FunctionSpec;
8
9/// Result of looking up a function in the analyzed registry.
10#[derive(Debug)]
11pub enum LookupResult<'a> {
12    /// YAML spec exists, with optional derived overlay.
13    Yaml(&'a FunctionSpec, Option<&'a DerivedSpec>),
14    /// Only analysis-derived spec exists (no YAML entry).
15    DerivedOnly(&'a DerivedSpec),
16}
17
18impl<'a> LookupResult<'a> {
19    /// Get the YAML spec if present.
20    #[must_use]
21    pub fn yaml(&self) -> Option<&'a FunctionSpec> {
22        match self {
23            Self::Yaml(spec, _) => Some(spec),
24            Self::DerivedOnly(_) => None,
25        }
26    }
27
28    /// Get the derived spec if present.
29    #[must_use]
30    pub fn derived(&self) -> Option<&'a DerivedSpec> {
31        match self {
32            Self::Yaml(_, d) => *d,
33            Self::DerivedOnly(d) => Some(d),
34        }
35    }
36}
37
38/// A layered spec registry that combines immutable YAML-authored specs
39/// with mutable analysis-derived overlays.
40///
41/// Consumers query via `lookup()`, which returns both the YAML spec (if any)
42/// and the derived overlay (if any).
43#[derive(Debug)]
44pub struct AnalyzedSpecRegistry {
45    /// Immutable YAML-authored specs.
46    yaml: SpecRegistry,
47    /// Analysis-derived overlay, keyed by function name.
48    derived: BTreeMap<String, DerivedSpec>,
49}
50
51impl AnalyzedSpecRegistry {
52    /// Create a new layered registry wrapping existing YAML specs.
53    #[must_use]
54    pub fn new(yaml: SpecRegistry) -> Self {
55        Self {
56            yaml,
57            derived: BTreeMap::new(),
58        }
59    }
60
61    /// Add an analysis-derived spec overlay for a function.
62    pub fn add_derived(&mut self, name: &str, spec: DerivedSpec) {
63        self.derived.insert(name.to_owned(), spec);
64    }
65
66    /// Look up a function by name. Returns the YAML spec and/or derived overlay.
67    ///
68    /// Returns `None` if neither YAML nor derived spec exists for this name.
69    #[must_use]
70    pub fn lookup(&self, name: &str) -> Option<LookupResult<'_>> {
71        let yaml_spec = self.yaml.lookup(name);
72        let derived_spec = self.derived.get(name);
73
74        match (yaml_spec, derived_spec) {
75            (Some(y), d) => Some(LookupResult::Yaml(y, d)),
76            (None, Some(d)) => Some(LookupResult::DerivedOnly(d)),
77            (None, None) => None,
78        }
79    }
80
81    /// Look up only the derived overlay for a function.
82    #[must_use]
83    pub fn lookup_derived(&self, name: &str) -> Option<&DerivedSpec> {
84        self.derived.get(name)
85    }
86
87    /// Look up only the YAML spec for a function (delegates to inner registry).
88    #[must_use]
89    pub fn lookup_yaml(&self, name: &str) -> Option<&FunctionSpec> {
90        self.yaml.lookup(name)
91    }
92
93    /// Access the underlying YAML registry.
94    #[must_use]
95    pub fn yaml(&self) -> &SpecRegistry {
96        &self.yaml
97    }
98
99    /// Iterate all YAML specs (delegates to inner registry).
100    pub fn iter_yaml(&self) -> impl Iterator<Item = &FunctionSpec> {
101        self.yaml.iter()
102    }
103
104    /// Iterate all derived specs.
105    pub fn iter_derived(&self) -> impl Iterator<Item = (&str, &DerivedSpec)> {
106        self.derived.iter().map(|(k, v)| (k.as_str(), v))
107    }
108
109    /// Number of derived overlay entries.
110    #[must_use]
111    pub fn derived_count(&self) -> usize {
112        self.derived.len()
113    }
114}
115
116impl Default for AnalyzedSpecRegistry {
117    fn default() -> Self {
118        Self::new(SpecRegistry::default())
119    }
120}