magellan/error_codes.rs
1//! Magellan-specific error codes
2//!
3//! Error codes follow the pattern: MAG-{CATEGORY}-{3-digit number}
4//!
5//! Categories (1-3 uppercase letters):
6//! - REF: Reference-related errors (symbol lookup, span resolution)
7//! - QRY: Query-related errors (invalid queries, file not found)
8//! - IO: I/O-related errors (file access, permissions)
9//! - V: Validation errors (checksum mismatch, invalid spans)
10//!
11//! Each error code is stable and should not be reused.
12
13/// Symbol not found
14pub const MAG_REF_001_SYMBOL_NOT_FOUND: &str = "MAG-REF-001";
15
16/// Ambiguous symbol (multiple matches)
17pub const MAG_REF_002_AMBIGUOUS_SYMBOL: &str = "MAG-REF-002";
18
19/// Invalid span (start > end, out of bounds)
20pub const MAG_REF_003_INVALID_SPAN: &str = "MAG-REF-003";
21
22/// Invalid query syntax
23pub const MAG_QRY_001_INVALID_QUERY: &str = "MAG-QRY-001";
24
25/// File not found in database
26pub const MAG_QRY_002_FILE_NOT_FOUND: &str = "MAG-QRY-002";
27
28/// Invalid query parameters
29pub const MAG_QRY_003_INVALID_PARAMS: &str = "MAG-QRY-003";
30
31/// File not found on filesystem
32pub const MAG_IO_001_FILE_NOT_FOUND: &str = "MAG-IO-001";
33
34/// Permission denied
35pub const MAG_IO_002_PERMISSION_DENIED: &str = "MAG-IO-002";
36
37/// Invalid file path
38pub const MAG_IO_003_INVALID_PATH: &str = "MAG-IO-003";
39
40/// Checksum mismatch
41pub const MAG_V_001_CHECKSUM_MISMATCH: &str = "MAG-V-001";
42
43/// Span validation failed
44pub const MAG_V_002_SPAN_INVALID: &str = "MAG-V-002";
45
46/// Database corruption detected
47pub const MAG_V_003_DB_CORRUPTION: &str = "MAG-V-003";
48
49/// Error code documentation
50///
51/// # Reference Errors (MAG-REF-*)
52///
53/// | Code | Description | Remediation |
54/// |------|-------------|-------------|
55/// | MAG-REF-001 | Symbol not found | Verify symbol name and file path; use `magellan find` to search |
56/// | MAG-REF-002 | Ambiguous symbol | Use fully-qualified name or specify file path |
57/// | MAG-REF-003 | Invalid span | Check byte offsets are within file bounds and start < end |
58///
59/// # Query Errors (MAG-QRY-*)
60///
61/// | Code | Description | Remediation |
62/// |------|-------------|-------------|
63/// | MAG-QRY-001 | Invalid query syntax | Check query format; see command help |
64/// | MAG-QRY-002 | File not found in database | Run `magellan watch` or `magellan verify` |
65/// | MAG-QRY-003 | Invalid parameters | Check required arguments for command |
66///
67/// # I/O Errors (MAG-IO-*)
68///
69/// | Code | Description | Remediation |
70/// |------|-------------|-------------|
71/// | MAG-IO-001 | File not found on filesystem | Check file path and permissions |
72/// | MAG-IO-002 | Permission denied | Check file/directory read permissions |
73/// | MAG-IO-003 | Invalid path | Verify path format and escaping |
74///
75/// # Validation Errors (MAG-V-*)
76///
77/// | Code | Description | Remediation |
78/// |------|-------------|-------------|
79/// | MAG-V-001 | Checksum mismatch | Re-index the file; data may be corrupted |
80/// | MAG-V-002 | Span validation failed | Re-index; file may have changed |
81/// | MAG-V-003 | Database corruption | Re-build database from source |
82pub const ERROR_CODE_DOCUMENTATION: &str = "Error code documentation available in source";
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 /// Verify all error codes are unique
89 #[test]
90 fn test_error_codes_are_unique() {
91 let codes = vec![
92 MAG_REF_001_SYMBOL_NOT_FOUND,
93 MAG_REF_002_AMBIGUOUS_SYMBOL,
94 MAG_REF_003_INVALID_SPAN,
95 MAG_QRY_001_INVALID_QUERY,
96 MAG_QRY_002_FILE_NOT_FOUND,
97 MAG_QRY_003_INVALID_PARAMS,
98 MAG_IO_001_FILE_NOT_FOUND,
99 MAG_IO_002_PERMISSION_DENIED,
100 MAG_IO_003_INVALID_PATH,
101 MAG_V_001_CHECKSUM_MISMATCH,
102 MAG_V_002_SPAN_INVALID,
103 MAG_V_003_DB_CORRUPTION,
104 ];
105
106 let mut unique = std::collections::HashSet::new();
107 for code in codes {
108 assert!(
109 unique.insert(code),
110 "Duplicate error code detected: {}",
111 code
112 );
113 }
114 }
115
116 /// Verify error code format
117 #[test]
118 fn test_error_code_format() {
119 let codes = vec![
120 MAG_REF_001_SYMBOL_NOT_FOUND,
121 MAG_REF_002_AMBIGUOUS_SYMBOL,
122 MAG_REF_003_INVALID_SPAN,
123 MAG_QRY_001_INVALID_QUERY,
124 MAG_QRY_002_FILE_NOT_FOUND,
125 MAG_QRY_003_INVALID_PARAMS,
126 MAG_IO_001_FILE_NOT_FOUND,
127 MAG_IO_002_PERMISSION_DENIED,
128 MAG_IO_003_INVALID_PATH,
129 MAG_V_001_CHECKSUM_MISMATCH,
130 MAG_V_002_SPAN_INVALID,
131 MAG_V_003_DB_CORRUPTION,
132 ];
133
134 for code in codes {
135 // Format: MAG-{CATEGORY}-{3-digit number}
136 assert!(
137 code.starts_with("MAG-"),
138 "Error code must start with 'MAG-': {}",
139 code
140 );
141 let parts: Vec<&str> = code.split('-').collect();
142 assert_eq!(parts.len(), 3, "Error code must have 3 parts: {}", code);
143
144 // Verify category is 1-3 uppercase letters
145 assert!(
146 !parts[1].is_empty() && parts[1].len() <= 3,
147 "Category must be 1-3 chars: {}",
148 code
149 );
150 assert!(parts[1].chars().all(|c| c.is_ascii_uppercase()));
151
152 // Verify number is 3 digits
153 assert_eq!(parts[2].len(), 3, "Number must be 3 digits: {}", code);
154 assert!(parts[2].chars().all(|c| c.is_ascii_digit()));
155 }
156 }
157}