helm-schema-ir 0.0.6

Generate an accurate JSON schema for any helm chart
Documentation
use std::collections::{BTreeMap, BTreeSet};

use crate::eval_effect::FailCapture;

pub(crate) type TypeHints = BTreeMap<String, BTreeSet<String>>;

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum HintScope {
    Unconditional,
    Guarded,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum HintIntent {
    Declared,
    Fallback,
    Tested,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct HintGrade {
    pub(crate) scope: HintScope,
    pub(crate) intent: HintIntent,
}

impl HintGrade {
    pub(crate) const DECLARED: Self = Self::new(HintScope::Unconditional, HintIntent::Declared);
    pub(crate) const GUARDED_DECLARED: Self = Self::new(HintScope::Guarded, HintIntent::Declared);
    pub(crate) const FALLBACK: Self = Self::new(HintScope::Unconditional, HintIntent::Fallback);
    pub(crate) const GUARDED_FALLBACK: Self = Self::new(HintScope::Guarded, HintIntent::Fallback);
    pub(crate) const TESTED: Self = Self::new(HintScope::Unconditional, HintIntent::Tested);

    const fn new(scope: HintScope, intent: HintIntent) -> Self {
        Self { scope, intent }
    }
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct ObservedFacts {
    pub(crate) type_hints: BTreeMap<HintGrade, TypeHints>,
    pub(crate) shape_erased_paths: BTreeSet<String>,
    pub(crate) range_modes: crate::range_modes::RangeModes,
    pub(crate) values_default_sources: BTreeSet<crate::ValuesDefaultSource>,
    pub(crate) values_root_overlay_prefixes: BTreeSet<String>,
    pub(crate) values_root_helper_includes: BTreeSet<String>,
    pub(crate) captures: BTreeSet<FailCapture>,
}

impl ObservedFacts {
    pub(crate) fn insert_type_hint(&mut self, grade: HintGrade, path: String, schema_type: &str) {
        crate::helper_meta::insert_type_hint(
            self.type_hints.entry(grade).or_default(),
            path,
            schema_type,
        );
    }

    pub(crate) fn extend_type_hints(
        &mut self,
        grade: HintGrade,
        path: &str,
        hints: &BTreeSet<String>,
    ) {
        self.type_hints
            .entry(grade)
            .or_default()
            .entry(path.to_owned())
            .or_default()
            .extend(hints.iter().cloned());
    }

    pub(crate) fn promote_tested_type_hints(&mut self) {
        let tested = self
            .type_hints
            .remove(&HintGrade::TESTED)
            .unwrap_or_default();
        for (path, hints) in tested {
            self.extend_type_hints(HintGrade::GUARDED_DECLARED, &path, &hints);
        }
    }

    pub(crate) fn execution_only(mut self) -> Self {
        self.type_hints.clear();
        self
    }

    pub(crate) fn map_value_paths<F>(&mut self, map: &mut F)
    where
        F: FnMut(&str) -> String,
    {
        for paths in self.type_hints.values_mut() {
            let mut mapped = TypeHints::new();
            for (path, hints) in std::mem::take(paths) {
                mapped.entry(map(&path)).or_default().extend(hints);
            }
            *paths = mapped;
        }
        self.shape_erased_paths = std::mem::take(&mut self.shape_erased_paths)
            .into_iter()
            .map(|path| map(&path))
            .collect();
        self.range_modes.map_value_paths(map);
        self.values_default_sources = std::mem::take(&mut self.values_default_sources)
            .into_iter()
            .map(|source| crate::ValuesDefaultSource {
                target_path: map(&source.target_path),
                source_path: map(&source.source_path),
            })
            .collect();
        self.values_root_overlay_prefixes = std::mem::take(&mut self.values_root_overlay_prefixes)
            .into_iter()
            .map(|path| map(&path))
            .collect();
        self.captures = std::mem::take(&mut self.captures)
            .into_iter()
            .map(|mut capture| {
                capture.conjunction = capture
                    .conjunction
                    .into_iter()
                    .map(|predicate| predicate.map_value_paths(map))
                    .collect();
                capture.ranged.map_value_paths(map);
                capture.kind.map_value_paths(map);
                capture
            })
            .collect();
    }

    pub(crate) fn absorb(&mut self, other: &Self) {
        let Self {
            type_hints,
            shape_erased_paths,
            range_modes,
            values_default_sources,
            values_root_overlay_prefixes,
            values_root_helper_includes,
            captures,
        } = other;
        for (grade, paths) in type_hints {
            for (path, hints) in paths {
                self.extend_type_hints(*grade, path, hints);
            }
        }
        self.shape_erased_paths
            .extend(shape_erased_paths.iter().cloned());
        self.range_modes.merge(range_modes);
        self.values_default_sources
            .extend(values_default_sources.iter().cloned());
        self.values_root_overlay_prefixes
            .extend(values_root_overlay_prefixes.iter().cloned());
        self.values_root_helper_includes
            .extend(values_root_helper_includes.iter().cloned());
        self.captures.extend(captures.iter().cloned());
    }
}