1use core::fmt;
5
6use minicbor::{Decode, Encode};
7
8use crate::capability::Capabilities;
9use crate::message::Method;
10
11#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
17#[cbor(map)]
18pub struct Error {
19 #[n(0)]
21 pub code: String,
22 #[n(1)]
24 pub message: String,
25 #[n(2)]
27 pub retryable: bool,
28}
29
30pub mod code {
33 pub const UNKNOWN_METHOD: &str = "unknown_method";
35 pub const INVALID_PARAMS: &str = "invalid_params";
37 pub const CAPABILITY_MISSING: &str = "capability_missing";
40 pub const DEADLINE_EXCEEDED: &str = "deadline_exceeded";
43 pub const BUSY: &str = "busy";
45 pub const WINDOW_CHANGED: &str = "window_changed";
47 pub const FRAME_TOO_LARGE: &str = "frame_too_large";
49 pub const VERSION_MISMATCH: &str = "version_mismatch";
51 pub const TOKEN_MISMATCH: &str = "token_mismatch";
53 pub const FRAME_LIMIT_TOO_SMALL: &str = "frame_limit_too_small";
56 pub const NOT_READY: &str = "not_ready";
58 pub const DISCONNECTED: &str = "disconnected";
61 pub const PROTOCOL_ERROR: &str = "protocol_error";
63 pub const INTERNAL: &str = "internal";
65
66 pub const ALL: &[&str] = &[
68 UNKNOWN_METHOD,
69 INVALID_PARAMS,
70 CAPABILITY_MISSING,
71 DEADLINE_EXCEEDED,
72 BUSY,
73 WINDOW_CHANGED,
74 FRAME_TOO_LARGE,
75 VERSION_MISMATCH,
76 TOKEN_MISMATCH,
77 FRAME_LIMIT_TOO_SMALL,
78 NOT_READY,
79 DISCONNECTED,
80 PROTOCOL_ERROR,
81 INTERNAL,
82 ];
83}
84
85pub fn retryable(code: &str) -> Option<bool> {
88 match code {
89 code::DEADLINE_EXCEEDED | code::BUSY | code::WINDOW_CHANGED | code::NOT_READY => Some(true),
90 code::UNKNOWN_METHOD
91 | code::INVALID_PARAMS
92 | code::CAPABILITY_MISSING
93 | code::FRAME_TOO_LARGE
94 | code::VERSION_MISMATCH
95 | code::TOKEN_MISMATCH
96 | code::FRAME_LIMIT_TOO_SMALL
97 | code::DISCONNECTED
98 | code::PROTOCOL_ERROR
99 | code::INTERNAL => Some(false),
100 _ => None,
101 }
102}
103
104impl Error {
105 pub fn new(code: &str, message: impl Into<String>) -> Error {
108 Error {
109 code: code.to_owned(),
110 message: message.into(),
111 retryable: retryable(code).unwrap_or(false),
112 }
113 }
114
115 pub fn unknown_method(method: Method) -> Error {
117 Error::new(
118 code::UNKNOWN_METHOD,
119 format!("{method} is not a method this agent implements"),
120 )
121 }
122
123 pub fn capability_missing(method: Method, missing: Capabilities) -> Error {
125 Error::new(
126 code::CAPABILITY_MISSING,
127 format!(
128 "the agent did not advertise {} ({missing}), which {method} needs",
129 missing.label()
130 ),
131 )
132 }
133
134 pub fn deadline_exceeded(method: Method, id: u32, deadline_ms: u32) -> Error {
136 Error::new(
137 code::DEADLINE_EXCEEDED,
138 format!(
139 "the {method} request {id} did not finish within its {deadline_ms} ms deadline"
140 ),
141 )
142 }
143
144 pub fn frame_too_large(method: Method, id: u32, size: u64, limit: u32) -> Error {
146 Error::new(
147 code::FRAME_TOO_LARGE,
148 format!(
149 "the {method} message for request {id} is {size} bytes, over the negotiated maximum frame size of {limit} bytes"
150 ),
151 )
152 }
153
154 pub fn disconnected(reason: impl fmt::Display) -> Error {
156 Error::new(
157 code::DISCONNECTED,
158 format!(
159 "the agent is disconnected ({reason}); reconnect to the target, which restarts or redeploys the agent"
160 ),
161 )
162 }
163}
164
165impl fmt::Display for Error {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 write!(f, "{}: {}", self.code, self.message)
168 }
169}
170
171impl std::error::Error for Error {}