use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum PassportStatus {
Draft,
Published,
Suspended,
Retired,
Superseded,
Deactivated,
}
impl PassportStatus {
pub const ALL: &'static [Self] = &[
Self::Draft,
Self::Published,
Self::Suspended,
Self::Retired,
Self::Superseded,
Self::Deactivated,
];
const fn wire_str(&self) -> &'static str {
match self {
PassportStatus::Draft => "draft",
PassportStatus::Published => "active",
PassportStatus::Suspended => "suspended",
PassportStatus::Retired => "retired",
PassportStatus::Superseded => "superseded",
PassportStatus::Deactivated => "deactivated",
}
}
}
impl Serialize for PassportStatus {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.wire_str())
}
}
impl<'de> Deserialize<'de> for PassportStatus {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
match s.as_str() {
"draft" => Ok(PassportStatus::Draft),
"active" | "published" => Ok(PassportStatus::Published),
"suspended" => Ok(PassportStatus::Suspended),
"retired" => Ok(PassportStatus::Retired),
"superseded" => Ok(PassportStatus::Superseded),
"deactivated" => Ok(PassportStatus::Deactivated),
"archived" => Err(serde::de::Error::custom(
"this status is now `retired`. `archived` was not dropped — it \
still names what EN 18221 clause 4.2 means by it, the retention \
of historical versions of a passport that is still live, which \
is a different thing from a terminal lifecycle status. That is \
why it is refused here rather than accepted as an alias: one \
word cannot carry both meanings on the same wire",
)),
other => Err(serde::de::Error::unknown_variant(
other,
&[
"draft",
"active",
"suspended",
"retired",
"superseded",
"deactivated",
],
)),
}
}
}
impl PassportStatus {
pub fn can_transition_to(&self, next: &PassportStatus) -> bool {
use PassportStatus::*;
matches!(
(self, next),
(Draft, Published)
| (Draft, Retired)
| (Published, Suspended)
| (Published, Retired)
| (Published, Superseded)
| (Published, Deactivated)
| (Suspended, Published)
| (Suspended, Retired)
| (Suspended, Deactivated)
)
}
}
impl std::fmt::Display for PassportStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.wire_str())
}
}