Skip to main content

dpp_domain/domain/
error.rs

1//! Top-level error type for the DPP domain.
2
3use thiserror::Error;
4
5use crate::domain::field_error::ValidationErrors;
6
7/// Top-level error type for the DPP domain.
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum DppError {
11    #[error("passport not found: {0}")]
12    NotFound(String),
13
14    #[error(
15        "passport is not in a state that allows this operation: current={current}, required={required}"
16    )]
17    InvalidTransition { current: String, required: String },
18
19    #[error("validation failed: {0}")]
20    Validation(ValidationErrors),
21
22    #[error("signing failed: {0}")]
23    Signing(String),
24
25    #[error("serialisation error: {0}")]
26    Serialisation(String),
27
28    /// Returned when an attempt is made to delete or overwrite a passport that
29    /// has been published and is therefore subject to EU ESPR retention obligations.
30    /// Published passports must remain accessible for the legally defined period
31    /// under the applicable delegated act (typically 10–15 years).
32    #[error("passport is retention-locked: published passports cannot be deleted")]
33    RetentionLocked,
34
35    #[error("internal error: {0}")]
36    Internal(String),
37}
38
39impl From<ValidationErrors> for DppError {
40    fn from(errors: ValidationErrors) -> Self {
41        DppError::Validation(errors)
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn not_found_display() {
51        let e = DppError::NotFound("passport-123".to_owned());
52        assert_eq!(e.to_string(), "passport not found: passport-123");
53    }
54
55    #[test]
56    fn invalid_transition_display() {
57        let e = DppError::InvalidTransition {
58            current: "archived".to_owned(),
59            required: "draft".to_owned(),
60        };
61        let msg = e.to_string();
62        assert!(
63            msg.contains("archived"),
64            "message should contain current state"
65        );
66        assert!(
67            msg.contains("draft"),
68            "message should contain required state"
69        );
70    }
71
72    #[test]
73    fn validation_display() {
74        let e = DppError::Validation("product_name is required".into());
75        assert!(e.to_string().contains("product_name is required"));
76    }
77}