Skip to main content

openapi_forge/
error.rs

1use thiserror::Error;
2
3/// Errors that can occur when loading, parsing, or querying an `OpenAPI` spec.
4#[derive(Debug, Error)]
5#[non_exhaustive]
6pub enum ForgeError {
7    /// An I/O error occurred while reading the spec file from disk.
8    #[error("failed to read spec file: {0}")]
9    Io(#[from] std::io::Error),
10
11    /// The spec content could not be parsed as valid YAML.
12    #[error("failed to parse YAML: {0}")]
13    Yaml(#[from] serde_yaml_ng::Error),
14
15    /// The spec content could not be parsed as valid JSON.
16    #[error("failed to parse JSON: {0}")]
17    Json(#[from] serde_json::Error),
18
19    /// A `$ref` pointer could not be resolved within the spec.
20    #[error("unresolved $ref: {0}")]
21    UnresolvedRef(String),
22
23    /// A referenced schema name does not exist in `components/schemas`.
24    #[error("schema not found: {0}")]
25    SchemaNotFound(String),
26
27    /// The spec declares a version other than 3.0.x.
28    #[error("unsupported spec version: {0} (expected 3.0.x)")]
29    UnsupportedVersion(String),
30
31    /// A string could not be parsed into the expected type.
32    #[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}