A better Rust ATProto crate
1use serde::{Deserialize, Serialize};
2
3// Re-export Lazy from crate root for submodule use via `super::Lazy`
4pub use crate::Lazy;
5
6/// AT Protocol URI (at://) types and validation
7pub mod aturi;
8/// Blob references for binary data
9pub mod blob;
10/// Content Identifier (CID) types for IPLD
11pub mod cid;
12/// Repository collection trait for records
13pub mod collection;
14pub mod crypto;
15/// AT Protocol datetime string type
16pub mod datetime;
17/// Decentralized Identifier (DID) types and validation
18pub mod did;
19/// DID Document types and helpers
20pub mod did_doc;
21/// AT Protocol handle types and validation
22pub mod handle;
23/// AT Protocol identifier types (handle or DID)
24pub mod ident;
25pub mod integer;
26/// Language tag types per BCP 47
27pub mod language;
28/// Namespaced Identifier (NSID) types and validation
29pub mod nsid;
30/// Record key types and validation
31pub mod recordkey;
32/// String types with format validation
33pub mod string;
34/// Timestamp Identifier (TID) types and generation
35pub mod tid;
36/// URI types with scheme validation
37pub mod uri;
38/// Generic data value types for lexicon data model
39pub mod value;
40
41/// Trait for a constant string literal type
42pub trait Literal: Clone + Copy + PartialEq + Eq + Send + Sync + 'static {
43 /// The string literal
44 const LITERAL: &'static str;
45}
46
47/// top-level domains which are not allowed in at:// handles or dids
48pub const DISALLOWED_TLDS: &[&str] = &[
49 ".local",
50 ".arpa",
51 ".invalid", // NOTE: if someone has a screwed up handle, this is what's returned
52 ".localhost",
53 ".internal",
54 ".example",
55 ".alt",
56 // policy could concievably change on ".onion" some day
57 ".onion",
58 // NOTE: .test is allowed in testing and devopment. In practical terms
59 // "should" "never" actually resolve and get registered in production
60];
61
62/// checks if a string ends with anything from the provided list of strings.
63pub fn ends_with(string: impl AsRef<str>, list: &[&str]) -> bool {
64 let string = string.as_ref();
65 for item in list {
66 if string.ends_with(item) {
67 return true;
68 }
69 }
70 false
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
74#[serde(rename_all = "kebab-case")]
75/// Valid types in the AT protocol [data model](https://atproto.com/specs/data-model). Type marker only, used in concert with `[Data<'_>]`.
76pub enum DataModelType {
77 /// Null type. IPLD type `null`, JSON type `Null`, CBOR Special Value (major 7)
78 Null,
79 /// Boolean type. IPLD type `boolean`, JSON type Boolean, CBOR Special Value (major 7)
80 Boolean,
81 /// Integer type. IPLD type `integer`, JSON type Number, CBOR Special Value (major 7)
82 Integer,
83 /// Byte type. IPLD type `bytes`, in JSON a `{ "$bytes": bytes }` Object, CBOR Byte String (major 2)
84 Bytes,
85 /// CID (content identifier) link. IPLD type `link`, in JSON a `{ "$link": cid }` Object, CBOR CID (tag 42)
86 CidLink,
87 /// Blob type. No special IPLD type. in JSON a `{ "$type": "blob" }` Object. in CBOR a `{ "$type": "blob" }` Map.
88 Blob,
89 /// Array type. IPLD type `list`. JSON type `Array`, CBOR type Array (major 4)
90 Array,
91 /// Object type. IPLD type `map`. JSON type `Object`, CBOR type Map (major 5). keys are always SmolStr.
92 Object,
93 #[serde(untagged)]
94 /// String type (lots of variants). JSON String, CBOR UTF-8 String (major 3)
95 String(LexiconStringType),
96}
97
98impl core::fmt::Display for DataModelType {
99 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
100 match self {
101 DataModelType::Null => write!(f, "null"),
102 DataModelType::Boolean => write!(f, "boolean"),
103 DataModelType::Integer => write!(f, "integer"),
104 DataModelType::Bytes => write!(f, "bytes"),
105 DataModelType::CidLink => write!(f, "cid-link"),
106 DataModelType::Blob => write!(f, "blob"),
107 DataModelType::Array => write!(f, "array"),
108 DataModelType::Object => write!(f, "object"),
109 DataModelType::String(s) => write!(f, "{}", s),
110 }
111 }
112}
113
114/// Lexicon string format types for typed strings in the AT Protocol data model
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
116#[serde(rename_all = "kebab-case")]
117pub enum LexiconStringType {
118 /// ISO 8601 datetime string
119 Datetime,
120 /// AT Protocol URI (at://)
121 AtUri,
122 /// Decentralized Identifier
123 Did,
124 /// AT Protocol handle
125 Handle,
126 /// Handle or DID
127 AtIdentifier,
128 /// Namespaced Identifier
129 Nsid,
130 /// Content Identifier
131 Cid,
132 /// BCP 47 language tag
133 Language,
134 /// Timestamp Identifier
135 Tid,
136 /// Record key
137 RecordKey,
138 /// URI with type constraint
139 Uri(UriType),
140 /// Plain string
141 #[serde(untagged)]
142 String,
143}
144
145impl core::fmt::Display for LexiconStringType {
146 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
147 match self {
148 LexiconStringType::Datetime => write!(f, "datetime"),
149 LexiconStringType::AtUri => write!(f, "at-uri"),
150 LexiconStringType::Did => write!(f, "did"),
151 LexiconStringType::Handle => write!(f, "handle"),
152 LexiconStringType::AtIdentifier => write!(f, "at-identifier"),
153 LexiconStringType::Nsid => write!(f, "nsid"),
154 LexiconStringType::Cid => write!(f, "cid"),
155 LexiconStringType::Language => write!(f, "language"),
156 LexiconStringType::Tid => write!(f, "tid"),
157 LexiconStringType::RecordKey => write!(f, "record-key"),
158 LexiconStringType::Uri(u) => write!(f, "uri({})", u),
159 LexiconStringType::String => write!(f, "string"),
160 }
161 }
162}
163
164/// URI scheme types for lexicon URI format constraints
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
166#[serde(tag = "type")]
167pub enum UriType {
168 /// DID URI (did:)
169 Did,
170 /// AT Protocol URI (at://)
171 At,
172 /// HTTPS URI
173 Https,
174 /// WebSocket Secure URI
175 Wss,
176 /// CID URI
177 Cid,
178 /// DNS name
179 Dns,
180 /// Any valid URI
181 Any,
182}
183
184impl core::fmt::Display for UriType {
185 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
186 match self {
187 UriType::Did => write!(f, "did"),
188 UriType::At => write!(f, "at"),
189 UriType::Https => write!(f, "https"),
190 UriType::Wss => write!(f, "wss"),
191 UriType::Cid => write!(f, "cid"),
192 UriType::Dns => write!(f, "dns"),
193 UriType::Any => write!(f, "any"),
194 }
195 }
196}