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>:NAelements and parse failures are collected across the whole vector into one batchedInvalidValue, 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>>:NAelements →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.