1use super::{TraitConst, TraitMethod, trait_method_body_lines, trait_method_preamble_lines};
9use crate::miniextendr_impl::ClassSystem;
10use crate::r_class_formatter::emit_s3_generic_guard;
11
12pub(super) struct TraitWrapperOpts {
14 pub(super) class_system: ClassSystem,
16 pub(super) class_has_no_rd: bool,
19 pub(super) internal: bool,
22 pub(super) noexport: bool,
25}
26
27pub(super) fn generate_trait_r_wrapper(
38 type_ident: &syn::Ident,
39 trait_name: &syn::Ident,
40 methods: &[TraitMethod],
41 consts: &[TraitConst],
42 opts: TraitWrapperOpts,
43) -> syn::Result<String> {
44 let TraitWrapperOpts {
45 class_system,
46 class_has_no_rd,
47 internal,
48 noexport,
49 } = opts;
50 let result = match class_system {
51 ClassSystem::Env => generate_trait_env_r_wrapper(type_ident, trait_name, methods, consts)?,
52 ClassSystem::S3 => generate_trait_s3_r_wrapper(type_ident, trait_name, methods, consts),
53 ClassSystem::S4 => generate_trait_s4_r_wrapper(type_ident, trait_name, methods, consts),
54 ClassSystem::S7 => generate_trait_s7_r_wrapper(type_ident, trait_name, methods, consts),
55 ClassSystem::R6 => generate_trait_r6_r_wrapper(type_ident, trait_name, methods, consts),
56 ClassSystem::Vctrs => generate_trait_s3_r_wrapper(type_ident, trait_name, methods, consts),
58 };
59
60 if class_has_no_rd {
64 if matches!(class_system, ClassSystem::S3 | ClassSystem::Vctrs) {
65 let mut filtered = Vec::new();
66 let mut roxygen_block: Vec<&str> = Vec::new();
67
68 let flush_block = |block: &mut Vec<&str>, out: &mut Vec<String>| {
69 if block.iter().any(|line| line.contains("@method ")) {
70 out.push("#' @noRd".to_string());
71 for &line in block.iter() {
72 if line.contains("@method ")
73 || line.contains("@param ")
74 || line.contains("@export")
75 {
76 out.push(line.to_string());
77 }
78 }
79 }
80 block.clear();
81 };
82
83 for line in result.lines() {
84 if line.starts_with("#'") {
85 roxygen_block.push(line);
86 continue;
87 }
88
89 if !roxygen_block.is_empty() {
90 flush_block(&mut roxygen_block, &mut filtered);
91 }
92 filtered.push(line.to_string());
93 }
94
95 if !roxygen_block.is_empty() {
96 flush_block(&mut roxygen_block, &mut filtered);
97 }
98
99 Ok(filtered.join("\n"))
100 } else {
101 Ok(result
102 .lines()
103 .filter(|line| !line.starts_with("#'"))
104 .collect::<Vec<_>>()
105 .join("\n"))
106 }
107 } else if !class_has_no_rd && (internal || noexport) {
108 let has_export = result.lines().any(|line| line.contains("@export"));
111 let mut processed: Vec<String> = result
112 .lines()
113 .flat_map(|line| {
114 if line.contains("@export") {
115 if internal {
118 vec!["#' @keywords internal".to_string()]
119 } else {
120 vec![]
121 }
122 } else {
123 vec![line.to_string()]
124 }
125 })
126 .collect();
127 if internal
130 && !has_export
131 && let Some(pos) = processed.iter().position(|l| l.starts_with("#'"))
132 {
133 processed.insert(pos, "#' @keywords internal".to_string());
134 }
135 Ok(processed.join("\n"))
136 } else {
137 Ok(result)
138 }
139}
140
141fn generate_trait_env_r_wrapper(
153 type_ident: &syn::Ident,
154 trait_name: &syn::Ident,
155 methods: &[TraitMethod],
156 consts: &[TraitConst],
157) -> syn::Result<String> {
158 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
159
160 let mut lines = Vec::new();
161 let type_str = type_ident.to_string();
162 let trait_str = trait_name.to_string();
163
164 lines.push(format!(
166 "# Trait methods and consts for {} implementing {}",
167 type_ident, trait_name
168 ));
169 lines.push(format!(
170 "# Generated by #[miniextendr] impl {} for {}",
171 trait_name, type_ident
172 ));
173 lines.push(String::new());
174
175 lines.push(format!(
177 "{}${} <- new.env(parent = emptyenv())",
178 type_ident, trait_name
179 ));
180 lines.push(String::new());
181
182 for method in methods {
183 let r_name = method.r_method_name();
184
185 let formals =
187 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
188
189 let params =
191 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
192
193 let roxygen = RoxygenBuilder::new()
195 .name(format!("{}${}${}", type_str, trait_str, r_name))
196 .rdname(&type_str)
197 .build();
198 lines.extend(roxygen);
199
200 if method.has_self {
202 for input in &method.sig.inputs {
203 if let syn::FnArg::Typed(pt) = input
204 && let syn::Pat::Ident(pat_ident) = pt.pat.as_ref()
205 && pat_ident.ident == "x"
206 {
207 return Err(syn::Error::new_spanned(
208 &pat_ident.ident,
209 "trait instance method parameter cannot be named `x` \
210 (collides with self parameter in env-class dispatch)",
211 ));
212 }
213 }
214 }
215
216 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
218 let (full_params, call) = if method.has_self {
219 let fp = if formals.is_empty() {
220 "x".to_string()
221 } else {
222 format!("x, {}", formals)
223 };
224 let c = DotCallBuilder::new(&c_ident)
225 .with_self("x")
226 .with_args(¶ms)
227 .build();
228 (fp, c)
229 } else {
230 (
231 formals.clone(),
232 DotCallBuilder::new(&c_ident).with_args(¶ms).build(),
233 )
234 };
235
236 lines.push(format!(
238 "{}${}${} <- function({}) {{",
239 type_ident, trait_name, r_name, full_params
240 ));
241 lines.extend(trait_method_preamble_lines(method, " "));
242 lines.extend(trait_method_body_lines(&call, " "));
243 if method.has_self && method.returns_unit() {
244 lines.push(" invisible(x)".to_string());
245 }
246 lines.push("}".to_string());
247
248 if method.has_self {
250 lines.push(format!(
251 "attr({}${}${}, \".__mx_instance__\") <- TRUE",
252 type_ident, trait_name, r_name
253 ));
254 }
255
256 lines.push(String::new());
257 }
258
259 for trait_const in consts {
261 let const_name = &trait_const.ident;
262 let const_str = const_name.to_string();
263
264 let roxygen = RoxygenBuilder::new()
266 .name(format!("{}${}${}", type_str, trait_str, const_str))
267 .rdname(&type_str)
268 .build();
269 lines.extend(roxygen);
270
271 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
273 let call = DotCallBuilder::new(&c_ident).build();
274
275 lines.push(format!(
277 "{}${}${} <- function() {{",
278 type_ident, trait_name, const_name
279 ));
280 lines.push(format!(" {}", call));
281 lines.push("}".to_string());
282 lines.push(String::new());
283 }
284
285 Ok(lines.join("\n"))
286}
287
288fn generate_trait_s3_r_wrapper(
300 type_ident: &syn::Ident,
301 trait_name: &syn::Ident,
302 methods: &[TraitMethod],
303 consts: &[TraitConst],
304) -> String {
305 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
306
307 let mut lines = Vec::new();
308 let type_str = type_ident.to_string();
309 let trait_str = trait_name.to_string();
310
311 lines.push(format!(
313 "# S3 trait methods for {} implementing {}",
314 type_ident, trait_name
315 ));
316 lines.push(format!(
317 "# Generated by #[miniextendr(s3)] impl {} for {}",
318 trait_name, type_ident
319 ));
320 lines.push(String::new());
321
322 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
324 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
325
326 for method in &instance_methods {
328 let generic_name = method.r_method_name();
329 let s3_method_name = format!("{}.{}", generic_name, type_str);
330
331 let formals =
333 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
334 let params =
336 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
337
338 let generic_roxygen = RoxygenBuilder::new()
341 .title(format!("S3 generic for `{}`", generic_name))
342 .custom(format!("S3 generic for `{}`", generic_name))
343 .name(format!("{}.{}", generic_name, type_str))
344 .rdname(&type_str)
345 .custom("@param x An object")
346 .custom("@param ... Additional arguments passed to methods")
347 .source(format!(
348 "Generated by miniextendr from `impl {} for {}`",
349 trait_name, type_ident
350 ))
351 .export()
352 .build();
353 lines.extend(generic_roxygen);
354
355 lines.push(emit_s3_generic_guard(generic_name.as_str()));
357 lines.push(String::new());
358
359 let mut method_roxygen = RoxygenBuilder::new()
361 .rdname(&type_str)
362 .export()
363 .method(&generic_name, &type_str);
364 for tag in &method.param_tags {
365 method_roxygen = method_roxygen.custom(tag.clone());
366 }
367 lines.extend(method_roxygen.build());
368
369 let full_params = if formals.is_empty() {
371 "x, ...".to_string()
372 } else {
373 format!("x, {}, ...", formals)
374 };
375
376 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
378 let call = DotCallBuilder::new(&c_ident)
379 .with_self("x")
380 .with_args(¶ms)
381 .build();
382
383 lines.push(format!(
385 "{} <- function({}) {{",
386 s3_method_name, full_params
387 ));
388 lines.extend(trait_method_preamble_lines(method, " "));
389 lines.extend(trait_method_body_lines(&call, " "));
390 if method.returns_unit() {
392 lines.push(" invisible(x)".to_string());
393 }
394 lines.push("}".to_string());
395
396 lines.push(format!(
399 "if (inherits(get0(\"{generic_name}\", mode = \"function\"), \"S7_generic\")) {{"
400 ));
401 lines.push(format!(
402 " S7::method({generic_name}, S7::new_S3_class(\"{type_str}\")) <- {s3_method_name}"
403 ));
404 lines.push("}".to_string());
405 lines.push(String::new());
406 }
407
408 if !static_methods.is_empty() || !consts.is_empty() {
410 lines.push(format!(
411 "{}${} <- new.env(parent = emptyenv())",
412 type_ident, trait_name
413 ));
414 lines.push(String::new());
415 }
416
417 for method in &static_methods {
419 let r_name = method.r_method_name();
420 let formals =
422 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
423 let params =
425 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
426
427 lines.push(format!(
429 "#' Static trait method {}::{}()",
430 trait_name, r_name
431 ));
432 let roxygen = RoxygenBuilder::new()
433 .name(format!("{}${}${}", type_str, trait_str, r_name))
434 .rdname(&type_str)
435 .build();
436 lines.extend(roxygen);
437
438 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
440 let call = DotCallBuilder::new(&c_ident).with_args(¶ms).build();
441
442 lines.push(format!(
443 "{}${}${} <- function({}) {{",
444 type_ident, trait_name, r_name, formals
445 ));
446 lines.extend(trait_method_preamble_lines(method, " "));
447 lines.extend(trait_method_body_lines(&call, " "));
448 lines.push("}".to_string());
449 lines.push(String::new());
450 }
451
452 for trait_const in consts {
454 let const_name = &trait_const.ident;
455 let const_str = const_name.to_string();
456
457 let roxygen = RoxygenBuilder::new()
458 .name(format!("{}${}${}", type_str, trait_str, const_str))
459 .rdname(&type_str)
460 .build();
461 lines.extend(roxygen);
462
463 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
464 let call = DotCallBuilder::new(&c_ident).build();
465
466 lines.push(format!(
467 "{}${}${} <- function() {{",
468 type_ident, trait_name, const_name
469 ));
470 lines.push(format!(" {}", call));
471 lines.push("}".to_string());
472 lines.push(String::new());
473 }
474
475 lines.join("\n")
476}
477
478fn generate_trait_s4_r_wrapper(
489 type_ident: &syn::Ident,
490 trait_name: &syn::Ident,
491 methods: &[TraitMethod],
492 consts: &[TraitConst],
493) -> String {
494 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
495
496 let mut lines = Vec::new();
497 let type_str = type_ident.to_string();
498 let trait_str = trait_name.to_string();
499
500 lines.push(format!(
502 "# S4 trait methods for {} implementing {}",
503 type_ident, trait_name
504 ));
505 lines.push(format!(
506 "# Generated by #[miniextendr(s4)] impl {} for {}",
507 trait_name, type_ident
508 ));
509 lines.push(String::new());
510
511 lines.push("#' @importFrom methods setGeneric setMethod".to_string());
515 lines.push(String::new());
516
517 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
519 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
520
521 for method in &instance_methods {
523 let method_name = &method.ident;
524 let generic_name = format!("s4_trait_{}_{}", trait_name, method.r_method_name());
525
526 let formals =
528 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
529 let params =
531 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
532
533 let full_params = if formals.is_empty() {
535 "x, ...".to_string()
536 } else {
537 format!("x, {}, ...", formals)
538 };
539
540 let mut generic_roxygen = RoxygenBuilder::new()
544 .custom(format!(
545 "S4 generic for trait method `{}::{}`",
546 trait_name, method_name
547 ))
548 .name(&generic_name)
549 .rdname(&type_str)
550 .source(format!(
551 "Generated by miniextendr from `impl {} for {}`",
552 trait_name, type_ident
553 ))
554 .custom(format!("@param x A `{}` object", type_str))
555 .custom("@param ... Additional arguments passed to methods");
556 for tag in &method.param_tags {
557 generic_roxygen = generic_roxygen.custom(tag.clone());
558 }
559 lines.extend(generic_roxygen.export().build());
560
561 lines.push(format!(
563 "if (!methods::isGeneric(\"{generic_name}\")) methods::setGeneric(\"{generic_name}\", function(x, ...) standardGeneric(\"{generic_name}\"))"
564 ));
565 lines.push(String::new());
566
567 lines.push(format!("#' @rdname {}", type_str));
569 for tag in &method.param_tags {
570 lines.push(format!("#' {}", tag));
571 }
572 lines.push(format!("#' @exportMethod {}", generic_name));
573
574 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
576 let call = DotCallBuilder::new(&c_ident)
577 .with_self("x")
578 .with_args(¶ms)
579 .build();
580
581 lines.push(format!(
582 "methods::setMethod(\"{}\", \"{}\", function({}) {{",
583 generic_name, type_str, full_params
584 ));
585 lines.push(" .ptr <- x@ptr".to_string());
587 let s4_call = call.replace(", x", ", .ptr");
588 lines.extend(trait_method_preamble_lines(method, " "));
589 lines.extend(trait_method_body_lines(&s4_call, " "));
590 if method.returns_unit() {
592 lines.push(" invisible(x)".to_string());
593 }
594 lines.push("})".to_string());
595 lines.push(String::new());
596 }
597
598 for method in &static_methods {
600 let r_name = method.r_method_name();
601 let fn_name = format!("{}_{}_{}", type_str, trait_str, r_name);
602 let formals =
604 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
605 let params =
607 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
608
609 lines.push(format!(
611 "#' Static trait method {}::{}() for {}",
612 trait_name, r_name, type_str
613 ));
614 let roxygen = RoxygenBuilder::new()
615 .name(&fn_name)
616 .rdname(&type_str)
617 .export()
618 .build();
619 lines.extend(roxygen);
620
621 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
623 let call = DotCallBuilder::new(&c_ident).with_args(¶ms).build();
624
625 lines.push(format!("{} <- function({}) {{", fn_name, formals));
626 lines.extend(trait_method_preamble_lines(method, " "));
627 lines.extend(trait_method_body_lines(&call, " "));
628 lines.push("}".to_string());
629 lines.push(String::new());
630 }
631
632 for trait_const in consts {
634 let const_name = &trait_const.ident;
635 let fn_name = format!("{}_{}_{}", type_str, trait_str, const_name);
636
637 let roxygen = RoxygenBuilder::new()
638 .name(&fn_name)
639 .rdname(&type_str)
640 .export()
641 .build();
642 lines.extend(roxygen);
643
644 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
645 let call = DotCallBuilder::new(&c_ident).build();
646
647 lines.push(format!("{} <- function() {{", fn_name));
648 lines.push(format!(" {}", call));
649 lines.push("}".to_string());
650 lines.push(String::new());
651 }
652
653 lines.join("\n")
654}
655
656fn generate_trait_s7_r_wrapper(
666 type_ident: &syn::Ident,
667 trait_name: &syn::Ident,
668 methods: &[TraitMethod],
669 consts: &[TraitConst],
670) -> String {
671 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
672
673 let mut lines = Vec::new();
674 let type_str = type_ident.to_string();
675 let trait_str = trait_name.to_string();
676 let s7_class_var = format!(".s7_class_{}", type_str);
677
678 lines.push(format!(
680 "# S7 trait methods for {} implementing {}",
681 type_ident, trait_name
682 ));
683 lines.push(format!(
684 "# Generated by #[miniextendr(s7)] impl {} for {}",
685 trait_name, type_ident
686 ));
687 lines.push(String::new());
688
689 lines.push("#' @importFrom S7 new_generic method S7_dispatch".to_string());
694 lines.push(format!("{} <- {}", s7_class_var, type_str));
695 lines.push(String::new());
696
697 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
699 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
700
701 for method in &instance_methods {
703 let method_name = &method.ident;
704 let generic_name = format!("s7_trait_{}_{}", trait_name, method.r_method_name());
705
706 let formals =
708 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
709 let params =
711 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
712
713 let full_params = if formals.is_empty() {
715 "x, ...".to_string()
716 } else {
717 format!("x, {}, ...", formals)
718 };
719
720 let generic_roxygen = RoxygenBuilder::new()
725 .custom(format!(
726 "S7 generic for trait method `{}::{}`",
727 trait_name, method_name
728 ))
729 .name(&generic_name)
730 .rdname(&type_str)
731 .source(format!(
732 "Generated by miniextendr from `impl {} for {}`",
733 trait_name, type_ident
734 ))
735 .export()
736 .build();
737 lines.extend(generic_roxygen);
738
739 lines.push(format!(
741 "if (!exists(\"{generic_name}\", mode = \"function\")) {{"
742 ));
743 lines.push(format!(
744 " {generic_name} <- S7::new_generic(\"{generic_name}\", \"x\", function(x, ...) S7::S7_dispatch())"
745 ));
746 lines.push("}".to_string());
747 lines.push(String::new());
748
749 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
751 let call = DotCallBuilder::new(&c_ident)
752 .with_self("x")
753 .with_args(¶ms)
754 .build();
755
756 lines.push(format!(
758 "S7::method({}, {}) <- function({}) {{",
759 generic_name, s7_class_var, full_params
760 ));
761 lines.push(" .ptr <- x@.ptr".to_string());
763 let s7_call = call.replace(", x", ", .ptr");
764 lines.extend(trait_method_preamble_lines(method, " "));
765 lines.extend(trait_method_body_lines(&s7_call, " "));
766 if method.returns_unit() {
768 lines.push(" invisible(x)".to_string());
769 }
770 lines.push("}".to_string());
771 lines.push(String::new());
772 }
773
774 let trait_env_var = format!(".{}__{}", type_ident, trait_name);
777 if !static_methods.is_empty() || !consts.is_empty() {
778 lines.push(format!("{} <- new.env(parent = emptyenv())", trait_env_var));
779 lines.push(String::new());
780 }
781
782 for method in &static_methods {
784 let r_name = method.r_method_name();
785 let formals =
787 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
788 let params =
790 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
791
792 lines.push(format!(
793 "#' Static trait method {}::{}()",
794 trait_name, r_name
795 ));
796 let roxygen = RoxygenBuilder::new()
797 .name(format!("{}${}${}", type_str, trait_str, r_name))
798 .rdname(&type_str)
799 .build();
800 lines.extend(roxygen);
801
802 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
804 let call = DotCallBuilder::new(&c_ident).with_args(¶ms).build();
805
806 lines.push(format!(
807 "{}${} <- function({}) {{",
808 trait_env_var, r_name, formals
809 ));
810 lines.extend(trait_method_preamble_lines(method, " "));
811 lines.extend(trait_method_body_lines(&call, " "));
812 lines.push("}".to_string());
813 lines.push(String::new());
814 }
815
816 for trait_const in consts {
818 let const_name = &trait_const.ident;
819 let const_str = const_name.to_string();
820
821 let roxygen = RoxygenBuilder::new()
822 .name(format!("{}${}${}", type_str, trait_str, const_str))
823 .rdname(&type_str)
824 .build();
825 lines.extend(roxygen);
826
827 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
828 let call = DotCallBuilder::new(&c_ident).build();
829
830 lines.push(format!("{}${} <- function() {{", trait_env_var, const_name));
831 lines.push(format!(" {}", call));
832 lines.push("}".to_string());
833 lines.push(String::new());
834 }
835
836 if !static_methods.is_empty() || !consts.is_empty() {
839 lines.push(format!(
840 "attr({}, \"{}\") <- {}",
841 type_ident, trait_name, trait_env_var
842 ));
843 lines.push(String::new());
844 }
845
846 lines.join("\n")
847}
848
849fn generate_trait_r6_r_wrapper(
862 type_ident: &syn::Ident,
863 trait_name: &syn::Ident,
864 methods: &[TraitMethod],
865 consts: &[TraitConst],
866) -> String {
867 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
868
869 let mut lines = Vec::new();
870 let type_str = type_ident.to_string();
871 let trait_str = trait_name.to_string();
872
873 lines.push(format!(
875 "# R6 trait methods for {} implementing {}",
876 type_ident, trait_name
877 ));
878 lines.push(format!(
879 "# Generated by #[miniextendr(r6)] impl {} for {}",
880 trait_name, type_ident
881 ));
882 lines.push("# Note: R6 trait methods are standalone functions".to_string());
883 lines.push(String::new());
884
885 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
887 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
888
889 for method in &instance_methods {
891 let method_name = &method.ident;
892 let fn_name = format!("r6_trait_{}_{}", trait_name, method.r_method_name());
893
894 let formals =
896 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
897 let params =
899 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
900
901 let full_params = if formals.is_empty() {
903 "x".to_string()
904 } else {
905 format!("x, {}", formals)
906 };
907
908 let mut roxygen = RoxygenBuilder::new()
910 .custom(format!(
911 "R6 trait method `{}::{}` for {}",
912 trait_name, method_name, type_str
913 ))
914 .name(&fn_name)
915 .rdname(&type_str)
916 .source(format!(
917 "Generated by miniextendr from `impl {} for {}`",
918 trait_name, type_ident
919 ))
920 .custom(format!("@param x A `{}` object", type_str));
921 for tag in &method.param_tags {
922 roxygen = roxygen.custom(tag.clone());
923 }
924 lines.extend(roxygen.export().build());
925
926 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
928 let call = DotCallBuilder::new(&c_ident)
929 .with_self("x")
930 .with_args(¶ms)
931 .build();
932
933 lines.push(format!("{} <- function({}) {{", fn_name, full_params));
934 lines.push(" .ptr <- x$.__enclos_env__$private$.ptr".to_string());
936 let r6_call = call.replace(", x", ", .ptr");
938 lines.extend(trait_method_preamble_lines(method, " "));
939 lines.extend(trait_method_body_lines(&r6_call, " "));
940 if method.returns_unit() {
942 lines.push(" invisible(x)".to_string());
943 }
944 lines.push("}".to_string());
945 lines.push(String::new());
946 }
947
948 if !static_methods.is_empty() || !consts.is_empty() {
950 lines.push(format!(
951 "{}${} <- new.env(parent = emptyenv())",
952 type_ident, trait_name
953 ));
954 lines.push(String::new());
955 }
956
957 for method in &static_methods {
959 let r_name = method.r_method_name();
960 let formals =
962 crate::r_wrapper_builder::build_r_formals_from_sig(&method.sig, &method.param_defaults);
963 let params =
965 crate::r_wrapper_builder::collect_param_idents(&method.sig.inputs, false, true);
966
967 lines.push(format!(
968 "#' Static trait method {}::{}()",
969 trait_name, r_name
970 ));
971 let roxygen = RoxygenBuilder::new()
972 .name(format!("{}${}${}", type_str, trait_str, r_name))
973 .rdname(&type_str)
974 .build();
975 lines.extend(roxygen);
976
977 let c_ident = method.c_wrapper_ident_string(type_ident, trait_name);
979 let call = DotCallBuilder::new(&c_ident).with_args(¶ms).build();
980
981 lines.push(format!(
982 "{}${}${} <- function({}) {{",
983 type_ident, trait_name, r_name, formals
984 ));
985 lines.extend(trait_method_preamble_lines(method, " "));
986 lines.extend(trait_method_body_lines(&call, " "));
987 lines.push("}".to_string());
988 lines.push(String::new());
989 }
990
991 for trait_const in consts {
993 let const_name = &trait_const.ident;
994 let const_str = const_name.to_string();
995
996 let roxygen = RoxygenBuilder::new()
997 .name(format!("{}${}${}", type_str, trait_str, const_str))
998 .rdname(&type_str)
999 .build();
1000 lines.extend(roxygen);
1001
1002 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
1003 let call = DotCallBuilder::new(&c_ident).build();
1004
1005 lines.push(format!(
1006 "{}${}${} <- function() {{",
1007 type_ident, trait_name, const_name
1008 ));
1009 lines.push(format!(" {}", call));
1010 lines.push("}".to_string());
1011 lines.push(String::new());
1012 }
1013
1014 lines.join("\n")
1015}