use std::fmt;
use std::str::FromStr;
use crate::did::Did;
use crate::handle::Handle;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AtIdentifier {
Did(Did),
Handle(Handle),
}
#[derive(Debug, Clone, thiserror::Error)]
#[error("Invalid AT identifier: {reason}")]
pub struct InvalidAtIdentifierError {
pub reason: String,
}
impl AtIdentifier {
pub fn new(s: &str) -> Result<Self, InvalidAtIdentifierError> {
if s.starts_with("did:") {
Did::new(s)
.map(AtIdentifier::Did)
.map_err(|e| InvalidAtIdentifierError {
reason: e.to_string(),
})
} else {
Handle::new(s)
.map(AtIdentifier::Handle)
.map_err(|e| InvalidAtIdentifierError {
reason: e.to_string(),
})
}
}
#[must_use]
pub fn is_valid(s: &str) -> bool {
Self::new(s).is_ok()
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Did(d) => d.as_str(),
Self::Handle(h) => h.as_str(),
}
}
#[must_use]
pub const fn is_did(&self) -> bool {
matches!(self, Self::Did(_))
}
#[must_use]
pub const fn is_handle(&self) -> bool {
matches!(self, Self::Handle(_))
}
}
impl fmt::Display for AtIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Did(d) => d.fmt(f),
Self::Handle(h) => h.fmt(f),
}
}
}
impl FromStr for AtIdentifier {
type Err = InvalidAtIdentifierError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
impl From<Did> for AtIdentifier {
fn from(d: Did) -> Self {
Self::Did(d)
}
}
impl From<Handle> for AtIdentifier {
fn from(h: Handle) -> Self {
Self::Handle(h)
}
}
impl serde::Serialize for AtIdentifier {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.as_str().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for AtIdentifier {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Self::new(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_dids() {
let id = AtIdentifier::new("did:plc:asdf123").unwrap();
assert!(id.is_did());
assert!(!id.is_handle());
}
#[test]
fn parses_handles() {
let id = AtIdentifier::new("alice.bsky.social").unwrap();
assert!(id.is_handle());
assert!(!id.is_did());
}
#[test]
fn invalid() {
assert!(AtIdentifier::new("").is_err());
assert!(AtIdentifier::new("not-valid").is_err());
}
#[test]
fn display() {
let id = AtIdentifier::new("did:plc:asdf123").unwrap();
assert_eq!(id.to_string(), "did:plc:asdf123");
let id = AtIdentifier::new("alice.bsky.social").unwrap();
assert_eq!(id.to_string(), "alice.bsky.social");
}
}