use crate::core::backend::GeneratedFile;
use crate::core::config::ResolvedCrateConfig;
use crate::core::hash::{self, CommentStyle};
use crate::e2e::config::E2eConfig;
use crate::e2e::fixture::FixtureGroup;
use anyhow::Result;
use heck::ToUpperCamelCase;
use std::fmt::Write as FmtWrite;
use std::path::PathBuf;
use super::{E2eCodegen, TestBackendEmission};
use crate::core::config::e2e::DependencyMode;
pub struct HomebrewCodegen;
impl E2eCodegen for HomebrewCodegen {
fn generate(
&self,
_groups: &[FixtureGroup],
e2e_config: &E2eConfig,
config: &ResolvedCrateConfig,
_type_defs: &[crate::core::ir::TypeDef],
_enums: &[crate::core::ir::EnumDef],
) -> Result<Vec<GeneratedFile>> {
let lang = self.language_name();
let output_base = PathBuf::from(e2e_config.effective_output()).join(lang);
if e2e_config.dep_mode != DependencyMode::Registry {
return Ok(vec![GeneratedFile {
path: output_base.join("README.md"),
content: stub_readme(),
generated_header: false,
}]);
}
let pkg = e2e_config.registry.packages.get(lang);
let tap = pkg
.and_then(|p| p.tap.as_ref())
.cloned()
.unwrap_or_else(|| "example/tap".to_string());
let cli_formula = pkg
.and_then(|p| p.cli_formula.as_ref())
.cloned()
.unwrap_or_else(|| config.name.clone());
let ffi_formula = pkg
.and_then(|p| p.ffi_formula.as_ref())
.cloned()
.unwrap_or_else(|| format!("lib{cli_formula}"));
let version = pkg
.and_then(|p| p.version.as_ref())
.cloned()
.unwrap_or_else(|| "0.1.0".to_string());
let ffi_header = config.ffi_header_name();
let ffi_lib_name = config.ffi_lib_name();
let ffi_prefix = config.ffi_prefix();
Ok(vec![
GeneratedFile {
path: output_base.join("Brewfile"),
content: render_brewfile(&tap, &cli_formula, &ffi_formula),
generated_header: false,
},
GeneratedFile {
path: output_base.join("run_tests.sh"),
content: render_run_tests(&tap, &cli_formula, &ffi_formula, &version, &ffi_lib_name),
generated_header: true,
},
GeneratedFile {
path: output_base.join("ffi_smoke.c"),
content: render_ffi_smoke_c(&ffi_header, &ffi_prefix),
generated_header: true,
},
GeneratedFile {
path: output_base.join("README.md"),
content: render_readme(&tap, &cli_formula, &ffi_formula, &version),
generated_header: false,
},
])
}
fn language_name(&self) -> &'static str {
"homebrew"
}
}
fn stub_readme() -> String {
"# homebrew test_app\n\nThis test_app is registry-mode only.\n\
Run `alef e2e generate --registry` (or `alef test-apps generate`) to generate it.\n"
.to_string()
}
fn render_brewfile(tap: &str, cli_formula: &str, ffi_formula: &str) -> String {
let mut out = String::new();
let _ = writeln!(out, "# Brewfile — managed by alef. DO NOT EDIT.");
let _ = writeln!(out, "tap \"{tap}\"");
let _ = writeln!(out, "brew \"{tap}/{cli_formula}\"");
let _ = writeln!(out, "brew \"{tap}/{ffi_formula}\"");
out
}
fn render_run_tests(tap: &str, cli_formula: &str, ffi_formula: &str, version: &str, ffi_lib_name: &str) -> String {
let mut out = String::new();
let _ = writeln!(out, "#!/usr/bin/env bash");
out.push_str(&hash::header(CommentStyle::Hash));
let _ = writeln!(out, "# Tests the Homebrew CLI formula and FFI formula.");
let _ = writeln!(out, "set -euo pipefail");
let _ = writeln!(out);
let _ = writeln!(out, "VERSION=\"{version}\"");
let _ = writeln!(out, "TAP=\"{tap}\"");
let _ = writeln!(out, "CLI_FORMULA=\"{cli_formula}\"");
let _ = writeln!(out, "FFI_FORMULA=\"{ffi_formula}\"");
let _ = writeln!(out, "FFI_FORMULA_QUALIFIED=\"$TAP/$FFI_FORMULA\"");
let _ = writeln!(out);
let _ = writeln!(
out,
"SCRIPT_DIR=\"$(cd \"$(dirname \"${{BASH_SOURCE[0]}}\")\" && pwd)\""
);
let _ = writeln!(out);
let _ = writeln!(out, "PASS=0");
let _ = writeln!(out, "FAIL=0");
let _ = writeln!(out);
let _ = writeln!(out, "pass() {{ echo \"PASS: $1\"; PASS=$((PASS + 1)); }}");
let _ = writeln!(out, "fail() {{ echo \"FAIL: $1 — $2\" >&2; FAIL=$((FAIL + 1)); }}");
let _ = writeln!(out);
let _ = writeln!(out, "# Install formulae.");
let _ = writeln!(out, "brew bundle install --file=\"$SCRIPT_DIR/Brewfile\"");
let _ = writeln!(out);
let _ = writeln!(out, "# Test: CLI version output contains VERSION.");
let _ = writeln!(out, "cli_version=$(\"$CLI_FORMULA\" --version 2>&1 || true)");
let _ = writeln!(out, "if [[ \"$cli_version\" == *\"$VERSION\"* ]]; then");
let _ = writeln!(out, " pass \"cli-version\"");
let _ = writeln!(out, "else");
let _ = writeln!(
out,
" fail \"cli-version\" \"expected '$VERSION' in version output, got: $cli_version\""
);
let _ = writeln!(out, "fi");
let _ = writeln!(out);
let _ = writeln!(out, "# Test: CLI converts <h1>Hi</h1> to markdown containing '# Hi'.");
let _ = writeln!(out, "cli_output=$(echo '<h1>Hi</h1>' | \"$CLI_FORMULA\" 2>&1 || true)");
let _ = writeln!(out, "if [[ \"$cli_output\" == *\"# Hi\"* ]]; then");
let _ = writeln!(out, " pass \"cli-convert-h1\"");
let _ = writeln!(out, "else");
let _ = writeln!(
out,
" fail \"cli-convert-h1\" \"expected '# Hi' in output, got: $cli_output\""
);
let _ = writeln!(out, "fi");
let _ = writeln!(out);
let _ = writeln!(out, "# Test: FFI formula — compile and run ffi_smoke.c.");
let _ = writeln!(out, "TMP_DIR=$(mktemp -d)");
let _ = writeln!(out, "trap 'rm -rf \"$TMP_DIR\"' EXIT");
let _ = writeln!(out);
let _ = writeln!(out, "if command -v pkg-config &>/dev/null; then");
let _ = writeln!(
out,
" FFI_CFLAGS=$(pkg-config --cflags \"$FFI_FORMULA\" 2>/dev/null || true)"
);
let _ = writeln!(
out,
" FFI_LIBS=$(pkg-config --libs \"$FFI_FORMULA\" 2>/dev/null || true)"
);
let _ = writeln!(out, "else");
let _ = writeln!(out, " # Fallback: use brew --prefix to locate headers and libs.");
let _ = writeln!(
out,
" FFI_PREFIX=$(brew --prefix \"$FFI_FORMULA_QUALIFIED\" 2>/dev/null || true)"
);
let _ = writeln!(out, " FFI_CFLAGS=\"-I$FFI_PREFIX/include\"");
let _ = writeln!(out, " FFI_LIBS=\"-L$FFI_PREFIX/lib -l{ffi_lib_name}\"");
let _ = writeln!(out, "fi");
let _ = writeln!(out);
let _ = writeln!(out, "# shellcheck disable=SC2086");
let _ = writeln!(
out,
"if cc $FFI_CFLAGS -o \"$TMP_DIR/ffi_smoke\" \"$SCRIPT_DIR/ffi_smoke.c\" $FFI_LIBS 2>&1; then"
);
let _ = writeln!(out, " if \"$TMP_DIR/ffi_smoke\"; then");
let _ = writeln!(out, " pass \"ffi-smoke\"");
let _ = writeln!(out, " else");
let _ = writeln!(out, " fail \"ffi-smoke\" \"ffi_smoke binary exited non-zero\"");
let _ = writeln!(out, " fi");
let _ = writeln!(out, "else");
let _ = writeln!(out, " fail \"ffi-smoke\" \"compilation of ffi_smoke.c failed\"");
let _ = writeln!(out, "fi");
let _ = writeln!(out);
let _ = writeln!(out, "echo \"\"");
let _ = writeln!(out, "echo \"Results: $PASS passed, $FAIL failed\"");
let _ = writeln!(out, "[ \"$FAIL\" -eq 0 ]");
out
}
fn render_ffi_smoke_c(ffi_header: &str, ffi_prefix: &str) -> String {
let mut out = String::new();
let result_type = format!("{}Result", ffi_prefix.to_upper_camel_case());
let convert_fn = format!("{ffi_prefix}_convert");
let free_fn = format!("{ffi_prefix}_result_free");
out.push_str(&hash::header(CommentStyle::Block));
let _ = writeln!(out, "#include <{ffi_header}>");
let _ = writeln!(out, "#include <stdio.h>");
let _ = writeln!(out, "#include <stdlib.h>");
let _ = writeln!(out, "#include <string.h>");
let _ = writeln!(out);
let _ = writeln!(out, "int main(void) {{");
let _ = writeln!(out, " const char *html = \"<h1>Hi</h1>\";");
let _ = writeln!(out, " {result_type} result = {convert_fn}(html, NULL);");
let _ = writeln!(out);
let _ = writeln!(out, " if (result.error_code != 0) {{");
let _ = writeln!(
out,
" fprintf(stderr, \"FAIL: {convert_fn} returned error %d: %s\\n\","
);
let _ = writeln!(
out,
" result.error_code, result.error_message ? result.error_message : \"\");"
);
let _ = writeln!(out, " {free_fn}(result);");
let _ = writeln!(out, " return 1;");
let _ = writeln!(out, " }}");
let _ = writeln!(out);
let _ = writeln!(
out,
" if (!result.content || strstr(result.content, \"Hi\") == NULL) {{"
);
let _ = writeln!(
out,
" fprintf(stderr, \"FAIL: expected 'Hi' in output, got: %s\\n\","
);
let _ = writeln!(out, " result.content ? result.content : \"(null)\");");
let _ = writeln!(out, " {free_fn}(result);");
let _ = writeln!(out, " return 1;");
let _ = writeln!(out, " }}");
let _ = writeln!(out);
let _ = writeln!(out, " printf(\"PASS: {convert_fn} returned: %s\\n\", result.content);");
let _ = writeln!(out, " {free_fn}(result);");
let _ = writeln!(out, " return 0;");
let _ = writeln!(out, "}}");
out
}
fn render_readme(tap: &str, cli_formula: &str, ffi_formula: &str, version: &str) -> String {
format!(
"# homebrew test_app\n\n\
Exercises the configured Homebrew formulae from tap `{tap}` at version `{version}`.\n\n\
| Formula | Purpose |\n\
|---------|--------|\n\
| `{cli_formula}` | CLI binary: converts HTML from stdin to Markdown on stdout |\n\
| `{ffi_formula}` | Shared library: C FFI for embedding in other languages |\n\n\
## Running\n\n\
```bash\n\
bash run_tests.sh\n\
```\n\n\
## What it tests\n\n\
1. `brew bundle install` succeeds (tap + both formulae install without error).\n\
2. `{cli_formula} --version` output contains `{version}`.\n\
3. `echo '<h1>Hi</h1>' | {cli_formula}` produces output containing `# Hi`.\n\
4. `ffi_smoke.c` compiles against `{ffi_formula}` (via `pkg-config`) and the\n\
compiled binary converts `<h1>Hi</h1>` successfully.\n"
)
}
pub fn emit_test_backend(
_trait_bridge: &crate::core::config::TraitBridgeConfig,
_methods: &[&crate::core::ir::MethodDef],
_fixture: &crate::e2e::fixture::Fixture,
) -> super::TestBackendEmission {
TestBackendEmission::unimplemented("homebrew")
}