use core::fmt;
use core::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct AcpProtocolVersion(
).
pub u16,
);
impl AcpProtocolVersion {
pub const CURRENT: Self = Self(1);
}
impl Default for AcpProtocolVersion {
fn default() -> Self {
Self::CURRENT
}
}
impl fmt::Display for AcpProtocolVersion {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(formatter)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AcpProtocolVersionParseError {
input: String,
reason: &'static str,
}
impl fmt::Display for AcpProtocolVersionParseError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"ACP protocol version {:?} is invalid: {}",
self.input, self.reason
)
}
}
impl std::error::Error for AcpProtocolVersionParseError {}
impl FromStr for AcpProtocolVersion {
type Err = AcpProtocolVersionParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let value = input.parse::<u16>().map_err(|error| {
let reason = match error.kind() {
core::num::IntErrorKind::Empty => "the input is empty",
core::num::IntErrorKind::InvalidDigit => "the input is not a decimal integer",
core::num::IntErrorKind::PosOverflow => "the value overflows u16",
_ => "the input is not a valid u16",
};
AcpProtocolVersionParseError {
input: input.to_owned(),
reason,
}
})?;
Ok(Self(value))
}
}
impl Serialize for AcpProtocolVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for AcpProtocolVersion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
u16::deserialize(deserializer).map(Self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
#[expect(
dead_code,
reason = "the test only constructs the error variant on failure; the field is read by the Debug derive"
)]
enum Failure {
Parse(AcpProtocolVersionParseError),
Json(serde_json::Error),
}
impl From<AcpProtocolVersionParseError> for Failure {
fn from(error: AcpProtocolVersionParseError) -> Self {
Self::Parse(error)
}
}
impl From<serde_json::Error> for Failure {
fn from(error: serde_json::Error) -> Self {
Self::Json(error)
}
}
#[test]
fn the_current_pin_round_trips_and_serialises_as_u16() -> Result<(), Failure> {
let value: AcpProtocolVersion = "1".parse()?;
assert_eq!(value, AcpProtocolVersion::CURRENT);
let round_tripped: AcpProtocolVersion = format!("{value}").parse()?;
assert_eq!(round_tripped, value);
let json = serde_json::to_string(&value)?;
assert_eq!(json, "1", "the wire form is a bare u16");
Ok(())
}
#[test]
fn parse_failures_carry_the_input_and_the_reason() -> Result<(), Failure> {
match "".parse::<AcpProtocolVersion>() {
Err(error) => assert_eq!(error.reason, "the input is empty"),
Ok(_) => {
return Err(Failure::Parse(AcpProtocolVersionParseError {
input: "<empty>".to_owned(),
reason: "an empty input should fail to parse",
}));
}
}
match "999999".parse::<AcpProtocolVersion>() {
Err(error) => assert_eq!(error.reason, "the value overflows u16"),
Ok(_) => {
return Err(Failure::Parse(AcpProtocolVersionParseError {
input: "999999".to_owned(),
reason: "an out-of-range input should fail to parse",
}));
}
}
match "1.5".parse::<AcpProtocolVersion>() {
Err(error) => assert_eq!(error.reason, "the input is not a decimal integer"),
Ok(_) => {
return Err(Failure::Parse(AcpProtocolVersionParseError {
input: "1.5".to_owned(),
reason: "a fractional input should fail to parse",
}));
}
}
Ok(())
}
}