orcs_runtime/engine/
error.rs1use orcs_event::EventCategory;
29use orcs_types::{ChannelId, ComponentId, ErrorCode, RequestId};
30use thiserror::Error;
31
32#[derive(Debug, Clone, Error)]
48pub enum EngineError {
49 #[error("component not found: {0}")]
51 ComponentNotFound(ComponentId),
52
53 #[error("channel not found: {0}")]
55 ChannelNotFound(ChannelId),
56
57 #[error("request target is required")]
59 NoTarget,
60
61 #[error("send failed: {0}")]
63 SendFailed(String),
64
65 #[error("channel closed")]
67 ChannelClosed,
68
69 #[error("engine not running")]
71 NotRunning,
72
73 #[error("request timed out: {0}")]
75 Timeout(RequestId),
76
77 #[error("component error: {0}")]
79 ComponentFailed(String),
80
81 #[error("no subscriber for category: {0}")]
83 NoSubscriber(EventCategory),
84
85 #[error("package error: {0}")]
87 PackageFailed(String),
88
89 #[error("component does not support packages: {0}")]
91 PackageNotSupported(ComponentId),
92}
93
94impl ErrorCode for EngineError {
95 fn code(&self) -> &'static str {
96 match self {
97 Self::ComponentNotFound(_) => "ENGINE_COMPONENT_NOT_FOUND",
98 Self::ChannelNotFound(_) => "ENGINE_CHANNEL_NOT_FOUND",
99 Self::NoTarget => "ENGINE_NO_TARGET",
100 Self::SendFailed(_) => "ENGINE_SEND_FAILED",
101 Self::ChannelClosed => "ENGINE_CHANNEL_CLOSED",
102 Self::NotRunning => "ENGINE_NOT_RUNNING",
103 Self::Timeout(_) => "ENGINE_TIMEOUT",
104 Self::ComponentFailed(_) => "ENGINE_COMPONENT_FAILED",
105 Self::NoSubscriber(_) => "ENGINE_NO_SUBSCRIBER",
106 Self::PackageFailed(_) => "ENGINE_PACKAGE_FAILED",
107 Self::PackageNotSupported(_) => "ENGINE_PACKAGE_NOT_SUPPORTED",
108 }
109 }
110
111 fn is_recoverable(&self) -> bool {
112 matches!(self, Self::SendFailed(_) | Self::Timeout(_))
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use orcs_types::{assert_error_codes, ChannelId};
120
121 fn all_variants() -> Vec<EngineError> {
122 vec![
123 EngineError::ComponentNotFound(ComponentId::builtin("x")),
124 EngineError::ChannelNotFound(ChannelId::new()),
125 EngineError::NoTarget,
126 EngineError::SendFailed("x".into()),
127 EngineError::ChannelClosed,
128 EngineError::NotRunning,
129 EngineError::Timeout(RequestId::new()),
130 EngineError::ComponentFailed("x".into()),
131 EngineError::NoSubscriber(EventCategory::Echo),
132 EngineError::PackageFailed("x".into()),
133 EngineError::PackageNotSupported(ComponentId::builtin("x")),
134 ]
135 }
136
137 #[test]
138 fn all_error_codes_valid() {
139 assert_error_codes(&all_variants(), "ENGINE_");
140 }
141
142 #[test]
143 fn engine_error_codes() {
144 let err = EngineError::NoTarget;
145 assert_eq!(err.code(), "ENGINE_NO_TARGET");
146
147 let err = EngineError::ComponentNotFound(ComponentId::builtin("x"));
148 assert_eq!(err.code(), "ENGINE_COMPONENT_NOT_FOUND");
149
150 let err = EngineError::Timeout(RequestId::new());
151 assert_eq!(err.code(), "ENGINE_TIMEOUT");
152
153 let err = EngineError::ComponentFailed("test".into());
154 assert_eq!(err.code(), "ENGINE_COMPONENT_FAILED");
155 }
156
157 #[test]
158 fn engine_error_recoverable() {
159 assert!(!EngineError::NoTarget.is_recoverable());
160 assert!(EngineError::SendFailed("x".into()).is_recoverable());
161 assert!(EngineError::Timeout(RequestId::new()).is_recoverable());
162 assert!(!EngineError::ComponentFailed("x".into()).is_recoverable());
163 }
164}