proto_blue_syntax/
at_identifier.rs1use std::fmt;
6use std::str::FromStr;
7
8use crate::did::Did;
9use crate::handle::Handle;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum AtIdentifier {
14 Did(Did),
16 Handle(Handle),
18}
19
20#[derive(Debug, Clone, thiserror::Error)]
22#[error("Invalid AT identifier: {reason}")]
23pub struct InvalidAtIdentifierError {
24 pub reason: String,
25}
26
27impl AtIdentifier {
28 pub fn new(s: &str) -> Result<Self, InvalidAtIdentifierError> {
30 if s.starts_with("did:") {
31 Did::new(s)
32 .map(AtIdentifier::Did)
33 .map_err(|e| InvalidAtIdentifierError {
34 reason: e.to_string(),
35 })
36 } else {
37 Handle::new(s)
38 .map(AtIdentifier::Handle)
39 .map_err(|e| InvalidAtIdentifierError {
40 reason: e.to_string(),
41 })
42 }
43 }
44
45 pub fn is_valid(s: &str) -> bool {
47 AtIdentifier::new(s).is_ok()
48 }
49
50 pub fn as_str(&self) -> &str {
52 match self {
53 AtIdentifier::Did(d) => d.as_str(),
54 AtIdentifier::Handle(h) => h.as_str(),
55 }
56 }
57
58 pub fn is_did(&self) -> bool {
60 matches!(self, AtIdentifier::Did(_))
61 }
62
63 pub fn is_handle(&self) -> bool {
65 matches!(self, AtIdentifier::Handle(_))
66 }
67}
68
69impl fmt::Display for AtIdentifier {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 AtIdentifier::Did(d) => d.fmt(f),
73 AtIdentifier::Handle(h) => h.fmt(f),
74 }
75 }
76}
77
78impl FromStr for AtIdentifier {
79 type Err = InvalidAtIdentifierError;
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
81 AtIdentifier::new(s)
82 }
83}
84
85impl From<Did> for AtIdentifier {
86 fn from(d: Did) -> Self {
87 AtIdentifier::Did(d)
88 }
89}
90
91impl From<Handle> for AtIdentifier {
92 fn from(h: Handle) -> Self {
93 AtIdentifier::Handle(h)
94 }
95}
96
97impl serde::Serialize for AtIdentifier {
98 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
99 self.as_str().serialize(serializer)
100 }
101}
102
103impl<'de> serde::Deserialize<'de> for AtIdentifier {
104 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
105 let s = String::deserialize(deserializer)?;
106 AtIdentifier::new(&s).map_err(serde::de::Error::custom)
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn parses_dids() {
116 let id = AtIdentifier::new("did:plc:asdf123").unwrap();
117 assert!(id.is_did());
118 assert!(!id.is_handle());
119 }
120
121 #[test]
122 fn parses_handles() {
123 let id = AtIdentifier::new("alice.bsky.social").unwrap();
124 assert!(id.is_handle());
125 assert!(!id.is_did());
126 }
127
128 #[test]
129 fn invalid() {
130 assert!(AtIdentifier::new("").is_err());
131 assert!(AtIdentifier::new("not-valid").is_err());
132 }
133
134 #[test]
135 fn display() {
136 let id = AtIdentifier::new("did:plc:asdf123").unwrap();
137 assert_eq!(id.to_string(), "did:plc:asdf123");
138
139 let id = AtIdentifier::new("alice.bsky.social").unwrap();
140 assert_eq!(id.to_string(), "alice.bsky.social");
141 }
142}