Skip to main content

runmat_runtime/context/
capability.rs

1use crate::{build_runtime_error, RuntimeError};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5/// Stable families of optional runtime capability.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum RuntimeCapability {
9    Builtin,
10    Call,
11    Workspace,
12    Object,
13    Host,
14    Error,
15    Cancellation,
16    Acceleration,
17    Placement,
18    Native,
19    Foreign,
20    Parallel,
21}
22
23impl RuntimeCapability {
24    pub const fn as_str(self) -> &'static str {
25        match self {
26            Self::Builtin => "builtin",
27            Self::Call => "call",
28            Self::Workspace => "workspace",
29            Self::Object => "object",
30            Self::Host => "host",
31            Self::Error => "error",
32            Self::Cancellation => "cancellation",
33            Self::Acceleration => "acceleration",
34            Self::Placement => "placement",
35            Self::Native => "native",
36            Self::Foreign => "foreign",
37            Self::Parallel => "parallel",
38        }
39    }
40}
41
42/// Stable failure returned when an invocation requests a service its host did
43/// not compose. This is intentionally target-neutral: browser/WASM reports the
44/// same capability failure as a native host with that service disabled.
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(deny_unknown_fields)]
47pub struct RuntimeCapabilityError {
48    pub capability: RuntimeCapability,
49    pub operation: String,
50}
51
52impl RuntimeCapabilityError {
53    pub const IDENTIFIER: &'static str = "RunMat:RuntimeContext:CapabilityUnavailable";
54
55    pub fn new(capability: RuntimeCapability, operation: impl Into<String>) -> Self {
56        Self {
57            capability,
58            operation: operation.into(),
59        }
60    }
61
62    pub fn into_runtime_error(self) -> RuntimeError {
63        build_runtime_error(self.to_string())
64            .with_identifier(Self::IDENTIFIER)
65            .build()
66    }
67}
68
69impl fmt::Display for RuntimeCapabilityError {
70    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(
72            formatter,
73            "runtime capability '{}' is unavailable for {}",
74            self.capability.as_str(),
75            self.operation
76        )
77    }
78}
79
80impl std::error::Error for RuntimeCapabilityError {}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn capability_failure_has_stable_identifier_and_payload() {
88        let error = RuntimeCapabilityError::new(RuntimeCapability::Foreign, "invoke JNI callback");
89        let json = serde_json::to_string(&error).expect("serialize capability error");
90        assert_eq!(
91            json,
92            r#"{"capability":"foreign","operation":"invoke JNI callback"}"#
93        );
94        let runtime = error.into_runtime_error();
95        assert_eq!(
96            runtime.identifier(),
97            Some(RuntimeCapabilityError::IDENTIFIER)
98        );
99        assert!(runtime.to_string().contains("foreign"));
100    }
101}