use crate::types::handle::Handle;
use crate::types::string::AtStrError;
use crate::{IntoStatic, types::did::Did};
use alloc::string::String;
use core::fmt;
use core::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::CowStr;
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)]
#[serde(untagged)]
pub enum AtIdentifier<'i> {
#[serde(borrow)]
Did(Did<'i>),
Handle(Handle<'i>),
}
impl<'i> AtIdentifier<'i> {
pub fn new(ident: &'i str) -> Result<Self, AtStrError> {
if let Ok(did) = ident.parse() {
Ok(AtIdentifier::Did(did))
} else {
ident.parse().map(AtIdentifier::Handle)
}
}
pub fn new_owned(ident: impl AsRef<str>) -> Result<Self, AtStrError> {
let ident = ident.as_ref();
if let Ok(did) = Did::new_owned(ident) {
Ok(AtIdentifier::Did(did))
} else {
Ok(AtIdentifier::Handle(Handle::new_owned(ident)?))
}
}
pub fn new_static(ident: &'static str) -> Result<AtIdentifier<'static>, AtStrError> {
if let Ok(did) = Did::new_static(ident) {
Ok(AtIdentifier::Did(did))
} else {
Ok(AtIdentifier::Handle(Handle::new_static(ident)?))
}
}
pub fn new_cow(ident: CowStr<'i>) -> Result<Self, AtStrError> {
if let Ok(did) = Did::new_cow(ident.clone()) {
Ok(AtIdentifier::Did(did))
} else {
Ok(AtIdentifier::Handle(Handle::new_cow(ident)?))
}
}
pub fn raw(ident: &'i str) -> Self {
if let Ok(did) = ident.parse() {
AtIdentifier::Did(did)
} else {
ident
.parse()
.map(AtIdentifier::Handle)
.expect("valid handle")
}
}
pub unsafe fn unchecked(ident: &'i str) -> Self {
if let Ok(did) = ident.parse() {
AtIdentifier::Did(did)
} else {
unsafe { AtIdentifier::Handle(Handle::unchecked(ident)) }
}
}
pub fn as_str(&self) -> &str {
match self {
AtIdentifier::Did(did) => did.as_str(),
AtIdentifier::Handle(handle) => handle.as_str(),
}
}
}
impl<'i> From<Did<'i>> for AtIdentifier<'i> {
fn from(did: Did<'i>) -> Self {
AtIdentifier::Did(did)
}
}
impl<'i> From<Handle<'i>> for AtIdentifier<'i> {
fn from(handle: Handle<'i>) -> Self {
AtIdentifier::Handle(handle)
}
}
impl FromStr for AtIdentifier<'_> {
type Err = AtStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(did) = s.parse() {
Ok(AtIdentifier::Did(did))
} else {
s.parse().map(AtIdentifier::Handle)
}
}
}
impl IntoStatic for AtIdentifier<'_> {
type Output = AtIdentifier<'static>;
fn into_static(self) -> Self::Output {
match self {
AtIdentifier::Did(did) => AtIdentifier::Did(did.into_static()),
AtIdentifier::Handle(handle) => AtIdentifier::Handle(handle.into_static()),
}
}
}
impl fmt::Display for AtIdentifier<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AtIdentifier::Did(did) => did.fmt(f),
AtIdentifier::Handle(handle) => handle.fmt(f),
}
}
}
impl From<String> for AtIdentifier<'static> {
fn from(value: String) -> Self {
if let Ok(did) = value.parse() {
AtIdentifier::Did(did)
} else {
value
.parse()
.map(AtIdentifier::Handle)
.expect("valid handle")
}
}
}
impl<'i> From<CowStr<'i>> for AtIdentifier<'i> {
fn from(value: CowStr<'i>) -> Self {
if let Ok(did) = value.parse() {
AtIdentifier::Did(did)
} else {
value
.parse()
.map(AtIdentifier::Handle)
.expect("valid handle")
}
}
}
impl<'i> From<AtIdentifier<'i>> for String {
fn from(value: AtIdentifier) -> Self {
match value {
AtIdentifier::Did(did) => did.into(),
AtIdentifier::Handle(handle) => handle.into(),
}
}
}
impl AsRef<str> for AtIdentifier<'_> {
fn as_ref(&self) -> &str {
match self {
AtIdentifier::Did(did) => did.as_ref(),
AtIdentifier::Handle(handle) => handle.as_ref(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_did() {
let ident = AtIdentifier::new("did:plc:foo").unwrap();
assert!(matches!(ident, AtIdentifier::Did(_)));
assert_eq!(ident.as_str(), "did:plc:foo");
}
#[test]
fn parses_handle() {
let ident = AtIdentifier::new("alice.test").unwrap();
assert!(matches!(ident, AtIdentifier::Handle(_)));
assert_eq!(ident.as_str(), "alice.test");
}
#[test]
fn did_takes_precedence() {
let ident = AtIdentifier::new("did:web:alice.test").unwrap();
assert!(matches!(ident, AtIdentifier::Did(_)));
}
#[test]
fn from_types() {
let did = Did::new("did:plc:foo").unwrap();
let ident: AtIdentifier = did.into();
assert!(matches!(ident, AtIdentifier::Did(_)));
let handle = Handle::new("alice.test").unwrap();
let ident: AtIdentifier = handle.into();
assert!(matches!(ident, AtIdentifier::Handle(_)));
}
}