1pub mod brew;
21pub mod c;
22pub mod client;
23pub mod csharp;
24pub mod dart;
25pub mod elixir;
26pub mod gleam;
27pub mod go;
28pub mod java;
29pub mod kotlin;
30pub mod php;
31pub mod python;
32pub mod r;
33pub mod ruby;
34pub mod rust;
35pub mod swift;
36pub mod typescript;
37pub mod wasm;
38pub mod zig;
39
40use crate::config::E2eConfig;
41use crate::fixture::{Fixture, FixtureGroup};
42use alef_core::backend::GeneratedFile;
43use alef_core::config::ResolvedCrateConfig;
44use anyhow::Result;
45
46pub(crate) fn should_include_fixture(fixture: &Fixture, language: &str, e2e_config: &E2eConfig) -> bool {
57 if !e2e_config.exclude_categories.is_empty() && e2e_config.exclude_categories.contains(&fixture.resolved_category())
58 {
59 return false;
60 }
61 if let Some(skip) = &fixture.skip {
62 if skip.should_skip(language) {
63 return false;
64 }
65 }
66 let call_config = e2e_config.resolve_call(fixture.call.as_deref());
67 if call_config.function.is_empty() && !call_config.overrides.contains_key(language) {
68 return false;
69 }
70 true
71}
72
73pub(crate) fn normalize_json_keys_to_snake_case(value: &serde_json::Value) -> serde_json::Value {
78 use heck::ToSnakeCase;
79 match value {
80 serde_json::Value::Object(obj) => {
81 let new_obj: serde_json::Map<String, serde_json::Value> = obj
82 .iter()
83 .map(|(k, v)| (k.to_snake_case(), normalize_json_keys_to_snake_case(v)))
84 .collect();
85 serde_json::Value::Object(new_obj)
86 }
87 serde_json::Value::Array(arr) => {
88 serde_json::Value::Array(arr.iter().map(normalize_json_keys_to_snake_case).collect())
89 }
90 other => other.clone(),
91 }
92}
93
94pub trait E2eCodegen: Send + Sync {
96 fn generate(
98 &self,
99 groups: &[FixtureGroup],
100 e2e_config: &E2eConfig,
101 config: &ResolvedCrateConfig,
102 ) -> Result<Vec<GeneratedFile>>;
103
104 fn language_name(&self) -> &'static str;
106}
107
108pub fn all_generators() -> Vec<Box<dyn E2eCodegen>> {
110 vec![
111 Box::new(rust::RustE2eCodegen),
112 Box::new(python::PythonE2eCodegen),
113 Box::new(typescript::TypeScriptCodegen),
114 Box::new(go::GoCodegen),
115 Box::new(java::JavaCodegen),
116 Box::new(kotlin::KotlinE2eCodegen),
117 Box::new(csharp::CSharpCodegen),
118 Box::new(php::PhpCodegen),
119 Box::new(ruby::RubyCodegen),
120 Box::new(elixir::ElixirCodegen),
121 Box::new(gleam::GleamE2eCodegen),
122 Box::new(r::RCodegen),
123 Box::new(wasm::WasmCodegen),
124 Box::new(c::CCodegen),
125 Box::new(zig::ZigE2eCodegen),
126 Box::new(dart::DartE2eCodegen),
127 Box::new(swift::SwiftE2eCodegen),
128 Box::new(brew::BrewCodegen),
129 ]
130}
131
132pub fn generators_for(languages: &[String]) -> Vec<Box<dyn E2eCodegen>> {
134 all_generators()
135 .into_iter()
136 .filter(|g| languages.iter().any(|l| l == g.language_name()))
137 .collect()
138}
139
140pub(crate) fn resolve_field<'a>(input: &'a serde_json::Value, field_path: &str) -> &'a serde_json::Value {
146 if field_path == "input" {
148 return input;
149 }
150 let path = field_path.strip_prefix("input.").unwrap_or(field_path);
151 let mut current = input;
152 for part in path.split('.') {
153 current = current.get(part).unwrap_or(&serde_json::Value::Null);
154 }
155 current
156}