1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct ParseError {
8 pub input: String,
10 pub kind: ParseErrorKind,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum ParseErrorKind {
17 Empty,
19 TooLong {
21 max: usize,
23 actual: usize,
25 },
26 InvalidScheme {
28 found: Option<String>,
30 },
31 InvalidTrustRoot(TrustRootError),
33 InvalidCapabilityPath(CapabilityPathError),
35 InvalidAgentId(AgentIdError),
37 InvalidQuery(QueryError),
39 InvalidFragment(FragmentError),
41 MissingComponent {
43 component: &'static str,
45 },
46 UnexpectedChar {
48 char: char,
50 position: usize,
52 },
53}
54
55impl fmt::Display for ParseError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "failed to parse agent URI '{}': ", self.input)?;
58 match &self.kind {
59 ParseErrorKind::Empty => write!(f, "input is empty"),
60 ParseErrorKind::TooLong { max, actual } => {
61 write!(
62 f,
63 "URI length {actual} exceeds maximum {max}; consider shorter capability path or trust root"
64 )
65 }
66 ParseErrorKind::InvalidScheme { found } => match found {
67 Some(s) => write!(f, "expected scheme 'agent://', found '{s}'"),
68 None => write!(f, "missing scheme; URI must start with 'agent://'"),
69 },
70 ParseErrorKind::InvalidTrustRoot(e) => write!(f, "invalid trust root: {e}"),
71 ParseErrorKind::InvalidCapabilityPath(e) => write!(f, "invalid capability path: {e}"),
72 ParseErrorKind::InvalidAgentId(e) => write!(f, "invalid agent ID: {e}"),
73 ParseErrorKind::InvalidQuery(e) => write!(f, "invalid query string: {e}"),
74 ParseErrorKind::InvalidFragment(e) => write!(f, "invalid fragment: {e}"),
75 ParseErrorKind::MissingComponent { component } => {
76 write!(f, "missing required component: {component}")
77 }
78 ParseErrorKind::UnexpectedChar { char, position } => {
79 write!(f, "unexpected character '{char}' at position {position}")
80 }
81 }
82 }
83}
84
85impl std::error::Error for ParseError {}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
89pub enum TrustRootError {
90 Empty,
92 TooLong {
94 max: usize,
96 actual: usize,
98 },
99 InvalidDomain {
101 domain: String,
103 reason: &'static str,
105 },
106 InvalidIpAddress {
108 value: String,
110 reason: &'static str,
112 },
113 InvalidPort {
115 value: String,
117 reason: &'static str,
119 },
120 LabelTooLong {
122 label: String,
124 max: usize,
126 actual: usize,
128 },
129 InvalidChar {
131 char: char,
133 position: usize,
135 },
136}
137
138impl fmt::Display for TrustRootError {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 match self {
141 Self::Empty => write!(f, "trust root cannot be empty"),
142 Self::TooLong { max, actual } => {
143 write!(f, "trust root length {actual} exceeds maximum {max}")
144 }
145 Self::InvalidDomain { domain, reason } => {
146 write!(f, "invalid domain '{domain}': {reason}")
147 }
148 Self::InvalidIpAddress { value, reason } => {
149 write!(f, "invalid IP address '{value}': {reason}")
150 }
151 Self::InvalidPort { value, reason } => {
152 write!(f, "invalid port '{value}': {reason}")
153 }
154 Self::LabelTooLong { label, max, actual } => {
155 write!(f, "DNS label '{label}' is {actual} chars, max is {max}")
156 }
157 Self::InvalidChar { char, position } => {
158 write!(f, "invalid character '{char}' at position {position}")
159 }
160 }
161 }
162}
163
164impl std::error::Error for TrustRootError {}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
168pub enum CapabilityPathError {
169 Empty,
171 TooLong {
173 max: usize,
175 actual: usize,
177 },
178 TooManySegments {
180 max: usize,
182 actual: usize,
184 },
185 InvalidSegment {
187 segment: String,
189 index: usize,
191 reason: PathSegmentError,
193 },
194}
195
196impl fmt::Display for CapabilityPathError {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 match self {
199 Self::Empty => write!(
200 f,
201 "capability path cannot be empty; at least one segment required"
202 ),
203 Self::TooLong { max, actual } => {
204 write!(f, "capability path length {actual} exceeds maximum {max}")
205 }
206 Self::TooManySegments { max, actual } => {
207 write!(f, "path has {actual} segments, maximum is {max}")
208 }
209 Self::InvalidSegment {
210 segment,
211 index,
212 reason,
213 } => {
214 write!(f, "invalid segment '{segment}' at index {index}: {reason}")
215 }
216 }
217 }
218}
219
220impl std::error::Error for CapabilityPathError {}
221
222#[derive(Debug, Clone, PartialEq, Eq)]
224pub enum PathSegmentError {
225 Empty,
227 TooLong {
229 max: usize,
231 actual: usize,
233 },
234 InvalidChar {
236 char: char,
238 position: usize,
240 },
241}
242
243impl fmt::Display for PathSegmentError {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 match self {
246 Self::Empty => write!(f, "segment cannot be empty"),
247 Self::TooLong { max, actual } => {
248 write!(f, "segment length {actual} exceeds maximum {max}")
249 }
250 Self::InvalidChar { char, position } => {
251 write!(
252 f,
253 "invalid character '{char}' at position {position}; only lowercase letters, digits, and hyphens allowed"
254 )
255 }
256 }
257 }
258}
259
260impl std::error::Error for PathSegmentError {}
261
262#[derive(Debug, Clone, PartialEq, Eq)]
264pub enum AgentIdError {
265 Empty,
267 TooLong {
269 max: usize,
271 actual: usize,
273 },
274 InvalidPrefix(AgentPrefixError),
276 InvalidSuffix {
278 value: String,
280 reason: &'static str,
282 },
283 MissingSeparator,
285 TypeIdError(String),
287}
288
289impl fmt::Display for AgentIdError {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 match self {
292 Self::Empty => write!(f, "agent ID cannot be empty"),
293 Self::TooLong { max, actual } => {
294 write!(f, "agent ID length {actual} exceeds maximum {max}")
295 }
296 Self::InvalidPrefix(e) => write!(f, "invalid prefix: {e}"),
297 Self::InvalidSuffix { value, reason } => {
298 write!(f, "invalid suffix '{value}': {reason}")
299 }
300 Self::MissingSeparator => {
301 write!(f, "missing underscore separator between prefix and suffix")
302 }
303 Self::TypeIdError(msg) => write!(f, "`TypeID` error: {msg}"),
304 }
305 }
306}
307
308impl std::error::Error for AgentIdError {}
309
310#[derive(Debug, Clone, PartialEq, Eq)]
312pub enum AgentPrefixError {
313 Empty,
315 TooLong {
317 max: usize,
319 actual: usize,
321 },
322 InvalidChar {
324 char: char,
326 position: usize,
328 },
329 MustStartWithLetter {
331 found: char,
333 },
334 MustEndWithLetter {
336 found: char,
338 },
339 ContainsDigit {
341 position: usize,
343 },
344}
345
346impl fmt::Display for AgentPrefixError {
347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348 match self {
349 Self::Empty => write!(f, "prefix cannot be empty"),
350 Self::TooLong { max, actual } => {
351 write!(f, "prefix length {actual} exceeds maximum {max}")
352 }
353 Self::InvalidChar { char, position } => {
354 write!(
355 f,
356 "invalid character '{char}' at position {position}; only lowercase letters and underscores allowed"
357 )
358 }
359 Self::MustStartWithLetter { found } => {
360 write!(f, "prefix must start with a letter, found '{found}'")
361 }
362 Self::MustEndWithLetter { found } => {
363 write!(f, "prefix must end with a letter, found '{found}'")
364 }
365 Self::ContainsDigit { position } => {
366 write!(f, "prefix cannot contain digits (found at position {position})")
367 }
368 }
369 }
370}
371
372impl std::error::Error for AgentPrefixError {}
373
374#[derive(Debug, Clone, PartialEq, Eq)]
376pub enum QueryError {
377 InvalidParamName {
379 name: String,
381 reason: &'static str,
383 },
384 InvalidParamValue {
386 name: String,
388 value: String,
390 reason: &'static str,
392 },
393 DuplicateParam {
395 name: String,
397 },
398 InvalidPercentEncoding {
400 value: String,
402 },
403}
404
405impl fmt::Display for QueryError {
406 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407 match self {
408 Self::InvalidParamName { name, reason } => {
409 write!(f, "invalid parameter name '{name}': {reason}")
410 }
411 Self::InvalidParamValue { name, value, reason } => {
412 write!(f, "invalid value '{value}' for parameter '{name}': {reason}")
413 }
414 Self::DuplicateParam { name } => {
415 write!(f, "duplicate parameter '{name}'")
416 }
417 Self::InvalidPercentEncoding { value } => {
418 write!(f, "invalid percent encoding in '{value}'")
419 }
420 }
421 }
422}
423
424impl std::error::Error for QueryError {}
425
426#[derive(Debug, Clone, PartialEq, Eq)]
428pub enum FragmentError {
429 InvalidChar {
431 char: char,
433 position: usize,
435 },
436}
437
438impl fmt::Display for FragmentError {
439 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440 match self {
441 Self::InvalidChar { char, position } => {
442 write!(
443 f,
444 "invalid character '{char}' at position {position}; allowed: alphanumeric, hyphen, underscore, dot, slash"
445 )
446 }
447 }
448 }
449}
450
451impl std::error::Error for FragmentError {}
452
453#[derive(Debug, Clone, PartialEq, Eq)]
455pub enum BuilderError {
456 UriTooLong {
458 max: usize,
460 actual: usize,
462 },
463}
464
465impl fmt::Display for BuilderError {
466 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467 match self {
468 Self::UriTooLong { max, actual } => {
469 write!(
470 f,
471 "resulting URI length {actual} exceeds maximum {max}; \
472 consider shorter component values"
473 )
474 }
475 }
476 }
477}
478
479impl std::error::Error for BuilderError {}