use crate::codegen::conversions::helpers::{core_prim_str, needs_f64_cast, needs_i32_cast};
use crate::core::ir::{ParamDef, TypeRef};
use ahash::AHashSet;
pub fn gen_call_args(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
params
.iter()
.enumerate()
.map(|(idx, p)| {
let promoted = crate::codegen::shared::is_promoted_optional(params, idx);
let unwrap_suffix = if promoted && p.optional {
format!(".expect(\"'{}' is required\")", p.name)
} else {
String::new()
};
if let Some(newtype_path) = &p.newtype_wrapper {
return if p.optional {
format!("{}.map({newtype_path})", p.name)
} else if promoted {
format!("{newtype_path}({}{})", p.name, unwrap_suffix)
} else {
format!("{newtype_path}({})", p.name)
};
}
match &p.ty {
TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
if p.optional {
format!("{}.as_ref().map(|v| &v.inner)", p.name)
} else if promoted {
format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
} else {
format!("&{}.inner", p.name)
}
}
TypeRef::Named(_) => {
if p.optional {
if p.is_ref {
format!("{}.as_ref()", p.name)
} else {
format!("{}.map(Into::into)", p.name)
}
} else if promoted {
format!("{}{}.into()", p.name, unwrap_suffix)
} else if p.is_mut {
format!("&mut {}", p.name)
} else {
format!("{}.into()", p.name)
}
}
TypeRef::String | TypeRef::Char => {
if p.optional {
if p.is_ref {
format!("{}.as_deref()", p.name)
} else {
p.name.clone()
}
} else if promoted {
if p.is_ref {
format!("&{}{}", p.name, unwrap_suffix)
} else {
format!("{}{}", p.name, unwrap_suffix)
}
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
TypeRef::Path => {
if p.optional && p.is_ref {
format!("{}.as_deref().map(std::path::Path::new)", p.name)
} else if p.optional {
format!("{}.map(std::path::PathBuf::from)", p.name)
} else if promoted {
format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
} else if p.is_ref {
format!("std::path::Path::new(&{})", p.name)
} else {
format!("std::path::PathBuf::from({})", p.name)
}
}
TypeRef::Bytes => {
if p.optional {
if p.is_ref {
format!("{}.as_deref()", p.name)
} else {
p.name.clone()
}
} else if promoted {
if p.is_ref {
format!("&{}{}", p.name, unwrap_suffix)
} else {
format!("{}{}", p.name, unwrap_suffix)
}
} else {
if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
}
TypeRef::Duration => {
if p.optional {
format!("{}.map(std::time::Duration::from_millis)", p.name)
} else if promoted {
format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
} else {
format!("std::time::Duration::from_millis({})", p.name)
}
}
TypeRef::Json => {
if p.optional {
format!("{}.as_ref().and_then(|s| serde_json::from_str(s).ok())", p.name)
} else if promoted {
format!("serde_json::from_str(&{}{}).unwrap_or_default()", p.name, unwrap_suffix)
} else {
format!("serde_json::from_str(&{}).unwrap_or_default()", p.name)
}
}
TypeRef::Vec(inner) => {
if matches!(inner.as_ref(), TypeRef::Named(_)) {
if p.optional {
if p.is_ref {
format!("{}.as_deref()", p.name)
} else {
p.name.clone()
}
} else if promoted {
if p.is_ref {
format!("&{}{}", p.name, unwrap_suffix)
} else {
format!("{}{}", p.name, unwrap_suffix)
}
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
} else if promoted {
format!("{}{}", p.name, unwrap_suffix)
} else if p.is_mut && p.optional {
format!("{}.as_deref_mut()", p.name)
} else if p.is_mut {
format!("&mut {}", p.name)
} else if p.is_ref && p.optional {
format!("{}.as_deref()", p.name)
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
TypeRef::Map(_, _) => {
if promoted {
format!("{}{}", p.name, unwrap_suffix)
} else if p.is_mut && p.optional {
format!("{}.as_mut()", p.name)
} else if p.is_mut {
format!("&mut {}", p.name)
} else if p.is_ref && p.optional {
format!("{}.as_ref()", p.name)
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
_ => {
if promoted {
format!("{}{}", p.name, unwrap_suffix)
} else if p.is_mut && p.optional {
format!("{}.as_deref_mut()", p.name)
} else if p.is_mut {
format!("&mut {}", p.name)
} else if p.is_ref && p.optional {
format!("{}.as_deref()", p.name)
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
}
})
.collect::<Vec<_>>()
.join(", ")
}
pub fn gen_call_args_cfg(
params: &[ParamDef],
opaque_types: &AHashSet<String>,
cast_uints_to_i32: bool,
cast_large_ints_to_f64: bool,
) -> String {
params
.iter()
.enumerate()
.map(|(idx, p)| {
let promoted = crate::codegen::shared::is_promoted_optional(params, idx);
let unwrap_suffix = if promoted && p.optional {
format!(".expect(\"'{}' is required\")", p.name)
} else {
String::new()
};
if p.newtype_wrapper.is_some() {
return gen_call_args(std::slice::from_ref(p), opaque_types);
}
if let TypeRef::Primitive(prim) = &p.ty {
let core_ty = core_prim_str(prim);
let needs_cast =
(cast_uints_to_i32 && needs_i32_cast(prim)) || (cast_large_ints_to_f64 && needs_f64_cast(prim));
if needs_cast {
return if p.optional {
format!("{}.map(|v| v as {core_ty})", p.name)
} else if promoted {
format!("({}{}) as {core_ty}", p.name, unwrap_suffix)
} else {
format!("{} as {core_ty}", p.name)
};
}
}
gen_call_args(std::slice::from_ref(p), opaque_types)
})
.collect::<Vec<_>>()
.join(", ")
}
pub fn gen_call_args_with_let_bindings(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
gen_call_args_with_let_bindings_inner(params, opaque_types, false)
}
pub fn gen_call_args_with_let_bindings_json_str(params: &[ParamDef], opaque_types: &AHashSet<String>) -> String {
gen_call_args_with_let_bindings_inner(params, opaque_types, true)
}
fn gen_call_args_with_let_bindings_inner(
params: &[ParamDef],
opaque_types: &AHashSet<String>,
json_from_str: bool,
) -> String {
params
.iter()
.enumerate()
.map(|(idx, p)| {
let promoted = crate::codegen::shared::is_promoted_optional(params, idx);
let unwrap_suffix = if promoted && p.optional {
format!(".expect(\"'{}' is required\")", p.name)
} else {
String::new()
};
if let Some(newtype_path) = &p.newtype_wrapper {
return if p.optional {
format!("{}.map({newtype_path})", p.name)
} else if promoted {
format!("{newtype_path}({}{})", p.name, unwrap_suffix)
} else {
format!("{newtype_path}({})", p.name)
};
}
match &p.ty {
TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
if p.optional {
format!("{}.as_ref().map(|v| &v.inner)", p.name)
} else if promoted {
format!("{}{}.inner.as_ref()", p.name, unwrap_suffix)
} else {
format!("&{}.inner", p.name)
}
}
TypeRef::Named(_) => {
if p.optional && p.is_ref {
format!("{}_core", p.name)
} else if p.is_mut {
format!("&mut {}_core", p.name)
} else if p.is_ref {
format!("&{}_core", p.name)
} else {
format!("{}_core", p.name)
}
}
TypeRef::String | TypeRef::Char => {
if p.optional {
if p.is_ref {
format!("{}.as_deref()", p.name)
} else {
p.name.clone()
}
} else if promoted {
if p.is_ref {
format!("&{}{}", p.name, unwrap_suffix)
} else {
format!("{}{}", p.name, unwrap_suffix)
}
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
TypeRef::Path => {
if promoted {
format!("std::path::PathBuf::from({}{})", p.name, unwrap_suffix)
} else if p.optional && p.is_ref {
format!("{}.as_deref().map(std::path::Path::new)", p.name)
} else if p.optional {
format!("{}.map(std::path::PathBuf::from)", p.name)
} else if p.is_ref {
format!("std::path::Path::new(&{})", p.name)
} else {
format!("std::path::PathBuf::from({})", p.name)
}
}
TypeRef::Bytes => {
if p.optional {
if p.is_ref {
format!("{}.as_deref()", p.name)
} else {
p.name.clone()
}
} else if promoted {
if p.is_ref {
format!("&{}{}", p.name, unwrap_suffix)
} else {
format!("{}{}", p.name, unwrap_suffix)
}
} else {
if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
}
TypeRef::Duration => {
if p.optional {
format!("{}.map(std::time::Duration::from_millis)", p.name)
} else if promoted {
format!("std::time::Duration::from_millis({}{})", p.name, unwrap_suffix)
} else {
format!("std::time::Duration::from_millis({})", p.name)
}
}
TypeRef::Json if json_from_str => {
if p.optional {
format!("{}.as_ref().and_then(|s| serde_json::from_str(s).ok())", p.name)
} else if promoted {
format!("serde_json::from_str(&{}{}).unwrap_or_default()", p.name, unwrap_suffix)
} else {
format!("serde_json::from_str(&{}).unwrap_or_default()", p.name)
}
}
TypeRef::Vec(inner) => {
if matches!(inner.as_ref(), TypeRef::String) && p.sanitized && p.original_type.is_some() {
if p.optional && p.is_ref {
format!("{}_core.as_deref()", p.name)
} else if p.optional {
format!("{}_core", p.name)
} else if p.is_ref {
format!("&{}_core", p.name)
} else {
format!("{}_core", p.name)
}
} else if matches!(inner.as_ref(), TypeRef::Named(_)) {
if p.optional && p.is_ref {
format!("{}_core.as_deref()", p.name)
} else if p.optional {
format!("{}_core", p.name)
} else if p.is_ref {
format!("&{}_core", p.name)
} else {
format!("{}_core", p.name)
}
} else if matches!(inner.as_ref(), TypeRef::String | TypeRef::Char)
&& p.is_ref
&& p.vec_inner_is_ref
{
if p.optional {
format!("{}.as_deref()", p.name)
} else {
format!("&{}_refs", p.name)
}
} else if promoted {
format!("{}{}", p.name, unwrap_suffix)
} else if p.is_ref && p.optional {
format!("{}.as_deref()", p.name)
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
TypeRef::Map(_, _) => {
let to_btree = |expr: String| {
if p.map_is_btree {
format!("{expr}.into_iter().collect::<std::collections::BTreeMap<_, _>>()")
} else {
expr
}
};
if promoted {
let owned = to_btree(format!("{}.unwrap_or_default()", p.name));
if p.is_ref { format!("&{owned}") } else { owned }
} else if p.is_ref && p.optional {
format!("{}.as_ref()", p.name)
} else if p.is_ref {
format!("&{}", to_btree(p.name.clone()))
} else {
to_btree(p.name.clone())
}
}
_ => {
if promoted {
format!("{}{}", p.name, unwrap_suffix)
} else if p.is_ref && p.optional {
format!("{}.as_deref()", p.name)
} else if p.is_ref {
format!("&{}", p.name)
} else {
p.name.clone()
}
}
}
})
.collect::<Vec<_>>()
.join(", ")
}
pub fn gen_call_args_with_let_bindings_mutex(
params: &[ParamDef],
opaque_types: &AHashSet<String>,
mutex_types: &AHashSet<String>,
) -> String {
gen_call_args_with_let_bindings_mutex_inner(params, opaque_types, mutex_types, false)
}
pub fn gen_call_args_with_let_bindings_mutex_json_str(
params: &[ParamDef],
opaque_types: &AHashSet<String>,
mutex_types: &AHashSet<String>,
) -> String {
gen_call_args_with_let_bindings_mutex_inner(params, opaque_types, mutex_types, true)
}
fn gen_call_args_with_let_bindings_mutex_inner(
params: &[ParamDef],
opaque_types: &AHashSet<String>,
mutex_types: &AHashSet<String>,
json_from_str: bool,
) -> String {
let base = gen_call_args_with_let_bindings_inner(params, opaque_types, json_from_str);
let mut patched = base;
for p in params {
if let TypeRef::Named(type_name) = &p.ty {
if opaque_types.contains(type_name.as_str())
&& mutex_types.contains(type_name.as_str())
&& p.is_ref
&& p.is_mut
&& !p.optional
{
let old_expr = format!("&{}.inner", p.name);
let new_expr = format!("&mut *{}.inner.lock().unwrap()", p.name);
patched = patched.replace(&old_expr, &new_expr);
}
}
}
patched
}