tangled
alpha
login
or
join now
nonbinary.computer
/
jacquard
80
fork
atom
A better Rust ATProto crate
80
fork
atom
overview
issues
10
pulls
pipelines
fix for nullable fields
Orual
3 months ago
ba9249d4
76e7ad63
+166
-12
3 changed files
expand all
collapse all
unified
split
crates
jacquard-common
src
opt_serde_bytes_helper.rs
serde_bytes_helper.rs
jacquard-lexicon
src
codegen
structs.rs
+89
-4
crates/jacquard-common/src/opt_serde_bytes_helper.rs
reviewed
···
1
1
//! Custom serde helpers for bytes::Bytes using serde_bytes
2
2
3
3
+
use base64::{
4
4
+
Engine,
5
5
+
prelude::{BASE64_STANDARD, BASE64_STANDARD_NO_PAD, BASE64_URL_SAFE, BASE64_URL_SAFE_NO_PAD},
6
6
+
};
3
7
use bytes::Bytes;
4
4
-
use serde::{Deserializer, Serializer};
8
8
+
use core::fmt;
9
9
+
use serde::{
10
10
+
Deserializer, Serializer,
11
11
+
de::{self, MapAccess, Visitor},
12
12
+
};
5
13
6
14
/// Serialize Bytes as a CBOR byte string
7
15
pub fn serialize<S>(bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
···
9
17
S: Serializer,
10
18
{
11
19
if let Some(bytes) = bytes {
12
12
-
serde_bytes::serialize(bytes.as_ref(), serializer)
20
20
+
if serializer.is_human_readable() {
21
21
+
// JSON: {"$bytes": "base64 string"}
22
22
+
use serde::ser::SerializeMap;
23
23
+
let mut map = serializer.serialize_map(Some(1))?;
24
24
+
map.serialize_entry("$bytes", &BASE64_STANDARD.encode(bytes))?;
25
25
+
map.end()
26
26
+
} else {
27
27
+
// CBOR: raw bytes
28
28
+
serde_bytes::serialize(bytes.as_ref(), serializer)
29
29
+
}
13
30
} else {
14
31
serializer.serialize_none()
15
32
}
···
20
37
where
21
38
D: Deserializer<'de>,
22
39
{
23
23
-
let vec: Option<Vec<u8>> = serde_bytes::deserialize(deserializer)?;
24
24
-
Ok(vec.map(Bytes::from))
40
40
+
if deserializer.is_human_readable() {
41
41
+
Ok(deserializer.deserialize_map(OptBytesVisitor)?)
42
42
+
} else {
43
43
+
let vec: Option<Vec<u8>> = serde_bytes::deserialize(deserializer)?;
44
44
+
Ok(vec.map(Bytes::from))
45
45
+
}
46
46
+
}
47
47
+
48
48
+
struct OptBytesVisitor;
49
49
+
50
50
+
impl<'de> Visitor<'de> for OptBytesVisitor {
51
51
+
type Value = Option<Bytes>;
52
52
+
53
53
+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54
54
+
formatter.write_str("a base64-encoded string")
55
55
+
}
56
56
+
57
57
+
fn visit_none<E>(self) -> Result<Self::Value, E>
58
58
+
where
59
59
+
E: de::Error,
60
60
+
{
61
61
+
Ok(None)
62
62
+
}
63
63
+
64
64
+
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
65
65
+
where
66
66
+
D: Deserializer<'de>,
67
67
+
{
68
68
+
let vec: Vec<u8> = serde_bytes::deserialize(deserializer)?;
69
69
+
Ok(Some(Bytes::from(vec)))
70
70
+
}
71
71
+
72
72
+
fn visit_unit<E>(self) -> Result<Self::Value, E> {
73
73
+
Ok(None)
74
74
+
}
75
75
+
76
76
+
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
77
77
+
where
78
78
+
A: MapAccess<'de>,
79
79
+
{
80
80
+
let mut bytes = None;
81
81
+
82
82
+
while let Some(key) = map.next_key()? {
83
83
+
match key {
84
84
+
"$bytes" => {
85
85
+
if bytes.is_some() {
86
86
+
return Err(de::Error::duplicate_field("$bytes"));
87
87
+
}
88
88
+
let bytes_str: String = map.next_value()?;
89
89
+
// First one should just work. rest are insurance.
90
90
+
bytes = if let Ok(bytes) = BASE64_STANDARD.decode(&bytes_str) {
91
91
+
Some(Bytes::from_owner(bytes))
92
92
+
} else if let Ok(bytes) = BASE64_STANDARD_NO_PAD.decode(&bytes_str) {
93
93
+
Some(Bytes::from_owner(bytes))
94
94
+
} else if let Ok(bytes) = BASE64_URL_SAFE.decode(&bytes_str) {
95
95
+
Some(Bytes::from_owner(bytes))
96
96
+
} else if let Ok(bytes) = BASE64_URL_SAFE_NO_PAD.decode(&bytes_str) {
97
97
+
Some(Bytes::from_owner(bytes))
98
98
+
} else {
99
99
+
return Err(de::Error::custom("invalid base64 string"));
100
100
+
}
101
101
+
}
102
102
+
_ => {
103
103
+
return Err(de::Error::unknown_field(key, &["$bytes"]));
104
104
+
}
105
105
+
}
106
106
+
}
107
107
+
108
108
+
Ok(bytes)
109
109
+
}
25
110
}
+71
-4
crates/jacquard-common/src/serde_bytes_helper.rs
reviewed
···
1
1
//! Custom serde helpers for bytes::Bytes using serde_bytes
2
2
3
3
+
use core::fmt;
4
4
+
5
5
+
use base64::{
6
6
+
Engine,
7
7
+
prelude::{BASE64_STANDARD, BASE64_STANDARD_NO_PAD, BASE64_URL_SAFE, BASE64_URL_SAFE_NO_PAD},
8
8
+
};
3
9
use bytes::Bytes;
4
4
-
use serde::{Deserializer, Serializer};
10
10
+
use serde::{
11
11
+
Deserializer, Serializer,
12
12
+
de::{self, MapAccess, Visitor},
13
13
+
};
5
14
6
15
/// Serialize Bytes as a CBOR byte string
7
16
pub fn serialize<S>(bytes: &Bytes, serializer: S) -> Result<S::Ok, S::Error>
8
17
where
9
18
S: Serializer,
10
19
{
11
11
-
serde_bytes::serialize(bytes.as_ref(), serializer)
20
20
+
if serializer.is_human_readable() {
21
21
+
// JSON: {"$bytes": "base64 string"}
22
22
+
use serde::ser::SerializeMap;
23
23
+
let mut map = serializer.serialize_map(Some(1))?;
24
24
+
map.serialize_entry("$bytes", &BASE64_STANDARD.encode(bytes))?;
25
25
+
map.end()
26
26
+
} else {
27
27
+
// CBOR: raw bytes
28
28
+
serde_bytes::serialize(bytes.as_ref(), serializer)
29
29
+
}
12
30
}
13
31
14
32
/// Deserialize Bytes from a CBOR byte string
···
16
34
where
17
35
D: Deserializer<'de>,
18
36
{
19
19
-
let vec: Vec<u8> = serde_bytes::deserialize(deserializer)?;
20
20
-
Ok(Bytes::from(vec))
37
37
+
if deserializer.is_human_readable() {
38
38
+
Ok(deserializer.deserialize_map(BytesVisitor)?)
39
39
+
} else {
40
40
+
let vec: Vec<u8> = serde_bytes::deserialize(deserializer)?;
41
41
+
Ok(Bytes::from(vec))
42
42
+
}
43
43
+
}
44
44
+
45
45
+
struct BytesVisitor;
46
46
+
47
47
+
impl<'de> Visitor<'de> for BytesVisitor {
48
48
+
type Value = Bytes;
49
49
+
50
50
+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
51
51
+
formatter.write_str("a base64-encoded string")
52
52
+
}
53
53
+
54
54
+
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
55
55
+
where
56
56
+
A: MapAccess<'de>,
57
57
+
{
58
58
+
let mut bytes = None;
59
59
+
60
60
+
while let Some(key) = map.next_key()? {
61
61
+
match key {
62
62
+
"$bytes" => {
63
63
+
if bytes.is_some() {
64
64
+
return Err(de::Error::duplicate_field("$bytes"));
65
65
+
}
66
66
+
let bytes_str: String = map.next_value()?;
67
67
+
// First one should just work. rest are insurance.
68
68
+
bytes = if let Ok(bytes) = BASE64_STANDARD.decode(&bytes_str) {
69
69
+
Some(Bytes::from_owner(bytes))
70
70
+
} else if let Ok(bytes) = BASE64_STANDARD_NO_PAD.decode(&bytes_str) {
71
71
+
Some(Bytes::from_owner(bytes))
72
72
+
} else if let Ok(bytes) = BASE64_URL_SAFE.decode(&bytes_str) {
73
73
+
Some(Bytes::from_owner(bytes))
74
74
+
} else if let Ok(bytes) = BASE64_URL_SAFE_NO_PAD.decode(&bytes_str) {
75
75
+
Some(Bytes::from_owner(bytes))
76
76
+
} else {
77
77
+
return Err(de::Error::custom("invalid base64 string"));
78
78
+
}
79
79
+
}
80
80
+
_ => {
81
81
+
return Err(de::Error::unknown_field(key, &["$bytes"]));
82
82
+
}
83
83
+
}
84
84
+
}
85
85
+
86
86
+
bytes.ok_or_else(|| de::Error::missing_field("$bytes"))
87
87
+
}
21
88
}
+6
-4
crates/jacquard-lexicon/src/codegen/structs.rs
reviewed
···
294
294
nsid: &str,
295
295
parent_type_name: &str,
296
296
obj: &LexObject<'static>,
297
297
-
is_builder: bool,
297
297
+
_is_builder: bool,
298
298
) -> Result<TokenStream> {
299
299
let required = obj.required.as_ref().map(|r| r.as_slice()).unwrap_or(&[]);
300
300
+
let nullable = obj.nullable.as_ref().map(|n| n.as_slice()).unwrap_or(&[]);
300
301
301
302
let mut fields = Vec::new();
302
303
for (field_name, field_type) in &obj.properties {
303
304
let is_required = required.contains(field_name);
305
305
+
let is_nullable = nullable.contains(field_name);
304
306
let field_tokens = self.generate_field(
305
307
nsid,
306
308
parent_type_name,
307
309
field_name,
308
310
field_type,
309
311
is_required,
310
310
-
is_builder,
312
312
+
is_nullable,
311
313
)?;
312
314
fields.push(field_tokens);
313
315
}
···
323
325
field_name: &str,
324
326
field_type: &LexObjectProperty<'static>,
325
327
is_required: bool,
326
326
-
_is_builder: bool,
328
328
+
is_nullable: bool,
327
329
) -> Result<TokenStream> {
328
330
if field_name.is_empty() {
329
331
eprintln!(
···
337
339
self.property_to_rust_type(nsid, parent_type_name, field_name, field_type)?;
338
340
let needs_lifetime = self.property_needs_lifetime(field_type);
339
341
340
340
-
let rust_type = if is_required {
342
342
+
let rust_type = if is_required && !is_nullable {
341
343
rust_type
342
344
} else {
343
345
quote! { std::option::Option<#rust_type> }