1use crate as extendr_api;
6use crate::*;
7use std::io::Write;
8
9#[doc(hidden)]
11#[derive(Debug, PartialEq, IntoList)]
12pub struct Arg {
13 pub name: &'static str,
14 pub arg_type: &'static str,
15 #[into_list(ignore)]
16 pub default: Option<&'static str>,
17}
18
19#[doc(hidden)]
21#[derive(Debug, PartialEq, IntoList)]
22pub struct Func {
23 pub doc: &'static str,
24 pub rust_name: &'static str,
25 pub mod_name: &'static str,
26 pub r_name: &'static str,
27 pub c_name: &'static str,
28 pub args: Vec<Arg>,
29 pub return_type: &'static str,
30 #[into_list(ignore)]
31 pub func_ptr: *const u8,
32 pub hidden: bool,
33 pub invisible: Option<bool>,
34}
35
36#[doc(hidden)]
38#[derive(Debug, PartialEq, IntoList)]
39pub struct Impl {
40 pub doc: &'static str,
41 pub name: &'static str,
42 pub methods: Vec<Func>,
43}
44
45#[doc(hidden)]
47#[derive(Debug, PartialEq, IntoList)]
48pub struct Metadata {
49 pub name: &'static str,
50 pub functions: Vec<Func>,
51 pub impls: Vec<Impl>,
52}
53
54struct RArg {
55 name: String,
56 default: Option<&'static str>,
57}
58
59impl RArg {
60 fn is_self(&self) -> bool {
61 self.name == "self"
62 }
63
64 fn to_actual_arg(&self) -> String {
65 self.name.clone()
66 }
67
68 fn to_formal_arg(&self) -> String {
69 match self.default {
70 Some(default_val) => format!("{} = {}", self.name, default_val),
71 None => self.name.clone(),
72 }
73 }
74}
75
76impl From<&Arg> for RArg {
77 fn from(arg: &Arg) -> Self {
78 Self {
79 name: sanitize_identifier(arg.name),
80 default: arg.default,
81 }
82 }
83}
84
85impl From<&Vec<Arg>> for Robj {
86 fn from(args: &Vec<Arg>) -> Self {
87 List::from_values(args).into()
88 }
89}
90
91impl From<&Vec<Func>> for Robj {
92 fn from(funcs: &Vec<Func>) -> Self {
93 List::from_values(funcs).into()
94 }
95}
96
97impl From<&Vec<Impl>> for Robj {
98 fn from(impls: &Vec<Impl>) -> Self {
99 List::from_values(impls).into()
100 }
101}
102
103fn write_doc(w: &mut Vec<u8>, doc: &str) -> std::io::Result<()> {
104 if !doc.is_empty() {
105 write!(w, "#'")?;
106 for c in doc.chars() {
107 if c == '\n' {
108 write!(w, "\n#'")?;
109 } else {
110 write!(w, "{}", c)?;
111 }
112 }
113 writeln!(w)?;
114 }
115 Ok(())
116}
117
118fn sanitize_identifier(name: &str) -> String {
121 if name.starts_with('_') {
122 format!("`{}`", name)
123 } else if name.starts_with("r#") {
124 name.strip_prefix("r#").unwrap().into()
125 } else {
126 name.to_string()
127 }
128}
129
130fn join_str(input: impl Iterator<Item = String>, sep: &str) -> String {
131 input.collect::<Vec<String>>().join(sep)
132}
133
134fn write_function_wrapper(
136 w: &mut Vec<u8>,
137 func: &Func,
138 package_name: &str,
139 use_symbols: bool,
140) -> std::io::Result<()> {
141 if func.hidden {
142 return Ok(());
143 }
144
145 write_doc(w, func.doc)?;
146
147 let r_args: Vec<RArg> = func.args.iter().map(Into::into).collect();
148 let actual_args = r_args.iter().map(|a| a.to_actual_arg());
149 let formal_args = r_args.iter().map(|a| a.to_formal_arg());
150
151 let should_be_invisible = match func.invisible {
152 Some(true) => true,
153 Some(false) => false,
154 None => false,
155 };
156
157 if should_be_invisible {
158 write!(
159 w,
160 "{} <- function({}) invisible(.Call(",
161 sanitize_identifier(func.r_name),
162 join_str(formal_args, ", ")
163 )?;
164 } else {
165 write!(
166 w,
167 "{} <- function({}) .Call(",
168 sanitize_identifier(func.r_name),
169 join_str(formal_args, ", ")
170 )?;
171 }
172
173 if use_symbols {
174 write!(w, "{}", func.c_name)?;
175 } else {
176 write!(w, "\"{}\"", func.c_name)?;
177 }
178
179 if !func.args.is_empty() {
180 write!(w, ", {}", join_str(actual_args, ", "))?;
181 }
182
183 if !use_symbols {
184 write!(w, ", PACKAGE = \"{}\"", package_name)?;
185 }
186
187 if should_be_invisible {
188 writeln!(w, "))\n")?;
189 } else {
190 writeln!(w, ")\n")?;
191 }
192
193 Ok(())
194}
195
196fn write_method_wrapper(
198 w: &mut Vec<u8>,
199 func: &Func,
200 package_name: &str,
201 use_symbols: bool,
202 class_name: &str,
203) -> std::io::Result<()> {
204 if func.hidden {
205 return Ok(());
206 }
207
208 let r_args: Vec<RArg> = func.args.iter().map(Into::into).collect();
209 let actual_args = r_args.iter().map(|a| a.to_actual_arg());
210
211 let formal_args = r_args
214 .iter()
215 .skip_while(|a| a.is_self())
216 .map(|a| a.to_formal_arg());
217
218 let should_be_invisible = match func.invisible {
221 Some(true) => true,
222 Some(false) => false,
223 None => false,
224 };
225
226 if should_be_invisible {
227 write!(
228 w,
229 "{}${} <- function({}) invisible(.Call(",
230 sanitize_identifier(class_name),
231 sanitize_identifier(func.r_name),
232 join_str(formal_args, ", ")
233 )?;
234 } else {
235 write!(
236 w,
237 "{}${} <- function({}) .Call(",
238 sanitize_identifier(class_name),
239 sanitize_identifier(func.r_name),
240 join_str(formal_args, ", ")
241 )?;
242 }
243
244 if use_symbols {
246 write!(w, "{}", func.c_name)?;
247 } else {
248 write!(w, "\"{}\"", func.c_name)?;
249 }
250
251 if actual_args.len() != 0 {
252 write!(w, ", {}", join_str(actual_args, ", "))?;
253 }
254
255 if !use_symbols {
256 write!(w, ", PACKAGE = \"{}\"", package_name)?;
257 }
258
259 if should_be_invisible {
260 writeln!(w, "))\n")?;
261 } else {
262 writeln!(w, ")\n")?;
263 }
264
265 Ok(())
266}
267
268fn write_impl_wrapper(
270 w: &mut Vec<u8>,
271 name: &str,
272 impls: &[Impl],
273 package_name: &str,
274 use_symbols: bool,
275) -> std::io::Result<()> {
276 let mut exported = false;
277 {
278 for imp in impls.iter().filter(|imp| imp.name == name) {
279 if !exported {
280 exported = imp.doc.contains("@export");
281 }
282 write_doc(w, imp.doc)?;
283 }
284 }
285
286 let imp_name_fixed = sanitize_identifier(name);
287
288 writeln!(w, "{} <- new.env(parent = emptyenv())\n", imp_name_fixed)?;
290
291 for imp in impls.iter().filter(|imp| imp.name == name) {
292 for func in &imp.methods {
293 write_method_wrapper(w, func, package_name, use_symbols, imp.name)?;
296 }
297 }
298
299 if exported {
300 writeln!(w, "#' @rdname {}", name)?;
301 writeln!(w, "#' @usage NULL")?;
302 }
303
304 writeln!(w, "#' @export")?;
309
310 writeln!(w, "`$.{}` <- function (self, name) {{ func <- {}[[name]]; environment(func) <- environment(); func }}\n", name, imp_name_fixed)?;
314
315 writeln!(w, "#' @export")?;
316 writeln!(w, "`[[.{}` <- `$.{}`\n", name, name)?;
317
318 Ok(())
319}
320
321impl Metadata {
322 pub fn make_r_wrappers(
323 &self,
324 use_symbols: bool,
325 package_name: &str,
326 ) -> std::io::Result<String> {
327 let mut w = Vec::new();
328
329 for func in &self.functions {
330 write_function_wrapper(&mut w, func, package_name, use_symbols)?;
331 }
332
333 for name in self.impl_names() {
334 write_impl_wrapper(&mut w, name, &self.impls, package_name, use_symbols)?;
335 }
336
337 unsafe { Ok(String::from_utf8_unchecked(w)) }
338 }
339
340 pub fn write_r_wrappers(&self, package_name: &str, out_path: &str) -> std::io::Result<()> {
341 let body = self.make_r_wrappers(true, package_name)?;
342 let contents = format!(
343 "# Generated by extendr: Do not edit by hand\n\
344 # nolint start\n\
345 \n\
346 #' @usage NULL\n\
347 #' @useDynLib {package_name}, .registration = TRUE\n\
348 NULL\n\
349 \n\
350 {body}\
351 # nolint end\n"
352 );
353 std::fs::write(out_path, contents)
354 }
355
356 fn impl_names(&self) -> Vec<&str> {
357 let mut vec: Vec<&str> = vec![];
358 for impls in &self.impls {
359 if !vec.contains(&impls.name) {
360 vec.push(impls.name)
361 }
362 }
363 vec
364 }
365}