ichen_openprotocol/
error.rs

1use derive_more::*;
2use std::borrow::Cow;
3
4/// Result error type.
5///
6#[derive(Debug, Display)]
7pub enum OpenProtocolError<'a> {
8    /// The value of a field is the empty string `""` or containing all white-spaces,
9    /// which is not allowed as value of that field.
10    #[display(fmt = "field {} cannot be empty or all whitespace", _0)]
11    EmptyField(&'a str),
12    //
13    /// The value of a field is not valid.
14    #[display(fmt = "value [{}] is invalid for the field {} - {}", value, field, description)]
15    InvalidField { field: &'a str, value: Cow<'a, str>, description: Cow<'a, str> },
16    //
17    /// The value of a field is not consistent with the matching value in the [`state`].
18    ///
19    /// [`state`]: struct.StateValues.html
20    ///
21    #[display(fmt = "value of field {} is not the same as the matching field in the state", _0)]
22    InconsistentState(&'a str),
23    //
24    /// The value of a field is not consistent with the matching value in the
25    /// [`Controller`] structure.
26    ///
27    /// [`Controller`]: struct.Controller.html
28    #[display(
29        fmt = "value of field {} is not the same as the matching field in the Controller",
30        _0
31    )]
32    InconsistentField(&'a str),
33    //
34    /// An enforced constraint is broken.
35    #[display(fmt = "{}", _0)]
36    ConstraintViolated(Cow<'a, str>),
37    //
38    /// Error when serializing/deserializing JSON.
39    #[display(fmt = "[{:?}] {}", "_0.classify()", _0)]
40    JsonError(serde_json::Error),
41    //
42    /// An unexpected system error.
43    #[display(fmt = "{}", _0)]
44    SystemError(Cow<'a, str>),
45}
46
47impl std::error::Error for OpenProtocolError<'_> {
48    fn description(&self) -> &str {
49        match self {
50            // JSON error
51            Self::JsonError(err) => err.description(),
52            //
53            // Invalid field value
54            Self::InvalidField { description, .. } => {
55                if description.is_empty() {
56                    "invalid field value"
57                } else {
58                    description
59                }
60            }
61            //
62            // Constraint violation
63            Self::ConstraintViolated(err) => err,
64            //
65            // System error
66            Self::SystemError(err) => err,
67            //
68            // Inconsistent field
69            Self::InconsistentField(_) => {
70                "value of field is not the same as matching field in the Controller"
71            }
72            //
73            // Inconsistent state
74            Self::InconsistentState(_) => {
75                "value of field is not the same as matching field in the state"
76            }
77            //
78            // Field empty
79            Self::EmptyField(_) => "field cannot be empty or all whitespace",
80        }
81    }
82
83    fn cause(&self) -> Option<&dyn std::error::Error> {
84        match self {
85            Self::JsonError(err) => Some(err),
86            _ => None,
87        }
88    }
89}
90
91impl PartialEq for OpenProtocolError<'_> {
92    /// Implement `PartialEq` for `OpenProtocolError`.
93    ///
94    /// Most variants already implement `PartialEq` and are simply delegated.
95    ///
96    /// The only variant that doesn't automatically implement `PartialEq` is [`JsonError`]
97    /// which encapsulates a `serde::error::Error` object that does not implement
98    /// `PartialEq`.  In this case, we test for equality simply by comparing the `Debug`
99    /// output of `self` and `other`.
100    ///
101    /// [`JsonError`]: #variant.JsonError
102    ///
103    fn eq(&self, other: &Self) -> bool {
104        match (self, other) {
105            // JSON error - since serde::error::Error does not implement PartialEq,
106            //              the only thing we can do is compare the debug representation.
107            (Self::JsonError(err1), Self::JsonError(err2)) => {
108                format!("{:?}", err1) == format!("{:?}", err2)
109            }
110            //
111            // All other variants need to manually implement PartialEq
112            (Self::EmptyField(err1), Self::EmptyField(err2)) => err1 == err2,
113            (
114                Self::InvalidField { field: field1, value: value1, description: err1 },
115                Self::InvalidField { field: field2, value: value2, description: err2 },
116            ) => field1 == field2 && value1 == value2 && err1 == err2,
117            (Self::InconsistentState(err1), Self::InconsistentState(err2)) => err1 == err2,
118            (Self::InconsistentField(err1), Self::InconsistentField(err2)) => err1 == err2,
119            (Self::ConstraintViolated(err1), Self::ConstraintViolated(err2)) => err1 == err2,
120            _ => false,
121        }
122    }
123}
124
125impl Eq for OpenProtocolError<'_> {}
126
127impl std::convert::From<OpenProtocolError<'_>> for String {
128    fn from(error: OpenProtocolError<'_>) -> Self {
129        error.to_string()
130    }
131}