Skip to main content

TryFromSexp

Derive Macro TryFromSexp 

Source
#[derive(TryFromSexp)]
{
    // Attributes available to this derive:
    #[try_from_sexp]
}
Expand description

Derive TryFromSexp for a single-field newtype: forward the R → Rust conversion to the inner type.

Generates a scalar TryFromSexp impl that delegates to the inner type (so the newtype inherits its exact SEXPTYPE checks, NA policy, and error text), plus a TryFromSexpElement marker impl. The marker lets miniextendr-api’s container blankets light up Vec<T> / Option<T> / Vec<Option<T>> automatically, each one where the inner type’s container converts.

Don’t use it on a type that also gets try_from_sexp_via_str_parse!: both implement TryFromSexpElement.

§Usage

ⓘ
use uuid::Uuid;

#[derive(TryFromSexp)]            // R -> Rust only
struct Pattern(regex::Regex);

#[derive(TryFromSexp, IntoR)]     // round-trip; Vec/Option containers work too
struct UserId(Uuid);

Direction is chosen by which derive you list — derive only TryFromSexp for inner types that read from R but cannot be written back (e.g. regex::Regex).

§Refusing values: #[try_from_sexp(validate = path)]

The containers read the inner type and wrap it; they never run the newtype’s own try_from_sexp. To refuse a value the inner type accepts, name a check, fn(SEXP) -> Result<(), E> with E: Into<SexpError>. It becomes TryFromSexpElement::check_sexp, which runs on the R value before it is read, in every shape: the scalar, on any input but NULL for Option<T>, and for Vec<T> and Vec<Option<T>> once on the whole vector of an atomic inner type, or on each element (but NULL) of a list when the inner type is List (a list of R objects, each with its own class).

ⓘ
use miniextendr_api::condition::RError;
use miniextendr_api::{SEXP, SexpExt, TryFromSexp};

fn plain_number(x: SEXP) -> Result<(), RError> {
    if x.inherits_class(c"difftime") {
        return Err(RError::new("give the duration as a plain number").class("pkg_unit_error"));
    }
    Ok(())
}

#[derive(TryFromSexp)]
#[try_from_sexp(validate = plain_number)]
struct Duration(f64);

An RError refusal (or one of any RConditionData type) keeps its classes and fields on the argument error, before the crate’s conversion_error_class, with e$param and the message '<p>' must be <expected>: <message>. With validate, the newtype’s scalar error is SexpError, so the inner type’s error must convert into it, as every built-in conversion error and every RConditionData type does. validate is the only key, and the attribute goes on the struct.