1use crate::miniextendr_impl::ParsedMethod;
7use crate::miniextendr_impl::ReceiverKind;
8
9pub fn condition_check_lines(indent: &str) -> Vec<String> {
34 condition_check_lines_with_default(indent, "sys.call()")
35}
36
37pub fn condition_check_lines_with_default(indent: &str, raise_default: &str) -> Vec<String> {
41 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}if (inherits(.val, \"rust_condition_value\") && isTRUE(attr(.val, \"__rust_condition__\"))) return(.miniextendr_raise_condition(.val, {1}))",
indent, raise_default))
})]))vec![format!(
42 "{indent}if (inherits(.val, \"rust_condition_value\") && isTRUE(attr(.val, \"__rust_condition__\"))) return(.miniextendr_raise_condition(.val, {raise_default}))"
43 )]
44}
45
46pub fn condition_check_inline_block(call_expr: &str, inner: &str, indent: &str) -> String {
56 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{\n{0}.val <- {1}\n{0}if (inherits(.val, \"rust_condition_value\") && isTRUE(attr(.val, \"__rust_condition__\"))) return(.miniextendr_raise_condition(.val, sys.call()))\n{0}{2}\n }}",
indent, call_expr, inner))
})format!(
57 "{{\n{indent}.val <- {call_expr}\n\
58 {indent}if (inherits(.val, \"rust_condition_value\") && isTRUE(attr(.val, \"__rust_condition__\"))) return(.miniextendr_raise_condition(.val, sys.call()))\n\
59 {indent}{inner}\n \
60 }}"
61 )
62}
63
64pub fn standalone_body(call_expr: &str, final_return: &str, indent: &str) -> String {
73 standalone_body_with_call_default(call_expr, final_return, indent, "sys.call()")
74}
75
76pub fn standalone_body_with_call_default(
81 call_expr: &str,
82 final_return: &str,
83 indent: &str,
84 call_default: &str,
85) -> String {
86 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".val <- {0}\n{1}if (inherits(.val, \"rust_condition_value\") && isTRUE(attr(.val, \"__rust_condition__\"))) return(.miniextendr_raise_condition(.val, {2}))\n{1}{3}",
call_expr, indent, call_default, final_return))
})format!(
87 ".val <- {call_expr}\n\
88 {indent}if (inherits(.val, \"rust_condition_value\") && isTRUE(attr(.val, \"__rust_condition__\"))) return(.miniextendr_raise_condition(.val, {call_default}))\n\
89 {indent}{final_return}"
90 )
91}
92#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ReturnStrategy {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ReturnStrategy::ReturnSelf => "ReturnSelf",
ReturnStrategy::ReturnOtherClass => "ReturnOtherClass",
ReturnStrategy::ReturnOtherClassList =>
"ReturnOtherClassList",
ReturnStrategy::ChainableMutation => "ChainableMutation",
ReturnStrategy::Direct => "Direct",
})
}
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ReturnStrategy { }
#[automatically_derived]
impl ::core::clone::Clone for ReturnStrategy {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ReturnStrategy { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for ReturnStrategy { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ReturnStrategy {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ReturnStrategy { }Eq)]
101pub enum ReturnStrategy {
102 ReturnSelf,
106 ReturnOtherClass,
110 ReturnOtherClassList,
121 ChainableMutation,
126 Direct,
128}
129
130impl ReturnStrategy {
131 pub fn for_method(method: &ParsedMethod) -> Self {
165 let is_self_ref_builder = method.env.is_instance()
171 && (method.returns_self_ref()
172 || method.returns_result_self_ref()
173 || method.returns_option_self_ref());
174 let is_unit_mutator = method.env.is_mut() && method.returns_unit();
175 let returns_self_shaped =
176 method.returns_self() || method.returns_result_self() || method.returns_option_self();
177 let is_consuming_builder = method.env == ReceiverKind::Value && returns_self_shaped;
178 if is_consuming_builder || is_self_ref_builder || is_unit_mutator {
179 ReturnStrategy::ChainableMutation
180 } else if returns_self_shaped {
181 ReturnStrategy::ReturnSelf
182 } else if method.returns_other_class().is_some() {
183 ReturnStrategy::ReturnOtherClass
184 } else if method.returns_other_class_list().is_some() || method.returns_self_list() {
185 ReturnStrategy::ReturnOtherClassList
186 } else {
187 ReturnStrategy::Direct
188 }
189 }
190}
191
192#[allow(clippy::type_complexity)]
204struct ReturnTails<'a> {
205 self_tail: Box<dyn Fn(&str, &str) -> Vec<String> + 'a>,
209 chain_tail: Box<dyn Fn(&str) -> Vec<String> + 'a>,
213 direct_tail: Box<dyn Fn(&str) -> Vec<String> + 'a>,
217}
218
219pub struct MethodReturnBuilder {
226 call_expr: String,
228 strategy: ReturnStrategy,
230 class_name: Option<String>,
233 return_class: Option<String>,
238 explicit_wrap: Option<crate::return_wrap::ReturnWrap>,
240 nullable: bool,
244 chain_var: Option<String>,
247 invisible: bool,
251 indent: usize,
253 raise_default: String,
256}
257
258impl MethodReturnBuilder {
259 pub fn new(call_expr: String) -> Self {
261 Self {
262 call_expr,
263 strategy: ReturnStrategy::Direct,
264 class_name: None,
265 return_class: None,
266 explicit_wrap: None,
267 nullable: false,
268 chain_var: None,
269 invisible: false,
270 indent: 2,
271 raise_default: "sys.call()".to_string(),
272 }
273 }
274
275 pub fn with_raise_default(mut self, raise_default: &str) -> Self {
278 self.raise_default = raise_default.to_string();
279 self
280 }
281
282 pub fn with_invisible(mut self, invisible: bool) -> Self {
285 self.invisible = invisible;
286 self
287 }
288
289 fn apply_visibility(&self, mut lines: Vec<String>) -> Vec<String> {
293 if self.invisible
294 && let Some(last) = lines.last_mut()
295 {
296 let expr = last.trim_start();
297 let indent = &last[..last.len() - expr.len()];
298 *last = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}invisible({1})", indent, expr))
})format!("{indent}invisible({expr})");
299 }
300 lines
301 }
302
303 fn apply_visibility_expr(&self, expr: String) -> String {
305 if self.invisible {
306 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invisible({0})", expr))
})format!("invisible({expr})")
307 } else {
308 expr
309 }
310 }
311
312 pub fn with_strategy(mut self, strategy: ReturnStrategy) -> Self {
314 self.strategy = strategy;
315 self
316 }
317
318 pub fn with_class_name(mut self, class_name: String) -> Self {
320 self.class_name = Some(class_name);
321 self
322 }
323
324 pub fn with_return_class(mut self, return_class: String) -> Self {
326 self.return_class = Some(return_class);
327 self
328 }
329
330 pub fn with_return_class_from_method(
346 mut self,
347 method: &ParsedMethod,
348 self_rust_type: &str,
349 ) -> Self {
350 if let Some(wrap) = &method.return_wrap {
351 return self.with_explicit_wrap(wrap.clone());
352 }
353 self.nullable = method.returns_option();
354 match self.strategy {
355 ReturnStrategy::ReturnOtherClass => {
356 let return_class = method
357 .returns_other_class()
358 .expect("return_class required for ReturnOtherClass strategy");
359 self = self.with_return_class(return_class.to_string());
360 }
361 ReturnStrategy::ReturnOtherClassList => {
362 let return_class = match method.returns_other_class_list() {
363 Some(ident) => ident.to_string(),
364 None => {
365 if !method.returns_self_list() {
{
::core::panicking::panic_fmt(format_args!("return_class required for ReturnOtherClassList strategy"));
}
};assert!(
366 method.returns_self_list(),
367 "return_class required for ReturnOtherClassList strategy"
368 );
369 self_rust_type.to_string()
370 }
371 };
372 self = self.with_return_class(return_class);
373 }
374 _ => {}
375 }
376 self
377 }
378
379 pub fn with_nullable(mut self, nullable: bool) -> Self {
383 self.nullable = nullable;
384 self
385 }
386
387 pub fn with_explicit_wrap(mut self, wrap: crate::return_wrap::ReturnWrap) -> Self {
389 self.explicit_wrap = Some(wrap);
390 self
391 }
392
393 pub fn with_chain_var(mut self, var: String) -> Self {
395 self.chain_var = Some(var);
396 self
397 }
398
399 pub fn with_indent(mut self, indent: usize) -> Self {
401 self.indent = indent;
402 self
403 }
404
405 fn return_other_class_expr(&self) -> String {
406 let return_class = self
407 .return_class
408 .as_ref()
409 .expect("return_class required for ReturnOtherClass strategy");
410 self.null_passes(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".__MX_WRAP_RETURN_{0}__(.val)",
return_class))
})format!(".__MX_WRAP_RETURN_{return_class}__(.val)"))
411 }
412
413 fn null_passes(&self, expr: String) -> String {
417 if self.nullable {
418 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("if (is.null(.val)) NULL else {0}",
expr))
})format!("if (is.null(.val)) NULL else {expr}")
419 } else {
420 expr
421 }
422 }
423
424 fn return_other_class_list_expr(&self) -> String {
432 let return_class = self
433 .return_class
434 .as_ref()
435 .expect("return_class required for ReturnOtherClassList strategy");
436 self.null_passes(::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".__MX_WRAP_LIST_RETURN_{0}__(.val)",
return_class))
})format!(".__MX_WRAP_LIST_RETURN_{return_class}__(.val)"))
437 }
438
439 fn build_with_tails(&self, tails: ReturnTails<'_>) -> Vec<String> {
451 let indent = " ".repeat(self.indent);
452 let class_name = self.class_name.as_deref().unwrap_or("");
453 let call_expr = &self.call_expr;
454
455 let mut lines = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.val <- {1}", indent,
call_expr))
})]))vec![format!("{}.val <- {}", indent, call_expr)];
456 lines.extend(condition_check_lines_with_default(
457 &indent,
458 &self.raise_default,
459 ));
460 if let Some(wrap) = &self.explicit_wrap {
461 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{1}{0}",
wrap.r_expression(".val", self.class_name.as_deref()),
indent))
})format!(
462 "{indent}{}",
463 wrap.r_expression(".val", self.class_name.as_deref())
464 ));
465 return self.apply_visibility(lines);
466 }
467 if self.nullable
470 && #[allow(non_exhaustive_omitted_patterns)] match self.strategy {
ReturnStrategy::ReturnSelf | ReturnStrategy::ChainableMutation => true,
_ => false,
}matches!(
471 self.strategy,
472 ReturnStrategy::ReturnSelf | ReturnStrategy::ChainableMutation
473 )
474 {
475 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}if (is.null(.val)) return(NULL)",
indent))
})format!("{indent}if (is.null(.val)) return(NULL)"));
476 }
477 match self.strategy {
478 ReturnStrategy::ReturnSelf => {
479 lines.extend((tails.self_tail)(&indent, class_name));
480 }
481 ReturnStrategy::ChainableMutation => {
482 lines.extend((tails.chain_tail)(&indent));
483 }
484 ReturnStrategy::ReturnOtherClass => {
485 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", indent,
self.return_other_class_expr()))
})format!("{}{}", indent, self.return_other_class_expr()));
486 }
487 ReturnStrategy::ReturnOtherClassList => {
488 lines.push(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", indent,
self.return_other_class_list_expr()))
})format!("{}{}", indent, self.return_other_class_list_expr()));
489 }
490 ReturnStrategy::Direct => {
491 lines.extend((tails.direct_tail)(&indent));
492 }
493 }
494 self.apply_visibility(lines)
495 }
496
497 pub fn build(&self) -> Vec<String> {
503 let chain_var = self.chain_var.as_deref().unwrap_or("self").to_owned();
504 self.build_with_tails(ReturnTails {
505 self_tail: Box::new(|indent, class_name| {
506 if !!class_name.is_empty() {
{
::core::panicking::panic_fmt(format_args!("class_name required for ReturnSelf strategy"));
}
};assert!(
507 !class_name.is_empty(),
508 "class_name required for ReturnSelf strategy"
509 );
510 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}class(.val) <- \"{1}\"",
indent, class_name))
}),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.val", indent))
})]))vec![
511 format!("{}class(.val) <- \"{}\"", indent, class_name),
512 format!("{}.val", indent),
513 ]
514 }),
515 chain_tail: Box::new(move |indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", indent,
chain_var))
})]))vec![format!("{}{}", indent, chain_var)]),
516 direct_tail: Box::new(|indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.val", indent))
})]))vec![format!("{}.val", indent)]),
517 })
518 }
519}
520
521impl MethodReturnBuilder {
523 pub fn build_r6_body(&self) -> Vec<String> {
526 self.build_with_tails(ReturnTails {
527 self_tail: Box::new(|indent, class_name| {
528 if !!class_name.is_empty() {
{
::core::panicking::panic_fmt(format_args!("class_name required for ReturnSelf strategy"));
}
};assert!(
529 !class_name.is_empty(),
530 "class_name required for ReturnSelf strategy"
531 );
532 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}$new(.ptr = .val)",
indent, class_name))
})]))vec![format!("{}{}$new(.ptr = .val)", indent, class_name)]
533 }),
534 chain_tail: Box::new(|indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}self", indent))
})]))vec![format!("{}self", indent)]),
535 direct_tail: Box::new(|indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.val", indent))
})]))vec![format!("{}.val", indent)]),
536 })
537 }
538
539 pub fn build_s3_body(&self) -> Vec<String> {
541 let chain_var = self.chain_var.as_deref().unwrap_or("x").to_owned();
542 self.build_with_tails(ReturnTails {
543 self_tail: Box::new(|indent, class_name| {
544 if !!class_name.is_empty() {
{
::core::panicking::panic_fmt(format_args!("class_name required for ReturnSelf strategy"));
}
};assert!(
545 !class_name.is_empty(),
546 "class_name required for ReturnSelf strategy"
547 );
548 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}structure(.val, class = \"{1}\")",
indent, class_name))
})]))vec![format!(
549 "{}structure(.val, class = \"{}\")",
550 indent, class_name
551 )]
552 }),
553 chain_tail: Box::new(move |indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", indent,
chain_var))
})]))vec![format!("{}{}", indent, chain_var)]),
554 direct_tail: Box::new(|indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.val", indent))
})]))vec![format!("{}.val", indent)]),
555 })
556 }
557
558 pub fn build_s7_body(&self) -> Vec<String> {
565 let chain_var = self.chain_var.as_deref().unwrap_or("x").to_owned();
569 self.build_with_tails(ReturnTails {
570 self_tail: Box::new(|indent, class_name| {
571 if !!class_name.is_empty() {
{
::core::panicking::panic_fmt(format_args!("class_name required for ReturnSelf strategy"));
}
};assert!(
572 !class_name.is_empty(),
573 "class_name required for ReturnSelf strategy"
574 );
575 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}(.ptr = .val)",
indent, class_name))
})]))vec![format!("{}{}(.ptr = .val)", indent, class_name)]
576 }),
577 chain_tail: Box::new(move |indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", indent,
chain_var))
})]))vec![format!("{}{}", indent, chain_var)]),
578 direct_tail: Box::new(|indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.val", indent))
})]))vec![format!("{}.val", indent)]),
579 })
580 }
581
582 pub fn build_s4_body(&self) -> Vec<String> {
587 let chain_var = self.chain_var.as_deref().unwrap_or("x");
588 self.build_with_tails(ReturnTails {
589 self_tail: Box::new(|indent, class_name| {
590 if !!class_name.is_empty() {
{
::core::panicking::panic_fmt(format_args!("class_name required for ReturnSelf strategy"));
}
};assert!(
591 !class_name.is_empty(),
592 "class_name required for ReturnSelf strategy"
593 );
594 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}methods::new(\"{1}\", ptr = .val)",
indent, class_name))
})]))vec![format!(
595 "{}methods::new(\"{}\", ptr = .val)",
596 indent, class_name
597 )]
598 }),
599 chain_tail: Box::new(|indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", indent,
chain_var))
})]))vec![format!("{indent}{chain_var}")]),
600 direct_tail: Box::new(|indent| ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.val", indent))
})]))vec![format!("{}.val", indent)]),
601 })
602 }
603
604 pub fn build_s7_inline(&self) -> String {
610 let inner = if let Some(wrap) = &self.explicit_wrap {
611 wrap.r_expression(".val", self.class_name.as_deref())
612 } else {
613 match self.strategy {
614 ReturnStrategy::ReturnSelf => {
615 let class_name = self
616 .class_name
617 .as_ref()
618 .expect("class_name required for ReturnSelf strategy");
619 self.null_passes(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}(.ptr = .val)", class_name))
})format!("{}(.ptr = .val)", class_name))
620 }
621 ReturnStrategy::ChainableMutation => self.null_passes("x".to_string()),
622 ReturnStrategy::ReturnOtherClass => self.return_other_class_expr(),
623 ReturnStrategy::ReturnOtherClassList => self.return_other_class_list_expr(),
624 ReturnStrategy::Direct => ".val".to_string(),
625 }
626 };
627 let inner = self.apply_visibility_expr(inner);
628 condition_check_inline_block(&self.call_expr, &inner, " ")
629 }
630
631 pub fn build_s4_inline(&self) -> String {
636 let inner = if let Some(wrap) = &self.explicit_wrap {
637 wrap.r_expression(".val", self.class_name.as_deref())
638 } else {
639 match self.strategy {
640 ReturnStrategy::ReturnSelf => {
641 let class_name = self
642 .class_name
643 .as_ref()
644 .expect("class_name required for ReturnSelf strategy");
645 self.null_passes(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("methods::new(\"{0}\", ptr = .val)",
class_name))
})format!("methods::new(\"{}\", ptr = .val)", class_name))
646 }
647 ReturnStrategy::ChainableMutation => self.null_passes("x".to_string()),
648 ReturnStrategy::ReturnOtherClass => self.return_other_class_expr(),
649 ReturnStrategy::ReturnOtherClassList => self.return_other_class_list_expr(),
650 ReturnStrategy::Direct => ".val".to_string(),
651 }
652 };
653 let inner = self.apply_visibility_expr(inner);
654 condition_check_inline_block(&self.call_expr, &inner, " ")
655 }
656}
657
658#[cfg(test)]
659mod tests;
660