use std::{path::Path, sync::Arc};
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum FileExt {
Tf,
Tfvars,
Hcl,
TerragruntHcl,
Json,
}
impl FileExt {
#[must_use]
pub fn classify(path: &Path) -> Option<Self> {
let name = path.file_name()?.to_str()?;
if name == "terragrunt.hcl" {
return Some(Self::TerragruntHcl);
}
let ext = path.extension()?.to_str()?;
match ext {
"tf" => Some(Self::Tf),
"tfvars" => Some(Self::Tfvars),
"hcl" => Some(Self::Hcl),
"json" => Some(Self::Json),
_ => None,
}
}
#[must_use]
pub const fn is_hcl(self) -> bool {
matches!(
self,
Self::Tf | Self::Hcl | Self::TerragruntHcl | Self::Tfvars
)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TypedBuilder)]
#[non_exhaustive]
#[serde(rename_all = "camelCase")]
#[builder(field_defaults(setter(into)))]
pub struct SourceFile {
#[serde(with = "crate::ir::path_serde::arc_path")]
pub path: Arc<Path>,
pub ext: FileExt,
pub size: u64,
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
#[test]
fn test_should_classify_terraform_extensions() {
assert_eq!(
FileExt::classify(&PathBuf::from("main.tf")),
Some(FileExt::Tf)
);
assert_eq!(
FileExt::classify(&PathBuf::from("prod.tfvars")),
Some(FileExt::Tfvars)
);
assert_eq!(
FileExt::classify(&PathBuf::from("root.hcl")),
Some(FileExt::Hcl)
);
assert_eq!(
FileExt::classify(&PathBuf::from("path/to/terragrunt.hcl")),
Some(FileExt::TerragruntHcl)
);
assert_eq!(
FileExt::classify(&PathBuf::from("policy.json")),
Some(FileExt::Json)
);
}
#[test]
fn test_should_reject_unknown_extension() {
assert!(FileExt::classify(&PathBuf::from("README.md")).is_none());
}
#[test]
fn test_should_classify_hcl_files_as_hcl_input() {
for ext in [
FileExt::Tf,
FileExt::Tfvars,
FileExt::Hcl,
FileExt::TerragruntHcl,
] {
assert!(ext.is_hcl(), "{ext:?} should be HCL-shaped");
}
assert!(!FileExt::Json.is_hcl());
}
}