use serde::{Deserialize, Serialize};
use crate::prelude::Address;
use super::{
IdentityError as Err, canonical_identity_string, is_valid_fqdn, is_valid_identity_string,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentId(String);
impl From<Address> for AgentId {
fn from(address: Address) -> Self {
address.agent().clone()
}
}
impl TryFrom<&str> for AgentId {
type Error = Err;
fn try_from(value: &str) -> Result<Self, Err> {
AgentId::parse(value)
}
}
impl TryFrom<String> for AgentId {
type Error = Err;
fn try_from(value: String) -> Result<Self, Err> {
AgentId::parse(value)
}
}
impl AgentId {
pub fn parse(input: impl AsRef<str>) -> Result<Self, Err> {
let input = input.as_ref();
if input.is_empty() {
return Err(Err::InvalidAgent);
}
if !is_valid_identity_string(input) {
return Err(Err::InvalidIdentityString);
}
if !is_valid_fqdn(input) {
return Err(Err::InvalidFqdnString);
}
let canonical = canonical_identity_string(input);
Ok(Self(canonical))
}
pub fn replace(&mut self, new_value: impl AsRef<str>) -> Result<(), Err> {
*self = Self::parse(new_value)?;
Ok(())
}
pub fn canonical(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for AgentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.canonical())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_and_canonicalizes() {
assert_eq!(
AgentId::parse("US.example.org").unwrap().canonical(),
"us.example.org"
);
}
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidAgent")]
fn rejects_empty() {
let _ = AgentId::parse("").unwrap();
}
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
fn rejects_invalid_chars() {
let _ = AgentId::parse("Invalid Agent!").unwrap();
}
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
fn reject_untrimmed() {
let _ = AgentId::parse(" trim_me ").unwrap();
}
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
fn rejects_non_ascii() {
let _ = AgentId::parse("调试输出").unwrap();
}
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidIdentityString")]
fn rejects_homoglyphs() {
let _ = AgentId::parse("us.exaмple.org").unwrap(); }
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: InvalidFqdnString")]
fn rejects_invalid_fqdn() {
let _ = AgentId::parse("us_east.example.org").unwrap();
}
}