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