miniextendr_api/sexp_ext.rs
1//! `SexpExt` — the ergonomic extension trait on `SEXP` — and `PairListExt`,
2//! its pairlist (call) counterpart.
3//!
4//! Provides safe(r) accessors and type checks. This is the actual user-facing
5//! API surface most callers reach for; both are re-exported from
6//! [`crate::prelude`].
7
8use std::marker::PhantomData;
9
10use crate::from_r::charsxp_to_str;
11use crate::gc_protect::ProtectScope;
12use crate::sexp_types::{R_xlen_t, RNativeType, Rboolean, Rcomplex, SEXPTYPE};
13use crate::{IsNA, SEXP};
14// Pull in everything raw from sys. Type re-exports (SEXP, SEXPTYPE, etc.)
15// that `sys` itself promotes are identical items, so glob import is safe;
16// we still bring SEXP/SEXPTYPE in via crate root above so the file reads
17// naturally.
18use crate::sys::{
19 ALTREP, CAR, CAR_unchecked, CDR, CDR_unchecked, COMPLEX_ELT, DATAPTR_RO, DATAPTR_RO_unchecked,
20 IDENT_USE_CLOENV, INTEGER_ELT, LOGICAL_ELT, PRINTNAME, R_BaseEnv, R_CHAR, R_CHAR_unchecked,
21 R_ClassSymbol, R_DimNamesSymbol, R_DimSymbol, R_LevelsSymbol, R_NaString, R_NamesSymbol,
22 R_RowNamesSymbol, R_compute_identical, RAW_ELT, REAL_ELT, Rf_asChar, Rf_asInteger,
23 Rf_asLogical, Rf_asReal, Rf_classgets, Rf_coerceVector, Rf_cons, Rf_cons_unchecked,
24 Rf_copyMostAttrib, Rf_copyMostAttrib_unchecked, Rf_dimgets, Rf_dimnamesgets, Rf_duplicate,
25 Rf_eval, Rf_getAttrib, Rf_getAttrib_unchecked, Rf_inherits, Rf_install, Rf_isArray,
26 Rf_isFactor, Rf_isFunction, Rf_isList, Rf_isMatrix, Rf_isObject, Rf_isS4, Rf_lang2, Rf_lcons,
27 Rf_namesgets, Rf_setAttrib, Rf_setAttrib_unchecked, Rf_shallow_duplicate, Rf_xlength,
28 Rf_xlength_unchecked, Rf_xlengthgets, SET_COMPLEX_ELT, SET_INTEGER_ELT, SET_LOGICAL_ELT,
29 SET_RAW_ELT, SET_REAL_ELT, SET_STRING_ELT, SET_STRING_ELT_unchecked, SET_TAG,
30 SET_TAG_unchecked, SET_VECTOR_ELT, SET_VECTOR_ELT_unchecked, SETCAR, SETCAR_unchecked, SETCDR,
31 SETCDR_unchecked, STRING_ELT, STRING_ELT_unchecked, TAG, TYPEOF, VECTOR_ELT,
32 VECTOR_ELT_unchecked,
33};
34
35/// Extension trait for SEXP providing safe(r) accessors and type checking.
36///
37/// This trait provides idiomatic Rust methods for working with SEXPs,
38/// equivalent to R's inline macros and type checking functions.
39pub trait SexpExt {
40 /// Get the type of this SEXP.
41 ///
42 /// Equivalent to `TYPEOF(x)` macro.
43 ///
44 /// # Safety
45 ///
46 /// The SEXP must be valid (not null and not freed).
47 #[must_use]
48 fn type_of(&self) -> SEXPTYPE;
49
50 /// Get the length of this SEXP as `usize`.
51 ///
52 /// # Safety
53 ///
54 /// The SEXP must be valid.
55 #[must_use]
56 fn len(&self) -> usize;
57
58 /// Get the length as `R_xlen_t`.
59 ///
60 /// # Safety
61 ///
62 /// The SEXP must be valid.
63 #[must_use]
64 fn xlength(&self) -> R_xlen_t;
65
66 /// Get the length as `R_xlen_t` without thread checks.
67 ///
68 /// # Safety
69 ///
70 /// Must be called from R's main thread. No debug assertions.
71 #[must_use]
72 unsafe fn xlength_unchecked(&self) -> R_xlen_t;
73
74 /// Get the length without thread checks.
75 ///
76 /// # Safety
77 ///
78 /// Must be called from R's main thread. No debug assertions.
79 #[must_use]
80 unsafe fn len_unchecked(&self) -> usize;
81
82 /// Get a slice view of this SEXP's data.
83 ///
84 /// # Safety
85 ///
86 /// - The SEXP must be valid and of the correct type for `T`
87 /// - The SEXP must be protected from R's garbage collector for the entire
88 /// duration the returned slice is used. This typically means the SEXP must
89 /// be either:
90 /// - An argument to a `.Call` function (protected by R's calling convention)
91 /// - Explicitly protected via `PROTECT`/`UNPROTECT` or `R_PreserveObject`
92 /// - Part of a protected container (e.g., element of a protected list)
93 /// - The returned slice has `'static` lifetime for API convenience, but this
94 /// is a lie - the actual lifetime is tied to the SEXP's protection status.
95 /// Holding the slice after the SEXP is unprotected is undefined behavior.
96 unsafe fn as_slice<T: RNativeType>(&self) -> &'static [T];
97
98 /// Get a slice view without thread checks.
99 ///
100 /// # Safety
101 ///
102 /// - All safety requirements of [`as_slice`](Self::as_slice) apply
103 /// - Additionally, must be called from R's main thread (no debug assertions)
104 unsafe fn as_slice_unchecked<T: RNativeType>(&self) -> &'static [T];
105
106 /// Get a mutable slice view of this SEXP's data.
107 ///
108 /// # Safety
109 ///
110 /// - All safety requirements of [`as_slice`](Self::as_slice) apply.
111 /// - The caller must ensure **exclusive access**: no other `&[T]` or `&mut [T]`
112 /// slices derived from this SEXP may exist simultaneously. Multiple calls to
113 /// `as_mut_slice` on the same SEXP without dropping the previous slice is UB.
114 /// - The SEXP must not be shared (ALTREP or NAMED > 0 objects may alias).
115 unsafe fn as_mut_slice<T: RNativeType>(&self) -> &'static mut [T];
116
117 // Type checking methods (equivalent to R's type check macros)
118
119 /// Check if this SEXP is an integer vector (INTSXP).
120 #[must_use]
121 fn is_integer(&self) -> bool;
122
123 /// Check if this SEXP is a real/numeric vector (REALSXP).
124 #[must_use]
125 fn is_real(&self) -> bool;
126
127 /// Check if this SEXP is a logical vector (LGLSXP).
128 #[must_use]
129 fn is_logical(&self) -> bool;
130
131 /// Check if this SEXP is a character/string vector (STRSXP).
132 #[must_use]
133 fn is_character(&self) -> bool;
134
135 /// Check if this SEXP is a raw vector (RAWSXP).
136 #[must_use]
137 fn is_raw(&self) -> bool;
138
139 /// Check if this SEXP is a complex vector (CPLXSXP).
140 #[must_use]
141 fn is_complex(&self) -> bool;
142
143 /// Check if this SEXP is a list/generic vector (VECSXP).
144 #[must_use]
145 fn is_list(&self) -> bool;
146
147 /// Check if this SEXP is an external pointer (EXTPTRSXP).
148 #[must_use]
149 fn is_external_ptr(&self) -> bool;
150
151 /// Check if this SEXP is an environment (ENVSXP).
152 #[must_use]
153 fn is_environment(&self) -> bool;
154
155 /// Check if this SEXP is a symbol (SYMSXP).
156 #[must_use]
157 fn is_symbol(&self) -> bool;
158
159 /// Check if this SEXP is a language object (LANGSXP).
160 #[must_use]
161 fn is_language(&self) -> bool;
162
163 /// Check if this SEXP is an ALTREP object.
164 ///
165 /// Equivalent to R's `ALTREP(x)` macro.
166 #[must_use]
167 fn is_altrep(&self) -> bool;
168
169 /// Check if this `SEXP` contains any elements.
170 #[must_use]
171 fn is_empty(&self) -> bool;
172
173 /// Check if this SEXP is a factor.
174 ///
175 /// Equivalent to R's `Rf_isFactor(x)`.
176 #[must_use]
177 fn is_factor(&self) -> bool;
178
179 /// Check if this SEXP is a pairlist (LISTSXP or NILSXP).
180 ///
181 /// Equivalent to R's `Rf_isList(x)`.
182 #[must_use]
183 fn is_pair_list(&self) -> bool;
184
185 /// Check if this SEXP is a matrix.
186 ///
187 /// Equivalent to R's `Rf_isMatrix(x)`.
188 #[must_use]
189 fn is_matrix(&self) -> bool;
190
191 /// Check if this SEXP is an array.
192 ///
193 /// Equivalent to R's `Rf_isArray(x)`.
194 #[must_use]
195 fn is_array(&self) -> bool;
196
197 /// Check if this SEXP is a function (closure, builtin, or special).
198 ///
199 /// Equivalent to R's `Rf_isFunction(x)`.
200 #[must_use]
201 fn is_function(&self) -> bool;
202
203 /// Check if this SEXP is an S4 object.
204 ///
205 /// Equivalent to R's `Rf_isS4(x)`.
206 #[must_use]
207 fn is_s4(&self) -> bool;
208
209 /// Check if this SEXP is a data.frame.
210 ///
211 /// Equivalent to R's `Rf_isDataFrame(x)`.
212 #[must_use]
213 fn is_data_frame(&self) -> bool;
214
215 /// Check if this SEXP is a numeric type (integer, logical, or real, excluding factors).
216 ///
217 /// Equivalent to R's `Rf_isNumeric(x)`.
218 #[must_use]
219 fn is_numeric(&self) -> bool;
220
221 /// Check if this SEXP is a number type (numeric or complex).
222 ///
223 /// Equivalent to R's `Rf_isNumber(x)`.
224 #[must_use]
225 fn is_number(&self) -> bool;
226
227 /// Check if this SEXP is an atomic vector.
228 ///
229 /// Returns true for logical, integer, real, complex, character, and raw vectors.
230 #[must_use]
231 fn is_vector_atomic(&self) -> bool;
232
233 /// Check if this SEXP is a vector list (VECSXP or EXPRSXP).
234 #[must_use]
235 fn is_vector_list(&self) -> bool;
236
237 /// Check if this SEXP is a vector (atomic vector or list).
238 #[must_use]
239 fn is_vector(&self) -> bool;
240
241 /// Check if this SEXP is an R "object" (has a class attribute).
242 #[must_use]
243 fn is_object(&self) -> bool;
244
245 // region: Coercion and scalar extraction
246
247 /// Coerce this SEXP to the given type, returning a new SEXP.
248 ///
249 /// The result is guaranteed to have the requested SEXPTYPE.
250 /// Equivalent to R's `Rf_coerceVector(x, target)`.
251 #[must_use]
252 fn coerce(&self, target: SEXPTYPE) -> SEXP;
253
254 /// Extract a scalar logical value.
255 ///
256 /// Returns `None` for `NA`. Coerces non-logical inputs.
257 /// Equivalent to R's `Rf_asLogical(x)`.
258 #[must_use]
259 fn as_logical(&self) -> Option<bool>;
260
261 /// Extract a scalar integer value.
262 ///
263 /// Returns `None` for `NA_integer_`. Coerces non-integer inputs.
264 /// Equivalent to R's `Rf_asInteger(x)`.
265 #[must_use]
266 fn as_integer(&self) -> Option<i32>;
267
268 /// Extract a scalar real value.
269 ///
270 /// Returns `None` for `NA_real_` (NaN). Coerces non-real inputs.
271 /// Equivalent to R's `Rf_asReal(x)`.
272 #[must_use]
273 fn as_real(&self) -> Option<f64>;
274
275 /// Extract a scalar CHARSXP from this SEXP.
276 ///
277 /// The result is guaranteed to be a CHARSXP.
278 /// Equivalent to R's `Rf_asChar(x)`.
279 #[must_use]
280 fn as_char(&self) -> SEXP;
281
282 // endregion
283
284 // region: Attribute access
285
286 /// Get an attribute by symbol.
287 #[must_use]
288 fn get_attr(&self, name: SEXP) -> SEXP;
289
290 /// Get an attribute by symbol, returning `None` for `R_NilValue`.
291 #[must_use]
292 fn get_attr_opt(&self, name: SEXP) -> Option<SEXP> {
293 let attr = self.get_attr(name);
294 if attr.is_nil() { None } else { Some(attr) }
295 }
296
297 /// Set an attribute by symbol.
298 fn set_attr(&self, name: SEXP, val: SEXP);
299
300 /// Get the `names` attribute.
301 #[must_use]
302 fn get_names(&self) -> SEXP;
303
304 /// Set the `names` attribute.
305 fn set_names(&self, names: SEXP);
306
307 /// Get the `class` attribute.
308 #[must_use]
309 fn get_class(&self) -> SEXP;
310
311 /// Set the `class` attribute.
312 fn set_class(&self, class: SEXP);
313
314 /// Get the `dim` attribute.
315 #[must_use]
316 fn get_dim(&self) -> SEXP;
317
318 /// Set the `dim` attribute.
319 fn set_dim(&self, dim: SEXP);
320
321 /// Get the `dimnames` attribute.
322 #[must_use]
323 fn get_dimnames(&self) -> SEXP;
324
325 /// Set the `dimnames` attribute.
326 fn set_dimnames(&self, dimnames: SEXP);
327
328 /// Get the `levels` attribute (factors).
329 #[must_use]
330 fn get_levels(&self) -> SEXP;
331
332 /// Set the `levels` attribute (factors).
333 fn set_levels(&self, levels: SEXP);
334
335 /// Get the `row.names` attribute.
336 #[must_use]
337 fn get_row_names(&self) -> SEXP;
338
339 /// Set the `row.names` attribute.
340 fn set_row_names(&self, row_names: SEXP);
341
342 /// Copy every attribute of `from` except `names`, `dim` and `dimnames` onto
343 /// this object, as R's `Rf_copyMostAttrib(from, self)` does.
344 ///
345 /// The rule is R's, used where a result built from a vector should keep
346 /// its class but not its shape (subsetting, `rbind` of typed parts):
347 ///
348 /// - `class`, `levels`, `tzone`, `units` and every other attribute are
349 /// copied. A copied attribute replaces one of the same name on `self`;
350 /// the rest of `self`'s attributes, its `names` included, stay.
351 /// - The values are shared, not duplicated: `self` and `from` point at the
352 /// same attribute objects.
353 /// - When `from` is an object (it has a `class` attribute), `self` is
354 /// marked as one. An object bit `self` already has stays set.
355 /// - `self`'s S4 bit is set to `from`'s.
356 ///
357 /// The attributes are not checked against `self`'s type: copying
358 /// `factor`'s class and levels onto a double vector gives an object R
359 /// considers malformed. R raises its error when `self` is `NULL`.
360 fn copy_most_attrib(&self, from: SEXP);
361
362 /// Check if this SEXP inherits from a class.
363 ///
364 /// Equivalent to R's `inherits(x, "class_name")`.
365 #[must_use]
366 fn inherits_class(&self, class: &std::ffi::CStr) -> bool;
367
368 /// Check whether this SEXP carries any attributes.
369 ///
370 /// Same answer as `!is.null(attributes(x))` in R. For a pairlist
371 /// (`LISTSXP`), tag names count, because `attributes()` reports them as a
372 /// `names` attribute: `pairlist(a = 1)` has attributes, `pairlist(1)`
373 /// does not. A `CHARSXP` (an element of a character vector) has none:
374 /// R uses its attribute slot for the global string cache, and
375 /// `identical()` skips it too.
376 ///
377 /// The check evaluates `attributes(quote(x))` in the base environment, so
378 /// it allocates a call and, when attributes exist, the list
379 /// `attributes()` returns. R's constant-time test `ANY_ATTRIB` only
380 /// exists from R 4.5.0 and miniextendr supports R 4.4. `ATTRIB` is not
381 /// part of R's API, and `R CMD check` (R >= 4.6) reports packages that
382 /// call it.
383 ///
384 /// To reproduce R's `identical(x, y)`, which compares attributes as well
385 /// as values, use [`is_identical`](Self::is_identical).
386 ///
387 /// ```ignore
388 /// // attributes(c(a = 1)) is list(names = "a")
389 /// assert!(named_vector.has_attributes());
390 /// assert!(!SEXP::scalar_logical(false).has_attributes());
391 /// ```
392 #[must_use]
393 fn has_attributes(&self) -> bool;
394
395 // endregion
396
397 // region: String element access
398
399 /// Get the i-th CHARSXP element from a STRSXP.
400 ///
401 /// Equivalent to R's `STRING_ELT(x, i)`.
402 #[must_use]
403 fn string_elt(&self, i: isize) -> SEXP;
404
405 /// Get the i-th string element as `Option<&str>`.
406 ///
407 /// Returns `None` for `NA_character_`. The returned `&str` borrows from R's
408 /// internal string cache (CHARSXP global pool) and is valid as long as the
409 /// parent STRSXP is protected from GC. The lifetime is tied to `&self` by
410 /// the borrow checker, but the true validity depends on GC protection —
411 /// do not hold the `&str` across allocation boundaries without ensuring
412 /// the SEXP remains protected.
413 #[must_use]
414 fn string_elt_str(&self, i: isize) -> Option<&str>;
415
416 /// Set the i-th CHARSXP element of a STRSXP.
417 ///
418 /// Equivalent to R's `SET_STRING_ELT(x, i, v)`.
419 fn set_string_elt(&self, i: isize, charsxp: SEXP);
420
421 /// Check if this CHARSXP is `NA_character_`.
422 #[must_use]
423 fn is_na_string(&self) -> bool;
424
425 // endregion
426
427 // region: List element access
428
429 /// Get the i-th element of a VECSXP (generic vector / list).
430 ///
431 /// Equivalent to R's `VECTOR_ELT(x, i)`.
432 #[must_use]
433 fn vector_elt(&self, i: isize) -> SEXP;
434
435 /// Set the i-th element of a VECSXP.
436 ///
437 /// Equivalent to R's `SET_VECTOR_ELT(x, i, v)`.
438 fn set_vector_elt(&self, i: isize, val: SEXP);
439
440 // endregion
441
442 // region: Typed single-element access
443
444 /// Get the i-th integer element.
445 #[must_use]
446 fn integer_elt(&self, i: isize) -> i32;
447 /// Get the i-th real element.
448 #[must_use]
449 fn real_elt(&self, i: isize) -> f64;
450 /// Get the i-th logical element (raw i32: 0/1/NA_LOGICAL).
451 #[must_use]
452 fn logical_elt(&self, i: isize) -> i32;
453 /// Get the i-th complex element.
454 #[must_use]
455 fn complex_elt(&self, i: isize) -> Rcomplex;
456 /// Get the i-th raw element.
457 #[must_use]
458 fn raw_elt(&self, i: isize) -> u8;
459
460 /// Set the i-th integer element.
461 fn set_integer_elt(&self, i: isize, v: i32);
462 /// Set the i-th real element.
463 fn set_real_elt(&self, i: isize, v: f64);
464 /// Set the i-th logical element (raw i32: 0/1/NA_LOGICAL).
465 fn set_logical_elt(&self, i: isize, v: i32);
466 /// Set the i-th complex element.
467 fn set_complex_elt(&self, i: isize, v: Rcomplex);
468 /// Set the i-th raw element.
469 fn set_raw_elt(&self, i: isize, v: u8);
470
471 // endregion
472
473 // region: Symbol and CHARSXP access
474
475 /// Get the print name (CHARSXP) of a symbol (SYMSXP).
476 ///
477 /// # Safety
478 ///
479 /// The SEXP must be a valid SYMSXP.
480 #[must_use]
481 fn printname(&self) -> SEXP;
482
483 /// Get the C string pointer from a CHARSXP.
484 ///
485 /// The returned pointer is valid as long as the CHARSXP is protected.
486 ///
487 /// # Safety
488 ///
489 /// The SEXP must be a valid CHARSXP.
490 #[must_use]
491 fn r_char(&self) -> *const ::std::os::raw::c_char;
492
493 /// Get a `&str` from a CHARSXP. Returns `None` for `NA_character_`.
494 #[must_use]
495 fn r_char_str(&self) -> Option<&str>;
496
497 // endregion
498
499 // region: Vector resizing
500
501 /// Resize a vector to a new length, returning a (possibly new) SEXP.
502 ///
503 /// If the new length is shorter, elements are truncated.
504 /// If longer, new elements are filled with NA/NULL.
505 /// Equivalent to R's `Rf_xlengthgets(x, newlen)`.
506 #[must_use]
507 fn resize(&self, newlen: R_xlen_t) -> SEXP;
508
509 // endregion
510
511 // region: Duplication
512
513 /// Deep-copy this SEXP. Equivalent to R's `Rf_duplicate(x)`.
514 #[must_use]
515 fn duplicate(&self) -> SEXP;
516
517 /// Shallow-copy this SEXP. Equivalent to R's `Rf_shallow_duplicate(x)`.
518 #[must_use]
519 fn shallow_duplicate(&self) -> SEXP;
520
521 // endregion
522
523 // region: Comparison
524
525 /// Check whether this SEXP is identical to `other` under the defaults of
526 /// R's `identical(x, y)`.
527 ///
528 /// Calls `R_compute_identical(x, y, IDENT_USE_CLOENV)`. Flag 16 is what
529 /// `identical()` passes with its default arguments: `num.eq`,
530 /// `single.NA`, `attrib.as.set`, `ignore.bytecode` and `ignore.srcref`
531 /// are `TRUE`, and `ignore.environment` and `extptr.as.ref` are `FALSE`.
532 /// Attributes take part in the comparison, so `structure(FALSE, foo = 1)`
533 /// is not identical to `FALSE`.
534 ///
535 /// The `==` operator on `SEXP` compares pointers, not values.
536 ///
537 /// ```ignore
538 /// // R's identical(x, FALSE)
539 /// let is_false = x.is_identical(SEXP::scalar_logical(false));
540 /// ```
541 #[must_use]
542 fn is_identical(&self, other: SEXP) -> bool;
543
544 // endregion
545
546 // region: Unchecked variants (bypass thread-check, for perf-critical paths)
547
548 /// Get the i-th CHARSXP from a STRSXP. No thread check.
549 ///
550 /// # Safety
551 /// Must be called from R's main thread.
552 #[must_use]
553 unsafe fn string_elt_unchecked(&self, i: isize) -> SEXP;
554 /// Set the i-th CHARSXP of a STRSXP. No thread check.
555 ///
556 /// # Safety
557 /// Must be called from R's main thread.
558 unsafe fn set_string_elt_unchecked(&self, i: isize, charsxp: SEXP);
559 /// Get the i-th element of a VECSXP. No thread check.
560 ///
561 /// # Safety
562 /// Must be called from R's main thread.
563 #[must_use]
564 unsafe fn vector_elt_unchecked(&self, i: isize) -> SEXP;
565 /// Set the i-th element of a VECSXP. No thread check.
566 ///
567 /// # Safety
568 /// Must be called from R's main thread.
569 unsafe fn set_vector_elt_unchecked(&self, i: isize, val: SEXP);
570 /// Get an attribute by symbol. No thread check.
571 ///
572 /// # Safety
573 /// Must be called from R's main thread.
574 #[must_use]
575 unsafe fn get_attr_unchecked(&self, name: SEXP) -> SEXP;
576 /// Set an attribute by symbol. No thread check.
577 ///
578 /// # Safety
579 /// Must be called from R's main thread.
580 unsafe fn set_attr_unchecked(&self, name: SEXP, val: SEXP);
581 /// Copy every attribute of `from` except `names`, `dim` and `dimnames`
582 /// onto this object, as [`copy_most_attrib`](Self::copy_most_attrib). No
583 /// thread check.
584 ///
585 /// # Safety
586 /// Must be called from R's main thread.
587 unsafe fn copy_most_attrib_unchecked(&self, from: SEXP);
588
589 /// Get C string pointer from a CHARSXP. No thread check.
590 ///
591 /// # Safety
592 /// Must be called from R's main thread. The SEXP must be a valid CHARSXP.
593 #[must_use]
594 unsafe fn r_char_unchecked(&self) -> *const ::std::os::raw::c_char;
595
596 // endregion
597}
598
599impl SexpExt for SEXP {
600 #[inline]
601 fn type_of(&self) -> SEXPTYPE {
602 unsafe { TYPEOF(*self) }
603 }
604
605 #[inline]
606 fn len(&self) -> usize {
607 unsafe { Rf_xlength(*self) as usize }
608 }
609
610 #[inline]
611 fn xlength(&self) -> R_xlen_t {
612 unsafe { Rf_xlength(*self) }
613 }
614
615 #[inline]
616 unsafe fn xlength_unchecked(&self) -> R_xlen_t {
617 unsafe { Rf_xlength_unchecked(*self) }
618 }
619
620 #[inline]
621 unsafe fn len_unchecked(&self) -> usize {
622 unsafe { Rf_xlength_unchecked(*self) as usize }
623 }
624
625 #[inline]
626 unsafe fn as_slice<T: RNativeType>(&self) -> &'static [T] {
627 debug_assert!(
628 self.type_of() == T::SEXP_TYPE,
629 "SEXP type mismatch: expected {:?}, got {:?}",
630 T::SEXP_TYPE,
631 self.type_of()
632 );
633 unsafe { crate::from_r::r_slice(DATAPTR_RO(*self).cast(), self.len()) }
634 }
635
636 #[inline]
637 unsafe fn as_mut_slice<T: RNativeType>(&self) -> &'static mut [T] {
638 debug_assert!(
639 self.type_of() == T::SEXP_TYPE,
640 "SEXP type mismatch: expected {:?}, got {:?}",
641 T::SEXP_TYPE,
642 self.type_of()
643 );
644 unsafe { crate::from_r::r_slice_mut(T::dataptr_mut(*self), self.len()) }
645 }
646
647 #[inline]
648 unsafe fn as_slice_unchecked<T: RNativeType>(&self) -> &'static [T] {
649 debug_assert!(
650 self.type_of() == T::SEXP_TYPE,
651 "SEXP type mismatch: expected {:?}, got {:?}",
652 T::SEXP_TYPE,
653 self.type_of()
654 );
655 unsafe { crate::from_r::r_slice(DATAPTR_RO_unchecked(*self).cast(), self.len_unchecked()) }
656 }
657
658 // Type checking methods
659
660 #[inline]
661 fn is_integer(&self) -> bool {
662 self.type_of() == SEXPTYPE::INTSXP
663 }
664
665 #[inline]
666 fn is_real(&self) -> bool {
667 self.type_of() == SEXPTYPE::REALSXP
668 }
669
670 #[inline]
671 fn is_logical(&self) -> bool {
672 self.type_of() == SEXPTYPE::LGLSXP
673 }
674
675 #[inline]
676 fn is_character(&self) -> bool {
677 self.type_of() == SEXPTYPE::STRSXP
678 }
679
680 #[inline]
681 fn is_raw(&self) -> bool {
682 self.type_of() == SEXPTYPE::RAWSXP
683 }
684
685 #[inline]
686 fn is_complex(&self) -> bool {
687 self.type_of() == SEXPTYPE::CPLXSXP
688 }
689
690 #[inline]
691 fn is_list(&self) -> bool {
692 self.type_of() == SEXPTYPE::VECSXP
693 }
694
695 #[inline]
696 fn is_external_ptr(&self) -> bool {
697 self.type_of() == SEXPTYPE::EXTPTRSXP
698 }
699
700 #[inline]
701 fn is_environment(&self) -> bool {
702 self.type_of() == SEXPTYPE::ENVSXP
703 }
704
705 #[inline]
706 fn is_symbol(&self) -> bool {
707 self.type_of() == SEXPTYPE::SYMSXP
708 }
709
710 #[inline]
711 fn is_language(&self) -> bool {
712 self.type_of() == SEXPTYPE::LANGSXP
713 }
714
715 #[inline]
716 fn is_altrep(&self) -> bool {
717 unsafe { ALTREP(*self) != 0 }
718 }
719
720 #[inline]
721 fn is_empty(&self) -> bool {
722 self.len() == 0
723 }
724
725 #[inline]
726 fn is_factor(&self) -> bool {
727 unsafe { Rf_isFactor(*self) != Rboolean::FALSE }
728 }
729
730 #[inline]
731 fn is_pair_list(&self) -> bool {
732 unsafe { Rf_isList(*self) != Rboolean::FALSE }
733 }
734
735 #[inline]
736 fn is_matrix(&self) -> bool {
737 unsafe { Rf_isMatrix(*self) != Rboolean::FALSE }
738 }
739
740 #[inline]
741 fn is_array(&self) -> bool {
742 unsafe { Rf_isArray(*self) != Rboolean::FALSE }
743 }
744
745 #[inline]
746 fn is_function(&self) -> bool {
747 unsafe { Rf_isFunction(*self) != Rboolean::FALSE }
748 }
749
750 #[inline]
751 fn is_s4(&self) -> bool {
752 unsafe { Rf_isS4(*self) != Rboolean::FALSE }
753 }
754
755 #[inline]
756 fn is_data_frame(&self) -> bool {
757 self.inherits_class(c"data.frame")
758 }
759
760 #[inline]
761 fn is_numeric(&self) -> bool {
762 let typ = self.type_of();
763 (typ == SEXPTYPE::INTSXP || typ == SEXPTYPE::LGLSXP || typ == SEXPTYPE::REALSXP)
764 && !self.is_factor()
765 }
766
767 #[inline]
768 fn is_number(&self) -> bool {
769 self.is_numeric() || self.is_complex()
770 }
771
772 #[inline]
773 fn is_vector_atomic(&self) -> bool {
774 matches!(
775 self.type_of(),
776 SEXPTYPE::LGLSXP
777 | SEXPTYPE::INTSXP
778 | SEXPTYPE::REALSXP
779 | SEXPTYPE::CPLXSXP
780 | SEXPTYPE::STRSXP
781 | SEXPTYPE::RAWSXP
782 )
783 }
784
785 #[inline]
786 fn is_vector_list(&self) -> bool {
787 let typ = self.type_of();
788 typ == SEXPTYPE::VECSXP || typ == SEXPTYPE::EXPRSXP
789 }
790
791 #[inline]
792 fn is_vector(&self) -> bool {
793 self.is_vector_atomic() || self.is_vector_list()
794 }
795
796 #[inline]
797 fn is_object(&self) -> bool {
798 unsafe { Rf_isObject(*self) != Rboolean::FALSE }
799 }
800
801 // region: Coercion and scalar extraction
802
803 #[inline]
804 fn coerce(&self, target: SEXPTYPE) -> SEXP {
805 unsafe { Rf_coerceVector(*self, target) }
806 }
807
808 #[inline]
809 fn as_logical(&self) -> Option<bool> {
810 let v = unsafe { Rf_asLogical(*self) };
811 if v.is_na() { None } else { Some(v != 0) }
812 }
813
814 #[inline]
815 fn as_integer(&self) -> Option<i32> {
816 let v = unsafe { Rf_asInteger(*self) };
817 if v.is_na() { None } else { Some(v) }
818 }
819
820 #[inline]
821 fn as_real(&self) -> Option<f64> {
822 let v = unsafe { Rf_asReal(*self) };
823 // `R_IsNA`: a computed NA is `None` too; any other NaN is a value.
824 if v.is_na() { None } else { Some(v) }
825 }
826
827 #[inline]
828 fn as_char(&self) -> SEXP {
829 unsafe { Rf_asChar(*self) }
830 }
831
832 // endregion
833
834 // region: Attribute access
835
836 #[inline]
837 fn get_attr(&self, name: SEXP) -> SEXP {
838 unsafe { Rf_getAttrib(*self, name) }
839 }
840
841 #[inline]
842 fn set_attr(&self, name: SEXP, val: SEXP) {
843 unsafe {
844 Rf_setAttrib(*self, name, val);
845 }
846 }
847
848 #[inline]
849 fn get_names(&self) -> SEXP {
850 unsafe { Rf_getAttrib(*self, R_NamesSymbol) }
851 }
852
853 #[inline]
854 fn set_names(&self, names: SEXP) {
855 unsafe {
856 Rf_namesgets(*self, names);
857 }
858 }
859
860 #[inline]
861 fn get_class(&self) -> SEXP {
862 unsafe { Rf_getAttrib(*self, R_ClassSymbol) }
863 }
864
865 #[inline]
866 fn set_class(&self, class: SEXP) {
867 unsafe {
868 Rf_classgets(*self, class);
869 }
870 }
871
872 #[inline]
873 fn get_dim(&self) -> SEXP {
874 unsafe { Rf_getAttrib(*self, R_DimSymbol) }
875 }
876
877 #[inline]
878 fn set_dim(&self, dim: SEXP) {
879 unsafe {
880 Rf_dimgets(*self, dim);
881 }
882 }
883
884 #[inline]
885 fn get_dimnames(&self) -> SEXP {
886 unsafe { Rf_getAttrib(*self, R_DimNamesSymbol) }
887 }
888
889 #[inline]
890 fn set_dimnames(&self, dimnames: SEXP) {
891 unsafe {
892 Rf_dimnamesgets(*self, dimnames);
893 }
894 }
895
896 #[inline]
897 fn get_levels(&self) -> SEXP {
898 unsafe { Rf_getAttrib(*self, R_LevelsSymbol) }
899 }
900
901 #[inline]
902 fn set_levels(&self, levels: SEXP) {
903 unsafe {
904 Rf_setAttrib(*self, R_LevelsSymbol, levels);
905 }
906 }
907
908 #[inline]
909 fn get_row_names(&self) -> SEXP {
910 unsafe { Rf_getAttrib(*self, R_RowNamesSymbol) }
911 }
912
913 #[inline]
914 fn set_row_names(&self, row_names: SEXP) {
915 unsafe {
916 Rf_setAttrib(*self, R_RowNamesSymbol, row_names);
917 }
918 }
919
920 #[inline]
921 fn copy_most_attrib(&self, from: SEXP) {
922 unsafe { Rf_copyMostAttrib(from, *self) }
923 }
924
925 #[inline]
926 fn inherits_class(&self, class: &std::ffi::CStr) -> bool {
927 unsafe { Rf_inherits(*self, class.as_ptr()) != Rboolean::FALSE }
928 }
929
930 fn has_attributes(&self) -> bool {
931 // A CHARSXP's ATTRIB slot links the string-cache hash chain;
932 // `attributes()` would walk those CHARSXPs as if they were pairlist
933 // nodes.
934 if self.type_of() == SEXPTYPE::CHARSXP {
935 return false;
936 }
937 // `attributes` is a builtin, so its argument is evaluated: wrap the
938 // value in `quote()` so a symbol, call or promise is passed as is.
939 unsafe {
940 let scope = ProtectScope::new();
941 let quoted = scope.protect_raw(Rf_lang2(Rf_install(c"quote".as_ptr()), *self));
942 let call = scope.protect_raw(Rf_lang2(Rf_install(c"attributes".as_ptr()), quoted));
943 !Rf_eval(call, R_BaseEnv).is_nil()
944 }
945 }
946
947 // endregion
948
949 // region: String element access
950
951 #[inline]
952 fn string_elt(&self, i: isize) -> SEXP {
953 unsafe { STRING_ELT(*self, i) }
954 }
955
956 #[inline]
957 fn string_elt_str(&self, i: isize) -> Option<&str> {
958 unsafe {
959 let charsxp = STRING_ELT(*self, i);
960 if std::ptr::addr_eq(charsxp.0, R_NaString.0) {
961 return None;
962 }
963 let p = R_CHAR(charsxp);
964 Some(std::ffi::CStr::from_ptr(p).to_str().unwrap_or(""))
965 }
966 }
967
968 #[inline]
969 fn set_string_elt(&self, i: isize, charsxp: SEXP) {
970 unsafe { SET_STRING_ELT(*self, i, charsxp) }
971 }
972
973 #[inline]
974 fn is_na_string(&self) -> bool {
975 unsafe { std::ptr::addr_eq(self.0, R_NaString.0) }
976 }
977
978 // endregion
979
980 // region: List element access
981
982 #[inline]
983 fn vector_elt(&self, i: isize) -> SEXP {
984 unsafe { VECTOR_ELT(*self, i) }
985 }
986
987 #[inline]
988 fn set_vector_elt(&self, i: isize, val: SEXP) {
989 unsafe {
990 SET_VECTOR_ELT(*self, i, val);
991 }
992 }
993
994 // endregion
995
996 // region: Typed single-element access
997
998 #[inline]
999 fn integer_elt(&self, i: isize) -> i32 {
1000 unsafe { INTEGER_ELT(*self, i) }
1001 }
1002 #[inline]
1003 fn real_elt(&self, i: isize) -> f64 {
1004 unsafe { REAL_ELT(*self, i) }
1005 }
1006 #[inline]
1007 fn logical_elt(&self, i: isize) -> i32 {
1008 unsafe { LOGICAL_ELT(*self, i) }
1009 }
1010 #[inline]
1011 fn complex_elt(&self, i: isize) -> Rcomplex {
1012 unsafe { COMPLEX_ELT(*self, i) }
1013 }
1014 #[inline]
1015 fn raw_elt(&self, i: isize) -> u8 {
1016 unsafe { RAW_ELT(*self, i) }
1017 }
1018 #[inline]
1019 fn set_integer_elt(&self, i: isize, v: i32) {
1020 unsafe { SET_INTEGER_ELT(*self, i, v) }
1021 }
1022 #[inline]
1023 fn set_real_elt(&self, i: isize, v: f64) {
1024 unsafe { SET_REAL_ELT(*self, i, v) }
1025 }
1026 #[inline]
1027 fn set_logical_elt(&self, i: isize, v: i32) {
1028 unsafe { SET_LOGICAL_ELT(*self, i, v) }
1029 }
1030 #[inline]
1031 fn set_complex_elt(&self, i: isize, v: Rcomplex) {
1032 unsafe { SET_COMPLEX_ELT(*self, i, v) }
1033 }
1034 #[inline]
1035 fn set_raw_elt(&self, i: isize, v: u8) {
1036 unsafe { SET_RAW_ELT(*self, i, v) }
1037 }
1038
1039 // endregion
1040
1041 // region: Symbol and CHARSXP access
1042
1043 #[inline]
1044 fn printname(&self) -> SEXP {
1045 unsafe { PRINTNAME(*self) }
1046 }
1047
1048 #[inline]
1049 fn r_char(&self) -> *const ::std::os::raw::c_char {
1050 unsafe { R_CHAR(*self) }
1051 }
1052
1053 #[inline]
1054 fn r_char_str(&self) -> Option<&str> {
1055 if self.is_na_string() {
1056 return None;
1057 }
1058 let p = unsafe { R_CHAR(*self) };
1059 Some(
1060 unsafe { std::ffi::CStr::from_ptr(p) }
1061 .to_str()
1062 .unwrap_or(""),
1063 )
1064 }
1065
1066 // endregion
1067
1068 // region: Vector resizing
1069
1070 #[inline]
1071 fn resize(&self, newlen: R_xlen_t) -> SEXP {
1072 unsafe { Rf_xlengthgets(*self, newlen) }
1073 }
1074
1075 // endregion
1076
1077 // region: Duplication
1078
1079 #[inline]
1080 fn duplicate(&self) -> SEXP {
1081 unsafe { Rf_duplicate(*self) }
1082 }
1083
1084 #[inline]
1085 fn shallow_duplicate(&self) -> SEXP {
1086 unsafe { Rf_shallow_duplicate(*self) }
1087 }
1088
1089 // endregion
1090
1091 // region: Comparison
1092
1093 #[inline]
1094 fn is_identical(&self, other: SEXP) -> bool {
1095 unsafe { R_compute_identical(*self, other, IDENT_USE_CLOENV) != Rboolean::FALSE }
1096 }
1097
1098 // endregion
1099
1100 // region: Unchecked variants
1101
1102 #[inline]
1103 unsafe fn string_elt_unchecked(&self, i: isize) -> SEXP {
1104 unsafe { STRING_ELT_unchecked(*self, i) }
1105 }
1106
1107 #[inline]
1108 unsafe fn set_string_elt_unchecked(&self, i: isize, charsxp: SEXP) {
1109 unsafe { SET_STRING_ELT_unchecked(*self, i, charsxp) }
1110 }
1111
1112 #[inline]
1113 unsafe fn vector_elt_unchecked(&self, i: isize) -> SEXP {
1114 unsafe { VECTOR_ELT_unchecked(*self, i) }
1115 }
1116
1117 #[inline]
1118 unsafe fn set_vector_elt_unchecked(&self, i: isize, val: SEXP) {
1119 unsafe {
1120 SET_VECTOR_ELT_unchecked(*self, i, val);
1121 }
1122 }
1123
1124 #[inline]
1125 unsafe fn get_attr_unchecked(&self, name: SEXP) -> SEXP {
1126 unsafe { Rf_getAttrib_unchecked(*self, name) }
1127 }
1128
1129 #[inline]
1130 unsafe fn set_attr_unchecked(&self, name: SEXP, val: SEXP) {
1131 unsafe {
1132 Rf_setAttrib_unchecked(*self, name, val);
1133 }
1134 }
1135
1136 #[inline]
1137 unsafe fn copy_most_attrib_unchecked(&self, from: SEXP) {
1138 unsafe { Rf_copyMostAttrib_unchecked(from, *self) }
1139 }
1140
1141 #[inline]
1142 unsafe fn r_char_unchecked(&self) -> *const ::std::os::raw::c_char {
1143 unsafe { R_CHAR_unchecked(*self) }
1144 }
1145
1146 // endregion
1147}
1148
1149/// Pairlist (cons cell) operations on a `SEXP`: R's calls (`LANGSXP`) and
1150/// pairlists (`LISTSXP`, such as `formals(f)` or `pairlist(...)`).
1151///
1152/// Each node has three slots: CAR (the value), CDR (the next node, `NULL`
1153/// after the last) and TAG (the name, a symbol, or `NULL` when unnamed). A
1154/// call's first node holds the function, the following nodes its arguments:
1155///
1156/// ```ignore
1157/// use miniextendr_api::{PairListExt, SEXP};
1158///
1159/// // `call` is `c(time = TAD, dose = NA)`, rooted by the caller.
1160/// fn roles(call: SEXP) -> Vec<(Option<String>, SEXP)> {
1161/// call.args().map(|(name, value)| (name.map(str::to_owned), value)).collect()
1162/// }
1163/// ```
1164///
1165/// The values read from a node are reachable from the pairlist, so they stay
1166/// rooted while it does. A pairlist R handed you (a `.Call()` argument, a
1167/// quosure's expression) is shared: change only a node you own, such as one
1168/// of a `shallow_duplicate()` of the call.
1169pub trait PairListExt {
1170 /// A new pairlist node, `CONS(self, cdr)`: this SEXP as its value, `cdr`
1171 /// as the rest. The node is unprotected.
1172 fn cons(self, cdr: SEXP) -> SEXP;
1173
1174 /// A new call node, `LCONS(self, cdr)`: this SEXP (the function) as its
1175 /// value, `cdr` (the arguments) as the rest. The node is unprotected.
1176 fn lcons(self, cdr: SEXP) -> SEXP;
1177
1178 /// The value of this node (`CAR`). On a call, the function.
1179 fn car(&self) -> SEXP;
1180
1181 /// The rest of the pairlist after this node (`CDR`): the next node, or
1182 /// `NULL` after the last. On a call, its arguments.
1183 fn cdr(&self) -> SEXP;
1184
1185 /// The name of this node (`TAG`): a symbol, or `NULL` when it has none.
1186 fn tag(&self) -> SEXP;
1187
1188 /// The arguments of a call, or the elements of a pairlist, as
1189 /// `(name, value)`. The name is `None` for an unnamed one; a repeated
1190 /// name is kept.
1191 ///
1192 /// | `self` | yields |
1193 /// |---|---|
1194 /// | a call, `f(a, b = 2)` | its arguments, `(None, a)` then `(Some("b"), 2)`; the function is not one of them |
1195 /// | a pairlist, `pairlist(a = 1, 2)` | every element, `(Some("a"), 1)` then `(None, 2)` |
1196 /// | `NULL` (the empty pairlist) | nothing |
1197 ///
1198 /// An empty argument, the slot of `f(a = )`, is the missing argument
1199 /// ([`SEXP::missing_arg()`]); a `NULL` argument does not end the walk.
1200 /// The names are the print names of the tags; the borrow of `self` stands
1201 /// for the root that keeps the pairlist alive.
1202 ///
1203 /// # Panics
1204 ///
1205 /// When `self` is not a call, a pairlist or `NULL`.
1206 fn args(&self) -> PairListArgs<'_>;
1207
1208 /// Set the name of this node (`SET_TAG`): a symbol, or `NULL`.
1209 ///
1210 /// Only on a node you own, such as one of a `shallow_duplicate()` of the
1211 /// call, never on a call or pairlist R handed you: R shares them.
1212 fn set_tag(&self, tag: SEXP);
1213
1214 /// Set the value of this node (`SETCAR`); returns `value`.
1215 ///
1216 /// Only on a node you own, such as one of a `shallow_duplicate()` of the
1217 /// call, never on a call or pairlist R handed you: R shares them.
1218 fn set_car(&self, value: SEXP) -> SEXP;
1219
1220 /// Set the rest of the pairlist after this node (`SETCDR`); returns
1221 /// `tail`.
1222 ///
1223 /// Only on a node you own, such as one of a `shallow_duplicate()` of the
1224 /// call, never on a call or pairlist R handed you: R shares them.
1225 fn set_cdr(&self, tail: SEXP) -> SEXP;
1226
1227 /// Create a cons cell (no thread check).
1228 /// # Safety
1229 /// Must be called from R's main thread.
1230 unsafe fn cons_unchecked(self, cdr: SEXP) -> SEXP;
1231
1232 /// Get the CAR (no thread check).
1233 /// # Safety
1234 /// Must be called from R's main thread.
1235 unsafe fn car_unchecked(&self) -> SEXP;
1236
1237 /// Get the CDR (no thread check).
1238 /// # Safety
1239 /// Must be called from R's main thread.
1240 unsafe fn cdr_unchecked(&self) -> SEXP;
1241
1242 /// Set the TAG (no thread check).
1243 /// # Safety
1244 /// Must be called from R's main thread.
1245 unsafe fn set_tag_unchecked(&self, tag: SEXP);
1246
1247 /// Set the CAR (no thread check).
1248 /// # Safety
1249 /// Must be called from R's main thread.
1250 unsafe fn set_car_unchecked(&self, value: SEXP) -> SEXP;
1251
1252 /// Set the CDR (no thread check).
1253 /// # Safety
1254 /// Must be called from R's main thread.
1255 unsafe fn set_cdr_unchecked(&self, tail: SEXP) -> SEXP;
1256}
1257
1258impl PairListExt for SEXP {
1259 #[inline]
1260 fn cons(self, cdr: SEXP) -> SEXP {
1261 unsafe { Rf_cons(self, cdr) }
1262 }
1263
1264 #[inline]
1265 fn lcons(self, cdr: SEXP) -> SEXP {
1266 unsafe { Rf_lcons(self, cdr) }
1267 }
1268
1269 #[inline]
1270 fn car(&self) -> SEXP {
1271 unsafe { CAR(*self) }
1272 }
1273
1274 #[inline]
1275 fn cdr(&self) -> SEXP {
1276 unsafe { CDR(*self) }
1277 }
1278
1279 #[inline]
1280 fn tag(&self) -> SEXP {
1281 unsafe { TAG(*self) }
1282 }
1283
1284 fn args(&self) -> PairListArgs<'_> {
1285 let first = match self.type_of() {
1286 SEXPTYPE::LANGSXP => self.cdr(),
1287 SEXPTYPE::LISTSXP | SEXPTYPE::NILSXP => *self,
1288 other => panic!(
1289 "`args()` reads a call, a pairlist or NULL, not {}",
1290 other.type_name()
1291 ),
1292 };
1293 PairListArgs {
1294 node: first,
1295 _list: PhantomData,
1296 }
1297 }
1298
1299 #[inline]
1300 fn set_tag(&self, tag: SEXP) {
1301 unsafe { SET_TAG(*self, tag) }
1302 }
1303
1304 #[inline]
1305 fn set_car(&self, value: SEXP) -> SEXP {
1306 unsafe { SETCAR(*self, value) }
1307 }
1308
1309 #[inline]
1310 fn set_cdr(&self, tail: SEXP) -> SEXP {
1311 unsafe { SETCDR(*self, tail) }
1312 }
1313
1314 #[inline]
1315 unsafe fn cons_unchecked(self, cdr: SEXP) -> SEXP {
1316 unsafe { Rf_cons_unchecked(self, cdr) }
1317 }
1318
1319 #[inline]
1320 unsafe fn car_unchecked(&self) -> SEXP {
1321 unsafe { CAR_unchecked(*self) }
1322 }
1323
1324 #[inline]
1325 unsafe fn cdr_unchecked(&self) -> SEXP {
1326 unsafe { CDR_unchecked(*self) }
1327 }
1328
1329 #[inline]
1330 unsafe fn set_tag_unchecked(&self, tag: SEXP) {
1331 unsafe { SET_TAG_unchecked(*self, tag) }
1332 }
1333
1334 #[inline]
1335 unsafe fn set_car_unchecked(&self, value: SEXP) -> SEXP {
1336 unsafe { SETCAR_unchecked(*self, value) }
1337 }
1338
1339 #[inline]
1340 unsafe fn set_cdr_unchecked(&self, tail: SEXP) -> SEXP {
1341 unsafe { SETCDR_unchecked(*self, tail) }
1342 }
1343}
1344
1345/// The arguments of a call, or the elements of a pairlist, as
1346/// `(name, value)`: see [`PairListExt::args`].
1347///
1348/// The `'a` borrow stands for the root that keeps the pairlist alive; the
1349/// names borrow it too.
1350pub struct PairListArgs<'a> {
1351 /// The next node to read, `NULL` once every node has been read.
1352 node: SEXP,
1353 /// Borrows the root of the pairlist.
1354 _list: PhantomData<&'a SEXP>,
1355}
1356
1357impl<'a> Iterator for PairListArgs<'a> {
1358 type Item = (Option<&'a str>, SEXP);
1359
1360 fn next(&mut self) -> Option<Self::Item> {
1361 let node = self.node;
1362 if node.is_nil() {
1363 return None;
1364 }
1365 self.node = node.cdr();
1366 let tag = node.tag();
1367 // A tag is a symbol; its print name lives for the session.
1368 let name = if tag.is_symbol() {
1369 let name = unsafe { charsxp_to_str(tag.printname()) };
1370 (!name.is_empty()).then_some(name)
1371 } else {
1372 None
1373 };
1374 Some((name, node.car()))
1375 }
1376}
1377
1378impl std::iter::FusedIterator for PairListArgs<'_> {}