1use std::fmt;
2
3#[derive(Debug, Clone, Copy)]
4pub struct ErrorCode(pub u16);
5
6impl fmt::Display for ErrorCode {
7 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
8 write!(f, "E{:04}", self.0)
9 }
10}
11
12impl std::error::Error for ErrorCode {}
13
14impl ErrorCode {
15 pub fn doc_url(&self) -> String {
16 format!(
17 "https://ferrflow.com/docs/reference/errors#{}",
18 self.to_string().to_lowercase()
19 )
20 }
21}
22
23#[allow(dead_code)]
24pub trait ErrorCodeExt<T> {
25 fn error_code(self, code: ErrorCode) -> anyhow::Result<T>;
26}
27
28impl<T> ErrorCodeExt<T> for anyhow::Result<T> {
29 fn error_code(self, code: ErrorCode) -> anyhow::Result<T> {
30 self.map_err(|e| e.context(code))
31 }
32}
33
34#[allow(dead_code)]
35pub const CONFIG_NOT_FOUND: ErrorCode = ErrorCode(1001);
36#[allow(dead_code)]
37pub const CONFIG_PARSE_JSON: ErrorCode = ErrorCode(1002);
38#[allow(dead_code)]
39pub const CONFIG_PARSE_JSON5: ErrorCode = ErrorCode(1003);
40#[allow(dead_code)]
41pub const CONFIG_PARSE_TOML: ErrorCode = ErrorCode(1004);
42#[allow(dead_code)]
43pub const CONFIG_SERIALIZE_TOML: ErrorCode = ErrorCode(1005);
44#[allow(dead_code)]
45pub const CONFIG_PARSE_DOTFILE: ErrorCode = ErrorCode(1006);
46#[allow(dead_code)]
47pub const CONFIG_SERIALIZE_DOTFILE: ErrorCode = ErrorCode(1007);
48#[allow(dead_code)]
49pub const CONFIG_RESOLVE_PATH: ErrorCode = ErrorCode(1008);
50#[allow(dead_code)]
51pub const CONFIG_WRITE_LOADER: ErrorCode = ErrorCode(1009);
52#[allow(dead_code)]
53pub const CONFIG_EVAL_TS: ErrorCode = ErrorCode(1010);
54#[allow(dead_code)]
55pub const CONFIG_EVAL_NODE: ErrorCode = ErrorCode(1011);
56#[allow(dead_code)]
57pub const CONFIG_EVAL_FAILED: ErrorCode = ErrorCode(1012);
58#[allow(dead_code)]
59pub const CONFIG_INVALID_OUTPUT: ErrorCode = ErrorCode(1013);
60#[allow(dead_code)]
61pub const CONFIG_INVALID_JSON: ErrorCode = ErrorCode(1014);
62#[allow(dead_code)]
63pub const CONFIG_READ_FAILED: ErrorCode = ErrorCode(1015);
64#[allow(dead_code)]
65pub const CONFIG_MULTIPLE_FILES: ErrorCode = ErrorCode(1016);
66#[allow(dead_code)]
67pub const CONFIG_ALREADY_EXISTS: ErrorCode = ErrorCode(1017);
68#[allow(dead_code)]
69pub const CONFIG_INVALID_PATH: ErrorCode = ErrorCode(1018);
70
71#[allow(dead_code)]
72pub const VALIDATE_INVALID_REPO_SPEC: ErrorCode = ErrorCode(1100);
73#[allow(dead_code)]
74pub const VALIDATE_GITHUB_API: ErrorCode = ErrorCode(1101);
75#[allow(dead_code)]
76pub const VALIDATE_GITLAB_API: ErrorCode = ErrorCode(1102);
77#[allow(dead_code)]
78pub const VALIDATE_INVALID_UTF8: ErrorCode = ErrorCode(1103);
79#[allow(dead_code)]
80pub const VALIDATE_PARSE_FAILED: ErrorCode = ErrorCode(1104);
81#[allow(dead_code)]
82pub const VALIDATE_FILE_NOT_FOUND: ErrorCode = ErrorCode(1105);
83#[allow(dead_code)]
84pub const VALIDATE_NO_CONFIG: ErrorCode = ErrorCode(1106);
85#[allow(dead_code)]
86pub const VALIDATE_REF_REQUIRES_REPO: ErrorCode = ErrorCode(1107);
87
88#[allow(dead_code)]
89pub const GIT_NOT_A_REPO: ErrorCode = ErrorCode(2001);
90#[allow(dead_code)]
91pub const GIT_BARE_REPO: ErrorCode = ErrorCode(2002);
92#[allow(dead_code)]
93pub const GIT_TAG_EXISTS: ErrorCode = ErrorCode(2003);
94#[allow(dead_code)]
95pub const GIT_PUSH_BRANCH: ErrorCode = ErrorCode(2004);
96#[allow(dead_code)]
97pub const GIT_PUSH_REJECTED: ErrorCode = ErrorCode(2005);
98#[allow(dead_code)]
99pub const GIT_PUSH_TAGS: ErrorCode = ErrorCode(2006);
100#[allow(dead_code)]
101pub const GIT_FLOATING_TAGS: ErrorCode = ErrorCode(2007);
102#[allow(dead_code)]
103pub const GIT_REMOTE_NOT_FOUND: ErrorCode = ErrorCode(2008);
104#[allow(dead_code)]
105pub const GIT_PUSH_VERIFY_FAILED: ErrorCode = ErrorCode(2009);
106#[allow(dead_code)]
107pub const GIT_REMOTE_BRANCH_NOT_FOUND: ErrorCode = ErrorCode(2010);
108#[allow(dead_code)]
109pub const GIT_LOCKED: ErrorCode = ErrorCode(2011);
110
111#[allow(dead_code)]
112pub const GITHUB_CREATE_RELEASE: ErrorCode = ErrorCode(3001);
113#[allow(dead_code)]
114pub const GITHUB_LIST_RELEASES: ErrorCode = ErrorCode(3002);
115#[allow(dead_code)]
116pub const GITHUB_PARSE_RELEASES: ErrorCode = ErrorCode(3003);
117#[allow(dead_code)]
118pub const GITHUB_PUBLISH_RELEASE: ErrorCode = ErrorCode(3004);
119#[allow(dead_code)]
120pub const GITHUB_CREATE_PR: ErrorCode = ErrorCode(3005);
121#[allow(dead_code)]
122pub const GITHUB_PARSE_PR: ErrorCode = ErrorCode(3006);
123#[allow(dead_code)]
124pub const GITHUB_PR_MISSING_FIELD: ErrorCode = ErrorCode(3007);
125#[allow(dead_code)]
126pub const GITHUB_AUTO_MERGE: ErrorCode = ErrorCode(3008);
127#[allow(dead_code)]
128pub const GITHUB_GRAPHQL_PARSE: ErrorCode = ErrorCode(3009);
129#[allow(dead_code)]
130pub const GITHUB_AUTO_MERGE_FAILED: ErrorCode = ErrorCode(3010);
131
132#[allow(dead_code)]
133pub const GITLAB_CREATE_RELEASE: ErrorCode = ErrorCode(3101);
134#[allow(dead_code)]
135pub const GITLAB_CREATE_MR: ErrorCode = ErrorCode(3102);
136#[allow(dead_code)]
137pub const GITLAB_PARSE_MR: ErrorCode = ErrorCode(3103);
138#[allow(dead_code)]
139pub const GITLAB_MR_MISSING_FIELD: ErrorCode = ErrorCode(3104);
140#[allow(dead_code)]
141pub const GITLAB_MERGE_MR: ErrorCode = ErrorCode(3105);
142
143#[allow(dead_code)]
144pub const TOML_READ: ErrorCode = ErrorCode(4101);
145#[allow(dead_code)]
146pub const TOML_PARSE: ErrorCode = ErrorCode(4102);
147#[allow(dead_code)]
148pub const TOML_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4103);
149#[allow(dead_code)]
150pub const TOML_WRITE: ErrorCode = ErrorCode(4104);
151#[allow(dead_code)]
152pub const TOML_INVALID_UTF8: ErrorCode = ErrorCode(4105);
153
154#[allow(dead_code)]
155pub const JSON_READ: ErrorCode = ErrorCode(4201);
156#[allow(dead_code)]
157pub const JSON_PARSE: ErrorCode = ErrorCode(4202);
158#[allow(dead_code)]
159pub const JSON_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4203);
160#[allow(dead_code)]
161pub const JSON_WRITE: ErrorCode = ErrorCode(4204);
162#[allow(dead_code)]
163pub const JSON_INVALID_UTF8: ErrorCode = ErrorCode(4205);
164
165#[allow(dead_code)]
166pub const HELM_READ: ErrorCode = ErrorCode(4301);
167#[allow(dead_code)]
168pub const HELM_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4302);
169#[allow(dead_code)]
170pub const HELM_WRITE: ErrorCode = ErrorCode(4303);
171#[allow(dead_code)]
172pub const HELM_INVALID_UTF8: ErrorCode = ErrorCode(4304);
173
174#[allow(dead_code)]
175pub const XML_READ: ErrorCode = ErrorCode(4401);
176#[allow(dead_code)]
177pub const XML_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4402);
178#[allow(dead_code)]
179pub const XML_WRITE: ErrorCode = ErrorCode(4403);
180#[allow(dead_code)]
181pub const XML_INVALID_UTF8: ErrorCode = ErrorCode(4404);
182#[allow(dead_code)]
183pub const CSPROJ_READ: ErrorCode = ErrorCode(4410);
184#[allow(dead_code)]
185pub const CSPROJ_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4411);
186#[allow(dead_code)]
187pub const CSPROJ_WRITE: ErrorCode = ErrorCode(4412);
188#[allow(dead_code)]
189pub const CSPROJ_INVALID_UTF8: ErrorCode = ErrorCode(4413);
190
191#[allow(dead_code)]
192pub const GRADLE_READ: ErrorCode = ErrorCode(4501);
193#[allow(dead_code)]
194pub const GRADLE_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4502);
195#[allow(dead_code)]
196pub const GRADLE_WRITE: ErrorCode = ErrorCode(4503);
197#[allow(dead_code)]
198pub const GRADLE_INVALID_UTF8: ErrorCode = ErrorCode(4504);
199
200#[allow(dead_code)]
201pub const GOMOD_GIT_DESCRIBE: ErrorCode = ErrorCode(4601);
202#[allow(dead_code)]
203pub const GOMOD_NO_TAG: ErrorCode = ErrorCode(4602);
204#[allow(dead_code)]
205pub const GOMOD_UNSUPPORTED: ErrorCode = ErrorCode(4603);
206
207#[allow(dead_code)]
208pub const TXT_READ: ErrorCode = ErrorCode(4701);
209#[allow(dead_code)]
210pub const TXT_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4702);
211#[allow(dead_code)]
212pub const TXT_WRITE: ErrorCode = ErrorCode(4703);
213#[allow(dead_code)]
214pub const TXT_INVALID_UTF8: ErrorCode = ErrorCode(4704);
215
216#[allow(dead_code)]
217pub const PUBSPEC_READ: ErrorCode = ErrorCode(4801);
218#[allow(dead_code)]
219pub const PUBSPEC_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4802);
220#[allow(dead_code)]
221pub const PUBSPEC_WRITE: ErrorCode = ErrorCode(4803);
222#[allow(dead_code)]
223pub const PUBSPEC_INVALID_UTF8: ErrorCode = ErrorCode(4804);
224
225#[allow(dead_code)]
226pub const MIX_EXS_READ: ErrorCode = ErrorCode(4811);
227#[allow(dead_code)]
228pub const MIX_EXS_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4812);
229#[allow(dead_code)]
230pub const MIX_EXS_WRITE: ErrorCode = ErrorCode(4813);
231#[allow(dead_code)]
232pub const MIX_EXS_INVALID_UTF8: ErrorCode = ErrorCode(4814);
233
234#[allow(dead_code)]
235pub const CHART_YAML_READ: ErrorCode = ErrorCode(4821);
236#[allow(dead_code)]
237pub const CHART_YAML_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4822);
238#[allow(dead_code)]
239pub const CHART_YAML_WRITE: ErrorCode = ErrorCode(4823);
240#[allow(dead_code)]
241pub const CHART_YAML_INVALID_UTF8: ErrorCode = ErrorCode(4824);
242
243#[allow(dead_code)]
244pub const GEMSPEC_READ: ErrorCode = ErrorCode(4831);
245#[allow(dead_code)]
246pub const GEMSPEC_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4832);
247#[allow(dead_code)]
248pub const GEMSPEC_WRITE: ErrorCode = ErrorCode(4833);
249#[allow(dead_code)]
250pub const GEMSPEC_INVALID_UTF8: ErrorCode = ErrorCode(4834);
251
252#[allow(dead_code)]
253pub const PACKAGE_SWIFT_READ: ErrorCode = ErrorCode(4841);
254#[allow(dead_code)]
255pub const PACKAGE_SWIFT_VERSION_NOT_FOUND: ErrorCode = ErrorCode(4842);
256#[allow(dead_code)]
257pub const PACKAGE_SWIFT_WRITE: ErrorCode = ErrorCode(4843);
258#[allow(dead_code)]
259pub const PACKAGE_SWIFT_INVALID_UTF8: ErrorCode = ErrorCode(4844);
260
261#[allow(dead_code)]
262pub const PRERELEASE_EMPTY_CHANNEL: ErrorCode = ErrorCode(5001);
263#[allow(dead_code)]
264pub const PRERELEASE_INVALID_CHANNEL: ErrorCode = ErrorCode(5002);
265
266#[allow(dead_code)]
267pub const VERSIONING_INVALID_SEMVER: ErrorCode = ErrorCode(5010);
268
269#[allow(dead_code)]
270pub const HOOK_FAILED: ErrorCode = ErrorCode(6001);
271
272#[allow(dead_code)]
273pub const QUERY_NO_PACKAGES: ErrorCode = ErrorCode(7001);
274#[allow(dead_code)]
275pub const QUERY_PACKAGE_NOT_FOUND: ErrorCode = ErrorCode(7002);
276
277#[allow(dead_code)]
278pub const MONOREPO_PACKAGE_NOT_FOUND: ErrorCode = ErrorCode(8001);
279#[allow(dead_code)]
280pub const MONOREPO_PUSH_FAILED: ErrorCode = ErrorCode(8002);
281#[allow(dead_code)]
282pub const MONOREPO_DEPENDENCY_CYCLE: ErrorCode = ErrorCode(8003);
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn display_formats_with_leading_zeros() {
290 assert_eq!(ErrorCode(1).to_string(), "E0001");
291 assert_eq!(ErrorCode(42).to_string(), "E0042");
292 assert_eq!(ErrorCode(1001).to_string(), "E1001");
293 assert_eq!(ErrorCode(9999).to_string(), "E9999");
294 }
295
296 #[test]
297 fn doc_url_uses_lowercase() {
298 let url = ErrorCode(1001).doc_url();
299 assert_eq!(url, "https://ferrflow.com/docs/reference/errors#e1001");
300 }
301
302 #[test]
303 fn error_code_ext_attaches_code() {
304 let err: anyhow::Result<()> = Err(anyhow::anyhow!("something broke"));
305 let err = err.error_code(ErrorCode(2001));
306 let err = err.unwrap_err();
307
308 let code = err.downcast_ref::<ErrorCode>().copied();
309 assert!(code.is_some());
310 assert_eq!(code.unwrap().0, 2001);
311 }
312
313 #[test]
314 fn error_without_code_returns_none() {
315 let err = anyhow::anyhow!("plain error");
316 let code = err.downcast_ref::<ErrorCode>().copied();
317 assert!(code.is_none());
318 }
319}