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