fastskill_core/validation/
extension_check.rs1use crate::validation::result::ValidationResult;
4use std::path::Path;
5
6pub(crate) struct ExtensionCheckConfig<'a> {
8 pub allowed_extensions: &'a [&'a str],
9 pub field_label: &'a str,
10 pub no_extension_message: Option<&'a str>,
11}
12
13pub(crate) enum ExtensionPreset {
15 Scripts,
16 References,
17}
18
19pub(crate) fn extension_config(preset: ExtensionPreset) -> ExtensionCheckConfig<'static> {
20 match preset {
21 ExtensionPreset::Scripts => ExtensionCheckConfig {
22 allowed_extensions: &["py", "js", "ts", "sh", "bash", "rb", "go", "rs"],
23 field_label: "scripts",
24 no_extension_message: Some("Script file without extension:"),
25 },
26 ExtensionPreset::References => ExtensionCheckConfig {
27 allowed_extensions: &["md", "txt", "json", "yaml", "yml", "csv", "tsv"],
28 field_label: "references",
29 no_extension_message: None,
30 },
31 }
32}
33
34pub(crate) fn process_file_extension(
35 path: &Path,
36 result: &mut ValidationResult,
37 config: &ExtensionCheckConfig<'_>,
38) {
39 if !path.is_file() {
40 return;
41 }
42 let Some(ext) = path.extension() else {
43 if let Some(msg) = config.no_extension_message {
44 *result = result
45 .clone()
46 .with_warning(config.field_label, &format!("{} {}", msg, path.display()));
47 }
48 return;
49 };
50 let ext_str = ext.to_string_lossy().to_lowercase();
51 if !config.allowed_extensions.contains(&ext_str.as_str()) {
52 *result = result.clone().with_warning(
53 config.field_label,
54 &format!("Unusual {} file extension: {}", config.field_label, ext_str),
55 );
56 }
57}