this repo has no description
1use serde::{Deserialize, Serialize};
2use std::borrow::Cow;
3use std::fmt;
4use std::ops::Deref;
5use std::str::FromStr;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, sqlx::Type)]
8#[serde(transparent)]
9#[sqlx(transparent)]
10pub struct Did(String);
11
12impl<'de> Deserialize<'de> for Did {
13 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
14 where
15 D: serde::Deserializer<'de>,
16 {
17 let s = String::deserialize(deserializer)?;
18 Did::new(&s).map_err(|e| serde::de::Error::custom(e.to_string()))
19 }
20}
21
22impl From<Did> for String {
23 fn from(did: Did) -> Self {
24 did.0
25 }
26}
27
28impl From<String> for Did {
29 fn from(s: String) -> Self {
30 Did(s)
31 }
32}
33
34impl<'a> From<&'a Did> for Cow<'a, str> {
35 fn from(did: &'a Did) -> Self {
36 Cow::Borrowed(&did.0)
37 }
38}
39
40impl Did {
41 pub fn new(s: impl Into<String>) -> Result<Self, DidError> {
42 let s = s.into();
43 jacquard::types::string::Did::new(&s).map_err(|_| DidError::Invalid(s.clone()))?;
44 Ok(Self(s))
45 }
46
47 pub fn new_unchecked(s: impl Into<String>) -> Self {
48 Self(s.into())
49 }
50
51 pub fn as_str(&self) -> &str {
52 &self.0
53 }
54
55 pub fn into_inner(self) -> String {
56 self.0
57 }
58
59 pub fn is_plc(&self) -> bool {
60 self.0.starts_with("did:plc:")
61 }
62
63 pub fn is_web(&self) -> bool {
64 self.0.starts_with("did:web:")
65 }
66}
67
68impl AsRef<str> for Did {
69 fn as_ref(&self) -> &str {
70 &self.0
71 }
72}
73
74impl PartialEq<str> for Did {
75 fn eq(&self, other: &str) -> bool {
76 self.0 == other
77 }
78}
79
80impl PartialEq<&str> for Did {
81 fn eq(&self, other: &&str) -> bool {
82 self.0 == *other
83 }
84}
85
86impl PartialEq<String> for Did {
87 fn eq(&self, other: &String) -> bool {
88 self.0 == *other
89 }
90}
91
92impl PartialEq<Did> for String {
93 fn eq(&self, other: &Did) -> bool {
94 *self == other.0
95 }
96}
97
98impl PartialEq<Did> for &str {
99 fn eq(&self, other: &Did) -> bool {
100 *self == other.0
101 }
102}
103
104impl Deref for Did {
105 type Target = str;
106
107 fn deref(&self) -> &Self::Target {
108 &self.0
109 }
110}
111
112impl fmt::Display for Did {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "{}", self.0)
115 }
116}
117
118impl FromStr for Did {
119 type Err = DidError;
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 Self::new(s)
123 }
124}
125
126#[derive(Debug, Clone)]
127pub enum DidError {
128 Invalid(String),
129}
130
131impl fmt::Display for DidError {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 Self::Invalid(s) => write!(f, "invalid DID: {}", s),
135 }
136 }
137}
138
139impl std::error::Error for DidError {}
140
141#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
142#[serde(transparent)]
143#[sqlx(transparent)]
144pub struct Handle(String);
145
146impl From<Handle> for String {
147 fn from(handle: Handle) -> Self {
148 handle.0
149 }
150}
151
152impl From<String> for Handle {
153 fn from(s: String) -> Self {
154 Handle(s)
155 }
156}
157
158impl<'a> From<&'a Handle> for Cow<'a, str> {
159 fn from(handle: &'a Handle) -> Self {
160 Cow::Borrowed(&handle.0)
161 }
162}
163
164impl Handle {
165 pub fn new(s: impl Into<String>) -> Result<Self, HandleError> {
166 let s = s.into();
167 jacquard::types::string::Handle::new(&s).map_err(|_| HandleError::Invalid(s.clone()))?;
168 Ok(Self(s))
169 }
170
171 pub fn new_unchecked(s: impl Into<String>) -> Self {
172 Self(s.into())
173 }
174
175 pub fn as_str(&self) -> &str {
176 &self.0
177 }
178
179 pub fn into_inner(self) -> String {
180 self.0
181 }
182}
183
184impl AsRef<str> for Handle {
185 fn as_ref(&self) -> &str {
186 &self.0
187 }
188}
189
190impl Deref for Handle {
191 type Target = str;
192
193 fn deref(&self) -> &Self::Target {
194 &self.0
195 }
196}
197
198impl PartialEq<str> for Handle {
199 fn eq(&self, other: &str) -> bool {
200 self.0 == other
201 }
202}
203
204impl PartialEq<&str> for Handle {
205 fn eq(&self, other: &&str) -> bool {
206 self.0 == *other
207 }
208}
209
210impl PartialEq<String> for Handle {
211 fn eq(&self, other: &String) -> bool {
212 self.0 == *other
213 }
214}
215
216impl PartialEq<Handle> for String {
217 fn eq(&self, other: &Handle) -> bool {
218 *self == other.0
219 }
220}
221
222impl PartialEq<Handle> for &str {
223 fn eq(&self, other: &Handle) -> bool {
224 *self == other.0
225 }
226}
227
228impl fmt::Display for Handle {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 write!(f, "{}", self.0)
231 }
232}
233
234impl FromStr for Handle {
235 type Err = HandleError;
236
237 fn from_str(s: &str) -> Result<Self, Self::Err> {
238 Self::new(s)
239 }
240}
241
242#[derive(Debug, Clone)]
243pub enum HandleError {
244 Invalid(String),
245}
246
247impl fmt::Display for HandleError {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 match self {
250 Self::Invalid(s) => write!(f, "invalid handle: {}", s),
251 }
252 }
253}
254
255impl std::error::Error for HandleError {}
256
257#[derive(Debug, Clone, PartialEq, Eq, Hash)]
258pub enum AtIdentifier {
259 Did(Did),
260 Handle(Handle),
261}
262
263impl AtIdentifier {
264 pub fn new(s: impl AsRef<str>) -> Result<Self, AtIdentifierError> {
265 let s = s.as_ref();
266 if s.starts_with("did:") {
267 Did::new(s)
268 .map(AtIdentifier::Did)
269 .map_err(|_| AtIdentifierError::Invalid(s.to_string()))
270 } else {
271 Handle::new(s)
272 .map(AtIdentifier::Handle)
273 .map_err(|_| AtIdentifierError::Invalid(s.to_string()))
274 }
275 }
276
277 pub fn as_str(&self) -> &str {
278 match self {
279 AtIdentifier::Did(d) => d.as_str(),
280 AtIdentifier::Handle(h) => h.as_str(),
281 }
282 }
283
284 pub fn into_inner(self) -> String {
285 match self {
286 AtIdentifier::Did(d) => d.into_inner(),
287 AtIdentifier::Handle(h) => h.into_inner(),
288 }
289 }
290
291 pub fn is_did(&self) -> bool {
292 matches!(self, AtIdentifier::Did(_))
293 }
294
295 pub fn is_handle(&self) -> bool {
296 matches!(self, AtIdentifier::Handle(_))
297 }
298
299 pub fn as_did(&self) -> Option<&Did> {
300 match self {
301 AtIdentifier::Did(d) => Some(d),
302 AtIdentifier::Handle(_) => None,
303 }
304 }
305
306 pub fn as_handle(&self) -> Option<&Handle> {
307 match self {
308 AtIdentifier::Handle(h) => Some(h),
309 AtIdentifier::Did(_) => None,
310 }
311 }
312}
313
314impl From<Did> for AtIdentifier {
315 fn from(did: Did) -> Self {
316 AtIdentifier::Did(did)
317 }
318}
319
320impl From<Handle> for AtIdentifier {
321 fn from(handle: Handle) -> Self {
322 AtIdentifier::Handle(handle)
323 }
324}
325
326impl AsRef<str> for AtIdentifier {
327 fn as_ref(&self) -> &str {
328 self.as_str()
329 }
330}
331
332impl Deref for AtIdentifier {
333 type Target = str;
334
335 fn deref(&self) -> &Self::Target {
336 self.as_str()
337 }
338}
339
340impl fmt::Display for AtIdentifier {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 write!(f, "{}", self.as_str())
343 }
344}
345
346impl FromStr for AtIdentifier {
347 type Err = AtIdentifierError;
348
349 fn from_str(s: &str) -> Result<Self, Self::Err> {
350 Self::new(s)
351 }
352}
353
354impl Serialize for AtIdentifier {
355 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
356 where
357 S: serde::Serializer,
358 {
359 serializer.serialize_str(self.as_str())
360 }
361}
362
363impl<'de> Deserialize<'de> for AtIdentifier {
364 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
365 where
366 D: serde::Deserializer<'de>,
367 {
368 let s = String::deserialize(deserializer)?;
369 AtIdentifier::new(&s).map_err(serde::de::Error::custom)
370 }
371}
372
373#[derive(Debug, Clone)]
374pub enum AtIdentifierError {
375 Invalid(String),
376}
377
378impl fmt::Display for AtIdentifierError {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 match self {
381 Self::Invalid(s) => write!(f, "invalid AT identifier: {}", s),
382 }
383 }
384}
385
386impl std::error::Error for AtIdentifierError {}
387
388#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
389#[serde(transparent)]
390#[sqlx(type_name = "rkey")]
391pub struct Rkey(String);
392
393impl From<Rkey> for String {
394 fn from(rkey: Rkey) -> Self {
395 rkey.0
396 }
397}
398
399impl From<String> for Rkey {
400 fn from(s: String) -> Self {
401 Rkey(s)
402 }
403}
404
405impl<'a> From<&'a Rkey> for Cow<'a, str> {
406 fn from(rkey: &'a Rkey) -> Self {
407 Cow::Borrowed(&rkey.0)
408 }
409}
410
411impl Rkey {
412 pub fn new(s: impl Into<String>) -> Result<Self, RkeyError> {
413 let s = s.into();
414 jacquard::types::string::Rkey::new(&s).map_err(|_| RkeyError::Invalid(s.clone()))?;
415 Ok(Self(s))
416 }
417
418 pub fn new_unchecked(s: impl Into<String>) -> Self {
419 Self(s.into())
420 }
421
422 pub fn generate() -> Self {
423 use jacquard::types::integer::LimitedU32;
424 Self(jacquard::types::string::Tid::now(LimitedU32::MIN).to_string())
425 }
426
427 pub fn as_str(&self) -> &str {
428 &self.0
429 }
430
431 pub fn into_inner(self) -> String {
432 self.0
433 }
434
435 pub fn is_tid(&self) -> bool {
436 jacquard::types::string::Tid::from_str(&self.0).is_ok()
437 }
438}
439
440impl AsRef<str> for Rkey {
441 fn as_ref(&self) -> &str {
442 &self.0
443 }
444}
445
446impl Deref for Rkey {
447 type Target = str;
448
449 fn deref(&self) -> &Self::Target {
450 &self.0
451 }
452}
453
454impl PartialEq<str> for Rkey {
455 fn eq(&self, other: &str) -> bool {
456 self.0 == other
457 }
458}
459
460impl PartialEq<&str> for Rkey {
461 fn eq(&self, other: &&str) -> bool {
462 self.0 == *other
463 }
464}
465
466impl PartialEq<String> for Rkey {
467 fn eq(&self, other: &String) -> bool {
468 self.0 == *other
469 }
470}
471
472impl PartialEq<Rkey> for String {
473 fn eq(&self, other: &Rkey) -> bool {
474 *self == other.0
475 }
476}
477
478impl PartialEq<Rkey> for &str {
479 fn eq(&self, other: &Rkey) -> bool {
480 *self == other.0
481 }
482}
483
484impl fmt::Display for Rkey {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486 write!(f, "{}", self.0)
487 }
488}
489
490impl FromStr for Rkey {
491 type Err = RkeyError;
492
493 fn from_str(s: &str) -> Result<Self, Self::Err> {
494 Self::new(s)
495 }
496}
497
498#[derive(Debug, Clone)]
499pub enum RkeyError {
500 Invalid(String),
501}
502
503impl fmt::Display for RkeyError {
504 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505 match self {
506 Self::Invalid(s) => write!(f, "invalid rkey: {}", s),
507 }
508 }
509}
510
511impl std::error::Error for RkeyError {}
512
513#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
514#[serde(transparent)]
515#[sqlx(type_name = "nsid")]
516pub struct Nsid(String);
517
518impl From<Nsid> for String {
519 fn from(nsid: Nsid) -> Self {
520 nsid.0
521 }
522}
523
524impl From<String> for Nsid {
525 fn from(s: String) -> Self {
526 Nsid(s)
527 }
528}
529
530impl<'a> From<&'a Nsid> for Cow<'a, str> {
531 fn from(nsid: &'a Nsid) -> Self {
532 Cow::Borrowed(&nsid.0)
533 }
534}
535
536impl Nsid {
537 pub fn new(s: impl Into<String>) -> Result<Self, NsidError> {
538 let s = s.into();
539 jacquard::types::string::Nsid::new(&s).map_err(|_| NsidError::Invalid(s.clone()))?;
540 Ok(Self(s))
541 }
542
543 pub fn new_unchecked(s: impl Into<String>) -> Self {
544 Self(s.into())
545 }
546
547 pub fn as_str(&self) -> &str {
548 &self.0
549 }
550
551 pub fn into_inner(self) -> String {
552 self.0
553 }
554
555 pub fn authority(&self) -> Option<&str> {
556 let parts: Vec<&str> = self.0.rsplitn(2, '.').collect();
557 if parts.len() == 2 {
558 Some(parts[1])
559 } else {
560 None
561 }
562 }
563
564 pub fn name(&self) -> Option<&str> {
565 self.0.rsplit('.').next()
566 }
567}
568
569impl AsRef<str> for Nsid {
570 fn as_ref(&self) -> &str {
571 &self.0
572 }
573}
574
575impl Deref for Nsid {
576 type Target = str;
577
578 fn deref(&self) -> &Self::Target {
579 &self.0
580 }
581}
582
583impl PartialEq<str> for Nsid {
584 fn eq(&self, other: &str) -> bool {
585 self.0 == other
586 }
587}
588
589impl PartialEq<&str> for Nsid {
590 fn eq(&self, other: &&str) -> bool {
591 self.0 == *other
592 }
593}
594
595impl PartialEq<String> for Nsid {
596 fn eq(&self, other: &String) -> bool {
597 self.0 == *other
598 }
599}
600
601impl PartialEq<Nsid> for String {
602 fn eq(&self, other: &Nsid) -> bool {
603 *self == other.0
604 }
605}
606
607impl PartialEq<Nsid> for &str {
608 fn eq(&self, other: &Nsid) -> bool {
609 *self == other.0
610 }
611}
612
613impl fmt::Display for Nsid {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 write!(f, "{}", self.0)
616 }
617}
618
619impl FromStr for Nsid {
620 type Err = NsidError;
621
622 fn from_str(s: &str) -> Result<Self, Self::Err> {
623 Self::new(s)
624 }
625}
626
627#[derive(Debug, Clone)]
628pub enum NsidError {
629 Invalid(String),
630}
631
632impl fmt::Display for NsidError {
633 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634 match self {
635 Self::Invalid(s) => write!(f, "invalid NSID: {}", s),
636 }
637 }
638}
639
640impl std::error::Error for NsidError {}
641
642#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
643#[serde(transparent)]
644#[sqlx(type_name = "at_uri")]
645pub struct AtUri(String);
646
647impl From<AtUri> for String {
648 fn from(uri: AtUri) -> Self {
649 uri.0
650 }
651}
652
653impl From<String> for AtUri {
654 fn from(s: String) -> Self {
655 AtUri(s)
656 }
657}
658
659impl<'a> From<&'a AtUri> for Cow<'a, str> {
660 fn from(uri: &'a AtUri) -> Self {
661 Cow::Borrowed(&uri.0)
662 }
663}
664
665impl AtUri {
666 pub fn new(s: impl Into<String>) -> Result<Self, AtUriError> {
667 let s = s.into();
668 jacquard::types::string::AtUri::new(&s).map_err(|_| AtUriError::Invalid(s.clone()))?;
669 Ok(Self(s))
670 }
671
672 pub fn new_unchecked(s: impl Into<String>) -> Self {
673 Self(s.into())
674 }
675
676 pub fn from_parts(did: &str, collection: &str, rkey: &str) -> Self {
677 Self(format!("at://{}/{}/{}", did, collection, rkey))
678 }
679
680 pub fn as_str(&self) -> &str {
681 &self.0
682 }
683
684 pub fn into_inner(self) -> String {
685 self.0
686 }
687}
688
689impl AsRef<str> for AtUri {
690 fn as_ref(&self) -> &str {
691 &self.0
692 }
693}
694
695impl Deref for AtUri {
696 type Target = str;
697
698 fn deref(&self) -> &Self::Target {
699 &self.0
700 }
701}
702
703impl PartialEq<str> for AtUri {
704 fn eq(&self, other: &str) -> bool {
705 self.0 == other
706 }
707}
708
709impl PartialEq<&str> for AtUri {
710 fn eq(&self, other: &&str) -> bool {
711 self.0 == *other
712 }
713}
714
715impl PartialEq<String> for AtUri {
716 fn eq(&self, other: &String) -> bool {
717 self.0 == *other
718 }
719}
720
721impl PartialEq<AtUri> for String {
722 fn eq(&self, other: &AtUri) -> bool {
723 *self == other.0
724 }
725}
726
727impl PartialEq<AtUri> for &str {
728 fn eq(&self, other: &AtUri) -> bool {
729 *self == other.0
730 }
731}
732
733impl fmt::Display for AtUri {
734 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
735 write!(f, "{}", self.0)
736 }
737}
738
739impl FromStr for AtUri {
740 type Err = AtUriError;
741
742 fn from_str(s: &str) -> Result<Self, Self::Err> {
743 Self::new(s)
744 }
745}
746
747#[derive(Debug, Clone)]
748pub enum AtUriError {
749 Invalid(String),
750}
751
752impl fmt::Display for AtUriError {
753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754 match self {
755 Self::Invalid(s) => write!(f, "invalid AT URI: {}", s),
756 }
757 }
758}
759
760impl std::error::Error for AtUriError {}
761
762#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
763#[serde(transparent)]
764#[sqlx(transparent)]
765pub struct Tid(String);
766
767impl From<Tid> for String {
768 fn from(tid: Tid) -> Self {
769 tid.0
770 }
771}
772
773impl From<String> for Tid {
774 fn from(s: String) -> Self {
775 Tid(s)
776 }
777}
778
779impl<'a> From<&'a Tid> for Cow<'a, str> {
780 fn from(tid: &'a Tid) -> Self {
781 Cow::Borrowed(&tid.0)
782 }
783}
784
785impl Tid {
786 pub fn new(s: impl Into<String>) -> Result<Self, TidError> {
787 let s = s.into();
788 jacquard::types::string::Tid::from_str(&s).map_err(|_| TidError::Invalid(s.clone()))?;
789 Ok(Self(s))
790 }
791
792 pub fn new_unchecked(s: impl Into<String>) -> Self {
793 Self(s.into())
794 }
795
796 pub fn now() -> Self {
797 use jacquard::types::integer::LimitedU32;
798 Self(jacquard::types::string::Tid::now(LimitedU32::MIN).to_string())
799 }
800
801 pub fn as_str(&self) -> &str {
802 &self.0
803 }
804
805 pub fn into_inner(self) -> String {
806 self.0
807 }
808}
809
810impl AsRef<str> for Tid {
811 fn as_ref(&self) -> &str {
812 &self.0
813 }
814}
815
816impl Deref for Tid {
817 type Target = str;
818
819 fn deref(&self) -> &Self::Target {
820 &self.0
821 }
822}
823
824impl PartialEq<str> for Tid {
825 fn eq(&self, other: &str) -> bool {
826 self.0 == other
827 }
828}
829
830impl PartialEq<&str> for Tid {
831 fn eq(&self, other: &&str) -> bool {
832 self.0 == *other
833 }
834}
835
836impl PartialEq<String> for Tid {
837 fn eq(&self, other: &String) -> bool {
838 self.0 == *other
839 }
840}
841
842impl PartialEq<Tid> for String {
843 fn eq(&self, other: &Tid) -> bool {
844 *self == other.0
845 }
846}
847
848impl PartialEq<Tid> for &str {
849 fn eq(&self, other: &Tid) -> bool {
850 *self == other.0
851 }
852}
853
854impl fmt::Display for Tid {
855 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
856 write!(f, "{}", self.0)
857 }
858}
859
860impl FromStr for Tid {
861 type Err = TidError;
862
863 fn from_str(s: &str) -> Result<Self, Self::Err> {
864 Self::new(s)
865 }
866}
867
868#[derive(Debug, Clone)]
869pub enum TidError {
870 Invalid(String),
871}
872
873impl fmt::Display for TidError {
874 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
875 match self {
876 Self::Invalid(s) => write!(f, "invalid TID: {}", s),
877 }
878 }
879}
880
881impl std::error::Error for TidError {}
882
883#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
884#[serde(transparent)]
885#[sqlx(transparent)]
886pub struct Datetime(String);
887
888impl From<Datetime> for String {
889 fn from(dt: Datetime) -> Self {
890 dt.0
891 }
892}
893
894impl From<String> for Datetime {
895 fn from(s: String) -> Self {
896 Datetime(s)
897 }
898}
899
900impl<'a> From<&'a Datetime> for Cow<'a, str> {
901 fn from(dt: &'a Datetime) -> Self {
902 Cow::Borrowed(&dt.0)
903 }
904}
905
906impl Datetime {
907 pub fn new(s: impl Into<String>) -> Result<Self, DatetimeError> {
908 let s = s.into();
909 jacquard::types::string::Datetime::from_str(&s)
910 .map_err(|_| DatetimeError::Invalid(s.clone()))?;
911 Ok(Self(s))
912 }
913
914 pub fn new_unchecked(s: impl Into<String>) -> Self {
915 Self(s.into())
916 }
917
918 pub fn now() -> Self {
919 Self(chrono::Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string())
920 }
921
922 pub fn as_str(&self) -> &str {
923 &self.0
924 }
925
926 pub fn into_inner(self) -> String {
927 self.0
928 }
929}
930
931impl AsRef<str> for Datetime {
932 fn as_ref(&self) -> &str {
933 &self.0
934 }
935}
936
937impl Deref for Datetime {
938 type Target = str;
939
940 fn deref(&self) -> &Self::Target {
941 &self.0
942 }
943}
944
945impl PartialEq<str> for Datetime {
946 fn eq(&self, other: &str) -> bool {
947 self.0 == other
948 }
949}
950
951impl PartialEq<&str> for Datetime {
952 fn eq(&self, other: &&str) -> bool {
953 self.0 == *other
954 }
955}
956
957impl PartialEq<String> for Datetime {
958 fn eq(&self, other: &String) -> bool {
959 self.0 == *other
960 }
961}
962
963impl PartialEq<Datetime> for String {
964 fn eq(&self, other: &Datetime) -> bool {
965 *self == other.0
966 }
967}
968
969impl PartialEq<Datetime> for &str {
970 fn eq(&self, other: &Datetime) -> bool {
971 *self == other.0
972 }
973}
974
975impl fmt::Display for Datetime {
976 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
977 write!(f, "{}", self.0)
978 }
979}
980
981impl FromStr for Datetime {
982 type Err = DatetimeError;
983
984 fn from_str(s: &str) -> Result<Self, Self::Err> {
985 Self::new(s)
986 }
987}
988
989#[derive(Debug, Clone)]
990pub enum DatetimeError {
991 Invalid(String),
992}
993
994impl fmt::Display for DatetimeError {
995 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
996 match self {
997 Self::Invalid(s) => write!(f, "invalid datetime: {}", s),
998 }
999 }
1000}
1001
1002impl std::error::Error for DatetimeError {}
1003
1004#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
1005#[serde(transparent)]
1006#[sqlx(transparent)]
1007pub struct Language(String);
1008
1009impl From<Language> for String {
1010 fn from(lang: Language) -> Self {
1011 lang.0
1012 }
1013}
1014
1015impl From<String> for Language {
1016 fn from(s: String) -> Self {
1017 Language(s)
1018 }
1019}
1020
1021impl<'a> From<&'a Language> for Cow<'a, str> {
1022 fn from(lang: &'a Language) -> Self {
1023 Cow::Borrowed(&lang.0)
1024 }
1025}
1026
1027impl Language {
1028 pub fn new(s: impl Into<String>) -> Result<Self, LanguageError> {
1029 let s = s.into();
1030 jacquard::types::string::Language::from_str(&s)
1031 .map_err(|_| LanguageError::Invalid(s.clone()))?;
1032 Ok(Self(s))
1033 }
1034
1035 pub fn new_unchecked(s: impl Into<String>) -> Self {
1036 Self(s.into())
1037 }
1038
1039 pub fn as_str(&self) -> &str {
1040 &self.0
1041 }
1042
1043 pub fn into_inner(self) -> String {
1044 self.0
1045 }
1046}
1047
1048impl AsRef<str> for Language {
1049 fn as_ref(&self) -> &str {
1050 &self.0
1051 }
1052}
1053
1054impl Deref for Language {
1055 type Target = str;
1056
1057 fn deref(&self) -> &Self::Target {
1058 &self.0
1059 }
1060}
1061
1062impl PartialEq<str> for Language {
1063 fn eq(&self, other: &str) -> bool {
1064 self.0 == other
1065 }
1066}
1067
1068impl PartialEq<&str> for Language {
1069 fn eq(&self, other: &&str) -> bool {
1070 self.0 == *other
1071 }
1072}
1073
1074impl PartialEq<String> for Language {
1075 fn eq(&self, other: &String) -> bool {
1076 self.0 == *other
1077 }
1078}
1079
1080impl PartialEq<Language> for String {
1081 fn eq(&self, other: &Language) -> bool {
1082 *self == other.0
1083 }
1084}
1085
1086impl PartialEq<Language> for &str {
1087 fn eq(&self, other: &Language) -> bool {
1088 *self == other.0
1089 }
1090}
1091
1092impl fmt::Display for Language {
1093 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1094 write!(f, "{}", self.0)
1095 }
1096}
1097
1098impl FromStr for Language {
1099 type Err = LanguageError;
1100
1101 fn from_str(s: &str) -> Result<Self, Self::Err> {
1102 Self::new(s)
1103 }
1104}
1105
1106#[derive(Debug, Clone)]
1107pub enum LanguageError {
1108 Invalid(String),
1109}
1110
1111impl fmt::Display for LanguageError {
1112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1113 match self {
1114 Self::Invalid(s) => write!(f, "invalid language tag: {}", s),
1115 }
1116 }
1117}
1118
1119impl std::error::Error for LanguageError {}
1120
1121#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
1122#[serde(transparent)]
1123#[sqlx(transparent)]
1124pub struct CidLink(String);
1125
1126impl From<CidLink> for String {
1127 fn from(cid: CidLink) -> Self {
1128 cid.0
1129 }
1130}
1131
1132impl From<String> for CidLink {
1133 fn from(s: String) -> Self {
1134 CidLink(s)
1135 }
1136}
1137
1138impl<'a> From<&'a CidLink> for Cow<'a, str> {
1139 fn from(cid: &'a CidLink) -> Self {
1140 Cow::Borrowed(&cid.0)
1141 }
1142}
1143
1144impl CidLink {
1145 pub fn new(s: impl Into<String>) -> Result<Self, CidLinkError> {
1146 let s = s.into();
1147 cid::Cid::from_str(&s).map_err(|_| CidLinkError::Invalid(s.clone()))?;
1148 Ok(Self(s))
1149 }
1150
1151 pub fn new_unchecked(s: impl Into<String>) -> Self {
1152 Self(s.into())
1153 }
1154
1155 pub fn as_str(&self) -> &str {
1156 &self.0
1157 }
1158
1159 pub fn into_inner(self) -> String {
1160 self.0
1161 }
1162
1163 pub fn to_cid(&self) -> Result<cid::Cid, cid::Error> {
1164 cid::Cid::from_str(&self.0)
1165 }
1166}
1167
1168impl AsRef<str> for CidLink {
1169 fn as_ref(&self) -> &str {
1170 &self.0
1171 }
1172}
1173
1174impl Deref for CidLink {
1175 type Target = str;
1176
1177 fn deref(&self) -> &Self::Target {
1178 &self.0
1179 }
1180}
1181
1182impl PartialEq<str> for CidLink {
1183 fn eq(&self, other: &str) -> bool {
1184 self.0 == other
1185 }
1186}
1187
1188impl PartialEq<&str> for CidLink {
1189 fn eq(&self, other: &&str) -> bool {
1190 self.0 == *other
1191 }
1192}
1193
1194impl PartialEq<String> for CidLink {
1195 fn eq(&self, other: &String) -> bool {
1196 self.0 == *other
1197 }
1198}
1199
1200impl PartialEq<CidLink> for String {
1201 fn eq(&self, other: &CidLink) -> bool {
1202 *self == other.0
1203 }
1204}
1205
1206impl PartialEq<CidLink> for &str {
1207 fn eq(&self, other: &CidLink) -> bool {
1208 *self == other.0
1209 }
1210}
1211
1212impl fmt::Display for CidLink {
1213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1214 write!(f, "{}", self.0)
1215 }
1216}
1217
1218impl FromStr for CidLink {
1219 type Err = CidLinkError;
1220
1221 fn from_str(s: &str) -> Result<Self, Self::Err> {
1222 Self::new(s)
1223 }
1224}
1225
1226#[derive(Debug, Clone)]
1227pub enum CidLinkError {
1228 Invalid(String),
1229}
1230
1231impl fmt::Display for CidLinkError {
1232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1233 match self {
1234 Self::Invalid(s) => write!(f, "invalid CID: {}", s),
1235 }
1236 }
1237}
1238
1239impl std::error::Error for CidLinkError {}
1240
1241#[derive(Debug, Clone, PartialEq, Eq)]
1242pub enum AccountState {
1243 Active,
1244 Deactivated {
1245 at: chrono::DateTime<chrono::Utc>,
1246 },
1247 TakenDown {
1248 reference: String,
1249 },
1250 Migrated {
1251 at: chrono::DateTime<chrono::Utc>,
1252 to_pds: String,
1253 },
1254}
1255
1256impl AccountState {
1257 pub fn from_db_fields(
1258 deactivated_at: Option<chrono::DateTime<chrono::Utc>>,
1259 takedown_ref: Option<String>,
1260 migrated_to_pds: Option<String>,
1261 migrated_at: Option<chrono::DateTime<chrono::Utc>>,
1262 ) -> Self {
1263 if let Some(reference) = takedown_ref {
1264 AccountState::TakenDown { reference }
1265 } else if let (Some(at), Some(to_pds)) = (deactivated_at, migrated_to_pds) {
1266 let migrated_at = migrated_at.unwrap_or(at);
1267 AccountState::Migrated {
1268 at: migrated_at,
1269 to_pds,
1270 }
1271 } else if let Some(at) = deactivated_at {
1272 AccountState::Deactivated { at }
1273 } else {
1274 AccountState::Active
1275 }
1276 }
1277
1278 pub fn is_active(&self) -> bool {
1279 matches!(self, AccountState::Active)
1280 }
1281
1282 pub fn is_deactivated(&self) -> bool {
1283 matches!(self, AccountState::Deactivated { .. })
1284 }
1285
1286 pub fn is_takendown(&self) -> bool {
1287 matches!(self, AccountState::TakenDown { .. })
1288 }
1289
1290 pub fn is_migrated(&self) -> bool {
1291 matches!(self, AccountState::Migrated { .. })
1292 }
1293
1294 pub fn can_login(&self) -> bool {
1295 matches!(self, AccountState::Active)
1296 }
1297
1298 pub fn can_access_repo(&self) -> bool {
1299 matches!(self, AccountState::Active | AccountState::Deactivated { .. })
1300 }
1301
1302 pub fn status_string(&self) -> &'static str {
1303 match self {
1304 AccountState::Active => "active",
1305 AccountState::Deactivated { .. } => "deactivated",
1306 AccountState::TakenDown { .. } => "takendown",
1307 AccountState::Migrated { .. } => "deactivated",
1308 }
1309 }
1310
1311 pub fn status_for_session(&self) -> Option<&'static str> {
1312 match self {
1313 AccountState::Active => None,
1314 AccountState::Deactivated { .. } => Some("deactivated"),
1315 AccountState::TakenDown { .. } => Some("takendown"),
1316 AccountState::Migrated { .. } => Some("migrated"),
1317 }
1318 }
1319}
1320
1321impl fmt::Display for AccountState {
1322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1323 match self {
1324 AccountState::Active => write!(f, "active"),
1325 AccountState::Deactivated { at } => write!(f, "deactivated ({})", at),
1326 AccountState::TakenDown { reference } => write!(f, "takendown ({})", reference),
1327 AccountState::Migrated { to_pds, .. } => write!(f, "migrated to {}", to_pds),
1328 }
1329 }
1330}
1331
1332#[derive(Debug, Clone, Deserialize)]
1333#[serde(transparent)]
1334pub struct PlainPassword(String);
1335
1336impl PlainPassword {
1337 pub fn new(s: impl Into<String>) -> Self {
1338 Self(s.into())
1339 }
1340
1341 pub fn as_str(&self) -> &str {
1342 &self.0
1343 }
1344
1345 pub fn into_inner(self) -> String {
1346 self.0
1347 }
1348
1349 pub fn is_empty(&self) -> bool {
1350 self.0.is_empty()
1351 }
1352}
1353
1354impl AsRef<str> for PlainPassword {
1355 fn as_ref(&self) -> &str {
1356 &self.0
1357 }
1358}
1359
1360impl AsRef<[u8]> for PlainPassword {
1361 fn as_ref(&self) -> &[u8] {
1362 self.0.as_bytes()
1363 }
1364}
1365
1366impl Deref for PlainPassword {
1367 type Target = str;
1368
1369 fn deref(&self) -> &Self::Target {
1370 &self.0
1371 }
1372}
1373
1374#[derive(Debug, Clone, Serialize, sqlx::Type)]
1375#[serde(transparent)]
1376#[sqlx(transparent)]
1377pub struct PasswordHash(String);
1378
1379impl PasswordHash {
1380 pub fn from_hash(hash: impl Into<String>) -> Self {
1381 Self(hash.into())
1382 }
1383
1384 pub fn as_str(&self) -> &str {
1385 &self.0
1386 }
1387
1388 pub fn into_inner(self) -> String {
1389 self.0
1390 }
1391}
1392
1393impl AsRef<str> for PasswordHash {
1394 fn as_ref(&self) -> &str {
1395 &self.0
1396 }
1397}
1398
1399impl From<String> for PasswordHash {
1400 fn from(s: String) -> Self {
1401 Self(s)
1402 }
1403}
1404
1405#[derive(Debug, Clone, PartialEq, Eq)]
1406pub enum TokenSource {
1407 Session,
1408 OAuth { client_id: Option<String> },
1409 ServiceAuth { lxm: Option<String>, aud: Option<String> },
1410}
1411
1412impl TokenSource {
1413 pub fn is_session(&self) -> bool {
1414 matches!(self, TokenSource::Session)
1415 }
1416
1417 pub fn is_oauth(&self) -> bool {
1418 matches!(self, TokenSource::OAuth { .. })
1419 }
1420
1421 pub fn is_service_auth(&self) -> bool {
1422 matches!(self, TokenSource::ServiceAuth { .. })
1423 }
1424}
1425
1426#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1427#[serde(transparent)]
1428pub struct JwkThumbprint(String);
1429
1430impl JwkThumbprint {
1431 pub fn new(s: impl Into<String>) -> Self {
1432 Self(s.into())
1433 }
1434
1435 pub fn as_str(&self) -> &str {
1436 &self.0
1437 }
1438
1439 pub fn into_inner(self) -> String {
1440 self.0
1441 }
1442}
1443
1444impl AsRef<str> for JwkThumbprint {
1445 fn as_ref(&self) -> &str {
1446 &self.0
1447 }
1448}
1449
1450impl Deref for JwkThumbprint {
1451 type Target = str;
1452
1453 fn deref(&self) -> &Self::Target {
1454 &self.0
1455 }
1456}
1457
1458impl From<String> for JwkThumbprint {
1459 fn from(s: String) -> Self {
1460 Self(s)
1461 }
1462}
1463
1464impl PartialEq<str> for JwkThumbprint {
1465 fn eq(&self, other: &str) -> bool {
1466 self.0 == other
1467 }
1468}
1469
1470impl PartialEq<String> for JwkThumbprint {
1471 fn eq(&self, other: &String) -> bool {
1472 &self.0 == other
1473 }
1474}
1475
1476impl PartialEq<JwkThumbprint> for String {
1477 fn eq(&self, other: &JwkThumbprint) -> bool {
1478 self == &other.0
1479 }
1480}
1481
1482#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1483#[serde(transparent)]
1484pub struct DPoPProofId(String);
1485
1486impl DPoPProofId {
1487 pub fn new(s: impl Into<String>) -> Self {
1488 Self(s.into())
1489 }
1490
1491 pub fn as_str(&self) -> &str {
1492 &self.0
1493 }
1494
1495 pub fn into_inner(self) -> String {
1496 self.0
1497 }
1498}
1499
1500impl AsRef<str> for DPoPProofId {
1501 fn as_ref(&self) -> &str {
1502 &self.0
1503 }
1504}
1505
1506impl Deref for DPoPProofId {
1507 type Target = str;
1508
1509 fn deref(&self) -> &Self::Target {
1510 &self.0
1511 }
1512}
1513
1514impl From<String> for DPoPProofId {
1515 fn from(s: String) -> Self {
1516 Self(s)
1517 }
1518}
1519
1520#[cfg(test)]
1521mod tests {
1522 use super::*;
1523
1524 #[test]
1525 fn test_did_validation() {
1526 assert!(Did::new("did:plc:abc123").is_ok());
1527 assert!(Did::new("did:web:example.com").is_ok());
1528 assert!(Did::new("not-a-did").is_err());
1529 assert!(Did::new("").is_err());
1530 }
1531
1532 #[test]
1533 fn test_did_methods() {
1534 let plc = Did::new("did:plc:abc123").unwrap();
1535 assert!(plc.is_plc());
1536 assert!(!plc.is_web());
1537 assert_eq!(plc.as_str(), "did:plc:abc123");
1538
1539 let web = Did::new("did:web:example.com").unwrap();
1540 assert!(!web.is_plc());
1541 assert!(web.is_web());
1542 }
1543
1544 #[test]
1545 fn test_did_conversions() {
1546 let did = Did::new("did:plc:test123").unwrap();
1547 let s: String = did.clone().into();
1548 assert_eq!(s, "did:plc:test123");
1549 assert_eq!(format!("{}", did), "did:plc:test123");
1550 }
1551
1552 #[test]
1553 fn test_did_serde() {
1554 let did = Did::new("did:plc:test123").unwrap();
1555 let json = serde_json::to_string(&did).unwrap();
1556 assert_eq!(json, "\"did:plc:test123\"");
1557
1558 let parsed: Did = serde_json::from_str(&json).unwrap();
1559 assert_eq!(parsed, did);
1560 }
1561
1562 #[test]
1563 fn test_handle_validation() {
1564 assert!(Handle::new("user.bsky.social").is_ok());
1565 assert!(Handle::new("test.example.com").is_ok());
1566 assert!(Handle::new("invalid handle with spaces").is_err());
1567 }
1568
1569 #[test]
1570 fn test_rkey_validation() {
1571 assert!(Rkey::new("self").is_ok());
1572 assert!(Rkey::new("3jzfcijpj2z2a").is_ok());
1573 assert!(Rkey::new("invalid/rkey").is_err());
1574 }
1575
1576 #[test]
1577 fn test_rkey_generate() {
1578 let rkey = Rkey::generate();
1579 assert!(rkey.is_tid());
1580 assert!(!rkey.as_str().is_empty());
1581 }
1582
1583 #[test]
1584 fn test_nsid_validation() {
1585 assert!(Nsid::new("app.bsky.feed.post").is_ok());
1586 assert!(Nsid::new("com.atproto.repo.createRecord").is_ok());
1587 assert!(Nsid::new("invalid").is_err());
1588 }
1589
1590 #[test]
1591 fn test_nsid_parts() {
1592 let nsid = Nsid::new("app.bsky.feed.post").unwrap();
1593 assert_eq!(nsid.name(), Some("post"));
1594 }
1595
1596 #[test]
1597 fn test_at_uri_validation() {
1598 assert!(AtUri::new("at://did:plc:abc123/app.bsky.feed.post/xyz").is_ok());
1599 assert!(AtUri::new("not-an-at-uri").is_err());
1600 }
1601
1602 #[test]
1603 fn test_at_uri_from_parts() {
1604 let uri = AtUri::from_parts("did:plc:abc123", "app.bsky.feed.post", "xyz");
1605 assert_eq!(uri.as_str(), "at://did:plc:abc123/app.bsky.feed.post/xyz");
1606 }
1607
1608 #[test]
1609 fn test_type_safety() {
1610 fn takes_did(_: &Did) {}
1611 fn takes_handle(_: &Handle) {}
1612
1613 let did = Did::new("did:plc:test").unwrap();
1614 let handle = Handle::new("test.bsky.social").unwrap();
1615
1616 takes_did(&did);
1617 takes_handle(&handle);
1618 }
1619
1620 #[test]
1621 fn test_tid_validation() {
1622 let tid = Tid::now();
1623 assert!(!tid.as_str().is_empty());
1624 assert!(Tid::new(tid.as_str()).is_ok());
1625 assert!(Tid::new("invalid").is_err());
1626 }
1627
1628 #[test]
1629 fn test_datetime_validation() {
1630 assert!(Datetime::new("2024-01-15T12:30:45.123Z").is_ok());
1631 assert!(Datetime::new("not-a-date").is_err());
1632 let now = Datetime::now();
1633 assert!(!now.as_str().is_empty());
1634 }
1635
1636 #[test]
1637 fn test_language_validation() {
1638 assert!(Language::new("en").is_ok());
1639 assert!(Language::new("en-US").is_ok());
1640 assert!(Language::new("ja").is_ok());
1641 }
1642
1643 #[test]
1644 fn test_cidlink_validation() {
1645 assert!(CidLink::new("bafyreib74ckyq525l3y6an5txykwwtb3dgex6ofzakml53di77oxwr5pfe").is_ok());
1646 assert!(CidLink::new("not-a-cid").is_err());
1647 }
1648
1649 #[test]
1650 fn test_at_identifier_validation() {
1651 let did_ident = AtIdentifier::new("did:plc:abc123").unwrap();
1652 assert!(did_ident.is_did());
1653 assert!(!did_ident.is_handle());
1654 assert!(did_ident.as_did().is_some());
1655 assert!(did_ident.as_handle().is_none());
1656
1657 let handle_ident = AtIdentifier::new("user.bsky.social").unwrap();
1658 assert!(!handle_ident.is_did());
1659 assert!(handle_ident.is_handle());
1660 assert!(handle_ident.as_did().is_none());
1661 assert!(handle_ident.as_handle().is_some());
1662
1663 assert!(AtIdentifier::new("invalid identifier").is_err());
1664 }
1665
1666 #[test]
1667 fn test_at_identifier_serde() {
1668 let ident = AtIdentifier::new("did:plc:test123").unwrap();
1669 let json = serde_json::to_string(&ident).unwrap();
1670 assert_eq!(json, "\"did:plc:test123\"");
1671
1672 let parsed: AtIdentifier = serde_json::from_str(&json).unwrap();
1673 assert_eq!(parsed.as_str(), "did:plc:test123");
1674 }
1675
1676 #[test]
1677 fn test_account_state_active() {
1678 let state = AccountState::from_db_fields(None, None, None, None);
1679 assert!(state.is_active());
1680 assert!(!state.is_deactivated());
1681 assert!(!state.is_takendown());
1682 assert!(!state.is_migrated());
1683 assert!(state.can_login());
1684 assert!(state.can_access_repo());
1685 assert_eq!(state.status_string(), "active");
1686 }
1687
1688 #[test]
1689 fn test_account_state_deactivated() {
1690 let now = chrono::Utc::now();
1691 let state = AccountState::from_db_fields(Some(now), None, None, None);
1692 assert!(!state.is_active());
1693 assert!(state.is_deactivated());
1694 assert!(!state.is_takendown());
1695 assert!(!state.is_migrated());
1696 assert!(!state.can_login());
1697 assert!(state.can_access_repo());
1698 assert_eq!(state.status_string(), "deactivated");
1699 }
1700
1701 #[test]
1702 fn test_account_state_takendown() {
1703 let state = AccountState::from_db_fields(None, Some("mod-action-123".into()), None, None);
1704 assert!(!state.is_active());
1705 assert!(!state.is_deactivated());
1706 assert!(state.is_takendown());
1707 assert!(!state.is_migrated());
1708 assert!(!state.can_login());
1709 assert!(!state.can_access_repo());
1710 assert_eq!(state.status_string(), "takendown");
1711 }
1712
1713 #[test]
1714 fn test_account_state_migrated() {
1715 let now = chrono::Utc::now();
1716 let state =
1717 AccountState::from_db_fields(Some(now), None, Some("https://other.pds".into()), None);
1718 assert!(!state.is_active());
1719 assert!(!state.is_deactivated());
1720 assert!(!state.is_takendown());
1721 assert!(state.is_migrated());
1722 assert!(!state.can_login());
1723 assert!(!state.can_access_repo());
1724 assert_eq!(state.status_string(), "deactivated");
1725 }
1726
1727 #[test]
1728 fn test_account_state_takedown_priority() {
1729 let now = chrono::Utc::now();
1730 let state = AccountState::from_db_fields(
1731 Some(now),
1732 Some("mod-action".into()),
1733 Some("https://other.pds".into()),
1734 None,
1735 );
1736 assert!(state.is_takendown());
1737 }
1738}