miniextendr_macros/miniextendr_impl/
vctrs_class.rs1use super::{ParsedImpl, VctrsKind};
12
13pub fn generate_vctrs_r_wrapper(parsed_impl: &ParsedImpl) -> String {
40 use crate::r_class_formatter::{
41 ClassDocBuilder, MethodDocBuilder, ParsedImplExt, emit_s3_generic_guard,
42 should_export_from_tags,
43 };
44
45 let class_name = parsed_impl.class_name();
46 let type_ident = &parsed_impl.type_ident;
47 let class_doc_tags = &parsed_impl.doc_tags;
48 let vctrs_attrs = &parsed_impl.vctrs_attrs;
49 let should_export =
50 should_export_from_tags(class_doc_tags, parsed_impl.noexport || parsed_impl.internal);
51 let class_has_no_rd = crate::roxygen::has_roxygen_tag(class_doc_tags, "noRd")
67 || (parsed_impl.noexport && !parsed_impl.internal);
68
69 let ctor_name = format!("new_{}", class_name.to_lowercase());
71
72 let mut lines = Vec::new();
73
74 if let Some(ctx) = parsed_impl.constructor_context() {
76 lines.push(ctx.source_comment(type_ident));
77 let mut ctor_doc_tags = Vec::new();
78 ctor_doc_tags.extend(class_doc_tags.iter().cloned());
79 ctor_doc_tags.extend(ctx.method.doc_tags.iter().cloned());
80
81 lines.extend(
82 ClassDocBuilder::new(&class_name, type_ident, &ctor_doc_tags, "vctrs S3")
83 .with_imports("@importFrom vctrs new_vctr new_rcrd new_list_of vec_ptype2 vec_cast vec_ptype_abbr")
84 .with_export_control(parsed_impl.internal, parsed_impl.noexport)
85 .build(),
86 );
87 if let Some(lc_import) = crate::lifecycle::collect_lifecycle_imports(
89 parsed_impl
90 .methods
91 .iter()
92 .filter_map(|m| m.method_attrs.lifecycle.as_ref()),
93 ) {
94 let insert_pos = lines.len().saturating_sub(1);
95 lines.insert(insert_pos, format!("#' {}", lc_import));
96 }
97
98 lines.push(format!("{} <- function({}) {{", ctor_name, ctx.params));
100 for check in ctx.precondition_checks() {
101 lines.push(format!(" {}", check));
102 }
103 for line in ctx.match_arg_prelude() {
105 lines.push(format!(" {}", line));
106 }
107 lines.push(format!(" .val <- {}", ctx.static_call()));
108 lines.extend(crate::method_return_builder::condition_check_lines(" "));
109 lines.push(" data <- .val".to_string());
110
111 match vctrs_attrs.kind {
112 VctrsKind::Vctr => {
113 let inherit_arg = match vctrs_attrs.inherit_base_type {
115 Some(true) => ", inherit_base_type = TRUE",
116 Some(false) => ", inherit_base_type = FALSE",
117 None => "",
118 };
119 lines.push(format!(
120 " vctrs::new_vctr(data, class = \"{}\"{})",
121 class_name, inherit_arg
122 ));
123 }
124 VctrsKind::Rcrd => {
125 lines.push(format!(
127 " vctrs::new_rcrd(data, class = \"{}\")",
128 class_name
129 ));
130 }
131 VctrsKind::ListOf => {
132 let ptype_arg = vctrs_attrs
134 .ptype
135 .as_ref()
136 .map(|p| format!(", ptype = {}", p))
137 .unwrap_or_default();
138 lines.push(format!(
139 " vctrs::new_list_of(data, class = \"{}\"{})",
140 class_name, ptype_arg
141 ));
142 }
143 }
144 lines.push("}".to_string());
145 lines.push(String::new());
146 }
147
148 if let Some(abbr) = &vctrs_attrs.abbr {
150 if class_has_no_rd {
151 lines.push("#' @noRd".to_string());
154 } else {
155 lines.push(format!("#' @rdname {}", class_name));
156 }
157 lines.push(format!("#' @method vec_ptype_abbr {}", class_name));
158 lines.push("#' @export".to_string());
159 lines.push(format!(
160 "vec_ptype_abbr.{} <- function(x, ...) \"{}\"",
161 class_name, abbr
162 ));
163 lines.push(String::new());
164 }
165
166 if class_has_no_rd {
169 lines.push("#' @noRd".to_string());
170 lines.push(format!(
171 "#' @method vec_ptype2 {}.{}",
172 class_name, class_name
173 ));
174 } else {
175 lines.push(format!("#' @rdname {}", class_name));
176 lines.push(format!(
177 "#' @method vec_ptype2 {}.{}",
178 class_name, class_name
179 ));
180 lines.push(format!("#' @param x A {} vector.", class_name));
181 lines.push(format!("#' @param y A {} vector.", class_name));
182 lines.push("#' @param ... Additional arguments (unused).".to_string());
183 }
184 lines.push("#' @export".to_string());
185 match vctrs_attrs.kind {
186 VctrsKind::Vctr => {
187 let base_type = vctrs_attrs
188 .base
189 .as_ref()
190 .map(|b| format!("{}()", b))
191 .unwrap_or_else(|| "double()".to_string());
192 let inherit_arg = match vctrs_attrs.inherit_base_type {
193 Some(true) => ", inherit_base_type = TRUE",
194 Some(false) => ", inherit_base_type = FALSE",
195 None => "",
196 };
197 lines.push(format!(
198 "vec_ptype2.{c}.{c} <- function(x, y, ...) vctrs::new_vctr({base}, class = \"{c}\"{inherit})",
199 c = class_name,
200 base = base_type,
201 inherit = inherit_arg
202 ));
203 }
204 VctrsKind::Rcrd => {
205 lines.push(format!(
207 "vec_ptype2.{c}.{c} <- function(x, y, ...) x[0]",
208 c = class_name
209 ));
210 }
211 VctrsKind::ListOf => {
212 let ptype_arg = vctrs_attrs
213 .ptype
214 .as_ref()
215 .map(|p| format!(", ptype = {}", p))
216 .unwrap_or_default();
217 lines.push(format!(
218 "vec_ptype2.{c}.{c} <- function(x, y, ...) vctrs::new_list_of(list(), class = \"{c}\"{ptype})",
219 c = class_name,
220 ptype = ptype_arg
221 ));
222 }
223 }
224 lines.push(String::new());
225
226 if class_has_no_rd {
228 lines.push("#' @noRd".to_string());
229 lines.push(format!("#' @method vec_cast {}.{}", class_name, class_name));
230 } else {
231 lines.push(format!("#' @rdname {}", class_name));
232 lines.push(format!("#' @method vec_cast {}.{}", class_name, class_name));
233 lines.push(format!("#' @param x A {} vector to cast.", class_name));
234 lines.push(format!("#' @param to A {} prototype.", class_name));
235 lines.push("#' @param ... Additional arguments (unused).".to_string());
236 }
237 lines.push("#' @export".to_string());
238 lines.push(format!(
239 "vec_cast.{c}.{c} <- function(x, to, ...) x",
240 c = class_name
241 ));
242 lines.push(String::new());
243
244 for ctx in parsed_impl.instance_method_contexts() {
246 lines.push(ctx.source_comment(type_ident));
247 let is_protocol = ctx.method.method_attrs.vctrs_protocol.is_some();
249 let generic_name = if let Some(ref proto) = ctx.method.method_attrs.vctrs_protocol {
250 proto.clone()
251 } else {
252 ctx.generic_name()
253 };
254 let method_class_suffix = ctx
256 .class_suffix()
257 .map(|s| s.to_string())
258 .unwrap_or_else(|| class_name.clone());
259 let s3_method_name = format!("{}.{}", generic_name, method_class_suffix);
260 let full_params = ctx.instance_formals(true); if !is_protocol && !ctx.has_generic_override() && !ctx.has_class_override() {
265 if class_has_no_rd {
266 lines.push("#' @noRd".to_string());
267 } else {
268 lines.push(format!("#' @title S3 generic for `{}`", generic_name));
269 lines.push(format!("#' @description S3 generic for `{}`", generic_name));
270 lines.push(format!("#' @rdname {}", class_name));
271 lines.push(format!("#' @name {}.{}", generic_name, class_name));
274 lines.push("#' @param x An object".to_string());
275 lines.push("#' @param ... Additional arguments passed to methods".to_string());
276 lines.push(crate::roxygen::method_source_tag(
277 type_ident,
278 &ctx.method.ident,
279 ));
280 if should_export {
281 lines.push(format!("#' @export {}", generic_name));
287 }
288 }
289 lines.push(emit_s3_generic_guard(&generic_name));
290 lines.push(String::new());
291 }
292
293 let qualified_name = format!("{}.{}", generic_name, method_class_suffix);
295 let mx_doc = ctx.match_arg_doc_placeholders();
296 let method_doc =
297 MethodDocBuilder::new(&class_name, &generic_name, type_ident, &ctx.method.doc_tags)
298 .with_r_params(&ctx.params)
299 .with_match_arg_doc_placeholders(&mx_doc)
300 .with_r_name(qualified_name)
301 .with_class_no_rd(class_has_no_rd);
302 lines.extend(method_doc.build());
303 lines.push(format!(
304 "#' @method {} {}",
305 generic_name, method_class_suffix
306 ));
307 lines.push("#' @export".to_string());
308 lines.push(format!(
309 "{} <- function({}) {{",
310 s3_method_name, full_params
311 ));
312
313 let what = format!("{}.{}", generic_name, class_name);
314 ctx.emit_method_prelude(&mut lines, " ", &what);
315
316 let call = ctx.instance_call("x");
317 let strategy = crate::ReturnStrategy::for_method(ctx.method);
318 let return_builder = crate::MethodReturnBuilder::new(call)
319 .with_strategy(strategy)
320 .with_class_name(class_name.clone())
321 .with_chain_var("x".to_string());
322 lines.extend(return_builder.build_s3_body());
323
324 lines.push("}".to_string());
325 lines.push(String::new());
326 }
327
328 for ctx in parsed_impl.static_method_contexts() {
331 lines.push(ctx.source_comment(type_ident));
332
333 let is_protocol = ctx.method.method_attrs.vctrs_protocol.is_some();
334 let fn_name = if let Some(ref proto) = ctx.method.method_attrs.vctrs_protocol {
335 format!("{}.{}", proto, class_name)
337 } else {
338 let method_name = ctx.method.r_method_name();
339 format!("{}_{}", class_name.to_lowercase(), method_name)
340 };
341 let r_name = fn_name.clone();
342
343 let mx_doc = ctx.match_arg_doc_placeholders();
344 let method_name = ctx.method.r_method_name();
345 if is_protocol {
346 let proto = ctx.method.method_attrs.vctrs_protocol.as_ref().unwrap();
347 let method_doc =
348 MethodDocBuilder::new(&class_name, &method_name, type_ident, &ctx.method.doc_tags)
349 .with_r_params(&ctx.params)
350 .with_match_arg_doc_placeholders(&mx_doc)
351 .with_r_name(r_name.clone())
352 .with_class_no_rd(class_has_no_rd);
353 lines.extend(method_doc.build());
354 lines.push(format!("#' @method {} {}", proto, class_name));
355 lines.push("#' @export".to_string());
356 } else {
357 let method_doc =
358 MethodDocBuilder::new(&class_name, &method_name, type_ident, &ctx.method.doc_tags)
359 .with_r_params(&ctx.params)
360 .with_match_arg_doc_placeholders(&mx_doc)
361 .with_r_name(r_name.clone())
362 .with_class_no_rd(class_has_no_rd);
363 lines.extend(method_doc.build());
364 }
365
366 let formals = if is_protocol {
370 format!("{}, ...", ctx.params)
371 } else {
372 ctx.params.to_string()
373 };
374 lines.push(format!("{} <- function({}) {{", fn_name, formals));
375
376 ctx.emit_method_prelude(&mut lines, " ", &fn_name);
377
378 let strategy = crate::ReturnStrategy::for_method(ctx.method);
379 let return_builder = crate::MethodReturnBuilder::new(ctx.static_call())
380 .with_strategy(strategy)
381 .with_class_name(class_name.clone());
382 lines.extend(return_builder.build_s3_body());
383
384 lines.push("}".to_string());
385 lines.push(String::new());
386 }
387
388 lines.join("\n")
389}