1use crate::config::E2eConfig;
8use alef_core::backend::GeneratedFile;
9use std::collections::HashSet;
10use std::path::Path;
11use tracing::warn;
12
13fn default_formatter(lang: &str) -> Option<&'static str> {
27 match lang {
28 "rust" => Some("(cd {dir} && cargo fmt --all)"),
29 "python" => Some("ruff format {dir}"),
30 _ => None,
31 }
32}
33
34pub fn run_formatters(files: &[GeneratedFile], e2e_config: &E2eConfig) {
42 let output_prefix = Path::new(e2e_config.effective_output());
46 let languages: HashSet<String> = files
47 .iter()
48 .filter_map(|f| {
49 let remainder = f.path.strip_prefix(output_prefix).ok()?;
50 let first = remainder.components().next()?;
51 Some(first.as_os_str().to_string_lossy().into_owned())
52 })
53 .collect();
54
55 for lang in &languages {
56 let cmd_template: &str = if let Some(custom) = e2e_config.format.get(lang.as_str()) {
58 custom.as_str()
59 } else if let Some(builtin) = default_formatter(lang.as_str()) {
60 builtin
61 } else {
62 continue;
63 };
64
65 let dir = format!("{}/{}", e2e_config.effective_output(), lang);
66 let cmd = cmd_template.replace("{dir}", &dir);
67
68 eprintln!(" Formatting {lang}: {cmd}");
69 let status = std::process::Command::new("sh").args(["-c", &cmd]).status();
70
71 match status {
72 Ok(s) if s.success() => {}
73 Ok(s) => {
74 warn!("Formatter for {lang} exited with {s}: {cmd}");
75 }
76 Err(e) => {
77 warn!("Failed to run formatter for {lang}: {e}");
78 }
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_default_formatter_rust_uses_cd_into_dir() {
89 let cmd = default_formatter("rust").expect("rust must have a default formatter");
90 assert!(
91 cmd.contains("cd {dir}") && cmd.contains("cargo fmt"),
92 "rust formatter must cd into {{dir}} before invoking cargo fmt so it works on \
93 standalone (non-workspace-member) e2e crates: {cmd}"
94 );
95 assert!(
96 !cmd.contains("--manifest-path"),
97 "rust formatter must not use --manifest-path; cargo-fmt does not accept it as a \
98 global flag: {cmd}"
99 );
100 assert!(
101 cmd.contains("{dir}"),
102 "rust formatter must include {{dir}} placeholder: {cmd}"
103 );
104 }
105
106 #[test]
107 fn test_default_formatter_python_uses_ruff_format() {
108 let cmd = default_formatter("python").expect("python must have a default formatter");
109 assert!(cmd.contains("ruff"), "python formatter must use ruff: {cmd}");
110 assert!(cmd.contains("format"), "python formatter must run ruff format: {cmd}");
111 assert!(
112 cmd.contains("{dir}"),
113 "python formatter must include {{dir}} placeholder: {cmd}"
114 );
115 }
116
117 #[test]
118 fn test_default_formatter_unknown_lang_returns_none() {
119 assert!(default_formatter("gleam").is_none());
120 assert!(default_formatter("zig").is_none());
121 assert!(default_formatter("java").is_none());
122 }
123}