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 as extendr_api;
6use crate::*;
7use std::io::Write;
8
9/// Metadata function argument.
10#[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/// Metadata function.
19#[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/// Metadata Impl.
35#[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/// Module metadata.
43#[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
114/// Wraps invalid R identifiers, like `_function_name`, into backticks.
115/// Removes raw identifiers (`r#`).
116fn 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
130/// Generate a wrapper for a non-method function.
131fn 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
192/// Generate a wrapper for a method.
193fn 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    // Skip a leading "self" argument.
208    // This is supplied by the environment.
209    let formal_args = r_args
210        .iter()
211        .skip_while(|a| a.is_self())
212        .map(|a| a.to_formal_arg());
213
214    // Both `class_name` and `func.name` should be processed
215    // because they are exposed to R
216    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    // Here no processing is needed because of `wrap__` prefix
241    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
264/// Generate a wrapper for an implementation block.
265fn 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    // Using fixed name because it is exposed to R
285    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_doc(& mut w, func.doc)?;
290            // `imp.name` is passed as is and sanitized within the function
291            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    // This is needed no matter whether the user added `@export` or
301    // not; even if we don't export the class itself and its
302    // initializers, we always export the `$` method so the method is
303    // correctly added to the NAMESPACE.
304    writeln!(w, "#' @export")?;
305
306    // LHS with dollar operator is wrapped in ``, so pass name as is,
307    // but in the body `imp_name_fixed` is called as valid R function,
308    // so we pass preprocessed value
309    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}