helm-schema-ir 0.0.7

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

use helm_schema_core::ValuesPath;

use crate::eval_effect::FailCapture;

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

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct ActivatedValuesDefaultSource {
    pub(crate) guards: Vec<crate::Guard>,
    pub(crate) source: crate::ValuesDefaultSource,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct ActivatedValuesRootOverlay {
    pub(crate) guards: Vec<crate::Guard>,
    pub(crate) target_path: ValuesPath,
    pub(crate) source_path: ValuesPath,
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct ValuesRootOverlay {
    pub(crate) target_path: ValuesPath,
    pub(crate) source_path: ValuesPath,
}

#[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<ValuesPath>,
    pub(crate) range_modes: crate::range_modes::RangeModes,
    pub(crate) values_default_sources: BTreeSet<crate::ValuesDefaultSource>,
    pub(crate) activated_values_default_sources: BTreeSet<ActivatedValuesDefaultSource>,
    pub(crate) values_root_overlays: BTreeSet<ValuesRootOverlay>,
    pub(crate) activated_values_root_overlays: BTreeSet<ActivatedValuesRootOverlay>,
    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: ValuesPath,
        schema_type: &str,
    ) {
        if path.segments().next().is_none() {
            return;
        }
        self.type_hints
            .entry(grade)
            .or_default()
            .entry(path)
            .or_default()
            .insert(schema_type.to_string());
    }

    pub(crate) fn extend_type_hints(
        &mut self,
        grade: HintGrade,
        path: &ValuesPath,
        hints: &BTreeSet<String>,
    ) {
        self.type_hints
            .entry(grade)
            .or_default()
            .entry(path.clone())
            .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(ValuesPath) -> ValuesPath,
    {
        let Self {
            type_hints,
            shape_erased_paths,
            range_modes,
            values_default_sources,
            activated_values_default_sources,
            values_root_overlays,
            activated_values_root_overlays,
            values_root_helper_includes: _,
            captures,
        } = self;
        for paths in 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;
        }
        *shape_erased_paths = std::mem::take(shape_erased_paths)
            .into_iter()
            .map(&mut *map)
            .collect();
        range_modes.map_value_paths(map);
        *values_default_sources = std::mem::take(values_default_sources)
            .into_iter()
            .map(|source| crate::ValuesDefaultSource {
                target_path: map(source.target_path),
                source_path: map(source.source_path),
            })
            .collect();
        *activated_values_default_sources = std::mem::take(activated_values_default_sources)
            .into_iter()
            .map(|fact| ActivatedValuesDefaultSource {
                guards: fact
                    .guards
                    .into_iter()
                    .map(|guard| guard.map_value_paths(map))
                    .collect(),
                source: crate::ValuesDefaultSource {
                    target_path: map(fact.source.target_path),
                    source_path: map(fact.source.source_path),
                },
            })
            .collect();
        *values_root_overlays = std::mem::take(values_root_overlays)
            .into_iter()
            .map(|fact| ValuesRootOverlay {
                target_path: map(fact.target_path),
                source_path: map(fact.source_path),
            })
            .collect();
        *activated_values_root_overlays = std::mem::take(activated_values_root_overlays)
            .into_iter()
            .map(|fact| ActivatedValuesRootOverlay {
                guards: fact
                    .guards
                    .into_iter()
                    .map(|guard| guard.map_value_paths(map))
                    .collect(),
                target_path: map(fact.target_path),
                source_path: map(fact.source_path),
            })
            .collect();
        *captures = std::mem::take(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,
            activated_values_default_sources,
            values_root_overlays,
            activated_values_root_overlays,
            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.activated_values_default_sources
            .extend(activated_values_default_sources.iter().cloned());
        self.values_root_overlays
            .extend(values_root_overlays.iter().cloned());
        self.activated_values_root_overlays
            .extend(activated_values_root_overlays.iter().cloned());
        self.values_root_helper_includes
            .extend(values_root_helper_includes.iter().cloned());
        self.captures.extend(captures.iter().cloned());
    }
}