1use thiserror::Error;
2
3#[derive(Debug, Error)]
5#[non_exhaustive]
6pub enum ForgeError {
7 #[error("failed to read spec file: {0}")]
9 Io(#[from] std::io::Error),
10
11 #[error("failed to parse YAML: {0}")]
13 Yaml(#[from] serde_yaml_ng::Error),
14
15 #[error("failed to parse JSON: {0}")]
17 Json(#[from] serde_json::Error),
18
19 #[error("unresolved $ref: {0}")]
21 UnresolvedRef(String),
22
23 #[error("schema not found: {0}")]
25 SchemaNotFound(String),
26
27 #[error("unsupported spec version: {0} (expected 3.0.x)")]
29 UnsupportedVersion(String),
30
31 #[error("invalid input: {0}")]
33 InvalidInput(String),
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn display_io_error() {
42 let inner = std::io::Error::new(std::io::ErrorKind::NotFound, "gone");
43 let err = ForgeError::Io(inner);
44 let msg = err.to_string();
45 assert!(
46 msg.contains("failed to read spec file"),
47 "IO error display should include context prefix, got: {msg}"
48 );
49 assert!(msg.contains("gone"), "IO error display should preserve inner message");
50 }
51
52 #[test]
53 fn display_yaml_error() {
54 let yaml_err = serde_yaml_ng::from_str::<serde_json::Value>("{{bad")
55 .expect_err("should fail");
56 let err = ForgeError::Yaml(yaml_err);
57 let msg = err.to_string();
58 assert!(
59 msg.contains("failed to parse YAML"),
60 "YAML error display should include context prefix, got: {msg}"
61 );
62 }
63
64 #[test]
65 fn display_json_error() {
66 let json_err = serde_json::from_str::<serde_json::Value>("not json")
67 .expect_err("should fail");
68 let err = ForgeError::Json(json_err);
69 let msg = err.to_string();
70 assert!(
71 msg.contains("failed to parse JSON"),
72 "JSON error display should include context prefix, got: {msg}"
73 );
74 }
75
76 #[test]
77 fn display_unresolved_ref() {
78 let err = ForgeError::UnresolvedRef("#/components/schemas/Missing".into());
79 assert_eq!(
80 err.to_string(),
81 "unresolved $ref: #/components/schemas/Missing"
82 );
83 }
84
85 #[test]
86 fn display_schema_not_found() {
87 let err = ForgeError::SchemaNotFound("NoSuchSchema".into());
88 assert_eq!(err.to_string(), "schema not found: NoSuchSchema");
89 }
90
91 #[test]
92 fn display_unsupported_version() {
93 let err = ForgeError::UnsupportedVersion("2.0".into());
94 assert_eq!(
95 err.to_string(),
96 "unsupported spec version: 2.0 (expected 3.0.x)"
97 );
98 }
99
100 #[test]
101 fn from_io_error() {
102 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
103 let forge_err: ForgeError = io_err.into();
104 assert!(matches!(forge_err, ForgeError::Io(_)));
105 }
106
107 #[test]
108 fn from_yaml_error() {
109 let yaml_err = serde_yaml_ng::from_str::<serde_json::Value>("{{bad")
110 .expect_err("should fail");
111 let forge_err: ForgeError = yaml_err.into();
112 assert!(matches!(forge_err, ForgeError::Yaml(_)));
113 }
114
115 #[test]
116 fn from_json_error() {
117 let json_err = serde_json::from_str::<serde_json::Value>("not json")
118 .expect_err("should fail");
119 let forge_err: ForgeError = json_err.into();
120 assert!(matches!(forge_err, ForgeError::Json(_)));
121 }
122
123 #[test]
124 fn display_invalid_input() {
125 let err = ForgeError::InvalidInput("bad value".into());
126 assert_eq!(err.to_string(), "invalid input: bad value");
127 }
128
129 #[test]
130 fn error_is_debug() {
131 let err = ForgeError::SchemaNotFound("X".into());
132 let debug = format!("{err:?}");
133 assert!(debug.contains("SchemaNotFound"));
134 }
135}