1use super::method_context::{TraitMethodContext, trait_namespace_env_var, trait_namespace_target};
9use super::{TraitConst, TraitMethod};
10use crate::miniextendr_impl::ClassSystem;
11use crate::r_class_formatter::emit_s3_generic_guard;
12
13pub(super) struct TraitWrapperOpts {
15 pub(super) class_system: ClassSystem,
17 pub(super) class_has_no_rd: bool,
20 pub(super) internal: bool,
23 pub(super) noexport: bool,
26}
27
28pub(super) fn generate_trait_r_wrapper(
39 type_ident: &syn::Ident,
40 trait_name: &syn::Ident,
41 methods: &[TraitMethod],
42 consts: &[TraitConst],
43 opts: TraitWrapperOpts,
44) -> syn::Result<String> {
45 let TraitWrapperOpts {
46 class_system,
47 class_has_no_rd,
48 internal,
49 noexport,
50 } = opts;
51 let result = match class_system {
52 ClassSystem::Env => generate_trait_env_r_wrapper(type_ident, trait_name, methods, consts)?,
53 ClassSystem::S3 => generate_trait_s3_r_wrapper(type_ident, trait_name, methods, consts),
54 ClassSystem::S4 => generate_trait_s4_r_wrapper(type_ident, trait_name, methods, consts),
55 ClassSystem::S7 => generate_trait_s7_r_wrapper(type_ident, trait_name, methods, consts),
56 ClassSystem::R6 => generate_trait_r6_r_wrapper(type_ident, trait_name, methods, consts),
57 ClassSystem::Vctrs => generate_trait_s3_r_wrapper(type_ident, trait_name, methods, consts),
59 };
60
61 let suppress_all_rd = class_has_no_rd || (noexport && !internal);
69 if suppress_all_rd {
70 if matches!(class_system, ClassSystem::S3 | ClassSystem::Vctrs) {
71 let keep_export = class_has_no_rd;
78 let mut filtered = Vec::new();
79 let mut roxygen_block: Vec<&str> = Vec::new();
80
81 let flush_block = |block: &mut Vec<&str>, out: &mut Vec<String>| {
82 if block.iter().any(|line| line.contains("@method ")) {
83 out.push("#' @noRd".to_string());
84 for &line in block.iter() {
85 if line.contains("@method ")
86 || line.contains("@param ")
87 || (keep_export && line.contains("@export"))
88 {
89 out.push(line.to_string());
90 }
91 }
92 }
93 block.clear();
94 };
95
96 for line in result.lines() {
97 if line.starts_with("#'") {
98 roxygen_block.push(line);
99 continue;
100 }
101
102 if !roxygen_block.is_empty() {
103 flush_block(&mut roxygen_block, &mut filtered);
104 }
105 filtered.push(line.to_string());
106 }
107
108 if !roxygen_block.is_empty() {
109 flush_block(&mut roxygen_block, &mut filtered);
110 }
111
112 Ok(filtered.join("\n"))
113 } else {
114 Ok(result
115 .lines()
116 .filter(|line| !line.starts_with("#'"))
117 .collect::<Vec<_>>()
118 .join("\n"))
119 }
120 } else if internal {
121 let has_export = result.lines().any(|line| line.contains("@export"));
123 let mut processed: Vec<String> = result
124 .lines()
125 .flat_map(|line| {
126 if line.contains("@export") {
127 vec!["#' @keywords internal".to_string()]
128 } else {
129 vec![line.to_string()]
130 }
131 })
132 .collect();
133 if !has_export && let Some(pos) = processed.iter().position(|l| l.starts_with("#'")) {
136 processed.insert(pos, "#' @keywords internal".to_string());
137 }
138 Ok(processed.join("\n"))
139 } else {
140 Ok(result)
141 }
142}
143
144fn generate_trait_env_r_wrapper(
156 type_ident: &syn::Ident,
157 trait_name: &syn::Ident,
158 methods: &[TraitMethod],
159 consts: &[TraitConst],
160) -> syn::Result<String> {
161 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
162
163 let mut lines = Vec::new();
164 let type_str = type_ident.to_string();
165
166 lines.push(format!(
168 "# Trait methods and consts for {} implementing {}",
169 type_ident, trait_name
170 ));
171 lines.push(format!(
172 "# Generated by #[miniextendr] impl {} for {}",
173 trait_name, type_ident
174 ));
175 lines.push(String::new());
176
177 lines.push(format!(
179 "{}${} <- new.env(parent = emptyenv())",
180 type_ident, trait_name
181 ));
182 lines.push(String::new());
183
184 for method in methods {
185 let r_name = method.r_method_name();
186 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
187
188 let target = ctx.namespace_target(ClassSystem::Env);
191
192 let roxygen = RoxygenBuilder::new()
194 .name(target.clone())
195 .rdname(&type_str)
196 .build();
197 lines.extend(roxygen);
198
199 if method.has_self {
201 for input in &method.sig.inputs {
202 if let syn::FnArg::Typed(pt) = input
203 && let syn::Pat::Ident(pat_ident) = pt.pat.as_ref()
204 && pat_ident.ident == "x"
205 {
206 return Err(syn::Error::new_spanned(
207 &pat_ident.ident,
208 "trait instance method parameter cannot be named `x` \
209 (collides with self parameter in env-class dispatch)",
210 ));
211 }
212 }
213 }
214
215 let (full_params, call) = if method.has_self {
217 let fp = if ctx.params.is_empty() {
218 "x".to_string()
219 } else {
220 format!("x, {}", ctx.params)
221 };
222 (fp, ctx.instance_call("x"))
223 } else {
224 (ctx.params.clone(), ctx.static_call())
225 };
226
227 lines.push(format!("{target} <- function({full_params}) {{"));
229 ctx.emit_method_prelude(&mut lines, " ", &r_name);
230 lines.extend(ctx.method_body_lines(&call, ClassSystem::Env));
231 if method.has_self && method.returns_unit() {
232 lines.push(" invisible(x)".to_string());
233 }
234 lines.push("}".to_string());
235
236 if method.has_self {
238 lines.push(format!("attr({target}, \".__mx_instance__\") <- TRUE"));
239 }
240
241 lines.push(String::new());
242 }
243
244 for trait_const in consts {
246 let const_name = &trait_const.ident;
247 let const_str = const_name.to_string();
248 let target = trait_namespace_target(ClassSystem::Env, type_ident, trait_name, &const_str);
249
250 let roxygen = RoxygenBuilder::new()
252 .name(target.clone())
253 .rdname(&type_str)
254 .build();
255 lines.extend(roxygen);
256
257 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
259 let call = DotCallBuilder::new(&c_ident).build();
260
261 lines.push(format!("{target} <- function() {{"));
263 lines.push(format!(" {}", call));
264 lines.push("}".to_string());
265 lines.push(String::new());
266 }
267
268 Ok(lines.join("\n"))
269}
270
271fn generate_trait_s3_r_wrapper(
283 type_ident: &syn::Ident,
284 trait_name: &syn::Ident,
285 methods: &[TraitMethod],
286 consts: &[TraitConst],
287) -> String {
288 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
289
290 let mut lines = Vec::new();
291 let type_str = type_ident.to_string();
292
293 lines.push(format!(
295 "# S3 trait methods for {} implementing {}",
296 type_ident, trait_name
297 ));
298 lines.push(format!(
299 "# Generated by #[miniextendr(s3)] impl {} for {}",
300 trait_name, type_ident
301 ));
302 lines.push(String::new());
303
304 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
306 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
307
308 for method in &instance_methods {
310 let generic_name = method.r_method_name();
311 let s3_method_name = format!("{}.{}", generic_name, type_str);
312 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
313
314 let generic_roxygen = RoxygenBuilder::new()
317 .title(format!("S3 generic for `{}`", generic_name))
318 .custom(format!("S3 generic for `{}`", generic_name))
319 .name(format!("{}.{}", generic_name, type_str))
320 .rdname(&type_str)
321 .custom("@param x An object")
322 .custom("@param ... Additional arguments passed to methods")
323 .source(format!(
324 "Generated by miniextendr from `impl {} for {}`",
325 trait_name, type_ident
326 ))
327 .export()
328 .build();
329 lines.extend(generic_roxygen);
330
331 lines.push(emit_s3_generic_guard(generic_name.as_str()));
333 lines.push(String::new());
334
335 let mut method_roxygen = RoxygenBuilder::new()
337 .rdname(&type_str)
338 .export()
339 .method(&generic_name, &type_str);
340 for tag in &method.param_tags {
341 method_roxygen = method_roxygen.custom(tag.clone());
342 }
343 lines.extend(method_roxygen.build());
344
345 let full_params = if ctx.params.is_empty() {
347 "x, ...".to_string()
348 } else {
349 format!("x, {}, ...", ctx.params)
350 };
351
352 let call = ctx.instance_call("x");
354
355 lines.push(format!(
357 "{} <- function({}) {{",
358 s3_method_name, full_params
359 ));
360 ctx.emit_method_prelude(&mut lines, " ", &generic_name);
361 lines.extend(ctx.method_body_lines(&call, ClassSystem::S3));
362 if method.returns_unit() {
364 lines.push(" invisible(x)".to_string());
365 }
366 lines.push("}".to_string());
367
368 lines.push(format!(
371 "if (inherits(get0(\"{generic_name}\", mode = \"function\"), \"S7_generic\")) {{"
372 ));
373 lines.push(format!(
374 " S7::method({generic_name}, S7::new_S3_class(\"{type_str}\")) <- {s3_method_name}"
375 ));
376 lines.push("}".to_string());
377 lines.push(String::new());
378 }
379
380 if !static_methods.is_empty() || !consts.is_empty() {
382 lines.push(format!(
383 "{}${} <- new.env(parent = emptyenv())",
384 type_ident, trait_name
385 ));
386 lines.push(String::new());
387 }
388
389 for method in &static_methods {
391 let r_name = method.r_method_name();
392 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
393 let target = ctx.namespace_target(ClassSystem::S3);
394
395 lines.push(format!(
397 "#' Static trait method {}::{}()",
398 trait_name, r_name
399 ));
400 let roxygen = RoxygenBuilder::new()
401 .name(target.clone())
402 .rdname(&type_str)
403 .build();
404 lines.extend(roxygen);
405
406 let call = ctx.static_call();
407
408 lines.push(format!("{target} <- function({}) {{", ctx.params));
409 ctx.emit_method_prelude(&mut lines, " ", &r_name);
410 lines.extend(ctx.method_body_lines(&call, ClassSystem::S3));
411 lines.push("}".to_string());
412 lines.push(String::new());
413 }
414
415 for trait_const in consts {
417 let const_name = &trait_const.ident;
418 let const_str = const_name.to_string();
419 let target = trait_namespace_target(ClassSystem::S3, type_ident, trait_name, &const_str);
420
421 let roxygen = RoxygenBuilder::new()
422 .name(target.clone())
423 .rdname(&type_str)
424 .build();
425 lines.extend(roxygen);
426
427 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
428 let call = DotCallBuilder::new(&c_ident).build();
429
430 lines.push(format!("{target} <- function() {{"));
431 lines.push(format!(" {}", call));
432 lines.push("}".to_string());
433 lines.push(String::new());
434 }
435
436 lines.join("\n")
437}
438
439fn generate_trait_s4_r_wrapper(
450 type_ident: &syn::Ident,
451 trait_name: &syn::Ident,
452 methods: &[TraitMethod],
453 consts: &[TraitConst],
454) -> String {
455 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
456
457 let mut lines = Vec::new();
458 let type_str = type_ident.to_string();
459
460 lines.push(format!(
462 "# S4 trait methods for {} implementing {}",
463 type_ident, trait_name
464 ));
465 lines.push(format!(
466 "# Generated by #[miniextendr(s4)] impl {} for {}",
467 trait_name, type_ident
468 ));
469 lines.push(String::new());
470
471 lines.push("#' @importFrom methods setGeneric setMethod".to_string());
475 lines.push(String::new());
476
477 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
479 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
480
481 for method in &instance_methods {
483 let method_name = &method.ident;
484 let generic_name = format!("s4_trait_{}_{}", trait_name, method.r_method_name());
485 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
486
487 let full_params = if ctx.params.is_empty() {
489 "x, ...".to_string()
490 } else {
491 format!("x, {}, ...", ctx.params)
492 };
493
494 let mut generic_roxygen = RoxygenBuilder::new()
498 .custom(format!(
499 "S4 generic for trait method `{}::{}`",
500 trait_name, method_name
501 ))
502 .name(&generic_name)
503 .rdname(&type_str)
504 .source(format!(
505 "Generated by miniextendr from `impl {} for {}`",
506 trait_name, type_ident
507 ))
508 .custom(format!("@param x A `{}` object", type_str))
509 .custom("@param ... Additional arguments passed to methods");
510 for tag in &method.param_tags {
511 generic_roxygen = generic_roxygen.custom(tag.clone());
512 }
513 lines.extend(generic_roxygen.export().build());
514
515 lines.push(format!(
520 "if (!exists(\"{generic_name}\", where = topenv(environment()), inherits = FALSE)) methods::setGeneric(\"{generic_name}\", function(x, ...) standardGeneric(\"{generic_name}\"))"
521 ));
522 lines.push(String::new());
523
524 lines.push(format!("#' @rdname {}", type_str));
526 for tag in &method.param_tags {
527 lines.push(format!("#' {}", tag));
528 }
529 lines.push(format!("#' @exportMethod {}", generic_name));
530
531 lines.push(format!(
532 "methods::setMethod(\"{}\", \"{}\", function({}) {{",
533 generic_name, type_str, full_params
534 ));
535 lines.push(" .ptr <- x@ptr".to_string());
537 let s4_call = ctx.instance_call(".ptr");
538 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
539 lines.extend(ctx.method_body_lines(&s4_call, ClassSystem::S4));
540 if method.returns_unit() {
542 lines.push(" invisible(x)".to_string());
543 }
544 lines.push("})".to_string());
545 lines.push(String::new());
546 }
547
548 for method in &static_methods {
552 let r_name = method.r_method_name();
553 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
554 let fn_name = ctx.namespace_target(ClassSystem::S4);
555
556 lines.push(format!(
558 "#' Static trait method {}::{}() for {}",
559 trait_name, r_name, type_str
560 ));
561 let roxygen = RoxygenBuilder::new()
562 .name(&fn_name)
563 .rdname(&type_str)
564 .export()
565 .build();
566 lines.extend(roxygen);
567
568 let call = ctx.static_call();
569
570 lines.push(format!("{} <- function({}) {{", fn_name, ctx.params));
571 ctx.emit_method_prelude(&mut lines, " ", &r_name);
572 lines.extend(ctx.method_body_lines(&call, ClassSystem::S4));
573 lines.push("}".to_string());
574 lines.push(String::new());
575 }
576
577 for trait_const in consts {
579 let const_name = &trait_const.ident;
580 let const_str = const_name.to_string();
581 let fn_name = trait_namespace_target(ClassSystem::S4, type_ident, trait_name, &const_str);
582
583 let roxygen = RoxygenBuilder::new()
584 .name(&fn_name)
585 .rdname(&type_str)
586 .export()
587 .build();
588 lines.extend(roxygen);
589
590 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
591 let call = DotCallBuilder::new(&c_ident).build();
592
593 lines.push(format!("{} <- function() {{", fn_name));
594 lines.push(format!(" {}", call));
595 lines.push("}".to_string());
596 lines.push(String::new());
597 }
598
599 lines.join("\n")
600}
601
602fn generate_trait_s7_r_wrapper(
612 type_ident: &syn::Ident,
613 trait_name: &syn::Ident,
614 methods: &[TraitMethod],
615 consts: &[TraitConst],
616) -> String {
617 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
618
619 let mut lines = Vec::new();
620 let type_str = type_ident.to_string();
621 let trait_str = trait_name.to_string();
622 let s7_class_var = format!(".s7_class_{}", type_str);
623
624 lines.push(format!(
626 "# S7 trait methods for {} implementing {}",
627 type_ident, trait_name
628 ));
629 lines.push(format!(
630 "# Generated by #[miniextendr(s7)] impl {} for {}",
631 trait_name, type_ident
632 ));
633 lines.push(String::new());
634
635 lines.push("#' @importFrom S7 new_generic method S7_dispatch".to_string());
640 lines.push(format!("{} <- {}", s7_class_var, type_str));
641 lines.push(String::new());
642
643 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
645 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
646
647 for method in &instance_methods {
649 let method_name = &method.ident;
650 let generic_name = format!("s7_trait_{}_{}", trait_name, method.r_method_name());
651 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
652
653 let full_params = if ctx.params.is_empty() {
655 "x, ...".to_string()
656 } else {
657 format!("x, {}, ...", ctx.params)
658 };
659
660 let generic_roxygen = RoxygenBuilder::new()
665 .custom(format!(
666 "S7 generic for trait method `{}::{}`",
667 trait_name, method_name
668 ))
669 .name(&generic_name)
670 .rdname(&type_str)
671 .source(format!(
672 "Generated by miniextendr from `impl {} for {}`",
673 trait_name, type_ident
674 ))
675 .export()
676 .build();
677 lines.extend(generic_roxygen);
678
679 lines.push(format!(
681 "if (!exists(\"{generic_name}\", mode = \"function\")) {{"
682 ));
683 lines.push(format!(
684 " {generic_name} <- S7::new_generic(\"{generic_name}\", \"x\", function(x, ...) S7::S7_dispatch())"
685 ));
686 lines.push("}".to_string());
687 lines.push(String::new());
688
689 lines.push(format!(
691 "S7::method({}, {}) <- function({}) {{",
692 generic_name, s7_class_var, full_params
693 ));
694 lines.push(" .ptr <- x@.ptr".to_string());
696 let s7_call = ctx.instance_call(".ptr");
697 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
698 lines.extend(ctx.method_body_lines(&s7_call, ClassSystem::S7));
699 if method.returns_unit() {
701 lines.push(" invisible(x)".to_string());
702 }
703 lines.push("}".to_string());
704 lines.push(String::new());
705
706 if !method.no_shortcut {
715 let shortcut_name = format!("{}_{}", type_str, method.r_method_name());
716 let shortcut_formals = if ctx.params.is_empty() {
717 "self, ...".to_string()
718 } else {
719 format!("self, {}, ...", ctx.params)
720 };
721 let shortcut_call = ctx.instance_call("self@.ptr");
722
723 lines.extend(crate::miniextendr_impl::s7_class::shortcut_advisory_lines(
728 &method.r_method_name(),
729 &type_str,
730 ));
731 lines.push(format!("#' @param self A `{}` object.", type_str));
732 for tag in &method.param_tags {
733 lines.push(format!("#' {}", tag));
734 }
735 for formal in crate::roxygen::split_r_formals(&shortcut_formals) {
741 let pname = crate::roxygen::formal_name(formal);
742 if pname == "self" {
743 continue;
744 }
745 let documented = crate::roxygen::param_documented(&method.param_tags, pname);
746 if documented {
747 continue;
748 }
749 if pname == "..." {
750 lines.push(
751 "#' @param ... Additional arguments; ignored by the fast-path shortcut."
752 .to_string(),
753 );
754 } else {
755 lines.push(format!("#' @param {} (undocumented)", pname));
756 }
757 }
758 lines.push(format!("#' @name {}", shortcut_name));
759 lines.push(format!("#' @rdname {}", type_str));
760 lines.push(format!(
761 "#' @source Generated by miniextendr from `impl {} for {}` (`{}` shortcut)",
762 trait_name, type_ident, method_name
763 ));
764 lines.push("#' @export".to_string());
765
766 lines.push(format!(
767 "{} <- function({}) {{",
768 shortcut_name, shortcut_formals
769 ));
770 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
771 lines.extend(ctx.method_body_lines(&shortcut_call, ClassSystem::S7));
772 if method.returns_unit() {
774 lines.push(" invisible(self)".to_string());
775 }
776 lines.push("}".to_string());
777 lines.push(String::new());
778 }
779 }
780
781 let trait_env_var = trait_namespace_env_var(type_ident, trait_name);
784 if !static_methods.is_empty() || !consts.is_empty() {
785 lines.push(format!("{} <- new.env(parent = emptyenv())", trait_env_var));
786 lines.push(String::new());
787 }
788
789 for method in &static_methods {
795 let r_name = method.r_method_name();
796 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
797
798 lines.push(format!(
799 "#' Static trait method {}::{}()",
800 trait_name, r_name
801 ));
802 let roxygen = RoxygenBuilder::new()
803 .name(format!("{}${}${}", type_str, trait_str, r_name))
804 .rdname(&type_str)
805 .build();
806 lines.extend(roxygen);
807
808 let call = ctx.static_call();
809
810 lines.push(format!(
811 "{} <- function({}) {{",
812 ctx.namespace_target(ClassSystem::S7),
813 ctx.params
814 ));
815 ctx.emit_method_prelude(&mut lines, " ", &r_name);
816 lines.extend(ctx.method_body_lines(&call, ClassSystem::S7));
817 lines.push("}".to_string());
818 lines.push(String::new());
819 }
820
821 for trait_const in consts {
824 let const_name = &trait_const.ident;
825 let const_str = const_name.to_string();
826
827 let roxygen = RoxygenBuilder::new()
828 .name(format!("{}${}${}", type_str, trait_str, const_str))
829 .rdname(&type_str)
830 .build();
831 lines.extend(roxygen);
832
833 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
834 let call = DotCallBuilder::new(&c_ident).build();
835
836 lines.push(format!(
837 "{} <- function() {{",
838 trait_namespace_target(ClassSystem::S7, type_ident, trait_name, &const_str)
839 ));
840 lines.push(format!(" {}", call));
841 lines.push("}".to_string());
842 lines.push(String::new());
843 }
844
845 if !static_methods.is_empty() || !consts.is_empty() {
848 lines.push(format!(
849 "attr({}, \"{}\") <- {}",
850 type_ident, trait_name, trait_env_var
851 ));
852 lines.push(String::new());
853 }
854
855 lines.join("\n")
856}
857
858fn generate_trait_r6_r_wrapper(
877 type_ident: &syn::Ident,
878 trait_name: &syn::Ident,
879 methods: &[TraitMethod],
880 consts: &[TraitConst],
881) -> String {
882 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
883
884 let mut lines = Vec::new();
885 let type_str = type_ident.to_string();
886
887 lines.push(format!(
889 "# R6 trait methods for {} implementing {}",
890 type_ident, trait_name
891 ));
892 lines.push(format!(
893 "# Generated by #[miniextendr(r6)] impl {} for {}",
894 trait_name, type_ident
895 ));
896 lines.push("# Note: R6 trait methods live in the Type$Trait$method namespace".to_string());
897 lines.push(String::new());
898
899 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
901 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
902
903 if !methods.is_empty() || !consts.is_empty() {
906 lines.push(format!(
907 "{}${} <- new.env(parent = emptyenv())",
908 type_ident, trait_name
909 ));
910 lines.push(String::new());
911 }
912
913 for method in &instance_methods {
915 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
916 let target = ctx.namespace_target(ClassSystem::R6);
917
918 let full_params = if ctx.params.is_empty() {
920 "x".to_string()
921 } else {
922 format!("x, {}", ctx.params)
923 };
924
925 let roxygen = RoxygenBuilder::new()
928 .name(target.clone())
929 .rdname(&type_str)
930 .build();
931 lines.extend(roxygen);
932
933 let call = ctx.instance_call(".ptr");
934
935 lines.push(format!("{target} <- function({full_params}) {{"));
936 lines.push(" .ptr <- x$.__enclos_env__$private$.ptr".to_string());
938 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
939 lines.extend(ctx.method_body_lines(&call, ClassSystem::R6));
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 for method in &static_methods {
950 let r_name = method.r_method_name();
951 let ctx = TraitMethodContext::new(method, type_ident, trait_name);
952 let target = ctx.namespace_target(ClassSystem::R6);
953
954 lines.push(format!(
955 "#' Static trait method {}::{}()",
956 trait_name, r_name
957 ));
958 let roxygen = RoxygenBuilder::new()
959 .name(target.clone())
960 .rdname(&type_str)
961 .build();
962 lines.extend(roxygen);
963
964 let call = ctx.static_call();
965
966 lines.push(format!("{target} <- function({}) {{", ctx.params));
967 ctx.emit_method_prelude(&mut lines, " ", &r_name);
968 lines.extend(ctx.method_body_lines(&call, ClassSystem::R6));
969 lines.push("}".to_string());
970 lines.push(String::new());
971 }
972
973 for trait_const in consts {
975 let const_name = &trait_const.ident;
976 let const_str = const_name.to_string();
977 let target = trait_namespace_target(ClassSystem::R6, type_ident, trait_name, &const_str);
978
979 let roxygen = RoxygenBuilder::new()
980 .name(target.clone())
981 .rdname(&type_str)
982 .build();
983 lines.extend(roxygen);
984
985 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
986 let call = DotCallBuilder::new(&c_ident).build();
987
988 lines.push(format!("{target} <- function() {{"));
989 lines.push(format!(" {}", call));
990 lines.push("}".to_string());
991 lines.push(String::new());
992 }
993
994 lines.join("\n")
995}