1mod artifacthub;
2mod file;
3mod git;
4mod registry;
5
6use std::collections::HashMap;
7use std::path::Path;
8
9use husako_config::ChartSource;
10
11#[derive(Debug, thiserror::Error)]
12pub enum HelmError {
13 #[error("chart I/O error: {0}")]
14 Io(String),
15 #[error("invalid values schema: {0}")]
16 InvalidSchema(String),
17 #[error("chart not found: {0}")]
18 NotFound(String),
19}
20
21pub(crate) fn cache_hash(s: &str) -> String {
23 let mut hash: u64 = 5381;
24 for byte in s.bytes() {
25 hash = hash.wrapping_mul(33).wrapping_add(byte as u64);
26 }
27 format!("{hash:016x}")
28}
29
30pub fn resolve(
32 name: &str,
33 source: &ChartSource,
34 project_root: &Path,
35 cache_dir: &Path,
36) -> Result<serde_json::Value, HelmError> {
37 match source {
38 ChartSource::File { path } => file::resolve(name, path, project_root),
39 ChartSource::Registry {
40 repo,
41 chart,
42 version,
43 } => registry::resolve(name, repo, chart, version, cache_dir),
44 ChartSource::ArtifactHub { package, version } => {
45 artifacthub::resolve(name, package, version, cache_dir)
46 }
47 ChartSource::Git { repo, tag, path } => git::resolve(name, repo, tag, path, cache_dir),
48 }
49}
50
51pub fn resolve_all(
55 charts: &HashMap<String, ChartSource>,
56 project_root: &Path,
57 cache_dir: &Path,
58) -> Result<HashMap<String, serde_json::Value>, HelmError> {
59 let mut result = HashMap::new();
60 for (name, source) in charts {
61 let schema = resolve(name, source, project_root, cache_dir)?;
62 result.insert(name.clone(), schema);
63 }
64 Ok(result)
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn cache_hash_deterministic() {
73 let h1 = cache_hash("https://kubernetes.github.io/ingress-nginx");
74 let h2 = cache_hash("https://kubernetes.github.io/ingress-nginx");
75 assert_eq!(h1, h2);
76 assert_eq!(h1.len(), 16);
77 }
78
79 #[test]
80 fn cache_hash_different_inputs() {
81 let h1 = cache_hash("repo-a");
82 let h2 = cache_hash("repo-b");
83 assert_ne!(h1, h2);
84 }
85
86 #[test]
87 fn resolve_all_empty() {
88 let charts = HashMap::new();
89 let tmp = tempfile::tempdir().unwrap();
90 let result = resolve_all(&charts, tmp.path(), &tmp.path().join("cache")).unwrap();
91 assert!(result.is_empty());
92 }
93
94 #[test]
95 fn resolve_all_file_source() {
96 let tmp = tempfile::tempdir().unwrap();
97 std::fs::write(
98 tmp.path().join("values.schema.json"),
99 r#"{"type": "object", "properties": {"replicas": {"type": "integer"}}}"#,
100 )
101 .unwrap();
102
103 let mut charts = HashMap::new();
104 charts.insert(
105 "my-chart".to_string(),
106 ChartSource::File {
107 path: "values.schema.json".to_string(),
108 },
109 );
110
111 let result = resolve_all(&charts, tmp.path(), &tmp.path().join("cache")).unwrap();
112 assert_eq!(result.len(), 1);
113 assert!(result.contains_key("my-chart"));
114 }
115}