use alef_core::ir::{CoreWrapper, PrimitiveType, TypeDef, TypeRef};
use super::ConversionConfig;
use super::helpers::{
core_prim_str, core_type_path_remapped, is_newtype, is_tuple_type_name, needs_f64_cast, needs_i32_cast,
needs_i64_cast,
};
pub fn gen_from_binding_to_core(typ: &TypeDef, core_import: &str) -> String {
gen_from_binding_to_core_cfg(typ, core_import, &ConversionConfig::default())
}
pub fn gen_from_binding_to_core_cfg(typ: &TypeDef, core_import: &str, config: &ConversionConfig) -> String {
let core_path = core_type_path_remapped(typ, core_import, config.source_crate_remaps);
let binding_name = format!("{}{}", config.type_name_prefix, typ.name);
if is_newtype(typ) {
let field = &typ.fields[0];
let newtype_inner_expr = match &field.ty {
TypeRef::Named(_) => "val._0.into()".to_string(),
TypeRef::Path => "val._0.into()".to_string(),
TypeRef::Duration => "std::time::Duration::from_millis(val._0)".to_string(),
_ => "val._0".to_string(),
};
return crate::template_env::render(
"conversions/binding_to_core_impl",
minijinja::context! {
core_path => core_path,
binding_name => binding_name,
is_newtype => true,
newtype_inner_expr => newtype_inner_expr,
builder_mode => false,
uses_builder_pattern => false,
has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
statements => vec![] as Vec<String>,
fields => vec![] as Vec<String>,
},
);
}
let uses_builder_pattern = (config.option_duration_on_defaults
&& typ.has_default
&& typ
.fields
.iter()
.any(|f| !f.optional && matches!(f.ty, TypeRef::Duration)))
|| (config.optionalize_defaults && typ.has_default);
let has_optionalized_duration = config.option_duration_on_defaults
&& typ.has_default
&& typ
.fields
.iter()
.any(|f| !f.optional && matches!(f.ty, TypeRef::Duration));
if has_optionalized_duration {
let optionalized = config.optionalize_defaults && typ.has_default;
let mut statements = Vec::new();
for field in &typ.fields {
if field.cfg.is_some() {
continue;
}
if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
continue;
}
if !config.exclude_types.is_empty()
&& super::helpers::field_references_excluded_type(&field.ty, config.exclude_types)
{
continue;
}
let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
if !field.optional && matches!(field.ty, TypeRef::Duration) {
let cast = if config.cast_large_ints_to_i64 { " as u64" } else { "" };
statements.push(format!(
"if let Some(__v) = val.{binding_name_field} {{ __result.{} = std::time::Duration::from_millis(__v{cast}); }}",
field.name
));
continue;
}
let conversion = if optionalized && !field.optional {
gen_optionalized_field_to_core(&field.name, &field.ty, config, false)
} else {
field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
};
let conversion = if binding_name_field != field.name {
conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
} else {
conversion
};
if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
statements.push(format!("__result.{} = {};", field.name, expr));
}
}
return crate::template_env::render(
"conversions/binding_to_core_impl",
minijinja::context! {
core_path => core_path,
binding_name => binding_name,
is_newtype => false,
newtype_inner_expr => "",
builder_mode => true,
uses_builder_pattern => uses_builder_pattern,
has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
statements => statements,
fields => vec![] as Vec<String>,
},
);
}
let optionalized = config.optionalize_defaults && typ.has_default;
let mut fields = Vec::new();
let mut statements = Vec::new();
for field in &typ.fields {
if field.cfg.is_some() {
continue;
}
let references_excluded = !config.exclude_types.is_empty()
&& super::helpers::field_references_excluded_type(&field.ty, config.exclude_types);
if references_excluded && typ.has_stripped_cfg_fields {
continue;
}
if optionalized && ((field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded) {
continue;
}
let field_was_optionalized = optionalized && !field.optional;
let conversion = if (field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded {
format!("{}: Default::default()", field.name)
} else if field_was_optionalized {
field_conversion_to_core_cfg(&field.name, &field.ty, false, config)
} else {
field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
};
let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
match &field.ty {
TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
TypeRef::Vec(_) => {
let inner_expr = if let Some(prefix) = expr.strip_suffix(".collect()") {
format!("{prefix}.collect::<Vec<_>>()")
} else {
expr.to_string()
};
format!(
"{}: ({inner_expr}).into_iter().map({newtype_path}).collect()",
field.name
)
}
_ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
_ => format!("{}: {newtype_path}({expr})", field.name),
}
} else {
conversion
}
} else {
conversion
};
let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
if field.optional {
format!("{}: {}.map(Box::new)", field.name, expr)
} else {
format!("{}: Box::new({})", field.name, expr)
}
} else {
conversion
}
} else {
conversion
};
let is_opaque_arc_field = field.core_wrapper == CoreWrapper::Arc
&& matches!(&field.ty, TypeRef::Named(n) if config
.opaque_types
.is_some_and(|opaque| opaque.contains(n.as_str())));
let is_opaque_no_wrapper_field = field.core_wrapper == CoreWrapper::None
&& matches!(&field.ty, TypeRef::Named(n) if config
.opaque_types
.is_some_and(|opaque| opaque.contains(n.as_str())));
let conversion = if is_opaque_arc_field {
if field.optional {
format!("{}: val.{}.map(|v| v.inner)", field.name, field.name)
} else {
format!("{}: val.{}.inner", field.name, field.name)
}
} else if is_opaque_no_wrapper_field {
format!("{}: Default::default()", field.name)
} else {
apply_core_wrapper_to_core(
&conversion,
&field.name,
&field.core_wrapper,
&field.vec_inner_core_wrapper,
field.optional,
)
};
let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
let conversion = if binding_name_field != field.name {
conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
} else {
conversion
};
if optionalized {
if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
if field_was_optionalized {
statements.push(format!(
"if let Some(__v) = val.{binding_name_field} {{ __result.{} = {}; }}",
field.name,
expr.replace(&format!("val.{binding_name_field}"), "__v")
));
} else {
statements.push(format!("__result.{} = {};", field.name, expr));
}
}
} else {
fields.push(conversion);
}
}
crate::template_env::render(
"conversions/binding_to_core_impl",
minijinja::context! {
core_path => core_path,
binding_name => binding_name,
is_newtype => false,
newtype_inner_expr => "",
builder_mode => optionalized,
uses_builder_pattern => uses_builder_pattern,
has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
statements => statements,
fields => fields,
},
)
}
pub(super) fn gen_optionalized_field_to_core(
name: &str,
ty: &TypeRef,
config: &ConversionConfig,
field_is_ir_optional: bool,
) -> String {
match ty {
TypeRef::Json if config.json_as_value => {
format!("{name}: val.{name}.unwrap_or_default()")
}
TypeRef::Json => {
format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
}
TypeRef::Named(_) => {
format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
}
TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
}
TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
format!("{name}: val.{name}.unwrap_or(0.0)")
}
TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
let core_ty = core_prim_str(p);
format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
}
TypeRef::Optional(inner)
if config.cast_large_ints_to_i64
&& matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
{
if let TypeRef::Primitive(p) = inner.as_ref() {
let core_ty = core_prim_str(p);
format!("{name}: val.{name}.map(|v| v as {core_ty})")
} else {
field_conversion_to_core(name, ty, false)
}
}
TypeRef::Duration if config.cast_large_ints_to_i64 => {
format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
}
TypeRef::Duration => {
format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
}
TypeRef::Path => {
format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
}
TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
format!("{name}: val.{name}.map(|s| std::path::PathBuf::from(s))")
}
TypeRef::Optional(_) => {
format!("{name}: val.{name}.map(Some)")
}
TypeRef::Char => {
format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
}
TypeRef::Vec(inner) => match inner.as_ref() {
TypeRef::Json => {
format!(
"{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
)
}
TypeRef::Named(_) => {
format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
}
TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
let core_ty = core_prim_str(p);
format!(
"{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
)
}
_ => format!("{name}: val.{name}.unwrap_or_default()"),
},
TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
let k_is_json = matches!(k.as_ref(), TypeRef::Json);
let k_expr = if k_is_json {
"serde_json::from_str(&k).unwrap_or_default()"
} else {
"k.into()"
};
format!(
"{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or(serde_json::json!(v)))).collect()"
)
}
TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
format!(
"{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (serde_json::from_str(&k).unwrap_or_default(), v)).collect()"
)
}
TypeRef::Map(k, v) => {
let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
let val_is_string_enum = matches!(v.as_ref(), TypeRef::Named(n)
if config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)));
if field_is_ir_optional {
if val_is_string_enum {
format!(
"{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect())"
)
} else if has_named_val {
format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
} else if has_named_key {
format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
} else {
format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
}
} else if val_is_string_enum {
format!(
"{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect()"
)
} else if has_named_val {
format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, v.into())).collect()")
} else if has_named_key {
format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k.into(), v)).collect()")
} else {
format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
}
}
_ => {
format!("{name}: val.{name}.unwrap_or_default()")
}
}
}
pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
match ty {
TypeRef::Primitive(_) | TypeRef::String | TypeRef::Unit => {
format!("{name}: val.{name}")
}
TypeRef::Bytes => {
if optional {
format!("{name}: val.{name}.map(|v| v.to_vec().into())")
} else {
format!("{name}: val.{name}.to_vec().into()")
}
}
TypeRef::Json => {
if optional {
format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
} else {
format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
}
}
TypeRef::Char => {
if optional {
format!("{name}: val.{name}.and_then(|s| s.chars().next())")
} else {
format!("{name}: val.{name}.chars().next().unwrap_or('*')")
}
}
TypeRef::Duration => {
if optional {
format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
} else {
format!("{name}: std::time::Duration::from_millis(val.{name})")
}
}
TypeRef::Path => {
if optional {
format!("{name}: val.{name}.map(Into::into)")
} else {
format!("{name}: val.{name}.into()")
}
}
TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
format!("{name}: val.{name}")
}
TypeRef::Named(_) => {
if optional {
format!("{name}: val.{name}.map(Into::into)")
} else {
format!("{name}: val.{name}.into()")
}
}
TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
"serde_json::from_str(&k).unwrap_or_default()"
} else {
"k.into()"
};
if optional {
format!(
"{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
)
} else {
format!(
"{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect()"
)
}
}
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
}
TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
"serde_json::from_str(&k).unwrap_or_default()"
} else {
"k.into()"
};
format!(
"{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
)
}
_ => format!("{name}: val.{name}"),
},
TypeRef::Vec(inner) => match inner.as_ref() {
TypeRef::Json => {
if optional {
format!(
"{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
)
} else {
format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
}
}
TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
format!("{name}: val.{name}")
}
TypeRef::Named(_) => {
if optional {
format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
}
}
_ => format!("{name}: val.{name}"),
},
TypeRef::Map(k, v) => {
let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
let has_json_val = matches!(v.as_ref(), TypeRef::Json);
let has_json_key = matches!(k.as_ref(), TypeRef::Json);
let has_vec_named_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n)));
let has_vec_json_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
if has_json_val || has_json_key || has_named_key || has_named_val || has_vec_named_val || has_vec_json_val {
let k_expr = if has_json_key {
"serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
} else {
"k.into()"
};
let v_expr = if has_json_val {
"serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
} else if has_named_val {
"v.into()"
} else if has_vec_named_val {
"v.into_iter().map(Into::into).collect()"
} else if has_vec_json_val {
"v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()"
} else {
"v"
};
if optional {
format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
}
} else {
let is_string_map = matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String);
if is_string_map {
if optional {
format!(
"{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
)
} else {
format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
}
} else {
if optional {
if has_named_val {
format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
} else {
format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
}
} else {
format!("{name}: val.{name}.into_iter().collect()")
}
}
}
}
}
}
pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
if optional && matches!(ty, TypeRef::Optional(_)) {
let inner_expr = field_conversion_to_core_cfg(name, ty, false, config);
if let Some(expr) = inner_expr.strip_prefix(&format!("{name}: ")) {
return format!("{name}: ({expr}).map(Some)");
}
return inner_expr;
}
if config.map_uses_jsvalue {
let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
let is_map = matches!(ty, TypeRef::Map(_, _));
if is_nested_vec || is_map || is_vec_json {
if optional {
return format!(
"{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
);
}
return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
}
if let TypeRef::Optional(inner) = ty {
let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
if is_inner_nested || is_inner_map || is_inner_vec_json {
return format!(
"{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
);
}
}
}
if config.vec_named_to_string {
if let TypeRef::Vec(inner) = ty {
if matches!(inner.as_ref(), TypeRef::Named(_)) {
if optional {
return format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())");
}
return format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()");
}
}
}
if config.map_as_string && matches!(ty, TypeRef::Map(_, _)) {
return format!("{name}: Default::default()");
}
if config.map_as_string {
if let TypeRef::Optional(inner) = ty {
if matches!(inner.as_ref(), TypeRef::Map(_, _)) {
return format!("{name}: Default::default()");
}
}
}
if let Some(untagged_names) = config.untagged_data_enum_names {
let direct_named = matches!(ty, TypeRef::Named(n) if untagged_names.contains(n));
let optional_named = matches!(ty, TypeRef::Optional(inner)
if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
let vec_named = matches!(ty, TypeRef::Vec(inner)
if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
if matches!(outer.as_ref(), TypeRef::Vec(inner)
if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n))));
if direct_named {
if optional {
return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
}
return format!("{name}: serde_json::from_value(val.{name}).unwrap_or_default()");
}
if optional_named {
return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
}
if vec_named {
if optional {
return format!(
"{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
);
}
return format!("{name}: val.{name}.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect()");
}
if optional_vec_named {
return format!(
"{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
);
}
}
if config.json_to_string && matches!(ty, TypeRef::Json) {
return format!("{name}: Default::default()");
}
if config.json_as_value && matches!(ty, TypeRef::Json) {
return format!("{name}: val.{name}");
}
if config.json_as_value {
if let TypeRef::Optional(inner) = ty {
if matches!(inner.as_ref(), TypeRef::Json) {
return format!("{name}: val.{name}");
}
}
if let TypeRef::Vec(inner) = ty {
if matches!(inner.as_ref(), TypeRef::Json) {
if optional {
return format!("{name}: val.{name}.unwrap_or_default()");
}
return format!("{name}: val.{name}");
}
}
if let TypeRef::Map(_k, v) = ty {
if matches!(v.as_ref(), TypeRef::Json) {
if optional {
return format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())");
}
return format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v)).collect()");
}
}
}
if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
if optional {
return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
}
return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
}
if !config.cast_large_ints_to_i64
&& !config.cast_large_ints_to_f64
&& !config.cast_uints_to_i32
&& !config.cast_f32_to_f64
&& !config.json_to_string
&& !config.vec_named_to_string
&& !config.map_as_string
&& config.from_binding_skip_types.is_empty()
{
return field_conversion_to_core(name, ty, optional);
}
match ty {
TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|v| v as {core_ty})")
} else {
format!("{name}: val.{name} as {core_ty}")
}
}
TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
if optional {
format!("{name}: val.{name}.map(|v| v as f32)")
} else {
format!("{name}: val.{name} as f32")
}
}
TypeRef::Duration if config.cast_large_ints_to_i64 => {
if optional {
format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
} else {
format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
}
}
TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
if let TypeRef::Primitive(p) = inner.as_ref() {
let core_ty = core_prim_str(p);
format!("{name}: val.{name}.map(|v| v as {core_ty})")
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Vec(inner)
if config.cast_large_ints_to_i64
&& matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
{
if let TypeRef::Primitive(p) = inner.as_ref() {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
}
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Map(_k, v)
if config.cast_large_ints_to_i64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
{
if let TypeRef::Primitive(p) = v.as_ref() {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
}
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Vec(inner)
if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
{
if optional {
format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
}
}
TypeRef::Optional(inner)
if config.cast_f32_to_f64
&& matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
{
format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
}
TypeRef::Primitive(p) if config.cast_uints_to_i32 && needs_i32_cast(p) => {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|v| v as {core_ty})")
} else {
format!("{name}: val.{name} as {core_ty}")
}
}
TypeRef::Optional(inner)
if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
{
if let TypeRef::Primitive(p) = inner.as_ref() {
let core_ty = core_prim_str(p);
format!("{name}: val.{name}.map(|v| v as {core_ty})")
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Vec(inner)
if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
{
if let TypeRef::Primitive(p) = inner.as_ref() {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
}
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Primitive(p) if config.cast_large_ints_to_f64 && needs_f64_cast(p) => {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|v| v as {core_ty})")
} else {
format!("{name}: val.{name} as {core_ty}")
}
}
TypeRef::Optional(inner)
if config.cast_large_ints_to_f64
&& matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
{
if let TypeRef::Primitive(p) = inner.as_ref() {
let core_ty = core_prim_str(p);
format!("{name}: val.{name}.map(|v| v as {core_ty})")
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Vec(inner)
if config.cast_large_ints_to_f64
&& matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
{
if let TypeRef::Primitive(p) = inner.as_ref() {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
}
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Map(_k, v)
if config.cast_large_ints_to_f64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
{
if let TypeRef::Primitive(p) = v.as_ref() {
let core_ty = core_prim_str(p);
if optional {
format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
} else {
format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
}
} else {
field_conversion_to_core(name, ty, optional)
}
}
TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
format!("{name}: Default::default()")
}
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
format!("{name}: Default::default()")
}
_ => field_conversion_to_core(name, ty, optional),
},
_ => field_conversion_to_core(name, ty, optional),
}
}
pub fn apply_core_wrapper_to_core(
conversion: &str,
name: &str,
core_wrapper: &CoreWrapper,
vec_inner_core_wrapper: &CoreWrapper,
optional: bool,
) -> String {
if *vec_inner_core_wrapper == CoreWrapper::Arc {
return conversion
.replace(
".map(Into::into).collect()",
".map(|v| std::sync::Arc::new(v.into())).collect()",
)
.replace(
"map(|v| v.into_iter().map(Into::into)",
"map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
);
}
match core_wrapper {
CoreWrapper::None => conversion.to_string(),
CoreWrapper::Cow => {
if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
if optional {
format!("{name}: {expr}.map(Into::into)")
} else if expr == format!("val.{name}") {
format!("{name}: val.{name}.into()")
} else if expr == "Default::default()" {
conversion.to_string()
} else {
format!("{name}: ({expr}).into()")
}
} else {
conversion.to_string()
}
}
CoreWrapper::Arc => {
if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
if expr == "Default::default()" {
conversion.to_string()
} else if optional {
format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
} else {
format!("{name}: std::sync::Arc::new({expr})")
}
} else {
conversion.to_string()
}
}
CoreWrapper::Bytes => {
if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
let already_converted_non_opt =
expr == format!("val.{name}.into()") || expr == format!("val.{name}.to_vec().into()");
let already_converted_opt = expr
.strip_prefix(&format!("val.{name}"))
.map(|s| s == ".map(Into::into)" || s == ".map(|v| v.to_vec().into())")
.unwrap_or(false);
if already_converted_non_opt || already_converted_opt {
conversion.to_string()
} else if optional {
format!("{name}: {expr}.map(Into::into)")
} else if expr == format!("val.{name}") {
format!("{name}: val.{name}.into()")
} else if expr == "Default::default()" {
conversion.to_string()
} else {
format!("{name}: ({expr}).into()")
}
} else {
conversion.to_string()
}
}
CoreWrapper::ArcMutex => {
if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
if optional {
format!("{name}: {expr}.map(|v| std::sync::Arc::new(std::sync::Mutex::new(v.into())))")
} else if expr == format!("val.{name}") {
format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(val.{name}.into()))")
} else {
format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(({expr}).into()))")
}
} else {
conversion.to_string()
}
}
}
}
#[cfg(test)]
mod tests {
use super::gen_from_binding_to_core;
use alef_core::ir::{CoreWrapper, DefaultValue, FieldDef, TypeDef, TypeRef};
fn type_with_field(field: FieldDef) -> TypeDef {
TypeDef {
name: "ProcessConfig".to_string(),
rust_path: "crate::ProcessConfig".to_string(),
original_rust_path: String::new(),
fields: vec![field],
methods: vec![],
is_opaque: false,
is_clone: true,
is_copy: false,
doc: String::new(),
cfg: None,
is_trait: false,
has_default: true,
has_stripped_cfg_fields: false,
is_return_type: false,
serde_rename_all: None,
has_serde: true,
super_traits: vec![],
}
}
#[test]
fn sanitized_cow_string_field_converts_to_core() {
let field = FieldDef {
name: "language".to_string(),
ty: TypeRef::String,
optional: false,
default: None,
doc: String::new(),
sanitized: true,
is_boxed: false,
type_rust_path: None,
cfg: None,
typed_default: Some(DefaultValue::Empty),
core_wrapper: CoreWrapper::Cow,
vec_inner_core_wrapper: CoreWrapper::None,
newtype_wrapper: None,
serde_rename: None,
serde_flatten: false,
};
let out = gen_from_binding_to_core(&type_with_field(field), "crate");
assert!(out.contains("language: val.language.into()"));
assert!(!out.contains("language: Default::default()"));
}
}