protocol_droid/domain/acp/
protocol_version.rs1use core::fmt;
29use core::str::FromStr;
30
31use serde::{Deserialize, Deserializer, Serialize, Serializer};
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub struct AcpProtocolVersion(
39 pub u16,
44);
45
46impl AcpProtocolVersion {
47 pub const CURRENT: Self = Self(1);
53}
54
55impl Default for AcpProtocolVersion {
56 fn default() -> Self {
57 Self::CURRENT
58 }
59}
60
61impl fmt::Display for AcpProtocolVersion {
62 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
63 self.0.fmt(formatter)
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct AcpProtocolVersionParseError {
72 input: String,
73 reason: &'static str,
74}
75
76impl fmt::Display for AcpProtocolVersionParseError {
77 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(
79 formatter,
80 "ACP protocol version {:?} is invalid: {}",
81 self.input, self.reason
82 )
83 }
84}
85
86impl std::error::Error for AcpProtocolVersionParseError {}
87
88impl FromStr for AcpProtocolVersion {
89 type Err = AcpProtocolVersionParseError;
90
91 fn from_str(input: &str) -> Result<Self, Self::Err> {
92 let value = input.parse::<u16>().map_err(|error| {
93 let reason = match error.kind() {
94 core::num::IntErrorKind::Empty => "the input is empty",
95 core::num::IntErrorKind::InvalidDigit => "the input is not a decimal integer",
96 core::num::IntErrorKind::PosOverflow => "the value overflows u16",
97 _ => "the input is not a valid u16",
98 };
99 AcpProtocolVersionParseError {
100 input: input.to_owned(),
101 reason,
102 }
103 })?;
104 Ok(Self(value))
105 }
106}
107
108impl Serialize for AcpProtocolVersion {
109 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110 where
111 S: Serializer,
112 {
113 self.0.serialize(serializer)
114 }
115}
116
117impl<'de> Deserialize<'de> for AcpProtocolVersion {
118 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
119 where
120 D: Deserializer<'de>,
121 {
122 u16::deserialize(deserializer).map(Self)
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[derive(Debug)]
134 #[expect(
135 dead_code,
136 reason = "the test only constructs the error variant on failure; the field is read by the Debug derive"
137 )]
138 enum Failure {
139 Parse(AcpProtocolVersionParseError),
140 Json(serde_json::Error),
141 }
142
143 impl From<AcpProtocolVersionParseError> for Failure {
144 fn from(error: AcpProtocolVersionParseError) -> Self {
145 Self::Parse(error)
146 }
147 }
148
149 impl From<serde_json::Error> for Failure {
150 fn from(error: serde_json::Error) -> Self {
151 Self::Json(error)
152 }
153 }
154
155 #[test]
160 fn the_current_pin_round_trips_and_serialises_as_u16() -> Result<(), Failure> {
161 let value: AcpProtocolVersion = "1".parse()?;
162 assert_eq!(value, AcpProtocolVersion::CURRENT);
163 let round_tripped: AcpProtocolVersion = format!("{value}").parse()?;
164 assert_eq!(round_tripped, value);
165 let json = serde_json::to_string(&value)?;
166 assert_eq!(json, "1", "the wire form is a bare u16");
167 Ok(())
168 }
169
170 #[test]
175 fn parse_failures_carry_the_input_and_the_reason() -> Result<(), Failure> {
176 match "".parse::<AcpProtocolVersion>() {
177 Err(error) => assert_eq!(error.reason, "the input is empty"),
178 Ok(_) => {
179 return Err(Failure::Parse(AcpProtocolVersionParseError {
180 input: "<empty>".to_owned(),
181 reason: "an empty input should fail to parse",
182 }));
183 }
184 }
185 match "999999".parse::<AcpProtocolVersion>() {
186 Err(error) => assert_eq!(error.reason, "the value overflows u16"),
187 Ok(_) => {
188 return Err(Failure::Parse(AcpProtocolVersionParseError {
189 input: "999999".to_owned(),
190 reason: "an out-of-range input should fail to parse",
191 }));
192 }
193 }
194 match "1.5".parse::<AcpProtocolVersion>() {
195 Err(error) => assert_eq!(error.reason, "the input is not a decimal integer"),
196 Ok(_) => {
197 return Err(Failure::Parse(AcpProtocolVersionParseError {
198 input: "1.5".to_owned(),
199 reason: "a fractional input should fail to parse",
200 }));
201 }
202 }
203 Ok(())
204 }
205}