Skip to main content

rain_metadata/error/
mod.rs

1use std::{string::FromUtf8Error, str::Utf8Error};
2
3use rain_metaboard_subgraph::metaboard_client::MetaboardSubgraphClientError;
4
5use crate::meta::KnownMagic;
6
7/// Covers all errors variants of Rain Metadat lib functionalities
8#[derive(Debug)]
9pub enum Error {
10    CorruptMeta,
11    InvalidHash,
12    UnknownMeta,
13    UnknownMagic,
14    NoRecordFound,
15    UnsupportedMeta,
16    BiggerThan32Bytes,
17    InflateError(String),
18    InvalidInput(String),
19    InvalidUrl(String),
20    Utf8Error(Utf8Error),
21    FromUtf8Error(FromUtf8Error),
22    ReqwestError(reqwest::Error),
23    SerdeCborError(serde_cbor::Error),
24    SerdeJsonError(serde_json::Error),
25    AbiCoderError(alloy::sol_types::Error),
26    ValidationErrors(validator::ValidationErrors),
27    DecodeHexStringError(alloy::primitives::hex::FromHexError),
28    InvalidMetaMagic(KnownMagic, KnownMagic),
29    MetaboardSubgraphClientError(MetaboardSubgraphClientError),
30}
31
32impl std::fmt::Display for Error {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        match self {
35            Error::CorruptMeta => f.write_str("corrupt meta"),
36            Error::UnknownMeta => f.write_str("unknown meta"),
37            Error::UnknownMagic => f.write_str("unknown magic"),
38            Error::UnsupportedMeta => f.write_str("unsupported meta"),
39            Error::InvalidHash => f.write_str("invalid keccak256 hash"),
40            Error::NoRecordFound => f.write_str("found no matching record"),
41            Error::BiggerThan32Bytes => {
42                f.write_str("unexpected input size, must be 32 bytes or less")
43            }
44            Error::InvalidInput(v) => write!(f, "invalid input: {}", v),
45            Error::InvalidUrl(v) => write!(f, "invalid URL: {}", v),
46            Error::ReqwestError(v) => write!(f, "{}", v),
47            Error::InflateError(v) => write!(f, "{}", v),
48            Error::Utf8Error(v) => write!(f, "{}", v),
49            Error::AbiCoderError(v) => write!(f, "{}", v),
50            Error::SerdeCborError(v) => write!(f, "{}", v),
51            Error::SerdeJsonError(v) => write!(f, "{}", v),
52            Error::FromUtf8Error(v) => write!(f, "{}", v),
53            Error::DecodeHexStringError(v) => write!(f, "{}", v),
54            Error::ValidationErrors(v) => write!(f, "{}", v),
55            Error::InvalidMetaMagic(expected, actual) => {
56                write!(
57                    f,
58                    "invalid meta magic: expected {:?}, got {:?}",
59                    expected, actual
60                )
61            }
62            Error::MetaboardSubgraphClientError(v) => write!(f, "{}", v),
63        }
64    }
65}
66
67impl std::error::Error for Error {}
68
69impl From<serde_json::Error> for Error {
70    fn from(value: serde_json::Error) -> Self {
71        Error::SerdeJsonError(value)
72    }
73}
74
75impl From<serde_cbor::Error> for Error {
76    fn from(value: serde_cbor::Error) -> Self {
77        Error::SerdeCborError(value)
78    }
79}
80
81impl From<FromUtf8Error> for Error {
82    fn from(value: FromUtf8Error) -> Self {
83        Error::FromUtf8Error(value)
84    }
85}
86
87impl From<Utf8Error> for Error {
88    fn from(value: Utf8Error) -> Self {
89        Error::Utf8Error(value)
90    }
91}
92
93impl From<validator::ValidationErrors> for Error {
94    fn from(value: validator::ValidationErrors) -> Self {
95        Error::ValidationErrors(value)
96    }
97}
98
99impl From<alloy::sol_types::Error> for Error {
100    fn from(value: alloy::sol_types::Error) -> Self {
101        Error::AbiCoderError(value)
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    /// Fixed-string Display arms are part of the CLI's user-facing surface:
110    /// pin them exactly.
111    #[test]
112    fn test_display_fixed_strings() {
113        assert_eq!(Error::CorruptMeta.to_string(), "corrupt meta");
114        assert_eq!(Error::UnknownMeta.to_string(), "unknown meta");
115        assert_eq!(Error::UnknownMagic.to_string(), "unknown magic");
116        assert_eq!(Error::UnsupportedMeta.to_string(), "unsupported meta");
117        assert_eq!(Error::InvalidHash.to_string(), "invalid keccak256 hash");
118        assert_eq!(Error::NoRecordFound.to_string(), "found no matching record");
119        assert_eq!(
120            Error::BiggerThan32Bytes.to_string(),
121            "unexpected input size, must be 32 bytes or less"
122        );
123    }
124
125    /// Formatted wrappers carry the wrapped value in the rendered message.
126    #[test]
127    fn test_display_formatted_wrappers() {
128        assert_eq!(
129            Error::InvalidInput("abc".to_string()).to_string(),
130            "invalid input: abc"
131        );
132        assert_eq!(
133            Error::InvalidUrl("not-a-url".to_string()).to_string(),
134            "invalid URL: not-a-url"
135        );
136        assert_eq!(Error::InflateError("boom".to_string()).to_string(), "boom");
137    }
138
139    /// InvalidMetaMagic renders expected first, actual second.
140    #[test]
141    fn test_display_invalid_meta_magic_field_order() {
142        let err = Error::InvalidMetaMagic(KnownMagic::RainMetaDocumentV1, KnownMagic::OpMetaV1);
143        assert_eq!(
144            err.to_string(),
145            "invalid meta magic: expected RainMetaDocumentV1, got OpMetaV1"
146        );
147    }
148
149    /// From impls must route each source error to its own variant, preserving
150    /// the source (observable through the rendered message).
151    #[test]
152    fn test_from_serde_json_routes_to_serde_json_error() {
153        let src = serde_json::from_str::<serde_json::Value>("{oops").unwrap_err();
154        let msg = src.to_string();
155        let err: Error = src.into();
156        match err {
157            Error::SerdeJsonError(e) => assert_eq!(e.to_string(), msg),
158            other => panic!("wrong variant: {:?}", other),
159        }
160    }
161
162    #[test]
163    fn test_from_utf8_error_routes_to_utf8_error() {
164        // The invalid byte is deliberate: it manufactures the source error.
165        #[allow(invalid_from_utf8)]
166        let src = std::str::from_utf8(&[0xff]).unwrap_err();
167        let err: Error = src.into();
168        match err {
169            Error::Utf8Error(e) => assert_eq!(e, src),
170            other => panic!("wrong variant: {:?}", other),
171        }
172    }
173
174    #[test]
175    fn test_from_from_utf8_error_routes_to_from_utf8_error() {
176        let src = String::from_utf8(vec![0xff]).unwrap_err();
177        let msg = src.to_string();
178        let err: Error = src.into();
179        match err {
180            Error::FromUtf8Error(e) => assert_eq!(e.to_string(), msg),
181            other => panic!("wrong variant: {:?}", other),
182        }
183    }
184}