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}