use blends_domain::content::helm::{fix, HelmInput};
use crate::content::custom_parsers::CustomParser;
use crate::content::Content;
use crate::language::Language;
use crate::parse::parse;
pub const PARSER: CustomParser = CustomParser {
languages: &[Language::Json, Language::Yaml],
validator,
transformer,
};
fn validator(content: &Content) -> bool {
content
.path
.extension()
.and_then(|extension| extension.to_str())
.map(str::to_ascii_lowercase)
.is_some_and(|extension| matches!(extension.as_str(), "json" | "yml" | "yaml"))
}
fn under_templates(content: &Content) -> bool {
content
.path
.components()
.any(|component| component.as_os_str().to_str() == Some("templates"))
}
fn rebuild(content: &Content, text: String) -> Content {
Content {
bytes: text.clone().into_bytes(),
text,
language: content.language,
path: content.path.clone(),
}
}
fn transformer(content: &Content) -> Option<Content> {
if parse(content).is_ok() {
return None;
}
try_fix_helm(content)
}
fn try_fix_helm(content: &Content) -> Option<Content> {
let fixed = rebuild(
content,
fix(HelmInput {
text: &content.text,
under_templates: under_templates(content),
})?,
);
if parse(&fixed).is_ok() {
tracing::debug!(path = %content.path.display(), "applied helm template fixup");
Some(fixed)
} else {
tracing::warn!(
path = %content.path.display(),
"skipping helm template: still malformed after fixup"
);
None
}
}
#[cfg(test)]
mod tests {
use super::transformer;
use crate::content::Content;
use crate::language::{Language, LanguageExt};
use crate::parse::parse;
use std::fs;
use std::path::Path;
fn raw_fixture(name: &str) -> Content {
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../test/data/helm_parser/templates")
.join(name);
let bytes = fs::read(&path).unwrap();
let text = String::from_utf8_lossy(&bytes).into_owned();
let language = Language::from_path(&path).unwrap();
Content {
bytes,
text,
language,
path,
}
}
fn raw_yaml(source: &str) -> Content {
Content {
bytes: source.as_bytes().to_vec(),
text: source.to_owned(),
language: Language::Yaml,
path: Path::new("snippet.yaml").to_path_buf(),
}
}
#[test]
fn root_flow_yaml_is_left_unchanged() {
assert!(transformer(&raw_fixture("json_yaml_template.yaml")).is_none());
}
#[test]
fn block_yaml_is_left_unchanged() {
assert!(transformer(&raw_yaml("version: 2\nmodels:\n - name: a\n")).is_none());
}
#[test]
fn unparseable_helm_template_is_fixed() {
let fixed = transformer(&raw_fixture("deployment_with_nested_conditionals.yaml"))
.expect("templated manifest is fixable");
assert!(parse(&fixed).is_ok());
}
}