Skip to main content

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#[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/// Metadata function.
20#[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/// Metadata Impl.
37#[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/// Module metadata.
46#[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
118/// Wraps invalid R identifiers, like `_function_name`, into backticks.
119/// Removes raw identifiers (`r#`).
120fn 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
134/// Generate a wrapper for a non-method function.
135fn 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
196/// Generate a wrapper for a method.
197fn 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    // Skip a leading "self" argument.
212    // This is supplied by the environment.
213    let formal_args = r_args
214        .iter()
215        .skip_while(|a| a.is_self())
216        .map(|a| a.to_formal_arg());
217
218    // Both `class_name` and `func.name` should be processed
219    // because they are exposed to R
220    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    // Here no processing is needed because of `wrap__` prefix
245    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
268/// Generate a wrapper for an implementation block.
269fn 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    // Using fixed name because it is exposed to R
289    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_doc(& mut w, func.doc)?;
294            // `imp.name` is passed as is and sanitized within the function
295            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    // This is needed no matter whether the user added `@export` or
305    // not; even if we don't export the class itself and its
306    // initializers, we always export the `$` method so the method is
307    // correctly added to the NAMESPACE.
308    writeln!(w, "#' @export")?;
309
310    // LHS with dollar operator is wrapped in ``, so pass name as is,
311    // but in the body `imp_name_fixed` is called as valid R function,
312    // so we pass preprocessed value
313    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}