miniextendr_macros/miniextendr_impl/
env_class.rs1use super::ParsedImpl;
12
13pub fn generate_env_class(parsed_impl: &ParsedImpl) -> super::ClassWrapper {
45 use crate::r_class_formatter::{
46 ClassDocBuilder, MethodDocBuilder, ParsedImplExt, should_export_from_tags,
47 };
48
49 let class_name = parsed_impl.class_name();
50 let type_ident = &parsed_impl.type_ident;
51 let class_has_no_rd = crate::roxygen::has_roxygen_tag(&parsed_impl.doc_tags, "noRd")
55 || (parsed_impl.noexport && !parsed_impl.internal);
56
57 let mut w = super::ClassWrapper::default();
58 let mut lines = Vec::new();
59
60 let class_docs = ClassDocBuilder::new(&class_name, type_ident, &parsed_impl.doc_tags, "")
62 .with_export_control(parsed_impl.internal, parsed_impl.noexport);
63 lines.extend(class_docs.build());
64 w.class_docs = class_docs.docs();
65 w.mark_class_doc_lines(&mut lines, parsed_impl);
66 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} <- new.env(parent = emptyenv())",
class_name))
})format!("{} <- new.env(parent = emptyenv())", class_name));
67 lines.push(String::new());
68
69 if let Some(ctx) = parsed_impl.constructor_context() {
71 w.member_names.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}$new`", class_name))
})format!("`{class_name}$new`"));
72 lines.push(ctx.source_comment(type_ident));
73 if !class_has_no_rd {
75 let choice_docs = ctx.choice_param_docs();
76 let method_doc =
77 MethodDocBuilder::new(&class_name, "new", type_ident, &ctx.method.doc_tags)
78 .with_source_method(&ctx.method.ident)
79 .with_name_prefix("$")
80 .with_params_as_details()
81 .with_r_params(&ctx.params)
82 .with_choice_param_docs(&choice_docs);
83 lines.extend(method_doc.build());
84 }
85 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}$new <- function({1}) {{",
class_name, ctx.params))
})format!("{}$new <- function({}) {{", class_name, ctx.params));
86 for check in ctx.precondition_checks() {
87 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", check))
})format!(" {}", check));
88 }
89 for line in ctx.match_arg_prelude() {
91 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", line))
})format!(" {}", line));
92 }
93 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" .val <- {0}", ctx.static_call()))
})format!(" .val <- {}", ctx.static_call()));
94 lines.extend(crate::method_return_builder::condition_check_lines(" "));
95 lines.push(" self <- .val".to_string());
96 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" class(self) <- \"{0}\"",
class_name))
})format!(" class(self) <- \"{}\"", class_name));
97 lines.push(" self".to_string());
98 lines.push("}".to_string());
99 lines.push(String::new());
100 }
101 w.head = std::mem::take(&mut lines);
102
103 for ctx in parsed_impl.instance_method_contexts() {
105 let method_name = ctx.method.r_method_name();
106 w.member_names.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}${1}`", class_name,
method_name))
})format!("`{class_name}${method_name}`"));
107 lines.push(ctx.source_comment(type_ident));
108 if !class_has_no_rd {
110 let choice_docs = ctx.choice_param_docs();
111 let method_doc =
112 MethodDocBuilder::new(&class_name, &method_name, type_ident, &ctx.method.doc_tags)
113 .with_source_method(&ctx.method.ident)
114 .with_name_prefix("$")
115 .with_params_as_details()
116 .with_r_params(&ctx.params)
117 .with_choice_param_docs(&choice_docs);
118 lines.extend(method_doc.build());
119 }
120
121 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1} <- function({2}) {{",
class_name, crate::naming::r_def_name(&method_name),
ctx.params))
})format!(
123 "{}${} <- function({}) {{",
124 class_name,
125 crate::naming::r_def_name(&method_name),
126 ctx.params
127 ));
128
129 let what = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1}", class_name, method_name))
})format!("{}${}", class_name, method_name);
130 ctx.emit_method_prelude(&mut lines, " ", &what, &ctx.params);
131
132 let call = ctx.instance_call(".mx_self");
133 let strategy = crate::ReturnStrategy::for_method(ctx.method);
134 let return_builder = crate::MethodReturnBuilder::new(call)
135 .with_strategy(strategy)
136 .with_invisible(ctx.method.is_invisible())
137 .with_chain_var(".mx_self".to_string())
138 .with_class_name(class_name.clone())
139 .with_return_class_from_method(ctx.method, &type_ident.to_string());
140 lines.extend(return_builder.build());
141
142 lines.push("}".to_string());
143 lines.push(String::new());
144 }
145
146 for ctx in parsed_impl.static_method_contexts() {
148 let method_name = ctx.method.r_method_name();
149 w.member_names.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}${1}`", class_name,
method_name))
})format!("`{class_name}${method_name}`"));
150 lines.push(ctx.source_comment(type_ident));
151 if !class_has_no_rd {
153 let choice_docs = ctx.choice_param_docs();
154 let doc_tags = parsed_impl.static_doc_tags(
157 ctx.method,
158 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1}()", class_name,
method_name))
})format!("{class_name}${method_name}()"),
159 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}$new()", class_name))
})format!("{class_name}$new()"),
160 false,
161 );
162 let method_doc =
163 MethodDocBuilder::new(&class_name, &method_name, type_ident, &doc_tags)
164 .with_source_method(&ctx.method.ident)
165 .with_name_prefix("$")
166 .with_params_as_details()
167 .with_r_params(&ctx.params)
168 .with_choice_param_docs(&choice_docs);
169 lines.extend(method_doc.build());
170 }
171
172 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1} <- function({2}) {{",
class_name, crate::naming::r_def_name(&method_name),
ctx.params))
})format!(
173 "{}${} <- function({}) {{",
174 class_name,
175 crate::naming::r_def_name(&method_name),
176 ctx.params
177 ));
178
179 let what = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}${1}", class_name, method_name))
})format!("{}${}", class_name, method_name);
180 ctx.emit_method_prelude(&mut lines, " ", &what, &ctx.params);
181
182 let strategy = crate::ReturnStrategy::for_method(ctx.method);
183 let return_builder = crate::MethodReturnBuilder::new(ctx.static_call())
184 .with_strategy(strategy)
185 .with_invisible(ctx.method.is_invisible())
186 .with_class_name(class_name.clone())
187 .with_return_class_from_method(ctx.method, &type_ident.to_string());
188 lines.extend(return_builder.build());
189
190 lines.push("}".to_string());
191 lines.push(String::new());
192 }
193 w.members = std::mem::take(&mut lines);
194
195 let should_export = should_export_from_tags(
198 &parsed_impl.doc_tags,
199 parsed_impl.noexport || parsed_impl.internal,
200 );
201
202 let fields = parsed_impl.r_data_accessors.enabled();
213 if fields {
214 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("# Field syntax for the `#[r_data]` fields of {0} (`env(r_data_accessors)`).",
type_ident))
})format!(
215 "# Field syntax for the `#[r_data]` fields of {type_ident} (`env(r_data_accessors)`)."
216 ));
217 lines.push(super::field_syntax::env_load_check(parsed_impl));
218 lines.push(String::new());
219 }
220 if class_has_no_rd {
221 lines.push("#' @noRd".to_string());
222 lines.push("#' @export".to_string());
223 } else if !should_export {
224 lines.push("#' @export".to_string());
225 } else {
226 lines.extend(super::field_syntax::page_lines(
228 parsed_impl,
229 &[
230 ("self", "The object instance."),
231 (
232 "name",
233 if fields {
234 super::field_syntax::env_name_param(parsed_impl)
235 } else {
236 "Method name for dispatch."
237 },
238 ),
239 ],
240 ));
241 lines.push("#' @export".to_string());
242 }
243 let mut lookup = Vec::new();
247 if fields {
248 lookup.extend(super::field_syntax::env_field_branch(parsed_impl));
249 }
250 let method_lookup = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".miniextendr_env_method(self, name, {0})",
class_name))
})format!(".miniextendr_env_method(self, name, {class_name})");
251 if fields {
252 lookup.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" obj <- {0}", method_lookup))
})format!(" obj <- {method_lookup}"));
253 lookup.push(" if (is.null(obj)) {".to_string());
254 lookup.extend(super::field_syntax::env_unknown_name_check(parsed_impl));
255 lookup.push(" }".to_string());
256 lookup.push(" obj".to_string());
257 } else {
258 lookup.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", method_lookup))
})format!(" {method_lookup}"));
259 }
260 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`$.{0}` <- function(self, name) {{",
class_name))
})format!("`$.{}` <- function(self, name) {{", class_name));
261 lines.extend(lookup.iter().cloned());
262 lines.push("}".to_string());
263 if class_has_no_rd {
267 lines.push("#' @noRd".to_string());
268 lines.push("#' @export".to_string());
269 } else if !should_export {
270 lines.push("#' @export".to_string());
271 } else {
272 lines.extend(super::field_syntax::page_lines(
273 parsed_impl,
274 &[
275 ("...", "Refused: `[[` takes one name."),
276 ("exact", "Ignored: names match exactly."),
277 ],
278 ));
279 lines.push("#' @export".to_string());
280 }
281 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`[[.{1}` <- function({0}) {{",
super::field_syntax::ENV_SUBSET_FORMALS, class_name))
})format!(
282 "`[[.{class_name}` <- function({}) {{",
283 super::field_syntax::ENV_SUBSET_FORMALS
284 ));
285 lines.push(super::field_syntax::env_subset_dots_check(parsed_impl));
286 lines.extend(lookup);
287 lines.push("}".to_string());
288 if fields {
289 lines.extend(super::field_syntax::env_dollar_names(
290 parsed_impl,
291 !class_has_no_rd && should_export,
292 ));
293 }
294 if fields && parsed_impl.r_data_accessors.setters() {
295 lines.extend(super::field_syntax::env_replacement_methods(
296 parsed_impl,
297 !class_has_no_rd && should_export,
298 ));
299 }
300
301 w.tail = lines;
302 w
303}
304
305#[cfg(test)]
307pub fn generate_env_r_wrapper(parsed_impl: &ParsedImpl) -> String {
308 generate_env_class(parsed_impl).render()
309}