use crate::codegen::naming::underscore_camel_case;
use crate::core::config::ResolvedCrateConfig;
use crate::core::ir::{EnumDef, TypeRef};
use crate::e2e::codegen::call_ir::TargetParams;
use crate::e2e::codegen::recipe::ResolvedE2eCallRecipe;
use crate::e2e::codegen::resolve_field;
use crate::e2e::config::ArgMapping;
use crate::e2e::fixture::Fixture;
use super::super::stubs::emit_test_backend;
use super::super::values::{escape_dart, mime_from_extension, type_name_to_create_from_json_dart};
pub(super) struct DartArgContext<'a> {
pub(super) fixture: &'a Fixture,
pub(super) is_snippet: bool,
pub(super) bridge_class: &'a str,
pub(super) config: &'a ResolvedCrateConfig,
pub(super) type_defs: &'a [crate::core::ir::TypeDef],
pub(super) enums: &'a [EnumDef],
pub(super) call_recipe: &'a ResolvedE2eCallRecipe<'a>,
pub(super) target_params: TargetParams<'a>,
pub(super) native_typed_dtos: bool,
pub(super) options_type: Option<&'a str>,
pub(super) options_via: &'a str,
pub(super) is_frb_bridge_call: bool,
pub(super) adapter_request_type: Option<&'a str>,
pub(super) file_path_for_mime: Option<&'a str>,
pub(super) routes_to_source_code: bool,
pub(super) client_factory_for_args: Option<&'a str>,
}
pub(super) fn build_args_and_setup(setup_lines: &mut Vec<String>, args: &mut Vec<String>, ctx: &DartArgContext<'_>) {
for (arg_index, arg_def) in ctx.call_recipe.args.iter().enumerate() {
match arg_def.arg_type.as_str() {
"mock_url" => {
render_mock_url_arg(setup_lines, args, arg_def, ctx);
continue;
}
"handle" => {
render_handle_arg(setup_lines, args, arg_def, ctx);
continue;
}
"mock_url_list" => {
render_mock_url_list_arg(setup_lines, args, arg_def, ctx);
continue;
}
"test_backend" => {
render_test_backend_arg(args, arg_def, ctx);
continue;
}
_ => {}
}
let arg_value = resolve_field(&ctx.fixture.input, &arg_def.field);
match arg_def.arg_type.as_str() {
"bytes" | "file_path" => render_bytes_or_file_path_arg(args, arg_def, arg_value, ctx),
"int" | "integer" | "i64" => render_int_arg(args, arg_def, arg_value, ctx),
"float" | "number" => render_float_arg(args, arg_def, arg_value, ctx),
"bool" | "boolean" => render_bool_arg(args, arg_def, arg_value, ctx),
"string" => render_string_arg(args, arg_def, arg_index, arg_value, ctx),
"json_object" => render_json_object_arg(setup_lines, args, arg_def, arg_value, ctx),
_ => {}
}
}
}
fn render_mock_url_arg(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
let name = arg_def.name.clone();
let value = resolve_field(&fixture.input, &arg_def.field);
if let Some(url) = crate::e2e::codegen::preserved_url_literal(fixture.preserve_input_urls, value)
.or_else(|| crate::e2e::codegen::snippet_url_literal(ctx.is_snippet, value))
{
setup_lines.push(format!("final {name} = '{}';", escape_dart(url)));
} else {
let fixture_id = &fixture.id;
setup_lines.push(format!(r#"final {name} = _fixtureUrl("{fixture_id}");"#));
}
if let Some(req_type) = ctx.adapter_request_type {
let req_var = format!("{}Req", name);
let req_type_name = req_type.rsplit("::").next().unwrap_or(req_type);
setup_lines.push(format!("final {req_var} = {req_type_name}(url: {name});"));
args.push(req_var);
} else {
args.push(name);
}
}
fn render_handle_arg(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
let bridge_class = ctx.bridge_class;
let name = arg_def.name.clone();
let field = arg_def.field.strip_prefix("input.").unwrap_or(&arg_def.field);
let config_value = fixture.input.get(field).cloned().unwrap_or(serde_json::Value::Null);
let create_fn = {
let mut chars = name.chars();
let pascal = match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
};
format!("create{pascal}")
};
if config_value.is_null() || config_value.is_object() && config_value.as_object().is_some_and(|o| o.is_empty()) {
setup_lines.push(format!("final {name} = await {bridge_class}.{create_fn}();"));
} else {
let json_str = serde_json::to_string(&config_value).unwrap_or_default();
let config_var = format!("{name}Config");
let config_type_name = ctx.call_recipe.handle_config_type(arg_def).unwrap_or(&arg_def.name);
let create_from_json_fn = type_name_to_create_from_json_dart(config_type_name);
setup_lines.push(format!(
"final {config_var} = await {create_from_json_fn}(json: r'{json_str}');"
));
setup_lines.push(format!(
"final {name} = await {bridge_class}.{create_fn}(config: {config_var});"
));
}
args.push(name);
}
fn render_mock_url_list_arg(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
let val = crate::e2e::codegen::resolve_urls_field(&fixture.input, &arg_def.field);
let preserved_urls = crate::e2e::codegen::preserved_url_list(fixture.preserve_input_urls, val);
let is_preserved = preserved_urls.is_some();
let paths: Vec<String> = if let Some(urls) = preserved_urls {
urls.into_iter().map(|url| format!("'{}'", escape_dart(url))).collect()
} else if let Some(arr) = val.as_array() {
arr.iter()
.filter_map(|v| v.as_str())
.map(|s| format!("'{}'", escape_dart(s)))
.collect()
} else {
Vec::new()
};
let var_name = &arg_def.name;
let paths_literal = paths.join(", ");
if is_preserved || ctx.is_snippet {
setup_lines.push(format!("final {var_name} = <String>[{paths_literal}];"));
} else {
let fixture_id = &fixture.id;
setup_lines.push(format!(r#"final {var_name}Base = _fixtureUrl("{fixture_id}");"#));
setup_lines.push(format!(
r#"final {var_name} = <String>[{paths_literal}].map((p) => p.startsWith('http') ? p : {var_name}Base + p).toList();"#
));
}
if let Some(req_type) = ctx.adapter_request_type {
let req_var = format!("{}Req", var_name);
let req_type_name = req_type.rsplit("::").next().unwrap_or(req_type);
setup_lines.push(format!("final {req_var} = {req_type_name}(urls: {var_name});"));
args.push(req_var);
} else {
args.push(var_name.to_string());
}
}
fn render_test_backend_arg(args: &mut Vec<String>, arg_def: &ArgMapping, ctx: &DartArgContext<'_>) {
let fixture = ctx.fixture;
let config = ctx.config;
let type_defs = ctx.type_defs;
let enums = ctx.enums;
if let Some(trait_name) = &arg_def.trait_name
&& let Some(trait_bridge) = config.trait_bridges.iter().find(|tb| tb.trait_name == *trait_name)
{
let methods: Vec<&crate::core::ir::MethodDef> = type_defs
.iter()
.find(|t| t.name == *trait_name)
.map(|t| t.methods.iter().collect())
.unwrap_or_default();
let emission = emit_test_backend(trait_bridge, &methods, fixture, enums);
args.push(emission.arg_expr);
return;
}
panic!(
"Dart e2e generator: fixture `{}` declares a `test_backend` arg `{}` with trait `{:?}`, but either it has no `trait_name` configured or no `[[crates.trait_bridges]]` entry matches it; cannot generate a Dart stub without a resolvable trait bridge",
fixture.id, arg_def.name, arg_def.trait_name
);
}
fn render_bytes_or_file_path_arg(
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
if let serde_json::Value::String(file_path) = arg_value {
let arg_expr = if arg_def.arg_type == "file_path" && ctx.routes_to_source_code {
format!("'{file_path}'")
} else {
format!("File('{}').readAsBytesSync()", file_path)
};
if ctx.is_frb_bridge_call {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {arg_expr}"));
} else {
args.push(arg_expr);
}
}
}
fn render_int_arg(
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let dart_param_name = underscore_camel_case(&arg_def.name);
match arg_value {
serde_json::Value::Number(n) => {
if ctx.is_frb_bridge_call {
args.push(format!("{dart_param_name}: {}", n));
} else {
args.push(n.to_string());
}
}
serde_json::Value::Null if arg_def.optional => {
}
_ => {
if ctx.is_frb_bridge_call {
args.push(format!("{dart_param_name}: 0"));
} else {
args.push("0".to_string());
}
}
}
}
fn render_float_arg(
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let dart_param_name = underscore_camel_case(&arg_def.name);
match arg_value {
serde_json::Value::Number(n) => {
if ctx.is_frb_bridge_call {
args.push(format!("{dart_param_name}: {}", n));
} else {
args.push(n.to_string());
}
}
serde_json::Value::Null if arg_def.optional => {
}
_ => {
if ctx.is_frb_bridge_call {
args.push(format!("{dart_param_name}: 0.0"));
} else {
args.push("0.0".to_string());
}
}
}
}
fn render_bool_arg(
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let dart_param_name = underscore_camel_case(&arg_def.name);
match arg_value {
serde_json::Value::Bool(b) => {
let bool_str = if *b { "true" } else { "false" };
if ctx.is_frb_bridge_call {
args.push(format!("{dart_param_name}: {bool_str}"));
} else {
args.push(bool_str.to_string());
}
}
serde_json::Value::Null if arg_def.optional => {
}
_ => {
if ctx.is_frb_bridge_call {
args.push(format!("{dart_param_name}: false"));
} else {
args.push("false".to_string());
}
}
}
}
fn render_string_arg(
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_index: usize,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let dart_param_name = underscore_camel_case(&arg_def.name);
let mime_type_is_positional =
arg_def.name == "mime_type" && !ctx.is_frb_bridge_call && ctx.client_factory_for_args.is_none();
match arg_value {
serde_json::Value::String(s) => {
let literal = ir_typed_dart_expression(arg_def, arg_index, arg_value, ctx.target_params, ctx.enums)
.unwrap_or_else(|| format!("'{}'", escape_dart(s)));
if ctx.is_frb_bridge_call || ctx.client_factory_for_args.is_some() || arg_def.optional {
if !mime_type_is_positional {
args.push(format!("{dart_param_name}: {literal}"));
} else {
args.push(literal);
}
} else {
args.push(literal);
}
}
serde_json::Value::Null
if arg_def.optional
&& arg_def.name == "mime_type" =>
{
let inferred = ctx
.file_path_for_mime
.and_then(mime_from_extension)
.unwrap_or("application/octet-stream");
if mime_type_is_positional {
args.push(format!("'{inferred}'"));
} else {
args.push(format!("{dart_param_name}: '{inferred}'"));
}
}
_ => {}
}
}
fn render_json_object_arg(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
if let Some(elem_type) = &arg_def.element_type {
render_json_object_with_element_type(setup_lines, args, arg_def, elem_type, arg_value, ctx);
} else if ctx.options_via == "from_json" {
render_json_object_from_json_options(setup_lines, args, arg_def, arg_value, ctx);
} else if ctx.call_recipe.should_materialize_json_object(arg_def, arg_value) && arg_value.is_null() {
render_json_object_materialize_default(setup_lines, args, arg_def, ctx);
} else if arg_def.name == "config" {
render_config_arg(setup_lines, args, arg_def, arg_value, ctx);
} else if arg_value.is_array() {
render_json_object_generic_array(setup_lines, args, arg_def, arg_value, ctx);
} else if let serde_json::Value::Object(_) = &arg_value {
render_json_object_generic_object(setup_lines, args, arg_def, arg_value, ctx);
}
}
fn render_json_object_with_element_type(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
elem_type: &str,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
if arg_value.is_object() {
render_json_object_element_object(setup_lines, args, arg_def, elem_type, arg_value, ctx);
} else if elem_type == "String" && arg_value.is_array() {
render_json_object_string_array(setup_lines, args, arg_def, arg_value, ctx);
} else if arg_value.is_array() {
render_json_object_typed_array(setup_lines, args, arg_def, elem_type, arg_value, ctx);
}
}
fn render_json_object_element_object(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
elem_type: &str,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
let json_str = serde_json::to_string(&arg_value).unwrap_or_default();
let escaped_json = escape_dart(&json_str);
let var_name = underscore_camel_case(&arg_def.name);
let dart_fn = type_name_to_create_from_json_dart(elem_type);
let json_source = if crate::e2e::codegen::value_contains_mock_url_placeholder(arg_value) {
setup_lines.push(format!(
"final {var_name}MockBaseUrl = _fixtureUrl(\"{}\");",
fixture.id
));
setup_lines.push(format!(
"final {var_name}Json = '{escaped_json}'.replaceAll(r'{}', {var_name}MockBaseUrl);",
crate::e2e::codegen::MOCK_URL_PLACEHOLDER
));
format!("{var_name}Json")
} else {
format!("'{escaped_json}'")
};
setup_lines.push(format!("final {var_name} = await {dart_fn}(json: {json_source});"));
if ctx.is_frb_bridge_call {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {var_name}"));
} else {
args.push(var_name);
}
}
fn render_json_object_string_array(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
let mock_base_var = if crate::e2e::codegen::value_contains_mock_url_placeholder(arg_value) {
let var_name = format!("{}MockBaseUrl", arg_def.name);
setup_lines.push(format!("final {var_name} = _fixtureUrl(\"{}\");", fixture.id));
Some(var_name)
} else {
None
};
let items: Vec<String> = arg_value
.as_array()
.unwrap()
.iter()
.filter_map(|v| v.as_str())
.map(|s| {
if let Some(base_var) = mock_base_var.as_deref()
&& s.contains(crate::e2e::codegen::MOCK_URL_PLACEHOLDER)
{
format!(
"'{}'.replaceAll(r'{}', {base_var})",
escape_dart(s),
crate::e2e::codegen::MOCK_URL_PLACEHOLDER
)
} else {
format!("'{}'", escape_dart(s))
}
})
.collect();
let list_literal = format!("<String>[{}]", items.join(", "));
if ctx.is_frb_bridge_call {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {list_literal}"));
} else {
args.push(list_literal);
}
}
fn render_json_object_typed_array(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
elem_type: &str,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
let json_str = serde_json::to_string(&arg_value).unwrap_or_default();
let var_name = arg_def.name.clone();
let json_source = if crate::e2e::codegen::value_contains_mock_url_placeholder(arg_value) {
setup_lines.push(format!(
"final {var_name}MockBaseUrl = _fixtureUrl(\"{}\");",
fixture.id
));
setup_lines.push(format!(
"final {var_name}Json = r'{json_str}'.replaceAll(r'{}', {var_name}MockBaseUrl);",
crate::e2e::codegen::MOCK_URL_PLACEHOLDER
));
format!("{var_name}Json")
} else {
format!("r'{json_str}'")
};
let dart_fn = type_name_to_create_from_json_dart(elem_type);
setup_lines.push(format!(
"final {var_name} = await Future.wait((jsonDecode({json_source}) as List<dynamic>).map((element) => {dart_fn}(json: jsonEncode(element))));"
));
if ctx.is_frb_bridge_call {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {var_name}"));
} else {
args.push(var_name);
}
}
fn render_json_object_from_json_options(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
let type_defs = ctx.type_defs;
let native_typed_dtos = ctx.native_typed_dtos;
if let Some(opts_type) = ctx
.call_recipe
.json_object_constructor_type(arg_def, arg_value)
.or(ctx.options_type)
&& !arg_value.is_null()
{
if native_typed_dtos
&& let Some(expression) = super::super::values::render_native_dart_dto(
opts_type,
arg_value,
type_defs,
&fixture.docs_files_for_arg(&arg_def.field),
)
{
let var_name = underscore_camel_case(&arg_def.name);
setup_lines.push(format!("final {var_name} = {expression};"));
args.push(format!("req: {var_name}"));
return;
}
let json_str = serde_json::to_string(&arg_value).unwrap_or_default();
let escaped_json = escape_dart(&json_str);
let var_name = underscore_camel_case(&arg_def.name);
let dart_fn = type_name_to_create_from_json_dart(opts_type);
setup_lines.push(format!("final {var_name} = await {dart_fn}(json: '{escaped_json}');"));
args.push(format!("req: {var_name}"));
}
}
fn render_json_object_materialize_default(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
ctx: &DartArgContext<'_>,
) {
let type_defs = ctx.type_defs;
let enums = ctx.enums;
if let Some(opts_type) = ctx.options_type {
let var_name = underscore_camel_case(&arg_def.name);
let dart_fn = type_name_to_create_from_json_dart(opts_type);
setup_lines.push(format!("final {var_name} = await {dart_fn}(json: '{{}}');"));
let is_config_positional = arg_def.name == "config" && is_dart_config_positional(opts_type, type_defs, enums);
if is_config_positional {
args.push(var_name);
} else {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {var_name}"));
}
}
}
fn render_config_arg(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
if let serde_json::Value::Object(map) = &arg_value {
if !map.is_empty() {
render_config_arg_object_nonempty(setup_lines, args, arg_def, arg_value, ctx);
} else {
render_config_arg_object_empty(setup_lines, args, arg_def, arg_value, ctx);
}
} else if arg_def.optional {
render_config_arg_optional_absent(setup_lines, args, arg_def, arg_value, ctx);
}
}
fn render_config_arg_object_nonempty(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let type_defs = ctx.type_defs;
let enums = ctx.enums;
let opts_type = ctx
.call_recipe
.json_object_constructor_type(arg_def, arg_value)
.or(arg_def.element_type.as_deref())
.or(ctx.options_type)
.unwrap_or(&arg_def.name);
let json_str = serde_json::to_string(&arg_value).unwrap_or_default();
let escaped_json = escape_dart(&json_str);
let var_name = underscore_camel_case(&arg_def.name);
let dart_fn = type_name_to_create_from_json_dart(opts_type);
setup_lines.push(format!("final {var_name} = await {dart_fn}(json: '{escaped_json}');"));
if is_dart_config_positional(opts_type, type_defs, enums) {
args.push(var_name);
} else {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {var_name}"));
}
}
fn render_config_arg_object_empty(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let type_defs = ctx.type_defs;
let enums = ctx.enums;
let opts_type = ctx
.call_recipe
.json_object_constructor_type(arg_def, arg_value)
.or(arg_def.element_type.as_deref())
.or(ctx.options_type);
if let Some(opts_type) = opts_type {
let var_name = underscore_camel_case(&arg_def.name);
let dart_fn = type_name_to_create_from_json_dart(opts_type);
setup_lines.push(format!("final {var_name} = await {dart_fn}(json: '{{}}');"));
if is_dart_config_positional(opts_type, type_defs, enums) {
args.push(var_name);
} else {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {var_name}"));
}
}
}
fn render_config_arg_optional_absent(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let type_defs = ctx.type_defs;
let enums = ctx.enums;
let opts_type = ctx
.call_recipe
.json_object_constructor_type(arg_def, arg_value)
.or(arg_def.element_type.as_deref())
.or(ctx.options_type);
if let Some(opts_type) = opts_type.filter(|_| {
ctx.call_recipe.json_object_arg_has_default(arg_def)
|| ctx.call_recipe.should_materialize_json_object(arg_def, arg_value)
}) {
let var_name = underscore_camel_case(&arg_def.name);
let dart_fn = type_name_to_create_from_json_dart(opts_type);
setup_lines.push(format!("final {var_name} = await {dart_fn}(json: '{{}}');"));
if is_dart_config_positional(opts_type, type_defs, enums) {
args.push(var_name);
} else {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {var_name}"));
}
}
}
fn render_json_object_generic_array(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let json_str = serde_json::to_string(&arg_value).unwrap_or_default();
let var_name = arg_def.name.clone();
setup_lines.push(format!(
"final {var_name} = (jsonDecode(r'{json_str}') as List<dynamic>).cast<String>();"
));
if ctx.is_frb_bridge_call {
let dart_param_name = underscore_camel_case(&arg_def.name);
args.push(format!("{dart_param_name}: {var_name}"));
} else {
args.push(var_name);
}
}
fn render_json_object_generic_object(
setup_lines: &mut Vec<String>,
args: &mut Vec<String>,
arg_def: &ArgMapping,
arg_value: &serde_json::Value,
ctx: &DartArgContext<'_>,
) {
let fixture = ctx.fixture;
if let serde_json::Value::Object(map) = &arg_value
&& !map.is_empty()
&& let Some(opts_type) = ctx
.call_recipe
.json_object_constructor_type(arg_def, arg_value)
.or(ctx.options_type)
{
let json_str = serde_json::to_string(&arg_value).unwrap_or_default();
let escaped_json = escape_dart(&json_str);
let dart_param_name = underscore_camel_case(&arg_def.name);
let var_name = underscore_camel_case(&arg_def.name);
let dart_fn = type_name_to_create_from_json_dart(opts_type);
if fixture.visitor.is_some() {
setup_lines.push(format!(
"final {var_name} = await {dart_fn}WithVisitor(json: '{escaped_json}', visitor: visitor);"
));
} else {
setup_lines.push(format!("final {var_name} = await {dart_fn}(json: '{escaped_json}');"));
}
args.push(format!("{dart_param_name}: {var_name}"));
}
}
fn is_dart_config_positional(opts_type: &str, type_defs: &[crate::core::ir::TypeDef], enums: &[EnumDef]) -> bool {
!crate::backends::dart::config_param_is_named_optional("config", opts_type, type_defs, enums)
}
fn ir_typed_dart_expression(
arg: &ArgMapping,
index: usize,
value: &serde_json::Value,
target_params: TargetParams<'_>,
enums: &[EnumDef],
) -> Option<String> {
let TypeRef::Named(declared) = &target_params.param_for(&arg.name, index)?.ty else {
return None;
};
let text = value.as_str()?;
let enum_def = enums
.iter()
.find(|enum_def| &enum_def.name == declared && enum_def.variants.iter().all(|v| v.fields.is_empty()))?;
let variant = enum_def.variants.iter().find(|variant| {
crate::codegen::naming::wire_variant_value(
&variant.name,
variant.serde_rename.as_deref(),
enum_def.serde_rename_all.as_deref(),
) == text
})?;
let variant_name = crate::codegen::naming::dart_value_identifier(&heck::ToLowerCamelCase::to_lower_camel_case(
variant.name.as_str(),
));
Some(format!("{declared}.{variant_name}"))
}