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