sherpack_engine/
subchart.rs

1//! Subchart discovery and configuration
2//!
3//! This module provides types for discovering and managing subcharts
4//! within a Sherpack pack.
5
6use sherpack_core::{Dependency, LoadedPack};
7use std::path::PathBuf;
8
9/// Configuration for subchart rendering
10#[derive(Debug, Clone)]
11pub struct SubchartConfig {
12    /// Maximum depth for nested subcharts (default: 10)
13    pub max_depth: usize,
14
15    /// Directory name for subcharts (default: "charts")
16    pub subcharts_dir: String,
17
18    /// Whether to fail on missing subcharts referenced in dependencies
19    pub strict: bool,
20}
21
22impl Default for SubchartConfig {
23    fn default() -> Self {
24        Self {
25            max_depth: 10,
26            subcharts_dir: "charts".to_string(),
27            strict: false,
28        }
29    }
30}
31
32impl SubchartConfig {
33    /// Create a new config with default values
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    /// Set maximum depth for nested subcharts
39    pub fn with_max_depth(mut self, depth: usize) -> Self {
40        self.max_depth = depth;
41        self
42    }
43
44    /// Set the subcharts directory name
45    pub fn with_subcharts_dir(mut self, dir: impl Into<String>) -> Self {
46        self.subcharts_dir = dir.into();
47        self
48    }
49
50    /// Enable strict mode (fail on missing subcharts)
51    pub fn strict(mut self) -> Self {
52        self.strict = true;
53        self
54    }
55}
56
57/// Information about a discovered subchart
58#[derive(Debug)]
59pub struct SubchartInfo {
60    /// Effective name (alias if set, otherwise directory name)
61    pub name: String,
62
63    /// Path to the subchart directory
64    pub path: PathBuf,
65
66    /// Loaded pack
67    pub pack: LoadedPack,
68
69    /// Whether enabled based on condition evaluation
70    pub enabled: bool,
71
72    /// The dependency definition from parent Pack.yaml (if any)
73    pub dependency: Option<Dependency>,
74
75    /// Reason if disabled
76    pub disabled_reason: Option<String>,
77}
78
79impl SubchartInfo {
80    /// Check if this subchart should be rendered
81    pub fn should_render(&self) -> bool {
82        self.enabled
83    }
84
85    /// Get the effective name for value scoping
86    pub fn scope_name(&self) -> &str {
87        &self.name
88    }
89}
90
91/// Result of subchart discovery
92#[derive(Debug, Default)]
93pub struct DiscoveryResult {
94    /// Successfully discovered subcharts
95    pub subcharts: Vec<SubchartInfo>,
96
97    /// Warnings during discovery (e.g., invalid Pack.yaml)
98    pub warnings: Vec<String>,
99
100    /// Missing subcharts referenced in dependencies
101    pub missing: Vec<String>,
102}
103
104impl DiscoveryResult {
105    /// Create a new empty discovery result
106    pub fn new() -> Self {
107        Self::default()
108    }
109
110    /// Get enabled subcharts only
111    pub fn enabled_subcharts(&self) -> impl Iterator<Item = &SubchartInfo> {
112        self.subcharts.iter().filter(|s| s.enabled)
113    }
114
115    /// Get disabled subcharts only
116    pub fn disabled_subcharts(&self) -> impl Iterator<Item = &SubchartInfo> {
117        self.subcharts.iter().filter(|s| !s.enabled)
118    }
119
120    /// Check if there are any warnings or issues
121    pub fn has_issues(&self) -> bool {
122        !self.warnings.is_empty() || !self.missing.is_empty()
123    }
124
125    /// Total count of discovered subcharts
126    pub fn total_count(&self) -> usize {
127        self.subcharts.len()
128    }
129
130    /// Count of enabled subcharts
131    pub fn enabled_count(&self) -> usize {
132        self.subcharts.iter().filter(|s| s.enabled).count()
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_subchart_config_default() {
142        let config = SubchartConfig::default();
143        assert_eq!(config.max_depth, 10);
144        assert_eq!(config.subcharts_dir, "charts");
145        assert!(!config.strict);
146    }
147
148    #[test]
149    fn test_subchart_config_builder() {
150        let config = SubchartConfig::new()
151            .with_max_depth(5)
152            .with_subcharts_dir("packs")
153            .strict();
154
155        assert_eq!(config.max_depth, 5);
156        assert_eq!(config.subcharts_dir, "packs");
157        assert!(config.strict);
158    }
159
160    #[test]
161    fn test_discovery_result_counts() {
162        let mut result = DiscoveryResult::new();
163
164        // Simulate adding subcharts (we can't create real SubchartInfo without LoadedPack)
165        assert_eq!(result.total_count(), 0);
166        assert_eq!(result.enabled_count(), 0);
167        assert!(!result.has_issues());
168
169        result.warnings.push("test warning".to_string());
170        assert!(result.has_issues());
171
172        result.missing.push("missing-chart".to_string());
173        assert!(result.has_issues());
174    }
175}