1use super::method_context::{
9 TraitMethodContext, s7_trait_member_access, trait_namespace_env_var, trait_namespace_target,
10};
11use super::{TraitConst, TraitMethod};
12use crate::miniextendr_impl::ClassSystem;
13use crate::r_class_formatter::emit_s3_generic_guard;
14
15pub(super) struct TraitWrapperOpts {
17 pub(super) class_system: ClassSystem,
19 pub(super) class_has_no_rd: bool,
22 pub(super) internal: bool,
25 pub(super) noexport: bool,
28 pub(super) impl_preconditions: Option<bool>,
31}
32
33pub(super) fn generate_trait_r_wrapper(
44 type_ident: &syn::Ident,
45 trait_name: &syn::Ident,
46 methods: &[TraitMethod],
47 consts: &[TraitConst],
48 opts: TraitWrapperOpts,
49) -> syn::Result<String> {
50 let TraitWrapperOpts {
51 class_system,
52 class_has_no_rd,
53 internal,
54 noexport,
55 impl_preconditions,
56 } = opts;
57 reject_unsupported_describe_in(methods, class_system)?;
58 check_trait_method_formals(methods, class_system)?;
59 let generate = match class_system {
60 ClassSystem::Env => generate_trait_env_r_wrapper,
61 ClassSystem::S3 | ClassSystem::Vctrs => generate_trait_s3_r_wrapper,
63 ClassSystem::S4 => generate_trait_s4_r_wrapper,
64 ClassSystem::S7 => generate_trait_s7_r_wrapper,
65 ClassSystem::R6 => generate_trait_r6_r_wrapper,
66 };
67 let result = generate(type_ident, trait_name, methods, consts, impl_preconditions);
68
69 let suppress_all_rd = class_has_no_rd || (noexport && !internal);
77 if suppress_all_rd {
78 if #[allow(non_exhaustive_omitted_patterns)] match class_system {
ClassSystem::S3 | ClassSystem::Vctrs => true,
_ => false,
}matches!(class_system, ClassSystem::S3 | ClassSystem::Vctrs) {
79 let keep_export = class_has_no_rd;
86 let mut filtered = Vec::new();
87 let mut roxygen_block: Vec<&str> = Vec::new();
88
89 let flush_block = |block: &mut Vec<&str>, out: &mut Vec<String>| {
90 if block.iter().any(|line| line.contains("@method ")) {
91 out.push("#' @noRd".to_string());
92 for &line in block.iter() {
93 if line.contains("@method ")
94 || line.contains("@param ")
95 || (keep_export && line.contains("@export"))
96 {
97 out.push(line.to_string());
98 }
99 }
100 }
101 block.clear();
102 };
103
104 for line in result.lines() {
105 if line.starts_with("#'") {
106 roxygen_block.push(line);
107 continue;
108 }
109
110 if !roxygen_block.is_empty() {
111 flush_block(&mut roxygen_block, &mut filtered);
112 }
113 filtered.push(line.to_string());
114 }
115
116 if !roxygen_block.is_empty() {
117 flush_block(&mut roxygen_block, &mut filtered);
118 }
119
120 Ok(filtered.join("\n"))
121 } else {
122 Ok(result
123 .lines()
124 .filter(|line| !line.starts_with("#'"))
125 .collect::<Vec<_>>()
126 .join("\n"))
127 }
128 } else if internal {
129 let has_export = result.lines().any(|line| line.contains("@export"));
136 let lines: Vec<&str> = result.lines().collect();
137 let mut processed: Vec<String> = Vec::with_capacity(lines.len());
138 let mut start = 0;
139 while start < lines.len() {
140 let roxygen = lines[start].starts_with("#'");
141 let end = lines[start..]
142 .iter()
143 .position(|line| line.starts_with("#'") != roxygen)
144 .map_or(lines.len(), |len| start + len);
145 let block = &lines[start..end];
146 let registers_s3 = roxygen && block.iter().any(|line| line.contains("@method "));
147 for &line in block {
148 if line.contains("@export") && !(registers_s3 && line.trim_end() == "#' @export") {
149 processed.push("#' @keywords internal".to_string());
150 } else {
151 processed.push(line.to_string());
152 }
153 }
154 start = end;
155 }
156 if !has_export && let Some(pos) = processed.iter().position(|l| l.starts_with("#'")) {
159 processed.insert(pos, "#' @keywords internal".to_string());
160 }
161 Ok(processed.join("\n"))
162 } else {
163 Ok(result)
164 }
165}
166
167const PAGE_TAGS: &[&str] = &[
179 "describeIn",
180 "rdname",
181 "name",
182 "order",
183 "inheritParams",
184 "inherit",
185 "inheritDotParams",
186];
187
188fn forwarded_body_tag(tag: &str) -> bool {
197 crate::roxygen::forwarded_member_tag(tag)
198 && crate::roxygen::roxygen_tag_name(tag)
199 .is_none_or(|name| name != "title" && !PAGE_TAGS.contains(&name))
200}
201
202fn namespace_member_body_lines(
210 ctx: &TraitMethodContext<'_>,
211 type_str: &str,
212 label: &str,
213) -> Vec<String> {
214 let choice_docs = ctx.choice_param_docs();
215 crate::r_class_formatter::describe_params_lines(
216 &ctx.method.doc_tags,
217 forwarded_body_tag,
218 &ctx.params,
219 type_str,
220 Some(&choice_docs),
221 label,
222 )
223}
224
225fn param_doc_tags(ctx: &TraitMethodContext<'_>, type_str: &str) -> Vec<String> {
233 let method = ctx.method;
234 let mut tags: Vec<String> = param_tags(method).cloned().collect();
235 if params_documented_elsewhere(method, type_str) {
236 return tags;
237 }
238 let choice_docs = ctx.choice_param_docs();
239 for formal in crate::roxygen::split_r_formals(&ctx.params) {
240 let pname = crate::roxygen::formal_name(formal);
241 if pname == "..." || crate::roxygen::param_documented(&method.doc_tags, pname) {
242 continue;
243 }
244 let body = choice_docs
245 .get(pname)
246 .map_or("(undocumented)", String::as_str);
247 tags.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("@param {0} {1}", pname, body))
})format!("@param {pname} {body}"));
248 }
249 tags
250}
251
252fn param_tags(method: &TraitMethod) -> impl Iterator<Item = &String> {
254 method
255 .doc_tags
256 .iter()
257 .filter(|tag| crate::roxygen::roxygen_tag_name(tag) == Some("param"))
258}
259
260fn method_page<'a>(method: &'a TraitMethod, type_str: &'a str) -> &'a str {
265 crate::roxygen::method_page(&method.doc_tags, type_str)
266}
267
268fn joins_author_topic(method: &TraitMethod, type_str: &str) -> bool {
274 crate::roxygen::joins_author_topic(&method.doc_tags, Some(type_str))
275}
276
277fn params_documented_elsewhere(method: &TraitMethod, type_str: &str) -> bool {
281 crate::roxygen::params_documented_elsewhere(&method.doc_tags, Some(type_str))
282}
283
284fn describes_in(method: &TraitMethod) -> bool {
287 crate::roxygen::describe_in_topic(&method.doc_tags).is_some()
288}
289
290fn own_block_page_lines(
304 method: &TraitMethod,
305 type_str: &str,
306 default_name: Option<&str>,
307 split_title: Option<&str>,
308) -> Vec<String> {
309 let tags = &method.doc_tags;
310 let mut lines = Vec::new();
311 if !describes_in(method) {
312 let has = |tag: &str| crate::roxygen::has_roxygen_tag(tags, tag);
313 if has("rdname")
314 && let Some(title) = split_title
315 {
316 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' @title {0}", title))
})format!("#' @title {title}"));
317 }
318 if !has("name")
319 && let Some(name) = default_name
320 {
321 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' @name {0}", name))
})format!("#' @name {name}"));
322 }
323 if !has("rdname") {
324 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' @rdname {0}", type_str))
})format!("#' @rdname {type_str}"));
325 }
326 }
327 let page_tags: Vec<String> = tags
328 .iter()
329 .filter(|tag| {
330 crate::roxygen::roxygen_tag_name(tag).is_some_and(|name| PAGE_TAGS.contains(&name))
331 })
332 .cloned()
333 .collect();
334 crate::roxygen::push_roxygen_tags(&mut lines, &page_tags);
335 crate::roxygen::push_order_after_topic_blocks(&mut lines, tags, type_str);
336 lines
337}
338
339fn push_param_tags(lines: &mut Vec<String>, method: &TraitMethod) {
342 let params: Vec<String> = param_tags(method).cloned().collect();
343 crate::roxygen::push_roxygen_tags(lines, ¶ms);
344}
345
346fn check_trait_method_formals(
353 methods: &[TraitMethod],
354 class_system: ClassSystem,
355) -> syn::Result<()> {
356 let system = match class_system {
357 ClassSystem::Env => "Env-class",
358 ClassSystem::R6 => "R6",
359 ClassSystem::S3 => "S3",
360 ClassSystem::S4 => "S4",
361 ClassSystem::S7 => "S7",
362 ClassSystem::Vctrs => "vctrs",
363 };
364 for method in methods {
365 if (method.generic.is_some() || method.receiver.is_some() || method.no_dispatch_dots)
366 && (!method.has_self || !#[allow(non_exhaustive_omitted_patterns)] match class_system {
ClassSystem::S3 | ClassSystem::S4 => true,
_ => false,
}matches!(class_system, ClassSystem::S3 | ClassSystem::S4))
367 {
368 return Err(syn::Error::new_spanned(
369 &method.sig,
370 "`generic`, `receiver` and `no_dots` apply only to S3/S4 instance methods",
371 ));
372 }
373 if (method.receiver.is_some() || method.no_dispatch_dots) && method.generic.is_none() {
374 return Err(syn::Error::new_spanned(
375 &method.sig,
376 "`receiver` and `no_dots` require an explicit S3/S4 `generic = \"...\"`",
377 ));
378 }
379 let mut reserved = Vec::new();
380 if method.has_self && !#[allow(non_exhaustive_omitted_patterns)] match class_system {
ClassSystem::Env | ClassSystem::R6 => true,
_ => false,
}matches!(class_system, ClassSystem::Env | ClassSystem::R6) {
381 reserved.push((
382 method.receiver.as_deref().unwrap_or("x").to_string(),
383 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("is the receiver of the generated {0} trait method",
system))
})format!("is the receiver of the generated {system} trait method"),
384 ));
385 }
386 crate::r_wrapper_builder::check_r_formals(
387 &method.sig.inputs,
388 &Default::default(),
389 &reserved,
390 )?;
391 }
392 Ok(())
393}
394
395fn reject_unsupported_describe_in(
416 methods: &[TraitMethod],
417 class_system: ClassSystem,
418) -> syn::Result<()> {
419 let system = match class_system {
420 ClassSystem::S3 => "S3",
421 ClassSystem::Vctrs => "vctrs",
422 ClassSystem::S4 => "S4",
423 ClassSystem::S7 => "S7",
424 ClassSystem::Env => "Env",
425 ClassSystem::R6 => "R6",
426 };
427 let mut errors: Option<syn::Error> = None;
428 for method in methods.iter().filter(|m| describes_in(m)) {
429 let supported = match class_system {
430 ClassSystem::S3 | ClassSystem::Vctrs => method.has_self,
431 ClassSystem::S4 => true,
432 ClassSystem::S7 => method.has_self && !method.no_shortcut,
433 ClassSystem::Env | ClassSystem::R6 => false,
434 };
435 if supported {
436 continue;
437 }
438 let (kind, qualifier) = if !method.has_self {
439 ("static method", "")
440 } else if #[allow(non_exhaustive_omitted_patterns)] match class_system {
ClassSystem::S7 => true,
_ => false,
}matches!(class_system, ClassSystem::S7) {
441 ("instance method", " without a fast-path shortcut")
442 } else {
443 ("instance method", "")
444 };
445 let error = syn::Error::new_spanned(
446 &method.ident,
447 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`@describeIn` is not supported on the {1} trait {2} `{0}`{3}: its R wrapper is not an R function roxygen2 can list in the destination\'s \"Functions\" section, so roxygen2 would drop the block. Use `@rdname <topic>` to document it on that page instead. On trait impls `@describeIn` works on S3, vctrs and S4 instance methods, on S4 static methods, and on S7 instance methods with a fast-path shortcut.",
method.ident, system, kind, qualifier))
})format!(
448 "`@describeIn` is not supported on the {system} trait {kind} `{}`{qualifier}: \
449 its R wrapper is not an R function roxygen2 can list in the destination's \
450 \"Functions\" section, so roxygen2 would drop the block. Use `@rdname <topic>` \
451 to document it on that page instead. On trait impls `@describeIn` works on \
452 S3, vctrs and S4 instance methods, on S4 static methods, and on S7 instance \
453 methods with a fast-path shortcut.",
454 method.ident,
455 ),
456 );
457 match errors.as_mut() {
458 Some(all) => all.combine(error),
459 None => errors = Some(error),
460 }
461 }
462 errors.map_or(Ok(()), Err)
463}
464
465fn generate_trait_env_r_wrapper(
479 type_ident: &syn::Ident,
480 trait_name: &syn::Ident,
481 methods: &[TraitMethod],
482 consts: &[TraitConst],
483 impl_preconditions: Option<bool>,
484) -> String {
485 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
486
487 let mut lines = Vec::new();
488 let type_str = type_ident.to_string();
489
490 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# Trait methods and consts for {0} implementing {1}",
type_ident, trait_name))
})format!(
492 "# Trait methods and consts for {} implementing {}",
493 type_ident, trait_name
494 ));
495 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# Generated by #[miniextendr] impl {0} for {1}",
trait_name, type_ident))
})format!(
496 "# Generated by #[miniextendr] impl {} for {}",
497 trait_name, type_ident
498 ));
499 lines.push(String::new());
500
501 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1} <- new.env(parent = emptyenv())",
type_ident, trait_name))
})format!(
503 "{}${} <- new.env(parent = emptyenv())",
504 type_ident, trait_name
505 ));
506 lines.push(String::new());
507
508 for method in methods {
509 let r_name = method.r_method_name();
510 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
511 .with_impl_preconditions(impl_preconditions);
512
513 let target = ctx.namespace_target(ClassSystem::Env);
516 let symbol = ctx.namespace_symbol(ClassSystem::Env);
517
518 lines.extend(namespace_member_body_lines(&ctx, &type_str, &target));
522 lines.extend(own_block_page_lines(
523 method,
524 &type_str,
525 Some(&target),
526 Some(&target),
527 ));
528
529 let (full_params, call) = if method.has_self {
531 let fp = if ctx.params.is_empty() {
532 ".mx_self".to_string()
533 } else {
534 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".mx_self, {0}", ctx.params))
})format!(".mx_self, {}", ctx.params)
535 };
536 (fp, ctx.instance_call(".mx_self"))
537 } else {
538 (ctx.params.clone(), ctx.static_call())
539 };
540
541 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{", symbol,
full_params))
})format!("{symbol} <- function({full_params}) {{"));
543 ctx.emit_method_prelude(&mut lines, " ", &r_name);
544 lines.extend(ctx.method_body_lines(&call, ClassSystem::Env));
545 if method.has_self {
546 finish_instance_body(&mut lines, method, ".mx_self");
547 }
548 lines.push("}".to_string());
549
550 if method.has_self {
552 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("attr({0}, \".__mx_instance__\") <- TRUE",
symbol))
})format!("attr({symbol}, \".__mx_instance__\") <- TRUE"));
553 }
554
555 lines.push(String::new());
556 }
557
558 for trait_const in consts {
560 let const_name = &trait_const.ident;
561 let const_str = const_name.to_string();
562 let target = trait_namespace_target(ClassSystem::Env, type_ident, trait_name, &const_str);
563
564 let roxygen = RoxygenBuilder::new()
566 .name(target.clone())
567 .rdname(&type_str)
568 .build();
569 lines.extend(roxygen);
570
571 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
573 let call = DotCallBuilder::new(&c_ident).build();
574
575 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function() {{", target))
})format!("{target} <- function() {{"));
577 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", call))
})format!(" {}", call));
578 lines.push("}".to_string());
579 lines.push(String::new());
580 }
581
582 lines.join("\n")
583}
584
585fn generate_trait_s3_r_wrapper(
598 type_ident: &syn::Ident,
599 trait_name: &syn::Ident,
600 methods: &[TraitMethod],
601 consts: &[TraitConst],
602 impl_preconditions: Option<bool>,
603) -> String {
604 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
605
606 let mut lines = Vec::new();
607 let type_str = type_ident.to_string();
608
609 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# S3 trait methods for {0} implementing {1}",
type_ident, trait_name))
})format!(
611 "# S3 trait methods for {} implementing {}",
612 type_ident, trait_name
613 ));
614 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# Generated by #[miniextendr(s3)] impl {0} for {1}",
trait_name, type_ident))
})format!(
615 "# Generated by #[miniextendr(s3)] impl {} for {}",
616 trait_name, type_ident
617 ));
618 lines.push(String::new());
619
620 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
622 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
623
624 for method in &instance_methods {
628 let qualified_generic = method
629 .generic
630 .clone()
631 .unwrap_or_else(|| method.r_method_name());
632 let (generic_package, generic_name) = crate::naming::split_generic(&qualified_generic);
633 let receiver = method.receiver.as_deref().unwrap_or("x");
634 let s3_method_name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.{1}", generic_name, type_str))
})format!("{}.{}", generic_name, type_str);
635 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
636 .with_impl_preconditions(impl_preconditions)
637 .with_call_attribution(crate::r_wrapper_builder::CallAttribution::Generic);
638
639 if method.generic.is_none() {
640 let tags = &method.doc_tags;
652 let generic_roxygen = RoxygenBuilder::new();
653 let generic_roxygen =
654 if crate::roxygen::has_roxygen_tag(tags, "rdname") || describes_in(method) {
655 generic_roxygen
656 } else {
657 generic_roxygen.title(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("S3 generic for `{0}`",
generic_name))
})format!("S3 generic for `{}`", generic_name))
658 };
659 let generic_roxygen = generic_roxygen
660 .name(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.{1}", generic_name, type_str))
})format!("{}.{}", generic_name, type_str))
661 .rdname(method_page(method, &type_str));
662 let generic_roxygen = if joins_author_topic(method, &type_str) {
665 generic_roxygen
666 } else {
667 generic_roxygen
668 .description(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("S3 generic for `{0}`",
generic_name))
})format!("S3 generic for `{}`", generic_name))
669 .custom("@param x An object")
670 .custom("@param ... Additional arguments passed to methods")
671 };
672 let generic_roxygen = if crate::roxygen::orders_after_topic_blocks(tags, &type_str) {
673 generic_roxygen.custom(crate::roxygen::ORDER_AFTER_TOPIC_BLOCKS)
674 } else {
675 generic_roxygen
676 };
677 let generic_roxygen = generic_roxygen
678 .source(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Generated by miniextendr from `impl {0} for {1}`",
trait_name, type_ident))
})format!(
679 "Generated by miniextendr from `impl {} for {}`",
680 trait_name, type_ident
681 ))
682 .custom(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("@export {0}", generic_name))
})format!("@export {}", generic_name))
687 .build();
688 lines.extend(generic_roxygen);
689
690 lines.push(emit_s3_generic_guard(generic_name));
692 lines.push(String::new());
693 }
694
695 let full_params = crate::r_class_formatter::instance_formals(
697 &ctx.params,
698 receiver,
699 !method.no_dispatch_dots,
700 );
701
702 lines.extend(own_block_page_lines(
711 method,
712 &type_str,
713 None,
714 Some(&s3_method_name),
715 ));
716 let mut method_roxygen = match generic_package {
719 Some(package) => RoxygenBuilder::new().custom(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("@exportS3Method {0}::{1} {2}",
package, generic_name, type_str))
})format!(
720 "@exportS3Method {package}::{generic_name} {type_str}"
721 )),
722 None => RoxygenBuilder::new().export(),
723 }
724 .method(generic_name, &type_str);
725 if method.generic.is_some() && !joins_author_topic(method, &type_str) {
726 method_roxygen = method_roxygen.custom(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("@param {0} An object", receiver))
})format!("@param {receiver} An object"));
727 if !method.no_dispatch_dots {
728 method_roxygen =
729 method_roxygen.custom("@param ... Additional arguments passed to methods");
730 }
731 }
732 for tag in param_doc_tags(&ctx, &type_str) {
733 method_roxygen = method_roxygen.custom(tag);
734 }
735 lines.extend(method_roxygen.build());
736
737 let call = ctx.instance_call(receiver);
739
740 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{",
crate::naming::r_def_name(&s3_method_name), full_params))
})format!(
742 "{} <- function({}) {{",
743 crate::naming::r_def_name(&s3_method_name),
744 full_params
745 ));
746 ctx.emit_method_prelude(&mut lines, " ", generic_name);
747 lines.extend(ctx.method_body_lines(&call, ClassSystem::S3));
748 finish_instance_body(&mut lines, method, receiver);
749 lines.push("}".to_string());
750
751 if generic_package.is_none() {
756 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("if (inherits(get0(\"{0}\", mode = \"function\"), \"S7_generic\")) {{",
generic_name))
})format!(
757 "if (inherits(get0(\"{generic_name}\", mode = \"function\"), \"S7_generic\")) {{"
758 ));
759 let generic_symbol = crate::naming::r_def_name(generic_name);
762 let method_symbol = crate::naming::r_def_name(&s3_method_name);
763 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" S7::method({0}, S7::new_S3_class(\"{1}\")) <- {2}",
generic_symbol, type_str, method_symbol))
})format!(
764 " S7::method({generic_symbol}, S7::new_S3_class(\"{type_str}\")) <- {method_symbol}"
765 ));
766 lines.push("}".to_string());
767 }
768 lines.push(String::new());
769 }
770
771 if !static_methods.is_empty() || !consts.is_empty() {
773 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1} <- new.env(parent = emptyenv())",
type_ident, trait_name))
})format!(
774 "{}${} <- new.env(parent = emptyenv())",
775 type_ident, trait_name
776 ));
777 lines.push(String::new());
778 }
779
780 for method in &static_methods {
782 let r_name = method.r_method_name();
783 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
784 .with_impl_preconditions(impl_preconditions);
785 let target = ctx.namespace_target(ClassSystem::S3);
786
787 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' Static trait method {0}::{1}()",
trait_name, r_name))
})format!(
790 "#' Static trait method {}::{}()",
791 trait_name, r_name
792 ));
793 lines.extend(namespace_member_body_lines(&ctx, &type_str, &target));
794 lines.extend(own_block_page_lines(method, &type_str, Some(&target), None));
795
796 let call = ctx.static_call();
797
798 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{",
ctx.namespace_symbol(ClassSystem::S3), ctx.params))
})format!(
799 "{} <- function({}) {{",
800 ctx.namespace_symbol(ClassSystem::S3),
801 ctx.params
802 ));
803 ctx.emit_method_prelude(&mut lines, " ", &r_name);
804 lines.extend(ctx.method_body_lines(&call, ClassSystem::S3));
805 lines.push("}".to_string());
806 lines.push(String::new());
807 }
808
809 for trait_const in consts {
811 let const_name = &trait_const.ident;
812 let const_str = const_name.to_string();
813 let target = trait_namespace_target(ClassSystem::S3, type_ident, trait_name, &const_str);
814
815 let roxygen = RoxygenBuilder::new()
816 .name(target.clone())
817 .rdname(&type_str)
818 .build();
819 lines.extend(roxygen);
820
821 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
822 let call = DotCallBuilder::new(&c_ident).build();
823
824 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function() {{", target))
})format!("{target} <- function() {{"));
825 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", call))
})format!(" {}", call));
826 lines.push("}".to_string());
827 lines.push(String::new());
828 }
829
830 lines.join("\n")
831}
832
833fn generate_trait_s4_r_wrapper(
844 type_ident: &syn::Ident,
845 trait_name: &syn::Ident,
846 methods: &[TraitMethod],
847 consts: &[TraitConst],
848 impl_preconditions: Option<bool>,
849) -> String {
850 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
851
852 let mut lines = Vec::new();
853 let type_str = type_ident.to_string();
854
855 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# S4 trait methods for {0} implementing {1}",
type_ident, trait_name))
})format!(
857 "# S4 trait methods for {} implementing {}",
858 type_ident, trait_name
859 ));
860 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# Generated by #[miniextendr(s4)] impl {0} for {1}",
trait_name, type_ident))
})format!(
861 "# Generated by #[miniextendr(s4)] impl {} for {}",
862 trait_name, type_ident
863 ));
864 lines.push(String::new());
865
866 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
868 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
869
870 if !instance_methods.is_empty() {
879 lines.push("#' @importFrom methods setGeneric setMethod".to_string());
880 lines.push(String::new());
881 }
882
883 for method in &instance_methods {
885 let generic_name = method
886 .generic
887 .clone()
888 .unwrap_or_else(|| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("s4_trait_{0}_{1}", trait_name,
method.r_method_name()))
})format!("s4_trait_{}_{}", trait_name, method.r_method_name()));
889 let receiver = method.receiver.as_deref().unwrap_or("x");
890 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
891 .with_impl_preconditions(impl_preconditions);
892
893 let full_params = crate::r_class_formatter::instance_formals(
895 &ctx.params,
896 receiver,
897 !method.no_dispatch_dots,
898 );
899
900 if method.generic.is_none() {
901 let mut generic_roxygen = RoxygenBuilder::new()
906 .description(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("S4 generic for trait method `{0}::{1}`",
trait_name, method.r_method_name()))
})format!(
907 "S4 generic for trait method `{}::{}`",
908 trait_name,
909 method.r_method_name()
910 ))
911 .name(&generic_name)
912 .rdname(&type_str)
913 .source(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Generated by miniextendr from `impl {0} for {1}`",
trait_name, type_ident))
})format!(
914 "Generated by miniextendr from `impl {} for {}`",
915 trait_name, type_ident
916 ))
917 .custom(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("@param x A `{0}` object",
type_str))
})format!("@param x A `{}` object", type_str))
918 .custom("@param ... Additional arguments passed to methods");
919 for tag in param_tags(method) {
920 generic_roxygen = generic_roxygen.custom(tag.clone());
921 }
922 lines.extend(generic_roxygen.export().build());
923 }
924 lines.push(crate::miniextendr_impl::s4_class::s4_generic_guard(
927 &generic_name,
928 method.generic.is_some(),
929 receiver,
930 ));
931 lines.push(String::new());
932
933 lines.extend(own_block_page_lines(
938 method,
939 &type_str,
940 None,
941 Some(&generic_name),
942 ));
943 if method.generic.is_some() && !joins_author_topic(method, &type_str) {
944 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' @param {0} A `{1}` object",
receiver, type_str))
})format!("#' @param {receiver} A `{type_str}` object"));
945 if !method.no_dispatch_dots {
946 lines.push("#' @param ... Additional arguments passed to methods".to_string());
947 }
948 }
949 push_param_tags(&mut lines, method);
950 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' @exportMethod {0}",
generic_name))
})format!("#' @exportMethod {}", generic_name));
951
952 let signature = if method.receiver.is_some() {
953 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("c({0} = \"{1}\")", receiver,
type_str))
})format!("c({receiver} = \"{type_str}\")")
954 } else {
955 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\"{0}\"", type_str))
})format!("\"{type_str}\"")
956 };
957 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("methods::setMethod(\"{0}\", {1}, function({2}) {{",
generic_name, signature, full_params))
})format!(
958 "methods::setMethod(\"{generic_name}\", {signature}, function({full_params}) {{"
959 ));
960 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" .ptr <- {0}@ptr", receiver))
})format!(" .ptr <- {receiver}@ptr"));
962 let s4_call = ctx.instance_call(".ptr");
963 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
964 lines.extend(ctx.method_body_lines(&s4_call, ClassSystem::S4));
965 finish_instance_body(&mut lines, method, receiver);
966 lines.push("})".to_string());
967 lines.push(String::new());
968 }
969
970 for method in &static_methods {
974 let r_name = method.r_method_name();
975 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
976 .with_impl_preconditions(impl_preconditions);
977 let fn_name = ctx.namespace_target(ClassSystem::S4);
978
979 if !describes_in(method) {
983 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' Static trait method {0}::{1}() for {2}",
trait_name, r_name, type_str))
})format!(
984 "#' Static trait method {}::{}() for {}",
985 trait_name, r_name, type_str
986 ));
987 }
988 lines.extend(own_block_page_lines(
989 method,
990 &type_str,
991 Some(&fn_name),
992 None,
993 ));
994 crate::roxygen::push_roxygen_tags(&mut lines, ¶m_doc_tags(&ctx, &type_str));
998 lines.push("#' @export".to_string());
999
1000 let call = ctx.static_call();
1001
1002 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{",
ctx.namespace_symbol(ClassSystem::S4), ctx.params))
})format!(
1003 "{} <- function({}) {{",
1004 ctx.namespace_symbol(ClassSystem::S4),
1005 ctx.params
1006 ));
1007 ctx.emit_method_prelude(&mut lines, " ", &r_name);
1008 lines.extend(ctx.method_body_lines(&call, ClassSystem::S4));
1009 lines.push("}".to_string());
1010 lines.push(String::new());
1011 }
1012
1013 for trait_const in consts {
1015 let const_name = &trait_const.ident;
1016 let const_str = const_name.to_string();
1017 let fn_name = trait_namespace_target(ClassSystem::S4, type_ident, trait_name, &const_str);
1018
1019 let roxygen = RoxygenBuilder::new()
1020 .name(&fn_name)
1021 .rdname(&type_str)
1022 .export()
1023 .build();
1024 lines.extend(roxygen);
1025
1026 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
1027 let call = DotCallBuilder::new(&c_ident).build();
1028
1029 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function() {{", fn_name))
})format!("{} <- function() {{", fn_name));
1030 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", call))
})format!(" {}", call));
1031 lines.push("}".to_string());
1032 lines.push(String::new());
1033 }
1034
1035 lines.join("\n")
1036}
1037
1038fn generate_trait_s7_r_wrapper(
1049 type_ident: &syn::Ident,
1050 trait_name: &syn::Ident,
1051 methods: &[TraitMethod],
1052 consts: &[TraitConst],
1053 impl_preconditions: Option<bool>,
1054) -> String {
1055 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
1056
1057 let mut lines = Vec::new();
1058 let type_str = type_ident.to_string();
1059 let s7_class_var = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".s7_class_{0}", type_str))
})format!(".s7_class_{}", type_str);
1060
1061 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# S7 trait methods for {0} implementing {1}",
type_ident, trait_name))
})format!(
1063 "# S7 trait methods for {} implementing {}",
1064 type_ident, trait_name
1065 ));
1066 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# Generated by #[miniextendr(s7)] impl {0} for {1}",
trait_name, type_ident))
})format!(
1067 "# Generated by #[miniextendr(s7)] impl {} for {}",
1068 trait_name, type_ident
1069 ));
1070 lines.push(String::new());
1071
1072 lines.push("#' @importFrom S7 new_generic method S7_dispatch".to_string());
1077 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- {1}", s7_class_var,
type_str))
})format!("{} <- {}", s7_class_var, type_str));
1078 lines.push(String::new());
1079
1080 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
1082 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
1083
1084 for method in &instance_methods {
1086 let generic_name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("s7_trait_{0}_{1}", trait_name,
method.r_method_name()))
})format!("s7_trait_{}_{}", trait_name, method.r_method_name());
1087 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
1088 .with_impl_preconditions(impl_preconditions);
1089
1090 let full_params = if ctx.params.is_empty() {
1092 "x, ...".to_string()
1093 } else {
1094 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("x, {0}, ...", ctx.params))
})format!("x, {}, ...", ctx.params)
1095 };
1096
1097 let generic_roxygen = RoxygenBuilder::new()
1103 .description(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("S7 generic for trait method `{0}::{1}`",
trait_name, method.r_method_name()))
})format!(
1104 "S7 generic for trait method `{}::{}`",
1105 trait_name,
1106 method.r_method_name()
1107 ))
1108 .name(&generic_name)
1109 .rdname(&type_str)
1110 .source(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Generated by miniextendr from `impl {0} for {1}`",
trait_name, type_ident))
})format!(
1111 "Generated by miniextendr from `impl {} for {}`",
1112 trait_name, type_ident
1113 ))
1114 .export()
1115 .build();
1116 lines.extend(generic_roxygen);
1117
1118 let generic_symbol = crate::naming::r_def_name(&generic_name);
1122 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("if (!exists(\"{0}\", mode = \"function\")) {{",
generic_name))
})format!(
1123 "if (!exists(\"{generic_name}\", mode = \"function\")) {{"
1124 ));
1125 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0} <- S7::new_generic(\"{1}\", \"x\", function(x, ...) S7::S7_dispatch())",
generic_symbol, generic_name))
})format!(
1126 " {generic_symbol} <- S7::new_generic(\"{generic_name}\", \"x\", function(x, ...) S7::S7_dispatch())"
1127 ));
1128 lines.push("}".to_string());
1129 lines.push(String::new());
1130
1131 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("S7::method({0}, {1}) <- function({2}) {{",
generic_symbol, s7_class_var, full_params))
})format!(
1133 "S7::method({}, {}) <- function({}) {{",
1134 generic_symbol, s7_class_var, full_params
1135 ));
1136 lines.push(" .ptr <- x@.ptr".to_string());
1138 let s7_call = ctx.instance_call(".ptr");
1139 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
1140 lines.extend(ctx.method_body_lines(&s7_call, ClassSystem::S7));
1141 finish_instance_body(&mut lines, method, "x");
1142 lines.push("}".to_string());
1143 lines.push(String::new());
1144
1145 let shortcut_name = if method.no_shortcut {
1155 None
1156 } else {
1157 crate::miniextendr_impl::s7_class::s7_shortcut_name(&type_str, &method.r_method_name())
1158 };
1159 if let Some(shortcut_name) = shortcut_name {
1160 let shortcut_formals = if ctx.params.is_empty() {
1161 ".mx_self, ...".to_string()
1162 } else {
1163 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".mx_self, {0}, ...", ctx.params))
})format!(".mx_self, {}, ...", ctx.params)
1164 };
1165 let shortcut_call = ctx.instance_call(".mx_self@.ptr");
1166
1167 let joined = joins_author_topic(method, &type_str);
1180 let advisory = crate::miniextendr_impl::s7_class::shortcut_advisory_lines(
1181 &method.r_method_name(),
1182 &type_str,
1183 );
1184 let advisory_len = if joined { 1 } else { advisory.len() };
1185 lines.extend(advisory.into_iter().take(advisory_len));
1186 if !joined {
1187 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' @param .mx_self A `{0}` object.",
type_str))
})format!("#' @param .mx_self A `{}` object.", type_str));
1188 }
1189 push_param_tags(&mut lines, method);
1190 let fillers = !params_documented_elsewhere(method, &type_str);
1199 let choice_docs = ctx.choice_param_docs();
1200 for formal in crate::roxygen::split_r_formals(&shortcut_formals) {
1201 let pname = crate::roxygen::formal_name(formal);
1202 if joined || pname == ".mx_self" {
1203 continue;
1204 }
1205 let documented = crate::roxygen::param_documented(&method.doc_tags, pname);
1206 if documented {
1207 continue;
1208 }
1209 if pname == "..." {
1210 lines.push(
1211 "#' @param ... Additional arguments; ignored by the fast-path shortcut."
1212 .to_string(),
1213 );
1214 } else if fillers {
1215 let body = choice_docs
1216 .get(pname)
1217 .map_or("(undocumented)", String::as_str);
1218 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' @param {0} {1}", pname, body))
})format!("#' @param {pname} {body}"));
1219 }
1220 }
1221 lines.extend(own_block_page_lines(
1222 method,
1223 &type_str,
1224 Some(&shortcut_name),
1225 None,
1226 ));
1227 lines.extend(crate::roxygen::source_tag(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Generated by miniextendr from `impl {0} for {1}` (`{2}` shortcut)",
trait_name, type_ident, method.r_method_name()))
})format!(
1228 "Generated by miniextendr from `impl {} for {}` (`{}` shortcut)",
1229 trait_name,
1230 type_ident,
1231 method.r_method_name()
1232 )));
1233 lines.push("#' @export".to_string());
1234
1235 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{",
shortcut_name, shortcut_formals))
})format!(
1236 "{} <- function({}) {{",
1237 shortcut_name, shortcut_formals
1238 ));
1239 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
1240 lines.extend(ctx.method_body_lines(&shortcut_call, ClassSystem::S7));
1241 finish_instance_body(&mut lines, method, ".mx_self");
1242 lines.push("}".to_string());
1243 lines.push(String::new());
1244 }
1245 }
1246
1247 let trait_env_var = trait_namespace_env_var(type_ident, trait_name);
1250 if !static_methods.is_empty() || !consts.is_empty() {
1251 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- new.env(parent = emptyenv())",
trait_env_var))
})format!("{} <- new.env(parent = emptyenv())", trait_env_var));
1252 lines.push(String::new());
1253 }
1254
1255 for method in &static_methods {
1260 let r_name = method.r_method_name();
1261 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
1262 .with_impl_preconditions(impl_preconditions);
1263
1264 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' Static trait method {0}::{1}()",
trait_name, r_name))
})format!(
1265 "#' Static trait method {}::{}()",
1266 trait_name, r_name
1267 ));
1268 let name = s7_trait_member_access(type_ident, trait_name, &r_name);
1269 lines.extend(namespace_member_body_lines(&ctx, &type_str, &name));
1270 lines.extend(own_block_page_lines(method, &type_str, Some(&name), None));
1271
1272 let call = ctx.static_call();
1273
1274 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{",
ctx.namespace_symbol(ClassSystem::S7), ctx.params))
})format!(
1275 "{} <- function({}) {{",
1276 ctx.namespace_symbol(ClassSystem::S7),
1277 ctx.params
1278 ));
1279 ctx.emit_method_prelude(&mut lines, " ", &r_name);
1280 lines.extend(ctx.method_body_lines(&call, ClassSystem::S7));
1281 lines.push("}".to_string());
1282 lines.push(String::new());
1283 }
1284
1285 for trait_const in consts {
1288 let const_name = &trait_const.ident;
1289 let const_str = const_name.to_string();
1290
1291 let roxygen = RoxygenBuilder::new()
1292 .name(s7_trait_member_access(type_ident, trait_name, &const_str))
1293 .rdname(&type_str)
1294 .build();
1295 lines.extend(roxygen);
1296
1297 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
1298 let call = DotCallBuilder::new(&c_ident).build();
1299
1300 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function() {{",
trait_namespace_target(ClassSystem::S7, type_ident,
trait_name, &const_str)))
})format!(
1301 "{} <- function() {{",
1302 trait_namespace_target(ClassSystem::S7, type_ident, trait_name, &const_str)
1303 ));
1304 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", call))
})format!(" {}", call));
1305 lines.push("}".to_string());
1306 lines.push(String::new());
1307 }
1308
1309 if !static_methods.is_empty() || !consts.is_empty() {
1313 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("attr({0}, \"{1}\") <- {2}",
type_ident, trait_name, trait_env_var))
})format!(
1314 "attr({}, \"{}\") <- {}",
1315 type_ident, trait_name, trait_env_var
1316 ));
1317 lines.push(String::new());
1318 }
1319
1320 lines.join("\n")
1321}
1322
1323fn generate_trait_r6_r_wrapper(
1342 type_ident: &syn::Ident,
1343 trait_name: &syn::Ident,
1344 methods: &[TraitMethod],
1345 consts: &[TraitConst],
1346 impl_preconditions: Option<bool>,
1347) -> String {
1348 use crate::r_wrapper_builder::{DotCallBuilder, RoxygenBuilder};
1349
1350 let mut lines = Vec::new();
1351 let type_str = type_ident.to_string();
1352
1353 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# R6 trait methods for {0} implementing {1}",
type_ident, trait_name))
})format!(
1355 "# R6 trait methods for {} implementing {}",
1356 type_ident, trait_name
1357 ));
1358 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# Generated by #[miniextendr(r6)] impl {0} for {1}",
trait_name, type_ident))
})format!(
1359 "# Generated by #[miniextendr(r6)] impl {} for {}",
1360 trait_name, type_ident
1361 ));
1362 lines.push("# Note: R6 trait methods live in the Type$Trait$method namespace".to_string());
1363 lines.push(String::new());
1364
1365 let instance_methods: Vec<_> = methods.iter().filter(|m| m.has_self).collect();
1367 let static_methods: Vec<_> = methods.iter().filter(|m| !m.has_self).collect();
1368
1369 if !methods.is_empty() || !consts.is_empty() {
1372 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1} <- new.env(parent = emptyenv())",
type_ident, trait_name))
})format!(
1373 "{}${} <- new.env(parent = emptyenv())",
1374 type_ident, trait_name
1375 ));
1376 lines.push(String::new());
1377 }
1378
1379 for method in &instance_methods {
1381 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
1382 .with_impl_preconditions(impl_preconditions);
1383 let target = ctx.namespace_target(ClassSystem::R6);
1384
1385 let full_params = if ctx.params.is_empty() {
1387 ".mx_self".to_string()
1388 } else {
1389 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".mx_self, {0}", ctx.params))
})format!(".mx_self, {}", ctx.params)
1390 };
1391
1392 lines.extend(own_block_page_lines(
1404 method,
1405 &type_str,
1406 Some(&target),
1407 Some(&target),
1408 ));
1409
1410 let call = ctx.instance_call(".ptr");
1411
1412 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{",
ctx.namespace_symbol(ClassSystem::R6), full_params))
})format!(
1413 "{} <- function({full_params}) {{",
1414 ctx.namespace_symbol(ClassSystem::R6)
1415 ));
1416 lines.push(" .ptr <- .mx_self$.__enclos_env__$private$.ptr".to_string());
1418 ctx.emit_method_prelude(&mut lines, " ", &method.r_method_name());
1419 lines.extend(ctx.method_body_lines(&call, ClassSystem::R6));
1420 finish_instance_body(&mut lines, method, ".mx_self");
1421 lines.push("}".to_string());
1422 lines.push(String::new());
1423 }
1424
1425 for method in &static_methods {
1427 let r_name = method.r_method_name();
1428 let ctx = TraitMethodContext::new(method, type_ident, trait_name)
1429 .with_impl_preconditions(impl_preconditions);
1430 let target = ctx.namespace_target(ClassSystem::R6);
1431
1432 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("#\' Static trait method {0}::{1}()",
trait_name, r_name))
})format!(
1433 "#' Static trait method {}::{}()",
1434 trait_name, r_name
1435 ));
1436 lines.extend(own_block_page_lines(method, &type_str, Some(&target), None));
1437
1438 let call = ctx.static_call();
1439
1440 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function({1}) {{",
ctx.namespace_symbol(ClassSystem::R6), ctx.params))
})format!(
1441 "{} <- function({}) {{",
1442 ctx.namespace_symbol(ClassSystem::R6),
1443 ctx.params
1444 ));
1445 ctx.emit_method_prelude(&mut lines, " ", &r_name);
1446 lines.extend(ctx.method_body_lines(&call, ClassSystem::R6));
1447 lines.push("}".to_string());
1448 lines.push(String::new());
1449 }
1450
1451 for trait_const in consts {
1453 let const_name = &trait_const.ident;
1454 let const_str = const_name.to_string();
1455 let target = trait_namespace_target(ClassSystem::R6, type_ident, trait_name, &const_str);
1456
1457 let roxygen = RoxygenBuilder::new()
1458 .name(target.clone())
1459 .rdname(&type_str)
1460 .build();
1461 lines.extend(roxygen);
1462
1463 let c_ident = trait_const.c_wrapper_ident_string(type_ident, trait_name);
1464 let call = DotCallBuilder::new(&c_ident).build();
1465
1466 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- function() {{", target))
})format!("{target} <- function() {{"));
1467 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", call))
})format!(" {}", call));
1468 lines.push("}".to_string());
1469 lines.push(String::new());
1470 }
1471
1472 lines.join("\n")
1473}
1474
1475fn finish_instance_body(lines: &mut Vec<String>, method: &super::TraitMethod, receiver: &str) {
1482 if method.returns_unit() {
1483 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", receiver))
})format!(" {receiver}"));
1484 }
1485 if method.is_invisible()
1486 && let Some(last) = lines.last_mut()
1487 {
1488 let expr = last.trim_start();
1489 let indent = &last[..last.len() - expr.len()];
1490 *last = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}invisible({1})", indent, expr))
})format!("{indent}invisible({expr})");
1491 }
1492}