pub trait RConditionData {
// Required method
fn message(&self) -> String;
// Provided methods
fn class(&self) -> Vec<String> { ... }
fn data(&self) -> Option<ConditionData> { ... }
fn call(&self) -> ConditionCall { ... }
fn argument_message(&self) -> Option<String> { ... }
}Expand description
Give a Result<T, E> error type an R class vector and structured fields.
Every #[miniextendr] function or method returning Result<T, E> raises
Err(e) as an R error. Implement this trait for E (or return
RError, which implements it) and the Err arm raises
c(<class()…>, "rust_error", "simpleError", "error", "condition") with
every data() field readable as e$<name>, so a thiserror-style error enum
can keep ? composition and give R handlers something to dispatch on.
Without an impl, the Err arm falls back in two steps. Under the serde
feature an E: serde::Serialize + Display is classed from its serde shape
(see serde_err_parts: variant → member class, fields → data, Display
→ message). Anything else is a bare rust_error whose message is
format!("{e:?}").
use miniextendr_api::condition::{ConditionData, RConditionData};
#[derive(Debug, thiserror::Error)]
pub enum PkgError {
#[error("column `{column}` is missing")]
MissingColumn { column: String },
#[error("{value} exceeds {max}")]
TooLarge { value: f64, max: f64 },
}
impl RConditionData for PkgError {
fn message(&self) -> String { self.to_string() }
fn class(&self) -> Vec<String> {
let member = match self {
PkgError::MissingColumn { .. } => "pkg_error_missing_column",
PkgError::TooLarge { .. } => "pkg_error_too_large",
};
vec![member.into(), "pkg_error".into()]
}
fn data(&self) -> Option<ConditionData> {
Some(match self {
PkgError::MissingColumn { column } => vec![("column".into(), column.clone().into())],
PkgError::TooLarge { value, max } => vec![("value".into(), (*value).into()), ("max".into(), (*max).into())],
})
}
}
#[miniextendr]
pub fn check(value: f64) -> Result<f64, PkgError> { /* uses `?` freely */ }tryCatch(check(1e9), pkg_error_too_large = function(e) e$max) # member
tryCatch(check(1e9), pkg_error = function(e) conditionMessage(e)) # familyDetection is by trait, not by attribute: the generated Err arm probes
E: RConditionData first and falls back to the Debug rendering
otherwise, so existing Result<T, String> / Result<T, MyDebugError>
functions are unchanged. The condition’s kind stays "result_err".
The same impl classes argument-conversion failures: when a parameter
type’s TryFromSexp::Error implements this trait, a failed conversion
raises its classes and fields with kind = "conversion", the message
'<p>' must be <expected>: <message()> (or invalid '<p>' argument: <message()> when the argument’s type has no R-facing expectation), the
parameter’s R name as e$param and the Rust type as e$rust_type
(unless data() has a field of the same name, which then wins). An error
type without the impl renders with Display and keeps the plain
rust_error class vector.
data() field names must not be message, call or kind (see
RESERVED_CONDITION_FIELDS); a reserved name raises a plain
rust_error explaining the clash, so rename such a field at the source.
call() returning ConditionCall::None drops
the call (conditionCall(e) is NULL), for errors and warnings about the
data rather than the call.
Required Methods§
Provided Methods§
Sourcefn data(&self) -> Option<ConditionData>
fn data(&self) -> Option<ConditionData>
Structured fields spliced into the condition object (e$<name>).
Sourcefn call(&self) -> ConditionCall
fn call(&self) -> ConditionCall
Whether the condition reports a call. ConditionCall::None makes
conditionCall() NULL, like R’s call. = FALSE, for a condition
about the data rather than the call; the default keeps the call the
transport captured. Honoured by defer_* and by Result returns;
an argument-conversion error always keeps the call (see
conversion_err_parts). #[condition(call = none)] on a derived
type or variant overrides this.
Sourcefn argument_message(&self) -> Option<String>
fn argument_message(&self) -> Option<String>
The whole message when this error refuses an argument, or None.
When the error comes out of a parameter’s conversion (the type’s
TryFromSexp, or its check_sexp
under Option<T> / Vec<T> / Vec<Option<T>>), a Some is the
condition’s message as given, in place of
'<p>' must be <expected>: <message()>. The classes, kind,
e$param, e$rust_type and the call are unchanged. This is how a type
words a refusal once for every parameter of that type, as
#[miniextendr(inherits(..., message = ...))] does for one parameter:
both use the author’s message as given and name the argument in
e$param. Ignored everywhere else (Result returns, defer_*), and
inside an Either, whose message names both arms. For a refused
element of a list of objects (Vec<T> of a newtype over List), it
is that element’s reason, before the element’s position, in the
prefixed batched message: an element is not the whole argument.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementors§
impl RConditionData for RError
impl RConditionData for REvalError
Returned as a #[miniextendr] function’s Err, the caught error is raised
again with its message and its specific classes,
ahead of the rust_error layers; the call is the function’s own.