1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum QrError {
10 DataTooLong,
12 InvalidColor(String),
14}
15
16impl QrError {
17 pub fn code(&self) -> &'static str {
20 match self {
21 Self::DataTooLong => "qrcode:data_too_long",
22 Self::InvalidColor(_) => "qrcode:invalid_color",
23 }
24 }
25}
26
27impl fmt::Display for QrError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::DataTooLong => write!(f, "input data exceeds QR code capacity"),
31 Self::InvalidColor(c) => write!(f, "invalid hex color: {c}"),
32 }
33 }
34}
35
36impl std::error::Error for QrError {}
37
38impl From<QrError> for crate::Error {
39 fn from(err: QrError) -> Self {
40 let code = err.code();
41 crate::Error::bad_request(err.to_string())
42 .chain(err)
43 .with_code(code)
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn all_variants_have_unique_codes() {
53 let variants = [QrError::DataTooLong, QrError::InvalidColor("#bad".into())];
54 let mut codes: Vec<&str> = variants.iter().map(|v| v.code()).collect();
55 let len_before = codes.len();
56 codes.sort();
57 codes.dedup();
58 assert_eq!(codes.len(), len_before, "duplicate error codes found");
59 }
60
61 #[test]
62 fn all_codes_start_with_qrcode_prefix() {
63 let variants = [QrError::DataTooLong, QrError::InvalidColor("x".into())];
64 for v in &variants {
65 assert!(
66 v.code().starts_with("qrcode:"),
67 "code {} missing prefix",
68 v.code()
69 );
70 }
71 }
72
73 #[test]
74 fn display_is_human_readable() {
75 assert_eq!(
76 QrError::DataTooLong.to_string(),
77 "input data exceeds QR code capacity"
78 );
79 assert_eq!(
80 QrError::InvalidColor("#xyz".into()).to_string(),
81 "invalid hex color: #xyz"
82 );
83 }
84
85 #[test]
86 fn converts_to_modo_error() {
87 let err: crate::Error = QrError::DataTooLong.into();
88 assert_eq!(err.status(), http::StatusCode::BAD_REQUEST);
89 assert_eq!(err.error_code(), Some("qrcode:data_too_long"));
90 }
91
92 #[test]
93 fn recoverable_via_source_as() {
94 let err: crate::Error = QrError::InvalidColor("#bad".into()).into();
95 let qr_err = err.source_as::<QrError>();
96 assert_eq!(qr_err, Some(&QrError::InvalidColor("#bad".into())));
97 }
98}