···
3
+
prelude::{BASE64_STANDARD, BASE64_STANDARD_NO_PAD, BASE64_URL_SAFE, BASE64_URL_SAFE_NO_PAD},
use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
-
use smol_str::SmolStr;
4
-
use std::collections::BTreeMap;
7
+
use smol_str::{SmolStr, ToSmolStr};
8
+
use std::{collections::BTreeMap, str::FromStr};
6
-
use crate::types::{blob::Blob, string::*};
12
+
DataModelType, LexiconStringType,
13
+
blob::{Blob, MimeType},
#[derive(Debug, Clone, PartialEq, Eq)]
···
31
+
pub fn from_json(json: &'s serde_json::Value) -> Self {
32
+
if let Some(value) = json.as_bool() {
33
+
Self::Boolean(value)
34
+
} else if let Some(value) = json.as_i64() {
35
+
Self::Integer(value)
36
+
} else if let Some(value) = json.as_str() {
37
+
Self::String(AtprotoStr::new(value))
38
+
} else if let Some(value) = json.as_array() {
39
+
Self::Array(Array::from_json(value))
40
+
} else if let Some(value) = json.as_object() {
41
+
Object::from_json(value)
42
+
} else if let Some(num) = json.as_number() {
43
+
// deliberately permissive here, just in case.
44
+
Self::String(AtprotoStr::new_owned(num.to_smolstr()))
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Array<'s>(pub Vec<Data<'s>>);
54
+
impl<'s> Array<'s> {
55
+
pub fn from_json(json: &'s Vec<serde_json::Value>) -> Self {
56
+
let mut array = Vec::with_capacity(json.len());
58
+
array.push(Data::from_json(item));
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Object<'s>(pub BTreeMap<SmolStr, Data<'s>>);
67
+
impl<'s> Object<'s> {
68
+
pub fn from_json(json: &'s serde_json::Map<String, serde_json::Value>) -> Data<'s> {
69
+
if let Some(type_field) = json.get("$type").and_then(|v| v.as_str()) {
70
+
if infer_from_type(type_field) == DataModelType::Blob {
71
+
if let Some(blob) = json_to_blob(json) {
72
+
return Data::Blob(blob);
76
+
let mut map = BTreeMap::new();
78
+
for (key, value) in json {
80
+
continue; // skip, because we've already handled it
82
+
match string_key_type_guess(key) {
83
+
DataModelType::Null => {
84
+
if value.is_null() {
85
+
map.insert(key.to_smolstr(), Data::Null);
87
+
map.insert(key.to_smolstr(), Data::from_json(value));
90
+
DataModelType::Boolean => {
91
+
if let Some(value) = value.as_bool() {
92
+
map.insert(key.to_smolstr(), Data::Boolean(value));
94
+
map.insert(key.to_smolstr(), Data::from_json(value));
97
+
DataModelType::Integer => {
98
+
if let Some(int) = value.as_i64() {
99
+
map.insert(key.to_smolstr(), Data::Integer(int));
101
+
map.insert(key.to_smolstr(), Data::from_json(value));
104
+
DataModelType::Bytes => {
105
+
if let Some(value) = value.as_str() {
106
+
map.insert(key.to_smolstr(), decode_bytes(value));
108
+
map.insert(key.to_smolstr(), Data::from_json(value));
111
+
DataModelType::CidLink => {
112
+
if let Some(value) = value.as_str() {
115
+
Data::String(AtprotoStr::Cid(Cid::Str(value.into()))),
118
+
map.insert(key.to_smolstr(), Data::from_json(value));
121
+
DataModelType::Blob => {
122
+
if let Some(value) = value.as_object() {
123
+
map.insert(key.to_smolstr(), Object::from_json(value));
125
+
map.insert(key.to_smolstr(), Data::from_json(value));
128
+
DataModelType::Array => {
129
+
if let Some(value) = value.as_array() {
130
+
map.insert(key.to_smolstr(), Data::Array(Array::from_json(value)));
132
+
map.insert(key.to_smolstr(), Data::from_json(value));
135
+
DataModelType::Object => {
136
+
if let Some(value) = value.as_object() {
137
+
map.insert(key.to_smolstr(), Object::from_json(value));
139
+
map.insert(key.to_smolstr(), Data::from_json(value));
142
+
DataModelType::String(string_type) => {
143
+
if let Some(value) = value.as_str() {
144
+
match string_type {
145
+
LexiconStringType::Datetime => {
146
+
if let Ok(datetime) = Datetime::from_str(value) {
149
+
Data::String(AtprotoStr::Datetime(datetime)),
154
+
Data::String(AtprotoStr::String(value.into())),
158
+
LexiconStringType::AtUri => {
159
+
if let Ok(value) = AtUri::new(value) {
162
+
Data::String(AtprotoStr::AtUri(value)),
167
+
Data::String(AtprotoStr::String(value.into())),
171
+
LexiconStringType::Did => {
172
+
if let Ok(value) = Did::new(value) {
175
+
Data::String(AtprotoStr::Did(value)),
180
+
Data::String(AtprotoStr::String(value.into())),
184
+
LexiconStringType::Handle => {
185
+
if let Ok(value) = Handle::new(value) {
188
+
Data::String(AtprotoStr::Handle(value)),
193
+
Data::String(AtprotoStr::String(value.into())),
197
+
LexiconStringType::AtIdentifier => {
198
+
if let Ok(value) = AtIdentifier::new(value) {
201
+
Data::String(AtprotoStr::AtIdentifier(value)),
206
+
Data::String(AtprotoStr::String(value.into())),
210
+
LexiconStringType::Nsid => {
211
+
if let Ok(value) = Nsid::new(value) {
214
+
Data::String(AtprotoStr::Nsid(value)),
219
+
Data::String(AtprotoStr::String(value.into())),
223
+
LexiconStringType::Cid => {
224
+
if let Ok(value) = Cid::new(value.as_bytes()) {
227
+
Data::String(AtprotoStr::Cid(value)),
232
+
Data::String(AtprotoStr::String(value.into())),
236
+
LexiconStringType::Language => {
237
+
if let Ok(value) = Language::new(value) {
240
+
Data::String(AtprotoStr::Language(value)),
245
+
Data::String(AtprotoStr::String(value.into())),
249
+
LexiconStringType::Tid => {
250
+
if let Ok(value) = Tid::new(value) {
253
+
Data::String(AtprotoStr::Tid(value)),
258
+
Data::String(AtprotoStr::String(value.into())),
262
+
LexiconStringType::RecordKey => {
263
+
if let Ok(value) = Rkey::new(value) {
266
+
Data::String(AtprotoStr::RecordKey(RecordKey::from(value))),
271
+
Data::String(AtprotoStr::String(value.into())),
275
+
LexiconStringType::Uri(_) => {
276
+
if let Ok(uri) = Uri::new(value) {
279
+
Data::String(AtprotoStr::Uri(uri)),
284
+
Data::String(AtprotoStr::String(value.into())),
288
+
LexiconStringType::String => {
289
+
map.insert(key.to_smolstr(), Data::String(parse_string(value)));
293
+
map.insert(key.to_smolstr(), Data::from_json(value));
299
+
Data::Object(Object(map))
302
+
//pub fn from_cbor(cbor: BTreeMap<String, ipld_core::ipld::Ipld>) -> Self {}
305
+
/// smarter parsing to avoid trying as many posibilities.
306
+
pub fn parse_string<'s>(string: &'s str) -> AtprotoStr<'s> {
307
+
if string.len() < 2048 && string.starts_with("did:") {
308
+
if let Ok(did) = Did::new(string) {
309
+
return AtprotoStr::Did(did);
311
+
} else if string.starts_with("20") && string.ends_with("Z") {
312
+
// probably a date (for the next 75 years)
313
+
if let Ok(datetime) = Datetime::from_str(string) {
314
+
return AtprotoStr::Datetime(datetime);
316
+
} else if string.starts_with("at://") {
317
+
if let Ok(uri) = AtUri::new(string) {
318
+
return AtprotoStr::AtUri(uri);
320
+
} else if string.starts_with("https://") {
321
+
if let Ok(uri) = Url::parse(string) {
322
+
return AtprotoStr::Uri(Uri::Https(uri));
324
+
} else if string.starts_with("wss://") {
325
+
if let Ok(uri) = Url::parse(string) {
326
+
return AtprotoStr::Uri(Uri::Https(uri));
328
+
} else if string.starts_with("ipfs://") {
329
+
return AtprotoStr::Uri(Uri::Cid(Cid::str(string)));
330
+
} else if string.contains('.') && !string.contains([' ', '\n']) {
331
+
if string.len() < 253 && Url::parse(string).is_ok() {
332
+
// probably a handle
333
+
if let Ok(handle) = AtIdentifier::new(string) {
334
+
return AtprotoStr::AtIdentifier(handle);
336
+
return AtprotoStr::Uri(Uri::Any(string.into()));
338
+
} else if let Ok(nsid) = Nsid::new(string) {
339
+
return AtprotoStr::Nsid(nsid);
341
+
} else if string.len() == 13 {
342
+
if let Ok(tid) = Tid::new(string) {
343
+
return AtprotoStr::Tid(tid);
345
+
} else if !string.contains([' ', '\n']) {
347
+
if let Ok(cid) = Cid::new(string.as_bytes()) {
348
+
return AtprotoStr::Cid(cid);
352
+
AtprotoStr::String(string.into())
355
+
/// First-level guess at what we should parse the corresponding value as
356
+
/// Helps speed up parsing, avoids some ambiguities.
357
+
pub fn string_key_type_guess(key: &str) -> DataModelType {
359
+
"cid" => DataModelType::String(LexiconStringType::Cid),
360
+
"uri" => DataModelType::String(LexiconStringType::Uri(super::UriType::Any)),
361
+
"did" => DataModelType::String(LexiconStringType::Did),
362
+
"handle" => DataModelType::String(LexiconStringType::AtIdentifier),
363
+
"ref" => DataModelType::CidLink,
364
+
"list" => DataModelType::String(LexiconStringType::AtUri),
365
+
"blobref" => DataModelType::Blob,
366
+
"createdAt" | "created" | "indexedAt" | "issuedAt" | "updatedAt" | "playedTime" => {
367
+
DataModelType::String(LexiconStringType::Datetime)
369
+
"size" | "width" | "height" => DataModelType::Integer,
370
+
"value" | "record" | "embed" => DataModelType::Object,
371
+
"text" | "displayName" | "alt" | "name" | "description" => {
372
+
DataModelType::String(LexiconStringType::String)
374
+
"langs" | "blobs" | "images" | "labels" => DataModelType::Array,
375
+
"$bytes" => DataModelType::Bytes,
376
+
"$link" => DataModelType::String(LexiconStringType::Cid),
377
+
"$type" => DataModelType::String(LexiconStringType::String),
379
+
// we assume others are strings speficially because it's easy to check if a serde_json::Value
380
+
// or Ipld value is at least a string, and then we fall back to Object/Map.
381
+
_ => DataModelType::String(LexiconStringType::String),
385
+
pub fn json_to_blob<'b>(blob: &'b serde_json::Map<String, serde_json::Value>) -> Option<Blob<'b>> {
386
+
let mime_type = blob.get("mimeType").and_then(|v| v.as_str());
387
+
if let Some(value) = blob.get("ref") {
388
+
if let Some(value) = value
390
+
.and_then(|o| o.get("$link"))
391
+
.and_then(|v| v.as_str())
393
+
let size = blob.get("size").and_then(|v| v.as_u64());
394
+
if let (Some(mime_type), Some(size)) = (mime_type, size) {
396
+
r#ref: Cid::str(value),
397
+
mime_type: MimeType::raw(mime_type),
398
+
size: size as usize,
402
+
} else if let Some(value) = blob.get("cid").and_then(|v| v.as_str()) {
403
+
if let Some(mime_type) = mime_type {
405
+
r#ref: Cid::str(value),
406
+
mime_type: MimeType::raw(mime_type),
415
+
pub fn infer_from_type(type_field: &str) -> DataModelType {
417
+
"blob" => DataModelType::Blob,
418
+
_ => DataModelType::Object,
422
+
pub fn decode_bytes<'s>(bytes: &'s str) -> Data<'s> {
423
+
// First one should just work. rest are insurance.
424
+
if let Ok(bytes) = BASE64_STANDARD.decode(bytes) {
425
+
Data::Bytes(Bytes::from_owner(bytes))
426
+
} else if let Ok(bytes) = BASE64_STANDARD_NO_PAD.decode(bytes) {
427
+
Data::Bytes(Bytes::from_owner(bytes))
428
+
} else if let Ok(bytes) = BASE64_URL_SAFE.decode(bytes) {
429
+
Data::Bytes(Bytes::from_owner(bytes))
430
+
} else if let Ok(bytes) = BASE64_URL_SAFE_NO_PAD.decode(bytes) {
431
+
Data::Bytes(Bytes::from_owner(bytes))
433
+
Data::String(AtprotoStr::String(bytes.into()))