miniextendr_macros/miniextendr_impl/
s3_class.rs1use super::ParsedImpl;
4
5pub fn generate_s3_r_wrapper(parsed_impl: &ParsedImpl) -> String {
21 use crate::r_class_formatter::{
22 ClassDocBuilder, MethodDocBuilder, ParsedImplExt, emit_s3_generic_guard,
23 should_export_from_tags,
24 };
25
26 let class_name = parsed_impl.class_name();
27 let type_ident = &parsed_impl.type_ident;
28 let ctor_name = format!("new_{}", class_name.to_lowercase());
30 let class_doc_tags = &parsed_impl.doc_tags;
31 let class_has_no_rd = crate::roxygen::has_roxygen_tag(class_doc_tags, "noRd");
32 let should_export =
36 should_export_from_tags(class_doc_tags, parsed_impl.noexport || parsed_impl.internal);
37 let should_register_s3method = !parsed_impl.noexport;
42
43 let mut lines = Vec::new();
44
45 if let Some(ctx) = parsed_impl.constructor_context() {
47 lines.push(ctx.source_comment(type_ident));
48 let mut ctor_doc_tags = Vec::new();
49 ctor_doc_tags.extend(class_doc_tags.iter().cloned());
50 ctor_doc_tags.extend(ctx.method.doc_tags.iter().cloned());
51
52 lines.extend(
53 ClassDocBuilder::new(&class_name, type_ident, &ctor_doc_tags, "S3")
54 .with_export_control(parsed_impl.internal, parsed_impl.noexport)
55 .build(),
56 );
57 if let Some(lc_import) = crate::lifecycle::collect_lifecycle_imports(
59 parsed_impl
60 .methods
61 .iter()
62 .filter_map(|m| m.method_attrs.lifecycle.as_ref()),
63 ) {
64 let insert_pos = lines.len().saturating_sub(1);
65 lines.insert(insert_pos, format!("#' {}", lc_import));
66 }
67 lines.push(format!("{} <- function({}) {{", ctor_name, ctx.params));
68 for line in ctx.missing_prelude() {
69 lines.push(format!(" {}", line));
70 }
71 for check in ctx.precondition_checks() {
72 lines.push(format!(" {}", check));
73 }
74 for line in ctx.match_arg_prelude() {
76 lines.push(format!(" {}", line));
77 }
78 lines.push(format!(" .val <- {}", ctx.static_call()));
79 lines.extend(crate::method_return_builder::condition_check_lines(" "));
80 lines.push(format!(" structure(.val, class = \"{}\")", class_name));
81 lines.push("}".to_string());
82 lines.push(String::new());
83 }
84
85 for ctx in parsed_impl.instance_method_contexts() {
87 lines.push(ctx.source_comment(type_ident));
88 let generic_name = ctx.generic_name();
89 let method_class_suffix = ctx
91 .class_suffix()
92 .map(|s| s.to_string())
93 .unwrap_or_else(|| class_name.clone());
94 let s3_method_name = format!("{}.{}", generic_name, method_class_suffix);
95 let full_params = ctx.instance_formals(true); if !ctx.has_generic_override() && !ctx.has_class_override() {
100 if class_has_no_rd {
102 lines.push("#' @noRd".to_string());
103 } else {
104 lines.push(format!("#' @title S3 generic for `{}`", generic_name));
105 lines.push(format!("#' @description S3 generic for `{}`", generic_name));
106 lines.push(format!("#' @name {}.{}", generic_name, class_name));
109 lines.push(format!("#' @rdname {}", class_name));
110 lines.push("#' @param x An object".to_string());
111 lines.push("#' @param ... Additional arguments passed to methods".to_string());
112 lines.push(crate::roxygen::method_source_tag(
113 type_ident,
114 &ctx.method.ident,
115 ));
116 if should_export {
117 lines.push(format!("#' @export {}", generic_name));
125 }
126 }
127 lines.push(emit_s3_generic_guard(&generic_name));
128 lines.push(String::new());
129 }
130
131 if class_has_no_rd {
133 lines.push(format!("#' @method {} {}", generic_name, class_name));
136 lines.push("#' @export".to_string());
137 } else {
138 let qualified_name = format!("{}.{}", generic_name, class_name);
139 let mx_doc = ctx.match_arg_doc_placeholders();
140 let method_doc =
141 MethodDocBuilder::new(&class_name, &generic_name, type_ident, &ctx.method.doc_tags)
142 .with_r_params(&ctx.params)
143 .with_match_arg_doc_placeholders(&mx_doc)
144 .with_r_name(qualified_name);
145 lines.extend(method_doc.build());
146 lines.push(format!("#' @method {} {}", generic_name, class_name));
147 lines.push("#' @param x An object.".to_string());
150 lines.push("#' @param ... Additional arguments.".to_string());
151 if should_register_s3method {
152 lines.push("#' @export".to_string());
153 }
154 }
155 lines.push(format!(
156 "{} <- function({}) {{",
157 s3_method_name, full_params
158 ));
159
160 let what = format!("{}.{}", generic_name, class_name);
161 ctx.emit_method_prelude(&mut lines, " ", &what);
162
163 let call = ctx.instance_call("x");
164 let strategy = crate::ReturnStrategy::for_method(ctx.method);
165 let return_builder = crate::MethodReturnBuilder::new(call)
166 .with_strategy(strategy)
167 .with_class_name(class_name.clone())
168 .with_chain_var("x".to_string());
169 lines.extend(return_builder.build_s3_body());
170
171 lines.push("}".to_string());
172 lines.push(String::new());
173 }
174
175 for ctx in parsed_impl.static_method_contexts() {
177 lines.push(ctx.source_comment(type_ident));
178 let method_name = ctx.method.r_method_name();
180 let fn_name = format!("{}_{}", class_name.to_lowercase(), method_name);
181
182 let mx_doc = ctx.match_arg_doc_placeholders();
183 let method_doc =
184 MethodDocBuilder::new(&class_name, &method_name, type_ident, &ctx.method.doc_tags)
185 .with_r_params(&ctx.params)
186 .with_match_arg_doc_placeholders(&mx_doc)
187 .with_r_name(fn_name.clone())
188 .with_class_no_rd(class_has_no_rd);
189 lines.extend(method_doc.build());
190 if !class_has_no_rd {
192 lines.push("#' @export".to_string());
193 }
194
195 lines.push(format!("{} <- function({}) {{", fn_name, ctx.params));
196
197 ctx.emit_method_prelude(&mut lines, " ", &fn_name);
198
199 let strategy = crate::ReturnStrategy::for_method(ctx.method);
200 let return_builder = crate::MethodReturnBuilder::new(ctx.static_call())
201 .with_strategy(strategy)
202 .with_class_name(class_name.clone());
203 lines.extend(return_builder.build_s3_body());
204
205 lines.push("}".to_string());
206 lines.push(String::new());
207 }
208
209 let export_line = if should_export { "#' @export\n" } else { "" };
212 if class_has_no_rd {
213 lines.push(format!(
214 "#' @noRd
215{} <- new.env(parent = emptyenv())",
216 class_name
217 ));
218 } else {
219 lines.push(format!(
220 "#' @rdname {}
221{}{} <- new.env(parent = emptyenv())",
222 class_name, export_line, class_name
223 ));
224 }
225 lines.push(String::new());
226
227 if parsed_impl.constructor_context().is_some() {
229 lines.push(format!("{}$new <- {}", class_name, ctor_name));
230 lines.push(String::new());
231 }
232
233 lines.join("\n")
234}