extendr_api/
metadata.rs

1//! Module metadata
2//!
3//! This data is returned by get_module_metadata()
4//! which is generated by [extendr_module!].
5use crate::*;
6use std::io::Write;
7
8/// Metadata function argument.
9#[derive(Debug, PartialEq)]
10pub struct Arg {
11    pub name: &'static str,
12    pub arg_type: &'static str,
13    pub default: Option<&'static str>,
14}
15
16/// Metadata function.
17#[derive(Debug, PartialEq)]
18pub struct Func {
19    pub doc: &'static str,
20    pub rust_name: &'static str,
21    pub mod_name: &'static str,
22    pub r_name: &'static str,
23    pub args: Vec<Arg>,
24    pub return_type: &'static str,
25    pub func_ptr: *const u8,
26    pub hidden: bool,
27    pub invisible: Option<bool>,
28}
29
30/// Metadata Impl.
31#[derive(Debug, PartialEq)]
32pub struct Impl {
33    pub doc: &'static str,
34    pub name: &'static str,
35    pub methods: Vec<Func>,
36}
37
38/// Module metadata.
39#[derive(Debug, PartialEq)]
40pub struct Metadata {
41    pub name: &'static str,
42    pub functions: Vec<Func>,
43    pub impls: Vec<Impl>,
44}
45
46struct RArg {
47    name: String,
48    default: Option<&'static str>,
49}
50
51impl RArg {
52    fn is_self(&self) -> bool {
53        self.name == "self"
54    }
55
56    fn to_actual_arg(&self) -> String {
57        self.name.clone()
58    }
59
60    fn to_formal_arg(&self) -> String {
61        match self.default {
62            Some(default_val) => format!("{} = {}", self.name, default_val),
63            None => self.name.clone(),
64        }
65    }
66}
67
68impl From<&Arg> for RArg {
69    fn from(arg: &Arg) -> Self {
70        Self {
71            name: sanitize_identifier(arg.name),
72            default: arg.default,
73        }
74    }
75}
76
77impl From<Arg> for Robj {
78    fn from(val: Arg) -> Self {
79        use crate as extendr_api;
80        let mut result = List::from_values(&[r!(val.name), r!(val.arg_type)]);
81        result
82            .set_names(&["name", "arg_type"])
83            .expect("From<Arg> failed");
84        result.into()
85    }
86}
87
88impl From<Func> for Robj {
89    fn from(val: Func) -> Self {
90        use crate as extendr_api;
91        let mut result = List::from_values(&[
92            r!(val.doc),
93            r!(val.rust_name),
94            r!(val.mod_name),
95            r!(val.r_name),
96            r!(List::from_values(val.args)),
97            r!(val.return_type),
98            r!(val.hidden),
99            r!(val.invisible.unwrap_or(false)),
100        ]);
101        result
102            .set_names(&[
103                "doc",
104                "rust_name",
105                "mod_name",
106                "r_name",
107                "args",
108                "return.type",
109                "hidden",
110                "invisible",
111            ])
112            .expect("From<Func> failed");
113        result.into()
114    }
115}
116
117impl From<Impl> for Robj {
118    fn from(val: Impl) -> Self {
119        use crate as extendr_api;
120        let mut result = List::from_values(&[
121            r!(val.doc),
122            r!(val.name),
123            r!(List::from_values(val.methods)),
124        ]);
125        result
126            .set_names(&["doc", "name", "methods"])
127            .expect("From<Impl> failed");
128        result.into()
129    }
130}
131
132impl From<Metadata> for Robj {
133    fn from(val: Metadata) -> Self {
134        use crate as extendr_api;
135        let mut result = List::from_values(&[
136            r!(val.name),
137            r!(List::from_values(val.functions)),
138            r!(List::from_values(val.impls)),
139        ]);
140        result
141            .set_names(&["name", "functions", "impls"])
142            .expect("From<Metadata> failed");
143        result.into()
144    }
145}
146
147fn write_doc(w: &mut Vec<u8>, doc: &str) -> std::io::Result<()> {
148    if !doc.is_empty() {
149        write!(w, "#'")?;
150        for c in doc.chars() {
151            if c == '\n' {
152                write!(w, "\n#'")?;
153            } else {
154                write!(w, "{}", c)?;
155            }
156        }
157        writeln!(w)?;
158    }
159    Ok(())
160}
161
162/// Wraps invalid R identifiers, like `_function_name`, into backticks.
163/// Removes raw identifiers (`r#`).
164fn sanitize_identifier(name: &str) -> String {
165    if name.starts_with('_') {
166        format!("`{}`", name)
167    } else if name.starts_with("r#") {
168        name.strip_prefix("r#").unwrap().into()
169    } else {
170        name.to_string()
171    }
172}
173
174fn join_str(input: impl Iterator<Item = String>, sep: &str) -> String {
175    input.collect::<Vec<String>>().join(sep)
176}
177
178/// Generate a wrapper for a non-method function.
179fn write_function_wrapper(
180    w: &mut Vec<u8>,
181    func: &Func,
182    package_name: &str,
183    use_symbols: bool,
184) -> std::io::Result<()> {
185    if func.hidden {
186        return Ok(());
187    }
188
189    write_doc(w, func.doc)?;
190
191    let r_args: Vec<RArg> = func.args.iter().map(Into::into).collect();
192    let actual_args = r_args.iter().map(|a| a.to_actual_arg());
193    let formal_args = r_args.iter().map(|a| a.to_formal_arg());
194
195    let should_be_invisible = match func.invisible {
196        Some(true) => true,
197        Some(false) => false,
198        None => {
199            // previous logic that guarantees invisible for () and Result types
200            func.return_type == "()" || func.return_type == "Result"
201        }
202    };
203
204    if should_be_invisible {
205        write!(
206            w,
207            "{} <- function({}) invisible(.Call(",
208            sanitize_identifier(func.r_name),
209            join_str(formal_args, ", ")
210        )?;
211    } else {
212        write!(
213            w,
214            "{} <- function({}) .Call(",
215            sanitize_identifier(func.r_name),
216            join_str(formal_args, ", ")
217        )?;
218    }
219
220    if use_symbols {
221        write!(w, "wrap__{}", func.mod_name)?;
222    } else {
223        write!(w, "\"wrap__{}\"", func.mod_name)?;
224    }
225
226    if !func.args.is_empty() {
227        write!(w, ", {}", join_str(actual_args, ", "))?;
228    }
229
230    if !use_symbols {
231        write!(w, ", PACKAGE = \"{}\"", package_name)?;
232    }
233
234    if should_be_invisible {
235        writeln!(w, "))\n")?;
236    } else {
237        writeln!(w, ")\n")?;
238    }
239
240    Ok(())
241}
242
243/// Generate a wrapper for a method.
244fn write_method_wrapper(
245    w: &mut Vec<u8>,
246    func: &Func,
247    package_name: &str,
248    use_symbols: bool,
249    class_name: &str,
250) -> std::io::Result<()> {
251    if func.hidden {
252        return Ok(());
253    }
254
255    let r_args: Vec<RArg> = func.args.iter().map(Into::into).collect();
256    let actual_args = r_args.iter().map(|a| a.to_actual_arg());
257
258    // Skip a leading "self" argument.
259    // This is supplied by the environment.
260    let formal_args = r_args
261        .iter()
262        .skip_while(|a| a.is_self())
263        .map(|a| a.to_formal_arg());
264
265    // Both `class_name` and `func.name` should be processed
266    // because they are exposed to R
267    let should_be_invisible = match func.invisible {
268        Some(true) => true,
269        Some(false) => false,
270        None => func.return_type == "()" || func.return_type == "Result",
271    };
272
273    if should_be_invisible {
274        write!(
275            w,
276            "{}${} <- function({}) invisible(.Call(",
277            sanitize_identifier(class_name),
278            sanitize_identifier(func.r_name),
279            join_str(formal_args, ", ")
280        )?;
281    } else {
282        write!(
283            w,
284            "{}${} <- function({}) .Call(",
285            sanitize_identifier(class_name),
286            sanitize_identifier(func.r_name),
287            join_str(formal_args, ", ")
288        )?;
289    }
290
291    // Here no processing is needed because of `wrap__` prefix
292    if use_symbols {
293        write!(w, "wrap__{}__{}", class_name, func.mod_name)?;
294    } else {
295        write!(w, "\"wrap__{}__{}\"", class_name, func.mod_name)?;
296    }
297
298    if actual_args.len() != 0 {
299        write!(w, ", {}", join_str(actual_args, ", "))?;
300    }
301
302    if !use_symbols {
303        write!(w, ", PACKAGE = \"{}\"", package_name)?;
304    }
305
306    if should_be_invisible {
307        writeln!(w, "))\n")?;
308    } else {
309        writeln!(w, ")\n")?;
310    }
311
312    Ok(())
313}
314
315/// Generate a wrapper for an implementation block.
316fn write_impl_wrapper(
317    w: &mut Vec<u8>,
318    name: &str,
319    impls: &[Impl],
320    package_name: &str,
321    use_symbols: bool,
322) -> std::io::Result<()> {
323    let mut exported = false;
324    {
325        for imp in impls.iter().filter(|imp| imp.name == name) {
326            if !exported {
327                exported = imp.doc.contains("@export");
328            }
329            write_doc(w, imp.doc)?;
330        }
331    }
332
333    let imp_name_fixed = sanitize_identifier(name);
334
335    // Using fixed name because it is exposed to R
336    writeln!(w, "{} <- new.env(parent = emptyenv())\n", imp_name_fixed)?;
337
338    for imp in impls.iter().filter(|imp| imp.name == name) {
339        for func in &imp.methods {
340            // write_doc(& mut w, func.doc)?;
341            // `imp.name` is passed as is and sanitized within the function
342            write_method_wrapper(w, func, package_name, use_symbols, imp.name)?;
343        }
344    }
345
346    if exported {
347        writeln!(w, "#' @rdname {}", name)?;
348        writeln!(w, "#' @usage NULL")?;
349    }
350
351    // This is needed no matter whether the user added `@export` or
352    // not; even if we don't export the class itself and its
353    // initializers, we always export the `$` method so the method is
354    // correctly added to the NAMESPACE.
355    writeln!(w, "#' @export")?;
356
357    // LHS with dollar operator is wrapped in ``, so pass name as is,
358    // but in the body `imp_name_fixed` is called as valid R function,
359    // so we pass preprocessed value
360    writeln!(w, "`$.{}` <- function (self, name) {{ func <- {}[[name]]; environment(func) <- environment(); func }}\n", name, imp_name_fixed)?;
361
362    writeln!(w, "#' @export")?;
363    writeln!(w, "`[[.{}` <- `$.{}`\n", name, name)?;
364
365    Ok(())
366}
367
368impl Metadata {
369    pub fn make_r_wrappers(
370        &self,
371        use_symbols: bool,
372        package_name: &str,
373    ) -> std::io::Result<String> {
374        let mut w = Vec::new();
375
376        writeln!(
377            w,
378            r#"# Generated by extendr: Do not edit by hand
379#
380# This file was created with the following call:
381#   .Call("wrap__make_{}_wrappers", use_symbols = {}, package_name = "{}")
382"#,
383            self.name,
384            if use_symbols { "TRUE" } else { "FALSE" },
385            package_name
386        )?;
387
388        if use_symbols {
389            writeln!(w, "#' @usage NULL")?;
390            writeln!(w, "#' @useDynLib {}, .registration = TRUE", package_name)?;
391            writeln!(w, "NULL")?;
392            writeln!(w)?;
393        }
394
395        for func in &self.functions {
396            write_function_wrapper(&mut w, func, package_name, use_symbols)?;
397        }
398
399        for name in self.impl_names() {
400            write_impl_wrapper(&mut w, name, &self.impls, package_name, use_symbols)?;
401        }
402
403        unsafe { Ok(String::from_utf8_unchecked(w)) }
404    }
405
406    fn impl_names<'a>(&'a self) -> Vec<&'a str> {
407        let mut vec: Vec<&str> = vec![];
408        for impls in &self.impls {
409            if !vec.contains(&impls.name) {
410                vec.push(&impls.name)
411            }
412        }
413        vec
414    }
415}