Skip to main content

miniextendr_api/
nargs.rs

1//! The argument-count marker: [`NArgs`].
2//!
3//! R tells some calls apart only by how many arguments were written. A `[`
4//! method receives `x[1:3]` and `x[1:3, ]` with the same `i` and a missing `j`;
5//! only `nargs()` says the second call had an empty argument (2 against 3), and
6//! `[.data.frame` decides between list-style and matrix-style subscripting on
7//! it. A `#[miniextendr]` function that needs the count takes a parameter of
8//! type `NArgs`:
9//!
10//! ```ignore
11//! use miniextendr_api::{Missing, NArgs, SEXP, miniextendr};
12//!
13//! /// `[.mx_vec1`: one subscript only.
14//! #[miniextendr(s3(generic = "[", class = "mx_vec1"))]
15//! pub fn mx_vec1_subset(
16//!     x: Vec<f64>,
17//!     i: Missing<Vec<i32>>,
18//!     _j: Missing<SEXP>,
19//!     _drop: Missing<SEXP>,
20//!     nargs: NArgs,
21//! ) -> Result<Vec<f64>, String> {
22//!     if nargs.get() > 2 {
23//!         return Err("an mx_vec1 takes one subscript: x[i]".into());
24//!     }
25//!     // ...
26//! #   let _ = (x, i); Ok(Vec::new())
27//! }
28//! ```
29//!
30//! The parameter is not an R formal: the generated R wrapper passes `nargs()`
31//! at its position in the `.Call()`, so the method above is
32//! `` `[.mx_vec1` <- function(x, i, j, drop) `` and the Rust function gets the
33//! count. It is the count R's `nargs()` gives in the generated function's own
34//! frame, as typed:
35//!
36//! | Typed | `nargs()` |
37//! |---|---|
38//! | `x[1:3]` | 2 |
39//! | `x[1:3, ]` | 3 |
40//! | `x[, 1:3]` | 3 |
41//! | `x[1, 2]` | 3 |
42//! | `x[1, , drop = FALSE]` | 4 |
43//! | `x[]` | 2 |
44//! | `x[i] <- v` / `x[i, ] <- v` | 3 / 4 |
45//!
46//! - `x` counts, and so does every empty argument (`x[1, ]`) and a named
47//!   argument such as `drop = FALSE`. A formal with a default that the caller
48//!   left out does not.
49//! - It is the generated function's own call that is counted. For an S3
50//!   method that is the generic's call as dispatched (`[.mx_vec1` above sees
51//!   the call to `[`), with the arguments a `...` formal collects. An
52//!   inherent R6 or environment-class method counts its own call without the
53//!   object (`obj$pick(1)` is 1). A trait method's function takes the object
54//!   as its first argument, which counts, and `obj$method(1)` passes it on
55//!   (2, as for `Type$Trait$method(obj, 1)`).
56//! - The wrapper calls `nargs()` only when the function takes an `NArgs`
57//!   parameter; every other wrapper is unchanged.
58//!
59//! `NArgs` is a parameter type only, matched by its last path segment like the
60//! other markers (`NArgs`, `miniextendr_api::NArgs`); `&NArgs` and
61//! `Option<NArgs>` are not the marker. It takes no per-parameter option
62//! (`default`, `match_arg`, `coerce`, `no_na`, ...), a function takes at most
63//! one, and an `extern "C-unwind"` function cannot take it (it has no generated
64//! R wrapper to pass the count). Standalone functions, `s3(...)` methods, and
65//! the methods of impl blocks and trait impls accept it. It holds a plain
66//! number, so it does not keep the function on R's main thread.
67
68use crate::SEXP;
69use crate::from_r::{SexpError, TryFromSexp};
70use crate::into_r::IntoR;
71
72/// The number of arguments in the generated R function's own call, as typed
73/// (`nargs()`): `x` included, an empty argument (`x[1, ]`) included, a named
74/// `drop = FALSE` included. See the [module docs](self).
75#[repr(transparent)]
76#[derive(#[automatically_derived]
impl ::core::fmt::Debug for NArgs {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "NArgs",
            &&self.0)
    }
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for NArgs { }
#[automatically_derived]
impl ::core::clone::Clone for NArgs {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<usize>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NArgs { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for NArgs { }
#[automatically_derived]
impl ::core::cmp::PartialEq for NArgs {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NArgs {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<usize>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for NArgs {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for NArgs {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord, #[automatically_derived]
impl ::core::hash::Hash for NArgs {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash)]
77pub struct NArgs(usize);
78
79impl NArgs {
80    /// Wrap a count. Generated code converts the parameter from `nargs()`;
81    /// this is for calling such a function from Rust.
82    #[inline]
83    pub const fn new(count: usize) -> Self {
84        NArgs(count)
85    }
86
87    /// The count.
88    #[inline]
89    pub const fn get(self) -> usize {
90        self.0
91    }
92}
93
94impl From<NArgs> for usize {
95    #[inline]
96    fn from(nargs: NArgs) -> usize {
97        nargs.0
98    }
99}
100
101impl std::fmt::Display for NArgs {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        self.0.fmt(f)
104    }
105}
106
107/// The integer scalar `nargs()` returns, as a non-negative count.
108impl TryFromSexp for NArgs {
109    type Error = SexpError;
110
111    #[inline]
112    fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
113        usize::try_from_sexp(sexp).map(NArgs)
114    }
115
116    #[inline]
117    unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
118        unsafe { usize::try_from_sexp_unchecked(sexp) }.map(NArgs)
119    }
120}
121
122/// The count as an R integer, for a `#[miniextendr]` trait's method that takes
123/// an `NArgs` parameter (its view passes each argument to the vtable as a
124/// SEXP).
125impl IntoR for NArgs {
126    type Error = <usize as IntoR>::Error;
127
128    #[inline]
129    fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
130        self.0.try_into_sexp()
131    }
132
133    #[inline]
134    unsafe fn try_into_sexp_unchecked(self) -> Result<SEXP, Self::Error> {
135        unsafe { self.0.try_into_sexp_unchecked() }
136    }
137}