mod callback_files;
pub(crate) mod cargo;
pub(crate) mod default_construction;
mod deferred_noop;
pub(crate) mod enums;
pub(crate) mod extern_block;
pub(crate) mod feature_gate;
mod json_bridge;
pub(crate) mod plugin_inbound;
pub(crate) mod service_app_wrappers;
pub(crate) mod shims;
pub mod trait_bridge;
pub(crate) mod type_bridge;
pub(crate) mod wrappers;
use crate::codegen::generators::type_paths::build_type_path_lookup;
use crate::core::backend::GeneratedFile;
use crate::core::config::extras::Language;
use crate::core::config::{BridgeBinding, ResolvedCrateConfig, TraitBridgeConfig};
use crate::core::ir::{ApiSurface, EnumDef, FunctionDef, TypeDef};
use crate::core::template_versions;
use heck::AsSnakeCase;
use std::collections::HashSet;
use std::path::PathBuf;
pub fn emit(api: &ApiSurface, config: &ResolvedCrateConfig) -> anyhow::Result<Vec<GeneratedFile>> {
let deduped_api = api.with_deduped_functions();
let api = &deduped_api;
let base = PathBuf::from("packages/swift/rust");
let crate_name = &api.crate_name;
let version = &api.version;
let swift_bridge_ver = crate::backends::swift::naming::swift_bridge_version(config);
let swift_bridge_build_ver = template_versions::cargo::SWIFT_BRIDGE_BUILD;
let core_crate_dir = config.core_crate_for_language(Language::Swift);
let swift_override = config.swift.as_ref().and_then(|c| c.core_crate_override.as_deref());
let same_as_workspace =
swift_override.is_none() && core_crate_dir == *crate_name && config.workspace_root.is_none();
let core_path = if same_as_workspace {
"../../..".to_string()
} else {
format!("../../../crates/{core_crate_dir}")
};
let core_dep_key: String = match swift_override {
Some(name) => name.to_string(),
None => crate_name.replace('-', "_"),
};
let features_owned = feature_gate::configured_swift_features(config, &core_crate_dir);
let features: &[String] = &features_owned;
let mut exclude_functions: HashSet<String> = config
.swift
.as_ref()
.map(|c| c.exclude_functions.iter().cloned().collect())
.unwrap_or_default();
let mut exclude_types: HashSet<String> = config
.swift
.as_ref()
.map(|c| c.exclude_types.iter().cloned().collect())
.unwrap_or_default();
if let Some(ffi) = &config.ffi {
exclude_functions.extend(ffi.exclude_functions.iter().cloned());
exclude_types.extend(ffi.exclude_types.iter().cloned());
}
exclude_types.extend(api.types.iter().filter(|t| t.binding_excluded).map(|t| t.name.clone()));
exclude_types.extend(api.enums.iter().filter(|e| e.binding_excluded).map(|e| e.name.clone()));
for contract in &api.handler_contracts {
if let Some(adapter) = contract.response_adapter.as_deref() {
if let Some(short) = adapter.rsplit("::").next() {
exclude_functions.insert(short.to_string());
}
}
}
let exclude_fields: HashSet<String> = config
.swift
.as_ref()
.map(|c| c.exclude_fields.iter().cloned().collect())
.unwrap_or_default();
let license = config
.scaffold
.as_ref()
.and_then(|s| s.license.as_deref())
.unwrap_or("MIT");
let has_streaming_adapters = config
.adapters
.iter()
.any(|a| matches!(a.pattern, crate::core::config::AdapterPattern::Streaming));
let extra_deps = crate::scaffold::render_extra_deps(config, Language::Swift);
let target_overrides = config
.swift
.as_ref()
.map(|c| c.target_dep_overrides.as_slice())
.unwrap_or(&[]);
let excluded_default_features = config
.swift
.as_ref()
.map(|c| c.excluded_default_features.as_slice())
.unwrap_or(&[]);
let cargo_toml = cargo::emit_cargo_toml(
crate_name,
&core_dep_key,
&core_crate_dir,
version,
&swift_bridge_ver,
swift_bridge_build_ver,
&core_path,
features,
&extra_deps,
license,
has_streaming_adapters,
target_overrides,
api,
excluded_default_features,
);
let effective_features = feature_gate::effective_swift_codegen_features(api, config, &core_crate_dir);
let configured_features: HashSet<&str> = effective_features.iter().map(String::as_str).collect();
let lib_rs = emit_lib_rs(
api,
config,
crate_name,
&exclude_functions,
&exclude_types,
&exclude_fields,
&configured_features,
);
let build_rs = cargo::emit_build_rs();
let mut files = vec![
GeneratedFile {
path: base.join("Cargo.toml"),
content: cargo_toml,
generated_header: false,
},
GeneratedFile {
path: base.join("src/lib.rs"),
content: lib_rs,
generated_header: false,
},
GeneratedFile {
path: base.join("build.rs"),
content: build_rs,
generated_header: false,
},
];
if let Some(callback_file) = callback_files::emit_callback_registration_file(api, &base) {
files.push(callback_file);
}
Ok(files)
}
fn emit_lib_rs(
api: &ApiSurface,
config: &ResolvedCrateConfig,
crate_name: &str,
exclude_functions: &HashSet<String>,
exclude_types: &HashSet<String>,
exclude_fields: &HashSet<String>,
configured_features: &HashSet<&str>,
) -> String {
let source_crate = crate_name.replace('-', "_");
let type_paths = build_type_path_lookup(api);
let mut out = String::new();
out.push_str("// Generated by alef. Do not edit by hand.\n\n");
out.push_str("#![allow(unused_variables, unreachable_code, unreachable_patterns, missing_docs)]\n");
out.push_str("#![allow(\n");
out.push_str(" clippy::collapsible_if,\n");
out.push_str(" clippy::collapsible_match,\n");
out.push_str(" clippy::needless_borrows_for_generic_args,\n");
out.push_str(" clippy::field_reassign_with_default,\n");
out.push_str(" clippy::too_many_arguments,\n");
out.push_str(" clippy::clone_on_copy,\n");
out.push_str(" clippy::unnecessary_cast,\n");
out.push_str(" clippy::manual_flatten,\n");
out.push_str(" clippy::match_single_binding,\n");
out.push_str(" clippy::redundant_closure,\n");
out.push_str(" clippy::useless_conversion,\n");
out.push_str(" clippy::inherent_to_string,\n");
out.push_str(" clippy::new_without_default,\n");
out.push_str(")]\n");
if let Some(extra_attr) = crate::codegen::shared::format_extra_clippy_allows(&config.extra_clippy_allows, &out) {
out.push_str(&format!("#![{extra_attr}]\n"));
}
out.push('\n');
out.push_str(shims::ALEF_TOKIO_RUNTIME_DEFINITION);
out.push('\n');
let visible_types: Vec<&TypeDef> = api
.types
.iter()
.filter(|t| !exclude_types.contains(&t.name) && !t.is_trait)
.filter(|t| feature_gate::cfg_satisfied(t.cfg.as_deref(), configured_features))
.collect();
let visible_enums: Vec<&EnumDef> = api
.enums
.iter()
.filter(|e| !exclude_types.contains(&e.name))
.filter(|e| feature_gate::cfg_satisfied(e.cfg.as_deref(), configured_features))
.collect();
let enum_names: HashSet<&str> = visible_enums.iter().map(|e| e.name.as_str()).collect();
let unit_enum_names: HashSet<&str> = visible_enums
.iter()
.filter(|e| e.variants.iter().all(|v| v.fields.is_empty()))
.map(|e| e.name.as_str())
.collect();
let tagged_enum_names: HashSet<&str> = visible_enums
.iter()
.filter(|e| e.variants.iter().any(|v| !v.fields.is_empty()))
.map(|e| e.name.as_str())
.collect();
let visible_type_names: HashSet<&str> = visible_types
.iter()
.map(|t| t.name.as_str())
.chain(enum_names.iter().copied())
.collect();
let no_serde_names: HashSet<&str> = api
.types
.iter()
.filter(|t| !t.has_serde)
.map(|t| t.name.as_str())
.collect();
let first_class_owned =
crate::backends::swift::gen_bindings::dto::compute_first_class_dto_names(api, exclude_types);
let first_class_names: HashSet<&str> = first_class_owned.iter().map(|s| s.as_str()).collect();
let no_serde_enum_names: HashSet<&str> = api
.enums
.iter()
.filter(|e| !e.has_serde)
.map(|e| e.name.as_str())
.collect();
let handle_returned_types: HashSet<String> = type_bridge::compute_handle_returned_types(api);
let visible_functions: Vec<&FunctionDef> = api
.functions
.iter()
.filter(|f| !exclude_functions.contains(&f.name))
.filter(|f| feature_gate::cfg_satisfied(f.cfg.as_deref(), configured_features))
.filter(|f| {
!crate::codegen::generators::trait_bridge::is_trait_bridge_managed_fn(&f.name, &config.trait_bridges)
})
.filter(|f| {
shims::is_bridgeable_fn(
f,
&unit_enum_names,
&type_paths,
&no_serde_names,
&no_serde_enum_names,
&handle_returned_types,
)
})
.collect();
let active_bridges: Vec<(&TraitBridgeConfig, &TypeDef)> = config
.trait_bridges
.iter()
.filter(|b| !b.exclude_languages.iter().any(|l| l == "swift"))
.filter_map(|b| {
api.types
.iter()
.find(|t| t.is_trait && t.name == b.trait_name)
.map(|t| (b, t))
})
.collect();
let result_type_enums: std::collections::HashSet<String> = active_bridges
.iter()
.filter_map(|(bridge_cfg, _)| bridge_cfg.result_type.as_deref().map(|s| s.to_string()))
.collect();
let enum_names_owned: std::collections::HashSet<String> = enum_names.iter().map(|s| s.to_string()).collect();
let mut extern_blocks: Vec<String> = Vec::new();
let mut deferred_empty_handle_types: std::collections::HashSet<String> = std::collections::HashSet::new();
for ty in &visible_types {
let is_handle_returned = handle_returned_types.contains(&ty.name);
let would_be_empty_type_block = ty.fields.is_empty()
&& !extern_block::has_constructor_extern(ty, exclude_fields, configured_features)
&& ty
.methods
.iter()
.all(|m| m.binding_excluded || m.sanitized || m.is_static);
if is_handle_returned && would_be_empty_type_block {
deferred_empty_handle_types.insert(ty.name.clone());
continue;
}
let cfg_open = ty
.cfg
.as_deref()
.map(|c| format!(" #[cfg({c})]\n"))
.unwrap_or_default();
let raw_block = extern_block::emit_extern_block_for_type(
ty,
exclude_fields,
&type_paths,
&no_serde_names,
&first_class_names,
&enum_names_owned,
configured_features,
);
extern_blocks.push(format!("{cfg_open}{raw_block}"));
if ty.is_opaque && !ty.methods.iter().all(|m| m.sanitized) && !ty.methods.is_empty() {
let has_ctor_override = config
.swift
.as_ref()
.is_some_and(|c| c.client_constructor_body.contains_key(&ty.name));
if has_ctor_override && let Some(ctor_block) = extern_block::emit_extern_block_for_type_constructor(ty) {
extern_blocks.push(format!("{cfg_open}{ctor_block}"));
}
if let Some(method_block) =
extern_block::emit_extern_block_for_type_methods(ty, &handle_returned_types, &enum_names)
{
extern_blocks.push(format!("{cfg_open}{method_block}"));
}
}
if let Some(dto_method_block) =
extern_block::emit_extern_block_for_first_class_dto_methods(ty, &handle_returned_types, &enum_names)
{
extern_blocks.push(format!("{cfg_open}{dto_method_block}"));
}
}
for en in &visible_enums {
if !result_type_enums.contains(&en.name) {
let raw_block = extern_block::emit_extern_block_for_enum(en);
if let Some(cfg) = en.cfg.as_deref() {
extern_blocks.push(format!(" #[cfg({cfg})]\n{raw_block}"));
} else {
extern_blocks.push(raw_block);
}
}
}
let swift_capsule_types: std::collections::HashMap<String, crate::core::config::HostCapsuleTypeConfig> = config
.swift
.as_ref()
.map(|c| c.capsule_types.clone())
.unwrap_or_default();
if !visible_functions.is_empty() {
let non_cfg_fns: Vec<FunctionDef> = visible_functions
.iter()
.filter(|f| f.cfg.is_none())
.map(|f| (*f).clone())
.collect();
if !non_cfg_fns.is_empty() {
extern_blocks.push(extern_block::emit_extern_block_for_functions(
&non_cfg_fns,
&handle_returned_types,
&enum_names_owned,
&deferred_empty_handle_types,
&swift_capsule_types,
));
}
let mut cfg_groups: std::collections::BTreeMap<String, Vec<FunctionDef>> = std::collections::BTreeMap::new();
for f in visible_functions.iter().filter(|f| f.cfg.is_some()) {
cfg_groups.entry(f.cfg.clone().unwrap()).or_default().push((*f).clone());
}
for (cfg_cond, fns) in &cfg_groups {
let empty_set = std::collections::HashSet::new();
let block = extern_block::emit_extern_block_for_functions(
fns,
&handle_returned_types,
&enum_names_owned,
&empty_set,
&swift_capsule_types,
);
extern_blocks.push(format!(" #[cfg({cfg_cond})]\n{block}"));
}
}
for (_bridge_cfg, trait_def) in &active_bridges {
extern_blocks.push(trait_bridge::emit_extern_block_for_trait_bridge(
trait_def,
&visible_type_names,
));
}
for (bridge_cfg, trait_def) in &active_bridges {
if bridge_cfg.bind_via != BridgeBinding::FunctionParam {
continue;
}
let reg_block = plugin_inbound::emit_extern_block_for_inbound_registration(trait_def, bridge_cfg);
if !reg_block.is_empty() {
extern_blocks.push(reg_block);
}
}
for (bridge_cfg, trait_def) in &active_bridges {
extern_blocks.push(plugin_inbound::emit_extern_block_for_inbound(trait_def, bridge_cfg));
}
for (bridge_cfg, trait_def) in &active_bridges {
if bridge_cfg.bind_via != BridgeBinding::OptionsField {
continue;
}
let (factory_extern, _factory_body) =
plugin_inbound::emit_options_field_factory(trait_def, bridge_cfg, api, &source_crate);
if !factory_extern.is_empty() {
extern_blocks.push(factory_extern);
}
let (helper_extern, _helper_body) =
plugin_inbound::emit_options_field_options_helper(bridge_cfg, api, &source_crate);
if !helper_extern.is_empty() {
extern_blocks.push(helper_extern);
}
}
let declared_owner_types: std::collections::HashSet<String> = visible_types
.iter()
.filter(|ty| ty.is_opaque)
.map(|ty| ty.name.clone())
.collect();
if let Some(streaming_block) =
extern_block::emit_extern_block_for_streaming_adapters(&config.adapters, &declared_owner_types)
{
extern_blocks.push(streaming_block);
}
let service_api_blocks = super::gen_bindings::service_api::generate_rust_extern_blocks(api).unwrap_or_default();
extern_blocks.extend(service_api_blocks);
let service_api_callback_funcs: Vec<String> =
super::gen_bindings::service_api::generate_rust_callback_c_functions(api).unwrap_or_default();
let extra_serde_param_types: Vec<&TypeDef> =
json_bridge::collect_serde_param_types(api, &visible_types, &visible_functions, &[]);
let extra_serde_param_names: std::collections::HashSet<&str> =
extra_serde_param_types.iter().map(|t| t.name.as_str()).collect();
let streaming_item_types: Vec<&TypeDef> = {
let streaming_item_names: std::collections::HashSet<&str> = config
.adapters
.iter()
.filter(|a| matches!(a.pattern, crate::core::config::AdapterPattern::Streaming))
.filter_map(|a| a.item_type.as_deref())
.collect();
visible_types
.iter()
.copied()
.filter(|ty| streaming_item_names.contains(ty.name.as_str()))
.filter(|ty| ty.has_serde && !ty.is_opaque && !ty.is_trait)
.filter(|ty| !extra_serde_param_names.contains(ty.name.as_str()))
.collect()
};
let streaming_item_names: std::collections::HashSet<&str> =
streaming_item_types.iter().map(|t| t.name.as_str()).collect();
let json_fallback_types: Vec<&TypeDef> = visible_types
.iter()
.copied()
.filter(|ty| ty.has_serde && !ty.is_opaque && !ty.is_trait)
.filter(|ty| !extra_serde_param_names.contains(ty.name.as_str()))
.filter(|ty| !streaming_item_names.contains(ty.name.as_str()))
.collect();
let json_fallback_enums: Vec<&EnumDef> = visible_enums.iter().copied().filter(|e| e.has_serde).collect();
out.push_str("#[swift_bridge::bridge]\nmod ffi {\n");
for block in &extern_blocks {
out.push_str(block);
}
json_bridge::emit_type_from_json_extern_block(&mut out, &extra_serde_param_types);
json_bridge::emit_type_from_json_extern_block(&mut out, &streaming_item_types);
json_bridge::emit_type_from_json_extern_block(&mut out, &json_fallback_types);
let json_fallback_enums_filtered: Vec<&EnumDef> = json_fallback_enums
.iter()
.filter(|en| !result_type_enums.contains(&en.name))
.copied()
.collect();
json_bridge::emit_enum_from_json_extern_block(&mut out, &json_fallback_enums_filtered);
let vec_accessible_enums: Vec<&EnumDef> = visible_enums
.iter()
.filter(|en| !result_type_enums.contains(&en.name))
.copied()
.collect();
let already_declared =
crate::backends::swift::gen_bindings::opaque_handles::collect_already_declared_owner_types(config);
let non_cfg_types: Vec<&TypeDef> = visible_types
.iter()
.copied()
.filter(|t| t.cfg.is_none() && !already_declared.contains(&t.name))
.collect();
let non_cfg_enums: Vec<&EnumDef> = vec_accessible_enums
.iter()
.copied()
.filter(|e| e.cfg.is_none())
.collect();
let vec_accessors_block = extern_block::emit_extern_block_for_vec_accessors(&non_cfg_types, &non_cfg_enums);
if !vec_accessors_block.is_empty() {
out.push_str(&vec_accessors_block);
}
for ty in visible_types
.iter()
.filter(|t| t.cfg.is_some() && !already_declared.contains(&t.name))
{
let cfg = ty.cfg.as_deref().unwrap();
let block = extern_block::emit_extern_block_for_vec_accessors(&[ty], &[]);
if !block.is_empty() {
out.push_str(&format!(" #[cfg({cfg})]\n{block}"));
}
}
for en in vec_accessible_enums.iter().filter(|e| e.cfg.is_some()) {
let cfg = en.cfg.as_deref().unwrap();
let block = extern_block::emit_extern_block_for_vec_accessors(&[], &[en]);
if !block.is_empty() {
out.push_str(&format!(" #[cfg({cfg})]\n{block}"));
}
}
out.push_str("}\n\n");
let vec_impl_types: Vec<&TypeDef> = visible_types
.iter()
.copied()
.filter(|t| !already_declared.contains(&t.name))
.collect();
let phantom_impl = extern_block::emit_phantom_vec_impl(&vec_impl_types, &vec_accessible_enums);
if !phantom_impl.is_empty() {
out.push_str(&phantom_impl);
}
out.push_str(&service_app_wrappers::emit_service_app_wrappers(api, &source_crate));
out.push('\n');
if !service_api_callback_funcs.is_empty() {
out.push_str("mod extern_callbacks;\n\n");
}
for ty in &visible_types {
out.push_str(&wrappers::emit_type_wrapper(
ty,
&source_crate,
&type_paths,
&enum_names,
&unit_enum_names,
&no_serde_names,
&first_class_names,
exclude_fields,
configured_features,
));
out.push('\n');
if ty.is_opaque && !ty.methods.iter().all(|m| m.sanitized) && !ty.methods.is_empty() {
let custom_body = config
.swift
.as_ref()
.and_then(|c| c.client_constructor_body.get(&ty.name))
.map(String::as_str);
if custom_body.is_some() {
out.push_str(&wrappers::emit_type_constructor_shim(
ty,
&source_crate,
&type_paths,
custom_body,
));
out.push('\n');
}
out.push_str(&wrappers::emit_type_method_shims(
ty,
&source_crate,
&type_paths,
&handle_returned_types,
&enum_names,
));
out.push('\n');
}
let dto_wrappers =
wrappers::emit_first_class_dto_method_wrappers(ty, &source_crate, &type_paths, &unit_enum_names);
if !dto_wrappers.is_empty() {
out.push_str(&dto_wrappers);
}
}
let mut noop_def_types = deferred_empty_handle_types.clone();
for ty in &visible_types {
if extern_block::type_needs_own_block_noop(ty) {
noop_def_types.insert(ty.name.clone());
}
}
out.push_str(&deferred_noop::emit_shims(&noop_def_types, &visible_types));
for en in &visible_enums {
out.push_str(&enums::emit_enum_wrapper(en, &source_crate, &type_paths));
out.push('\n');
}
let function_shim_context = shims::FunctionShimContext {
source_crate: &source_crate,
type_paths: &type_paths,
unit_enum_names: &unit_enum_names,
tagged_enum_names: &tagged_enum_names,
no_serde_names: &no_serde_names,
handle_returned_types: &handle_returned_types,
capsule_types: &swift_capsule_types,
};
for f in &visible_functions {
out.push_str(&shims::emit_function_shim(f, &function_shim_context));
out.push('\n');
}
for (_bridge_cfg, trait_def) in &active_bridges {
out.push_str(&trait_bridge::emit_trait_bridge_wrapper(
trait_def,
&source_crate,
&unit_enum_names,
&visible_type_names,
&type_paths,
));
out.push('\n');
}
if !active_bridges.is_empty() {
out.push_str(&plugin_inbound::emit_plugin_error_helper(
&source_crate,
&config.error_type_name(),
&config.error_constructor_expr(),
));
}
for (bridge_cfg, trait_def) in &active_bridges {
out.push_str(&plugin_inbound::emit_inbound_wrapper(
trait_def,
bridge_cfg,
api,
&source_crate,
&type_paths,
&config.error_type_name(),
&config.error_constructor_expr(),
));
out.push('\n');
}
let mut options_field_from_emitted: std::collections::HashSet<String> = std::collections::HashSet::new();
for (bridge_cfg, trait_def) in &active_bridges {
if bridge_cfg.bind_via != BridgeBinding::OptionsField {
continue;
}
let from_impls = plugin_inbound::emit_options_field_from_impls(
bridge_cfg,
api,
&source_crate,
&mut options_field_from_emitted,
);
if !from_impls.is_empty() {
out.push_str(&from_impls);
out.push('\n');
}
let (_factory_extern, factory_body) =
plugin_inbound::emit_options_field_factory(trait_def, bridge_cfg, api, &source_crate);
if !factory_body.is_empty() {
out.push_str(&factory_body);
out.push('\n');
}
let (_helper_extern, helper_body) =
plugin_inbound::emit_options_field_options_helper(bridge_cfg, api, &source_crate);
if !helper_body.is_empty() {
out.push_str(&helper_body);
out.push('\n');
}
}
let streaming_shims = wrappers::emit_streaming_adapter_shims(&config.adapters, &source_crate);
if !streaming_shims.is_empty() {
out.push_str(&streaming_shims);
}
for ty in &extra_serde_param_types {
let type_snake = AsSnakeCase(ty.name.as_str()).to_string();
let type_name = &ty.name;
let source_path_base =
crate::codegen::generators::type_paths::resolve_type_path(type_name, &source_crate, &type_paths);
let source_path = if ty.has_lifetime_params {
format!("{source_path_base}<'static>")
} else {
source_path_base
};
json_bridge::emit_from_json_shim(&mut out, &type_snake, type_name, &source_path, type_name);
}
for ty in &streaming_item_types {
let type_snake = AsSnakeCase(ty.name.as_str()).to_string();
let type_name = &ty.name;
let source_path_base =
crate::codegen::generators::type_paths::resolve_type_path(type_name, &source_crate, &type_paths);
let source_path = if ty.has_lifetime_params {
format!("{source_path_base}<'static>")
} else {
source_path_base
};
json_bridge::emit_from_json_shim(&mut out, &type_snake, type_name, &source_path, type_name);
}
for ty in &json_fallback_types {
let type_snake = AsSnakeCase(ty.name.as_str()).to_string();
let type_name = &ty.name;
let source_path_base =
crate::codegen::generators::type_paths::resolve_type_path(type_name, &source_crate, &type_paths);
let source_path = if ty.has_lifetime_params {
format!("{source_path_base}<'static>")
} else {
source_path_base
};
json_bridge::emit_from_json_shim(&mut out, &type_snake, type_name, &source_path, type_name);
}
for en in &json_fallback_enums_filtered {
let enum_snake = AsSnakeCase(en.name.as_str()).to_string();
let enum_name = &en.name;
let source_path =
crate::codegen::generators::type_paths::resolve_type_path(enum_name, &source_crate, &type_paths);
let map_expr = format!("{enum_name}::from");
json_bridge::emit_from_json_shim(&mut out, &enum_snake, enum_name, &source_path, &map_expr);
}
out
}