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!(
50 msg.contains("gone"),
51 "IO error display should preserve inner message"
52 );
53 }
54
55 #[test]
56 fn display_yaml_error() {
57 let yaml_err =
58 serde_yaml_ng::from_str::<serde_json::Value>("{{bad").expect_err("should fail");
59 let err = ForgeError::Yaml(yaml_err);
60 let msg = err.to_string();
61 assert!(
62 msg.contains("failed to parse YAML"),
63 "YAML error display should include context prefix, got: {msg}"
64 );
65 }
66
67 #[test]
68 fn display_json_error() {
69 let json_err =
70 serde_json::from_str::<serde_json::Value>("not json").expect_err("should fail");
71 let err = ForgeError::Json(json_err);
72 let msg = err.to_string();
73 assert!(
74 msg.contains("failed to parse JSON"),
75 "JSON error display should include context prefix, got: {msg}"
76 );
77 }
78
79 #[test]
80 fn display_unresolved_ref() {
81 let err = ForgeError::UnresolvedRef("#/components/schemas/Missing".into());
82 assert_eq!(
83 err.to_string(),
84 "unresolved $ref: #/components/schemas/Missing"
85 );
86 }
87
88 #[test]
89 fn display_schema_not_found() {
90 let err = ForgeError::SchemaNotFound("NoSuchSchema".into());
91 assert_eq!(err.to_string(), "schema not found: NoSuchSchema");
92 }
93
94 #[test]
95 fn display_unsupported_version() {
96 let err = ForgeError::UnsupportedVersion("2.0".into());
97 assert_eq!(
98 err.to_string(),
99 "unsupported spec version: 2.0 (expected 3.0.x)"
100 );
101 }
102
103 #[test]
104 fn from_io_error() {
105 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
106 let forge_err: ForgeError = io_err.into();
107 assert!(matches!(forge_err, ForgeError::Io(_)));
108 }
109
110 #[test]
111 fn from_yaml_error() {
112 let yaml_err =
113 serde_yaml_ng::from_str::<serde_json::Value>("{{bad").expect_err("should fail");
114 let forge_err: ForgeError = yaml_err.into();
115 assert!(matches!(forge_err, ForgeError::Yaml(_)));
116 }
117
118 #[test]
119 fn from_json_error() {
120 let json_err =
121 serde_json::from_str::<serde_json::Value>("not json").expect_err("should fail");
122 let forge_err: ForgeError = json_err.into();
123 assert!(matches!(forge_err, ForgeError::Json(_)));
124 }
125
126 #[test]
127 fn display_invalid_input() {
128 let err = ForgeError::InvalidInput("bad value".into());
129 assert_eq!(err.to_string(), "invalid input: bad value");
130 }
131
132 #[test]
133 fn error_is_debug() {
134 let err = ForgeError::SchemaNotFound("X".into());
135 let debug = format!("{err:?}");
136 assert!(debug.contains("SchemaNotFound"));
137 }
138}