use crate::core::backend::GeneratedFile;
use crate::core::config::ResolvedCrateConfig;
use crate::e2e::config::E2eConfig;
use crate::e2e::escape::sanitize_filename;
use crate::e2e::fixture::{Fixture, FixtureGroup};
use anyhow::Result;
use std::path::PathBuf;
use super::E2eCodegen;
pub struct DartE2eCodegen;
impl E2eCodegen for DartE2eCodegen {
fn generate(
&self,
groups: &[FixtureGroup],
e2e_config: &E2eConfig,
config: &ResolvedCrateConfig,
type_defs: &[crate::core::ir::TypeDef],
enums: &[crate::core::ir::EnumDef],
functions: &[crate::core::ir::FunctionDef],
errors: &[crate::core::ir::ErrorDef],
) -> Result<Vec<GeneratedFile>> {
let lang = self.language_name();
let output_base = PathBuf::from(e2e_config.effective_output()).join(lang);
let mut files = Vec::new();
let dart_pkg = e2e_config.resolve_package("dart");
let pkg_name = dart_pkg
.as_ref()
.and_then(|p| p.name.as_ref())
.cloned()
.unwrap_or_else(|| config.dart_pubspec_name());
let pkg_path = dart_pkg
.as_ref()
.and_then(|p| p.path.as_ref())
.cloned()
.unwrap_or_else(|| "../../packages/dart".to_string());
let pkg_version = dart_pkg
.as_ref()
.and_then(|p| p.version.as_ref())
.cloned()
.or_else(|| config.resolved_version())
.unwrap_or_else(|| "0.1.0".to_string());
files.push(GeneratedFile {
path: output_base.join("pubspec.yaml"),
content: project::render_pubspec(&pkg_name, &pkg_path, &pkg_version, e2e_config.dep_mode),
generated_header: false,
});
files.push(GeneratedFile {
path: output_base.join("dart_test.yaml"),
content: concat!(
"# Generated by alef — DO NOT EDIT.\n",
"# Run test files sequentially to avoid overwhelming the SUT with\n",
"# concurrent keep-alive connections.\n",
"concurrency: 1\n",
)
.to_string(),
generated_header: false,
});
let test_base = output_base.join("test");
let bridge_class = config.dart_bridge_class_name();
let frb_module_name = config.name.replace('-', "_");
let dart_stub_methods: std::collections::HashSet<String> = config
.dart
.as_ref()
.map(|d| d.stub_methods.iter().cloned().collect())
.unwrap_or_default();
let dart_first_class_map = values::build_dart_first_class_map(type_defs, enums, e2e_config);
for group in groups {
let active: Vec<&Fixture> = group
.fixtures
.iter()
.filter(|f| super::should_include_fixture(f, lang, e2e_config))
.filter(|f| {
let call_config = e2e_config.resolve_call_for_fixture(
f.call.as_deref(),
&f.id,
&f.resolved_category(),
&f.tags,
&f.input,
);
let resolved_function = call_config
.overrides
.get(lang)
.and_then(|o| o.function.as_ref())
.cloned()
.unwrap_or_else(|| call_config.function.clone());
!dart_stub_methods.contains(&resolved_function)
})
.collect();
if active.is_empty() {
continue;
}
let filename = format!("{}_test.dart", sanitize_filename(&group.category));
let content = test_file::render_test_file(
&group.category,
&active,
e2e_config,
lang,
&pkg_name,
&frb_module_name,
&bridge_class,
&dart_first_class_map,
&config.adapters,
config,
type_defs,
enums,
functions,
errors,
);
files.push(GeneratedFile {
path: test_base.join(filename),
content,
generated_header: true,
});
}
Ok(files)
}
fn language_name(&self) -> &'static str {
"dart"
}
fn render_snippet_body(
&self,
fixture: &Fixture,
e2e_config: &E2eConfig,
config: &ResolvedCrateConfig,
type_defs: &[crate::core::ir::TypeDef],
enums: &[crate::core::ir::EnumDef],
) -> Result<String> {
snippet::render_snippet_body(fixture, e2e_config, config, type_defs, enums)
}
fn render_snippet_body_with_functions(
&self,
fixture: &Fixture,
e2e_config: &E2eConfig,
config: &ResolvedCrateConfig,
type_defs: &[crate::core::ir::TypeDef],
enums: &[crate::core::ir::EnumDef],
functions: &[crate::core::ir::FunctionDef],
) -> Result<String> {
snippet::render_snippet_body_with_ir(fixture, e2e_config, config, type_defs, enums, functions)
}
}
mod assertions;
mod http;
mod project;
mod snippet;
mod stubs;
mod test_case;
mod test_file;
mod values;
pub use stubs::emit_test_backend;
pub(super) use values::escape_dart;
#[cfg(test)]
mod enum_field_classification_tests;
#[cfg(test)]
mod local_naming_tests;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod void_not_error_call_tests;
pub(super) fn dart_call_result_type(call_config: &crate::core::config::e2e::CallConfig) -> Option<String> {
const LOOKUP_LANGS: &[&str] = &["c", "csharp", "java", "kotlin", "go", "php"];
for lang in LOOKUP_LANGS {
if let Some(o) = call_config.overrides.get(*lang)
&& let Some(rt) = o.result_type.as_deref()
&& !rt.is_empty()
{
return Some(rt.to_string());
}
}
None
}