use std::{
fmt::Display,
num::{NonZeroU64, ParseIntError},
str::FromStr,
};
use thiserror::Error;
use crate::{EMLError, EMLValueResultExt, utils::StringValueData};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct AffiliationId(NonZeroU64);
impl AffiliationId {
pub fn new(value: NonZeroU64) -> Self {
AffiliationId(value)
}
pub fn from_u64(value: u64) -> Result<Self, InvalidAffiliationIdError> {
let value = NonZeroU64::new(value).ok_or(InvalidAffiliationIdError::ZeroInteger)?;
Ok(AffiliationId::new(value))
}
pub fn value(&self) -> NonZeroU64 {
self.0
}
}
impl FromStr for AffiliationId {
type Err = EMLError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
StringValueData::parse_from_str(s).wrap_value_error()
}
}
impl Display for AffiliationId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Error)]
pub enum InvalidAffiliationIdError {
#[error("Failed to parse affiliation id: {0}")]
ParseError(ParseIntError),
#[error("Affiliation id must be a non-zero positive integer")]
ZeroInteger,
#[error("Affiliation id cannot start with a zero")]
StartsWithZero,
}
impl StringValueData for AffiliationId {
type Error = InvalidAffiliationIdError;
fn parse_from_str(s: &str) -> Result<Self, Self::Error>
where
Self: Sized,
{
if s.starts_with("0") {
return Err(InvalidAffiliationIdError::StartsWithZero);
}
let value = u64::from_str(s).map_err(InvalidAffiliationIdError::ParseError)?;
let value = NonZeroU64::new(value).ok_or(InvalidAffiliationIdError::ZeroInteger)?;
Ok(AffiliationId::new(value))
}
fn to_raw_value(&self) -> Box<str> {
self.0.to_string().into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_affiliation_ids() {
let valid_ids = ["1", "12345"];
for id in valid_ids {
assert!(
AffiliationId::from_str(id).is_ok(),
"AffiliationId should accept valid id: {}",
id
);
}
}
#[test]
fn test_invalid_affiliation_ids() {
let invalid_ids = ["0", " 0123", "0123", "abc", "", "-1"];
for id in invalid_ids {
assert!(
AffiliationId::from_str(id).is_err(),
"AffiliationId should reject invalid id: {}",
id
);
}
}
}