1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::core::ir::{CoreWrapper, ParamDef, TypeRef};
use ahash::AHashSet;
/// Build call argument expressions for Rustler opaque method (receiver is `resource`).
pub(super) fn gen_rustler_method_call_args(
params: &[ParamDef],
opaque_types: &AHashSet<String>,
default_types: &AHashSet<String>,
) -> String {
params
.iter()
.map(|p| match &p.ty {
TypeRef::Named(name) if opaque_types.contains(name.as_str()) => {
format!("&{}.inner", p.name)
}
// Default-typed Named params are passed as Option<String> JSON and decoded
// by the caller into a `{name}_core` local. Reference that local here.
TypeRef::Named(name) if default_types.contains(name.as_str()) => {
if p.optional {
format!("{}_core", p.name)
} else if p.is_ref && p.is_mut {
// Core expects &mut T → reference the mutable binding created by preamble
format!("&mut {}_mut", p.name)
} else if p.is_ref {
format!("{}_core.as_ref().unwrap_or(&Default::default())", p.name)
} else {
format!("{}_core.unwrap_or_default()", p.name)
}
}
TypeRef::Named(_) => {
if p.optional {
if p.is_ref {
format!("{}.as_ref().map(Into::into)", p.name)
} else {
format!("{}.map(Into::into)", p.name)
}
} else if p.is_ref {
format!("&{}.clone().into()", p.name)
} else {
format!("{}.into()", p.name)
}
}
TypeRef::String | TypeRef::Char if p.optional && p.is_ref => {
format!("{}.as_deref()", p.name)
}
// Optional<String> where core expects Option<Cow<'_, str>>: wrap owned values
// via Cow::Owned. Without this the binding's Option<String> doesn't satisfy
// the core's Option<Cow<'_, str>> parameter signature.
TypeRef::String | TypeRef::Char if p.optional && p.core_wrapper == CoreWrapper::Cow => {
format!("{}.map(std::borrow::Cow::Owned)", p.name)
}
TypeRef::String | TypeRef::Char if p.optional => p.name.to_string(),
TypeRef::String | TypeRef::Char if p.is_ref && p.is_mut => format!("&mut {}", p.name),
TypeRef::String | TypeRef::Char if p.is_ref => format!("&{}", p.name),
// String where core expects Cow<'_, str>: String implements Into<Cow<str>>,
// so `.into()` performs the coercion.
TypeRef::String | TypeRef::Char if p.core_wrapper == CoreWrapper::Cow => {
format!("{}.into()", p.name)
}
TypeRef::String | TypeRef::Char => p.name.clone(),
TypeRef::Path => {
if p.is_ref && p.is_mut {
format!("&mut std::path::PathBuf::from({})", p.name)
} else if p.is_ref {
format!("&std::path::PathBuf::from({})", p.name)
} else {
format!("std::path::PathBuf::from({})", p.name)
}
}
TypeRef::Bytes => {
if p.is_ref {
format!("{}.as_slice()", p.name)
} else {
format!("{}.as_slice().to_vec()", p.name)
}
}
TypeRef::Duration => format!("std::time::Duration::from_millis({})", p.name),
// Json params: String (from NIF) is converted to serde_json::Value in the preamble.
// The caller builds a `{name}_json` local via JSON deserialization preamble.
TypeRef::Json => {
if p.optional {
// Option<serde_json::Value> for optional params
format!("{}_json", p.name)
} else if p.is_ref && p.is_mut {
// &mut serde_json::Value for mutable references
format!("&mut {}_json", p.name)
} else if p.is_ref {
// &serde_json::Value for references
format!("&{}_json", p.name)
} else {
// serde_json::Value for owned params
format!("{}_json", p.name)
}
}
TypeRef::Vec(_) => {
if p.is_ref && p.is_mut {
// `&mut Vec<T>` derefs to `&mut [T]`. When the preamble creates a mutable
// binding (e.g., `let mut handles_mut = ...`), pass the mutable reference.
format!("&mut {}_mut", p.name)
} else if p.is_ref {
// `&Vec<T>` derefs to `&[T]`, which matches sample_core core for `&[String]`.
// For `&[&str]` signatures (Vec<String> inner), a refs intermediate is
// emitted in the caller body (gen_nif_function deser_lines) instead.
format!("&{}", p.name)
} else {
p.name.to_string()
}
}
// Map: when the core fn expects BTreeMap but the binding receives a
// HashMap (Rustler decodes BEAM maps as HashMap), collect into a BTreeMap.
TypeRef::Map(_, _) if p.map_is_btree => {
if p.is_ref {
// Pre-bound let binding emitted by the caller body would be needed for
// borrows; we emit the inline collect for the owned case below. For the
// method-receiver path, refs to maps are rare; fall through to owned-collect.
format!("{}.into_iter().collect::<std::collections::BTreeMap<_, _>>()", p.name)
} else {
format!("{}.into_iter().collect::<std::collections::BTreeMap<_, _>>()", p.name)
}
}
_ => p.name.clone(),
})
.collect::<Vec<_>>()
.join(", ")
}