jacquard_common/types/
ident.rs1use crate::bos::{Bos, DefaultStr};
2use crate::types::handle::Handle;
3use crate::types::string::AtStrError;
4use crate::{
5 CowStr, IntoStatic,
6 types::did::{Did, validate_did},
7};
8use alloc::string::String;
9use alloc::string::ToString;
10use core::fmt;
11use core::str::FromStr;
12
13use serde::{Deserialize, Serialize};
14
15#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)]
23#[serde(untagged)]
24#[serde(bound(
25 serialize = "S: Bos<str> + AsRef<str> + Serialize",
26 deserialize = "S: Bos<str> + AsRef<str> + Deserialize<'de>"
27))]
28pub enum AtIdentifier<S: Bos<str> + AsRef<str> = DefaultStr> {
29 Did(Did<S>),
31 Handle(Handle<S>),
33}
34
35impl<S: Bos<str> + AsRef<str>> AtIdentifier<S> {
40 pub fn as_str(&self) -> &str {
42 match self {
43 AtIdentifier::Did(did) => did.as_str(),
44 AtIdentifier::Handle(handle) => handle.as_str(),
45 }
46 }
47}
48
49impl<S: Bos<str> + AsRef<str>> AtIdentifier<S> {
54 pub fn new(ident: S) -> Result<Self, AtStrError> {
59 let s = ident.as_ref();
60 if validate_did(s).is_ok() {
61 Ok(AtIdentifier::Did(unsafe { Did::unchecked(ident) }))
62 } else {
63 Handle::new(ident).map(AtIdentifier::Handle)
64 }
65 }
66
67 pub fn raw(ident: S) -> Self {
69 Self::new(ident).expect("valid identifier")
70 }
71
72 pub unsafe fn unchecked(ident: S) -> Self {
78 if validate_did(ident.as_ref()).is_ok() {
79 AtIdentifier::Did(unsafe { Did::unchecked(ident) })
80 } else {
81 unsafe { AtIdentifier::Handle(Handle::unchecked(ident)) }
82 }
83 }
84}
85
86impl<S: Bos<str> + AsRef<str> + FromStr> AtIdentifier<S> {
91 pub fn new_owned(ident: impl AsRef<str>) -> Result<Self, AtStrError> {
94 let ident = ident.as_ref();
95 if let Ok(did) = Did::new_owned(ident) {
96 Ok(AtIdentifier::Did(did))
97 } else {
98 Handle::new_owned(ident).map(AtIdentifier::Handle)
99 }
100 }
101
102 pub fn new_static(ident: &'static str) -> Result<Self, AtStrError> {
104 if let Ok(did) = Did::new_static(ident) {
105 Ok(AtIdentifier::Did(did))
106 } else {
107 Handle::new_static(ident).map(AtIdentifier::Handle)
108 }
109 }
110}
111
112impl<S: Bos<str> + AsRef<str> + IntoStatic> IntoStatic for AtIdentifier<S>
117where
118 S::Output: Bos<str> + AsRef<str>,
119{
120 type Output = AtIdentifier<S::Output>;
121
122 fn into_static(self) -> Self::Output {
123 match self {
124 AtIdentifier::Did(did) => AtIdentifier::Did(did.into_static()),
125 AtIdentifier::Handle(handle) => AtIdentifier::Handle(handle.into_static()),
126 }
127 }
128}
129
130impl<S: Bos<str> + AsRef<str>> AtIdentifier<S> {
131 pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> AtIdentifier<B> {
133 match self {
134 AtIdentifier::Did(did) => AtIdentifier::Did(did.convert()),
135 AtIdentifier::Handle(handle) => AtIdentifier::Handle(handle.convert()),
136 }
137 }
138}
139
140impl<S: Bos<str> + AsRef<str>> From<Did<S>> for AtIdentifier<S> {
141 fn from(did: Did<S>) -> Self {
142 AtIdentifier::Did(did)
143 }
144}
145
146impl<S: Bos<str> + AsRef<str>> From<Handle<S>> for AtIdentifier<S> {
147 fn from(handle: Handle<S>) -> Self {
148 AtIdentifier::Handle(handle)
149 }
150}
151
152impl FromStr for AtIdentifier {
153 type Err = AtStrError;
154
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 Self::new_owned(s)
157 }
158}
159
160impl FromStr for AtIdentifier<CowStr<'static>> {
161 type Err = AtStrError;
162
163 fn from_str(s: &str) -> Result<Self, Self::Err> {
164 Self::new_owned(s)
165 }
166}
167
168impl FromStr for AtIdentifier<String> {
169 type Err = AtStrError;
170
171 fn from_str(s: &str) -> Result<Self, Self::Err> {
172 Self::new_owned(s)
173 }
174}
175
176impl<S: Bos<str> + AsRef<str>> fmt::Display for AtIdentifier<S> {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 match self {
179 AtIdentifier::Did(did) => did.fmt(f),
180 AtIdentifier::Handle(handle) => handle.fmt(f),
181 }
182 }
183}
184
185impl From<String> for AtIdentifier {
186 fn from(value: String) -> Self {
187 Self::new_owned(value).expect("valid identifier")
188 }
189}
190
191impl<'i> From<CowStr<'i>> for AtIdentifier<CowStr<'i>> {
192 fn from(value: CowStr<'i>) -> Self {
193 Self::new(value).expect("valid identifier")
194 }
195}
196
197impl<S: Bos<str> + AsRef<str>> From<AtIdentifier<S>> for String {
198 fn from(value: AtIdentifier<S>) -> Self {
199 value.as_str().to_string()
200 }
201}
202
203impl<S: Bos<str> + AsRef<str>> AsRef<str> for AtIdentifier<S> {
204 fn as_ref(&self) -> &str {
205 self.as_str()
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use smol_str::SmolStr;
212
213 use super::*;
214 use crate::cowstr::ToCowStr;
215
216 #[test]
217 fn parses_did() {
218 let ident = AtIdentifier::<&str>::new("did:plc:foo").unwrap();
219 assert!(matches!(ident, AtIdentifier::Did(_)));
220 assert_eq!(ident.as_str(), "did:plc:foo");
221 }
222
223 #[test]
224 fn parses_handle() {
225 let ident = AtIdentifier::<&str>::new("alice.test").unwrap();
226 assert!(matches!(ident, AtIdentifier::Handle(_)));
227 assert_eq!(ident.as_str(), "alice.test");
228 }
229
230 #[test]
231 fn did_takes_precedence() {
232 let ident = AtIdentifier::<&str>::new("did:web:alice.test").unwrap();
233 assert!(matches!(ident, AtIdentifier::Did(_)));
234 }
235
236 #[test]
237 fn from_types() {
238 let did = Did::<SmolStr>::new_owned("did:plc:foo").unwrap();
239 let ident: AtIdentifier<SmolStr> = did.into();
240 assert!(matches!(ident, AtIdentifier::Did(_)));
241
242 let handle = Handle::new("alice.test".to_cowstr()).unwrap();
243 let ident: AtIdentifier<CowStr> = handle.into();
244 assert!(matches!(ident, AtIdentifier::Handle(_)));
245 }
246
247 #[test]
248 fn owned_construction() {
249 let ident = AtIdentifier::<SmolStr>::new_owned("did:plc:foo").unwrap();
250 assert!(matches!(ident, AtIdentifier::Did(_)));
251
252 let ident = AtIdentifier::<SmolStr>::new_owned("alice.test").unwrap();
253 assert!(matches!(ident, AtIdentifier::Handle(_)));
254 }
255}