signstar_config/
state.rs

1//! State handling and comparison.
2
3use std::any::Any;
4
5use strum::IntoStaticStr;
6
7/// The type of a state.
8#[derive(Clone, Copy, Debug, strum::Display, Eq, IntoStaticStr, PartialEq)]
9pub enum StateType {
10    /// A NetHSM backend.
11    #[strum(to_string = "NetHSM")]
12    NetHsm,
13
14    /// Configuration items relevant for a NetHSM backend in a Signstar configuration file.
15    #[strum(to_string = "Signstar configuration (NetHSM)")]
16    SignstarConfigNetHsm,
17
18    /// Configuration items relevant for a YubiHSM2 backend in a Signstar configuration file.
19    #[strum(to_string = "Signstar configuration (YubiHSM2)")]
20    SignstarConfigYubiHsm2,
21
22    /// A YubiHSM2 backend.
23    #[strum(to_string = "YubiHSM2")]
24    YubiHsm2,
25}
26
27impl StateType {
28    /// Checks whether this and another [`StateType`] are comparable.
29    ///
30    /// Returns `true`, if `self` and `other` can be compared, `false` otherwise.
31    fn is_compatible(&self, other: StateType) -> bool {
32        matches!(
33            (self, other),
34            (StateType::NetHsm, StateType::SignstarConfigNetHsm)
35                | (StateType::NetHsm, StateType::NetHsm)
36                | (StateType::SignstarConfigNetHsm, StateType::NetHsm)
37                | (
38                    StateType::SignstarConfigNetHsm,
39                    StateType::SignstarConfigNetHsm
40                )
41                | (StateType::YubiHsm2, StateType::SignstarConfigYubiHsm2)
42                | (StateType::YubiHsm2, StateType::YubiHsm2)
43                | (StateType::SignstarConfigYubiHsm2, StateType::YubiHsm2)
44                | (
45                    StateType::SignstarConfigYubiHsm2,
46                    StateType::SignstarConfigYubiHsm2
47                )
48        )
49    }
50}
51
52/// The description of a failure in comparing an item between two [`StateHandling`] implementations.
53#[derive(Debug)]
54pub struct StateComparisonFailure {
55    /// A message describing the discrepancy.
56    pub message: String,
57
58    /// The [`StateType`] of the calling [`StateHandling`] implementation.
59    pub self_state_type: StateType,
60
61    /// The [`StateType`] of the [`StateHandling`] implementation that is compared against.
62    pub other_state_type: StateType,
63}
64
65/// A report on the comparison between two compatible [`StateHandling`] implementations.
66#[derive(Debug)]
67pub enum StateComparisonReport {
68    /// The two [`StateHandling`] implementations are not compatible.
69    Incompatible {
70        /// The type of state of the caller.
71        self_state: StateType,
72
73        /// The type of state of the called.
74        other_state: StateType,
75    },
76
77    /// The comparison of two [`StateHandling`] implementations failed.
78    ///
79    /// Tracks a list of strings that explain a failure each.
80    Failure(Vec<String>),
81
82    /// The state of the two [`StateHandling`] implementations is equal.
83    Success,
84}
85
86/// An interface to handle and compare the state of various types.
87pub trait StateHandling {
88    /// Returns the [`StateType`] of the implementation.
89    fn state_type(&self) -> StateType;
90
91    /// Returns `self` as an [`Any`].
92    fn as_any(&self) -> &dyn Any;
93
94    /// Checks whether this [`StateHandling`] implementation is comparable to another.
95    ///
96    /// Returns `true`, if the [`StateType`] of `self` and that of `other` can be compared, `false`
97    /// otherwise.
98    ///
99    /// # Note
100    ///
101    /// It should not be necessary to specifically implement this method.
102    fn is_comparable(&self, other: &dyn StateHandling) -> bool {
103        self.state_type().is_compatible(other.state_type())
104    }
105
106    /// Compares this and another [`StateHandling`] implementation.
107    ///
108    /// # Notes
109    ///
110    /// An implementation is expected to return
111    ///
112    /// - an [`Error`][`crate::Error`] if the facilities of a backend cannot be used
113    /// - a [`StateComparisonReport::Incompatible`] if `self` and `other` are not compatible (see
114    ///   [`StateHandling::is_comparable`])
115    fn compare(&self, other: &dyn StateHandling) -> StateComparisonReport;
116}
117
118#[cfg(test)]
119mod tests {
120    use log::LevelFilter;
121    use rstest::rstest;
122    use signstar_common::logging::setup_logging;
123    use testresult::TestResult;
124
125    use super::*;
126
127    /// Ensures that [`StateType::to_string`] shows correctly.
128    #[rstest]
129    #[case(StateType::NetHsm, "NetHSM")]
130    #[case(StateType::SignstarConfigNetHsm, "Signstar configuration (NetHSM)")]
131    #[case(StateType::SignstarConfigYubiHsm2, "Signstar configuration (YubiHSM2)")]
132    #[case(StateType::YubiHsm2, "YubiHSM2")]
133    fn state_type_display(#[case] state_type: StateType, #[case] expected: &str) -> TestResult {
134        setup_logging(LevelFilter::Debug)?;
135
136        assert_eq!(state_type.to_string(), expected);
137        Ok(())
138    }
139}