Skip to main content

RConditionData

Trait RConditionData 

Source
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)) # family

Detection 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§

Source

fn message(&self) -> String

The condition message (conditionMessage(e)).

Provided Methods§

Source

fn class(&self) -> Vec<String>

User classes, most specific first; empty for none.

Source

fn data(&self) -> Option<ConditionData>

Structured fields spliced into the condition object (e$<name>).

Source

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.

Source

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§

Source§

impl RConditionData for RError

Source§

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.