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!(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}