aorist_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::robj::IntoRobj;
6use crate::*;
7use std::io::Write;
8
9/// Metadata function argument.
10#[derive(Debug, PartialEq)]
11pub struct Arg {
12    pub name: &'static str,
13    pub arg_type: &'static str,
14}
15
16/// Metadata function.
17#[derive(Debug, PartialEq)]
18pub struct Func {
19    pub doc: &'static str,
20    pub name: &'static str,
21    pub args: Vec<Arg>,
22    pub return_type: &'static str,
23    pub func_ptr: *const u8,
24    pub hidden: bool,
25}
26
27/// Metadata Impl.
28#[derive(Debug, PartialEq)]
29pub struct Impl {
30    pub doc: &'static str,
31    pub name: &'static str,
32    pub methods: Vec<Func>,
33}
34
35/// Module metadata.
36#[derive(Debug, PartialEq)]
37pub struct Metadata {
38    pub name: &'static str,
39    pub functions: Vec<Func>,
40    pub impls: Vec<Impl>,
41}
42
43impl From<Arg> for Robj {
44    fn from(val: Arg) -> Self {
45        List::from_values(&[r!(val.name), r!(val.arg_type)])
46            .into_robj()
47            .set_names(&["name", "arg_type"])
48            .expect("From<Arg> failed")
49    }
50}
51
52impl From<Func> for Robj {
53    fn from(val: Func) -> Self {
54        List::from_values(&[
55            r!(val.doc),
56            r!(val.name),
57            r!(List::from_values(val.args)),
58            r!(val.return_type),
59            r!(val.hidden),
60        ])
61        .into_robj()
62        .set_names(&["doc", "name", "args", "return.type", "hidden"])
63        .expect("From<Func> failed")
64    }
65}
66
67impl From<Impl> for Robj {
68    fn from(val: Impl) -> Self {
69        List::from_values(&[
70            r!(val.doc),
71            r!(val.name),
72            r!(List::from_values(val.methods)),
73        ])
74        .into_robj()
75        .set_names(&["doc", "name", "methods"])
76        .expect("From<Impl> failed")
77    }
78}
79
80impl From<Metadata> for Robj {
81    fn from(val: Metadata) -> Self {
82        List::from_values(&[
83            r!(val.name),
84            r!(List::from_values(val.functions)),
85            r!(List::from_values(val.impls)),
86        ])
87        .into_robj()
88        .set_names(&["name", "functions", "impls"])
89        .expect("From<Metadata> failed")
90    }
91}
92
93fn write_doc(w: &mut Vec<u8>, doc: &str) -> std::io::Result<()> {
94    if !doc.is_empty() {
95        write!(w, "#'")?;
96        for c in doc.chars() {
97            if c == '\n' {
98                write!(w, "\n#'")?;
99            } else {
100                write!(w, "{}", c)?;
101            }
102        }
103        writeln!(w)?;
104    }
105    Ok(())
106}
107
108/// Wraps invalid R identifers, like `_function_name`, into backticks.
109fn sanitize_identifier(name: &str) -> String {
110    if name.starts_with('_') {
111        format!("`{}`", name)
112    } else {
113        name.to_string()
114    }
115}
116
117/// Generate a wrapper for a non-method function.
118fn write_function_wrapper(
119    w: &mut Vec<u8>,
120    func: &Func,
121    package_name: &str,
122    use_symbols: bool,
123) -> std::io::Result<()> {
124    if func.hidden {
125        return Ok(());
126    }
127
128    write_doc(w, func.doc)?;
129
130    let args = func
131        .args
132        .iter()
133        .map(|arg| sanitize_identifier(arg.name))
134        .collect::<Vec<_>>()
135        .join(", ");
136
137    if func.return_type == "()" {
138        write!(
139            w,
140            "{} <- function({}) invisible(.Call(",
141            sanitize_identifier(func.name),
142            args
143        )?;
144    } else {
145        write!(
146            w,
147            "{} <- function({}) .Call(",
148            sanitize_identifier(func.name),
149            args
150        )?;
151    }
152
153    if use_symbols {
154        write!(w, "wrap__{}", func.name)?;
155    } else {
156        write!(w, "\"wrap__{}\"", func.name)?;
157    }
158
159    if !func.args.is_empty() {
160        write!(w, ", {}", args)?;
161    }
162
163    if !use_symbols {
164        write!(w, ", PACKAGE = \"{}\"", package_name)?;
165    }
166
167    if func.return_type == "()" {
168        writeln!(w, "))\n")?;
169    } else {
170        writeln!(w, ")\n")?;
171    }
172
173    Ok(())
174}
175
176/// Generate a wrapper for a method.
177fn write_method_wrapper(
178    w: &mut Vec<u8>,
179    func: &Func,
180    package_name: &str,
181    use_symbols: bool,
182    class_name: &str,
183) -> std::io::Result<()> {
184    if func.hidden {
185        return Ok(());
186    }
187
188    let actual_args = func
189        .args
190        .iter()
191        .map(|arg| sanitize_identifier(arg.name))
192        .collect::<Vec<_>>();
193    let formal_args = if !actual_args.is_empty() && actual_args[0] == "self" {
194        // Skip a leading "self" argument.
195        // This is supplied by the environment.
196        actual_args
197            .iter()
198            .skip(1)
199            .map(|x| x.to_string())
200            .collect::<Vec<_>>()
201    } else {
202        actual_args.clone()
203    };
204
205    let formal_args = formal_args.join(", ");
206    let actual_args = actual_args.join(", ");
207
208    // Both `class_name` and `func.name` should be processed
209    // because they are exposed to R
210    if func.return_type == "()" {
211        write!(
212            w,
213            "{}${} <- function({}) invisible(.Call(",
214            sanitize_identifier(class_name),
215            sanitize_identifier(func.name),
216            formal_args
217        )?;
218    } else {
219        write!(
220            w,
221            "{}${} <- function({}) .Call(",
222            sanitize_identifier(class_name),
223            sanitize_identifier(func.name),
224            formal_args
225        )?;
226    }
227
228    // Here no processing is needed because of `wrap__` prefix
229    if use_symbols {
230        write!(w, "wrap__{}__{}", class_name, func.name)?;
231    } else {
232        write!(w, "\"wrap__{}__{}\"", class_name, func.name)?;
233    }
234
235    if !actual_args.is_empty() {
236        write!(w, ", {}", actual_args)?;
237    }
238
239    if !use_symbols {
240        write!(w, ", PACKAGE = \"{}\"", package_name)?;
241    }
242
243    if func.return_type == "()" {
244        writeln!(w, "))\n")?;
245    } else {
246        writeln!(w, ")\n")?;
247    }
248
249    Ok(())
250}
251
252/// Generate a wrapper for an implementation block.
253fn write_impl_wrapper(
254    w: &mut Vec<u8>,
255    imp: &Impl,
256    package_name: &str,
257    use_symbols: bool,
258) -> std::io::Result<()> {
259    let exported = imp.doc.contains("@export");
260
261    write_doc(w, imp.doc)?;
262
263    let imp_name_fixed = sanitize_identifier(imp.name);
264
265    // Using fixed name because it is exposed to R
266    writeln!(w, "{} <- new.env(parent = emptyenv())\n", imp_name_fixed)?;
267
268    for func in &imp.methods {
269        // write_doc(& mut w, func.doc)?;
270        // `imp.name` is passed as is and sanitized within the function
271        write_method_wrapper(w, func, package_name, use_symbols, imp.name)?;
272    }
273
274    if exported {
275        writeln!(w, "#' @rdname {}", imp.name)?;
276        writeln!(w, "#' @usage NULL")?;
277    }
278
279    // This is needed no matter whether the user added `@export` or
280    // not; even if we don't export the class itself and its
281    // initializers, we always export the `$` method so the method is
282    // correctly added to the NAMESPACE.
283    writeln!(w, "#' @export")?;
284
285    // LHS with dollar operator is wrapped in ``, so pass name as is,
286    // but in the body `imp_name_fixed` is called as valid R function,
287    // so we pass preprocessed value
288    writeln!(w, "`$.{}` <- function (self, name) {{ func <- {}[[name]]; environment(func) <- environment(); func }}\n", imp.name, imp_name_fixed)?;
289
290    Ok(())
291}
292
293impl Metadata {
294    pub fn make_r_wrappers(
295        &self,
296        use_symbols: bool,
297        package_name: &str,
298    ) -> std::io::Result<String> {
299        let mut w = Vec::new();
300
301        writeln!(
302            w,
303            r#"# Generated by extendr: Do not edit by hand
304#
305# This file was created with the following call:
306#   .Call("wrap__make_{}_wrappers", use_symbols = {}, package_name = "{}")
307"#,
308            self.name,
309            if use_symbols { "TRUE" } else { "FALSE" },
310            package_name
311        )?;
312
313        if use_symbols {
314            writeln!(w, "#' @docType package")?;
315            writeln!(w, "#' @usage NULL")?;
316            writeln!(w, "#' @useDynLib {}, .registration = TRUE", package_name)?;
317            writeln!(w, "NULL")?;
318            writeln!(w)?;
319        }
320
321        for func in &self.functions {
322            write_function_wrapper(&mut w, func, package_name, use_symbols)?;
323        }
324
325        for imp in &self.impls {
326            write_impl_wrapper(&mut w, imp, package_name, use_symbols)?;
327        }
328        unsafe { Ok(String::from_utf8_unchecked(w)) }
329    }
330}