Skip to main content

try_from_sexp_via_str_parse

Macro try_from_sexp_via_str_parse 

Source
macro_rules! try_from_sexp_via_str_parse {
    ($ty:ty, $label:literal, |$s:ident| $parse:expr) => { ... };
}
Expand description

Give a type parsed from an R string its four TryFromSexp conversions: T, Option<T>, Vec<T> and Vec<Option<T>>.

Expands in any crate. It implements ParseRStr (the parse step), TryFromSexp for the type, and TryFromSexpElement, through which the container blankets in miniextendr-api convert Option<T>, Vec<T> and Vec<Option<T>>. These are all impls on your own type, so the orphan rule allows them; the container impls themselves can only live in miniextendr-api (#1766).

The built-in uuid, url, regex and num-bigint conversions use this macro, so a type given it reads R input the same way:

  • T: NA_character_ / NULL → SexpError::Na; a parse failure → InvalidValue("invalid <label>: <err>").
  • Option<T>: NA_character_ / NULL → None.
  • Vec<T>: NA elements and parse failures are collected across the whole vector into one batched InvalidValue, each reason followed by the 1-based positions that failed with it: NA is not allowed (element 2); invalid <label>: <err> (elements 3, 5). The first 10 failures are listed and the remainder is summarized as "and N more".
  • Vec<Option<T>>: NA elements → None; parse failures batch as above.

All four read only character input (TryFromSexp::CHARACTER_ONLY).

The parse body is a closure-style |s| expr where s: &str, returning Result<T, E> with E: Display.

ⓘ
pub struct Slug(String);

impl std::str::FromStr for Slug {
    type Err = &'static str;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() || !s.chars().all(|c| c.is_ascii_lowercase() || c == '-') {
            return Err("expected lowercase letters and '-'");
        }
        Ok(Slug(s.to_owned()))
    }
}

miniextendr_api::try_from_sexp_via_str_parse!(Slug, "slug", |s| s.parse::<Slug>());

#[miniextendr]
pub fn slugs(x: Vec<Option<Slug>>) -> i32 { /* ... */ }

The type must not be generic, and the macro must not be used on a type that also derives TryFromSexp: both implement TryFromSexpElement.