baseid_oid4vci/
error.rs

1//! OID4VCI-specific error types.
2
3use baseid_core::error::ProtocolError;
4use baseid_core::language::Language;
5
6/// Errors specific to OID4VCI protocol flows.
7#[derive(Debug, thiserror::Error)]
8pub enum Oid4vciError {
9    #[error("metadata discovery failed: {0}")]
10    MetadataDiscovery(String),
11    #[error("token request failed: {0}")]
12    TokenRequestFailed(String),
13    #[error("credential request failed: {0}")]
14    CredentialRequestFailed(String),
15    #[error("unsupported grant type")]
16    UnsupportedGrantType,
17    #[error("invalid proof of possession")]
18    InvalidProof,
19    #[error("server error: {error} - {error_description}")]
20    ServerError {
21        error: String,
22        error_description: String,
23    },
24}
25
26impl baseid_core::error::BilingualError for Oid4vciError {
27    fn message(&self, lang: Language) -> &str {
28        match (self, lang) {
29            (Oid4vciError::MetadataDiscovery(_), Language::Fr) => {
30                "échec de la découverte des métadonnées"
31            }
32            (Oid4vciError::MetadataDiscovery(_), _) => "metadata discovery failed",
33            (Oid4vciError::TokenRequestFailed(_), Language::Fr) => "échec de la demande de jeton",
34            (Oid4vciError::TokenRequestFailed(_), _) => "token request failed",
35            (Oid4vciError::CredentialRequestFailed(_), Language::Fr) => {
36                "échec de la demande d'attestation"
37            }
38            (Oid4vciError::CredentialRequestFailed(_), _) => "credential request failed",
39            (Oid4vciError::UnsupportedGrantType, Language::Fr) => {
40                "type d'autorisation non pris en charge"
41            }
42            (Oid4vciError::UnsupportedGrantType, _) => "unsupported grant type",
43            (Oid4vciError::InvalidProof, Language::Fr) => "preuve de possession invalide",
44            (Oid4vciError::InvalidProof, _) => "invalid proof of possession",
45            (Oid4vciError::ServerError { .. }, Language::Fr) => "erreur du serveur",
46            (Oid4vciError::ServerError { .. }, _) => "server error",
47        }
48    }
49}
50
51impl From<Oid4vciError> for baseid_core::Error {
52    fn from(e: Oid4vciError) -> Self {
53        match &e {
54            Oid4vciError::MetadataDiscovery(_) | Oid4vciError::TokenRequestFailed(_) => {
55                ProtocolError::InvalidResponse.into()
56            }
57            Oid4vciError::CredentialRequestFailed(_) | Oid4vciError::ServerError { .. } => {
58                ProtocolError::InvalidResponse.into()
59            }
60            Oid4vciError::UnsupportedGrantType | Oid4vciError::InvalidProof => {
61                ProtocolError::General.into()
62            }
63        }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn error_display_metadata_discovery() {
73        let e = Oid4vciError::MetadataDiscovery("network timeout".to_string());
74        assert!(e.to_string().contains("network timeout"));
75    }
76
77    #[test]
78    fn error_display_server_error() {
79        let e = Oid4vciError::ServerError {
80            error: "invalid_grant".to_string(),
81            error_description: "expired".to_string(),
82        };
83        let msg = e.to_string();
84        assert!(msg.contains("invalid_grant"));
85        assert!(msg.contains("expired"));
86    }
87
88    #[test]
89    fn error_conversion_metadata() {
90        let e: baseid_core::Error = Oid4vciError::MetadataDiscovery("x".to_string()).into();
91        // Just verify it converts without panic
92        let _ = format!("{e}");
93    }
94
95    #[test]
96    fn error_conversion_unsupported_grant() {
97        let e: baseid_core::Error = Oid4vciError::UnsupportedGrantType.into();
98        let _ = format!("{e}");
99    }
100
101    #[test]
102    fn error_conversion_invalid_proof() {
103        let e: baseid_core::Error = Oid4vciError::InvalidProof.into();
104        let _ = format!("{e}");
105    }
106
107    #[test]
108    fn bilingual_error_en() {
109        use baseid_core::error::BilingualError;
110        let e = Oid4vciError::UnsupportedGrantType;
111        assert_eq!(
112            e.message(baseid_core::language::Language::En),
113            "unsupported grant type"
114        );
115    }
116
117    #[test]
118    fn bilingual_error_fr() {
119        use baseid_core::error::BilingualError;
120        let e = Oid4vciError::UnsupportedGrantType;
121        assert_eq!(
122            e.message(baseid_core::language::Language::Fr),
123            "type d'autorisation non pris en charge"
124        );
125    }
126
127    #[test]
128    fn error_conversion_all_variants() {
129        // Each variant must convert to baseid_core::Error
130        let variants: Vec<Oid4vciError> = vec![
131            Oid4vciError::MetadataDiscovery("a".into()),
132            Oid4vciError::TokenRequestFailed("b".into()),
133            Oid4vciError::CredentialRequestFailed("c".into()),
134            Oid4vciError::UnsupportedGrantType,
135            Oid4vciError::InvalidProof,
136            Oid4vciError::ServerError {
137                error: "e".into(),
138                error_description: "f".into(),
139            },
140        ];
141        for v in variants {
142            let core_err: baseid_core::Error = v.into();
143            assert!(!format!("{core_err}").is_empty());
144        }
145    }
146}