Skip to content

Commit 89d0f10

Browse files
committed
glib: Add optional support for serialization and deserialization with serde
This feature is gated as `serde` Supports both serialization and deserialization: - glib::ByteArray - glib::Bytes - glib::GString (with in-place deserialization) Supports serialization only: - glib::GStr - glib::StrV Collection types are also supported as long as the type parameters implement the necessary traits: - glib::Slice<T: TransparentType + ..> - glib::PtrSlice<T: TransparentPtrType + ..> - glib::List<T: TransparentPtrType + ..> - glib::SList<T: TransparentPtrType + ..>
1 parent 9c172ea commit 89d0f10

File tree

9 files changed

+636
-2
lines changed

9 files changed

+636
-2
lines changed

Diff for: .github/workflows/CI.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- { name: "cairo", features: "png,pdf,svg,ps,use_glib,v1_18,freetype,script,xcb,xlib,win32-surface", nightly: "--features 'png,pdf,svg,ps,use_glib,v1_18,freetype,script,xcb,xlib,win32-surface'", test_sys: true }
2424
- { name: "gdk-pixbuf", features: "v2_42", nightly: "--all-features", test_sys: true }
2525
- { name: "gio", features: "v2_74", nightly: "--all-features", test_sys: true }
26-
- { name: "glib", features: "v2_74", nightly: "--all-features", test_sys: true }
26+
- { name: "glib", features: "v2_74,serde", nightly: "--all-features", test_sys: true }
2727
- { name: "graphene", features: "", nightly: "", test_sys: true }
2828
- { name: "pango", features: "v1_50", nightly: "--all-features", test_sys: true }
2929
- { name: "pangocairo", features: "", nightly: "--all-features", test_sys: true }

Diff for: .github/workflows/windows.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,14 @@ jobs:
126126
with:
127127
command: test
128128
args: --manifest-path ${{ matrix.conf.name }}/sys/Cargo.toml ${{ matrix.conf.args }}
129-
if: matrix.conf.name != 'examples' && matrix.conf.name != 'glib-build-tools'
129+
if: matrix.conf.name != 'examples' && matrix.conf.name != 'glib' && matrix.conf.name != 'glib-build-tools'
130+
131+
- name: test glib-sys
132+
uses: actions-rs/cargo@v1
133+
with:
134+
command: test
135+
args: --manifest-path ${{ matrix.conf.name }}/sys/Cargo.toml --features v2_74
136+
if: matrix.conf.name == 'glib'
130137

131138
- name: build
132139
uses: actions-rs/cargo@v1

Diff for: glib/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ smallvec = { version = "1.0", features = ["union", "const_generics", "const_new"
3535
thiserror = "1"
3636
gio_ffi = { package = "gio-sys", path = "../gio/sys", optional = true }
3737
memchr = "2.5.0"
38+
serde = { version = "1.0", optional = true }
3839

3940
[dev-dependencies]
4041
tempfile = "3"
4142
gir-format-check = "^0.1"
4243
trybuild2 = "1"
4344
criterion = "0.4.0"
45+
serde_json = "1.0"
4446

4547
[features]
4648
default = ["gio"]
@@ -59,6 +61,7 @@ log_macros = ["log"]
5961
dox = ["ffi/dox", "gobject_ffi/dox", "log_macros"]
6062
compiletests = []
6163
gio = ["gio_ffi"]
64+
serde = ["dep:serde"]
6265

6366
[package.metadata.docs.rs]
6467
features = ["dox"]

Diff for: glib/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ pub use self::thread_pool::{ThreadHandle, ThreadPool};
213213

214214
pub mod thread_guard;
215215

216+
#[cfg(feature = "serde")]
217+
mod serde;
218+
216219
// rustdoc-stripper-ignore-next
217220
/// This is the log domain used by the [`clone!`][crate::clone!] macro. If you want to use a custom
218221
/// logger (it prints to stdout by default), you can set your own logger using the corresponding

Diff for: glib/src/serde/byte_array.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use serde::Deserializer;
4+
5+
use super::*;
6+
7+
use crate::ByteArray;
8+
9+
serialize_impl!(ByteArray, Bytes(b) => b);
10+
11+
deserialize_impl! {
12+
ByteArray,
13+
"a sequence of bytes",
14+
Deserializer::deserialize_seq => match impl {
15+
Bytes(b) => Ok(ByteArray::from(b)),
16+
ByteBuf(buf) => Ok(ByteArray::from(buf.as_slice())),
17+
Seq(s) => {
18+
// See https://docs.rs/serde/1.0.159/src/serde/de/impls.rs.html#1038
19+
// and https://docs.rs/serde/1.0.159/src/serde/private/size_hint.rs.html#13
20+
let mut byte_vec = Vec::with_capacity(min(s.size_hint().unwrap_or(0), 4096));
21+
22+
while let Some(byte) = s.next_element()? {
23+
byte_vec.push(byte)
24+
}
25+
26+
Ok(ByteArray::from(byte_vec.as_slice()))
27+
},
28+
}
29+
}
30+
31+
#[cfg(test)]
32+
mod tests {
33+
use crate::{gformat, ByteArray};
34+
35+
#[test]
36+
fn serialization() {
37+
let json = match serde_json::to_value(ByteArray::from(
38+
gformat!("Lorem ipsum dolor sit amet").as_bytes(),
39+
)) {
40+
Ok(v) => Some(v),
41+
Err(_) => None,
42+
};
43+
44+
assert_ne!(json, None);
45+
}
46+
47+
#[test]
48+
fn deserialization() {
49+
let json_str = r#"[76,111,114,101,109,32,105,112,115,117,109,32,100,111,108,111,114,32,115,105,116,32,97,109,101]"#;
50+
51+
serde_json::from_str::<ByteArray>(json_str).unwrap();
52+
}
53+
}

Diff for: glib/src/serde/bytes.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use serde::Deserializer;
4+
5+
use super::*;
6+
7+
use crate::Bytes;
8+
9+
serialize_impl!(Bytes, Bytes(b) => b);
10+
11+
deserialize_impl! {
12+
Bytes,
13+
"a sequence of bytes",
14+
Deserializer::deserialize_seq => match impl {
15+
Bytes(b) => Ok(Bytes::from_owned(b.to_owned())),
16+
ByteBuf(buf) => Ok(Bytes::from_owned(buf)),
17+
Seq(s) => {
18+
let mut byte_vec = Vec::with_capacity(min(s.size_hint().unwrap_or(0), 4096));
19+
20+
while let Some(byte) = s.next_element()? {
21+
byte_vec.push(byte)
22+
}
23+
24+
Ok(Bytes::from_owned(byte_vec))
25+
},
26+
}
27+
}
28+
29+
#[cfg(test)]
30+
mod tests {
31+
use crate::{gformat, Bytes};
32+
33+
#[test]
34+
fn serialization() {
35+
let json = match serde_json::to_value(Bytes::from_owned(
36+
gformat!("Lorem ipsum dolor sit amet").into_bytes(),
37+
)) {
38+
Ok(v) => Some(v),
39+
Err(_) => None,
40+
};
41+
42+
assert_ne!(json, None);
43+
}
44+
45+
#[test]
46+
fn deserialization() {
47+
let json_str = r#"[76,111,114,101,109,32,105,112,115,117,109,32,100,111,108,111,114,32,115,105,116,32,97,109,101]"#;
48+
49+
serde_json::from_str::<Bytes>(json_str).unwrap();
50+
}
51+
}

Diff for: glib/src/serde/collections.rs

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use super::*;
4+
5+
use crate::{
6+
translate::{TransparentPtrType, TransparentType},
7+
List, PtrSlice, SList, Slice, StrV,
8+
};
9+
10+
serialize_impl!(Slice<T: TransparentType>, Sequence(iter) => iter);
11+
12+
deserialize_impl! {
13+
Slice<T: TransparentType>,
14+
"a sequence of GLib transparent values",
15+
Deserializer::deserialize_seq => match impl {
16+
Seq(s) => {
17+
let mut slice = Slice::with_capacity(min(s.size_hint().unwrap_or(0), 4096));
18+
19+
while let Some(item) = s.next_element()? {
20+
slice.push(item)
21+
}
22+
23+
Ok(slice)
24+
},
25+
@in_place(this) => match impl {
26+
Seq(s) => {
27+
this.clear();
28+
this.reserve(min(s.size_hint().unwrap_or(0), 4096));
29+
30+
while let Some(item) = s.next_element()? {
31+
this.push(item)
32+
}
33+
34+
Ok(())
35+
},
36+
},
37+
}
38+
}
39+
40+
serialize_impl!(PtrSlice<T: TransparentPtrType>, Sequence(iter) => iter);
41+
42+
deserialize_impl! {
43+
PtrSlice<T: TransparentPtrType>,
44+
"a sequence of GLib transparent pointer values",
45+
Deserializer::deserialize_seq => match impl {
46+
Seq(s) => {
47+
let mut slice = PtrSlice::with_capacity(min(s.size_hint().unwrap_or(0), 4096));
48+
49+
while let Some(item) = s.next_element()? {
50+
slice.push(item)
51+
}
52+
53+
Ok(slice)
54+
},
55+
@in_place(this) => match impl {
56+
Seq(s) => {
57+
this.clear();
58+
this.reserve(min(s.size_hint().unwrap_or(0), 4096));
59+
60+
while let Some(item) = s.next_element()? {
61+
this.push(item)
62+
}
63+
64+
Ok(())
65+
},
66+
},
67+
}
68+
}
69+
70+
serialize_impl!(List<T: TransparentPtrType>, Sequence(iter) => iter.iter());
71+
72+
deserialize_impl! {
73+
List<T: TransparentPtrType>,
74+
"a sequence of GLib transparent pointer values",
75+
Deserializer::deserialize_seq => match impl {
76+
Seq(s) => {
77+
let mut list = List::new();
78+
79+
while let Some(item) = s.next_element()? {
80+
list.push_front(item)
81+
}
82+
list.reverse();
83+
84+
Ok(list)
85+
},
86+
@in_place(this) => match impl {
87+
Seq(s) => {
88+
this.clear();
89+
90+
while let Some(item) = s.next_element()? {
91+
this.push_front(item)
92+
}
93+
this.reverse();
94+
95+
Ok(())
96+
},
97+
},
98+
}
99+
}
100+
101+
serialize_impl!(SList<T: TransparentPtrType>, Sequence(iter) => iter.iter());
102+
103+
deserialize_impl! {
104+
SList<T: TransparentPtrType>,
105+
"a sequence of GLib transparent pointer values",
106+
Deserializer::deserialize_seq => match impl {
107+
Seq(s) => {
108+
let mut list = SList::new();
109+
110+
while let Some(item) = s.next_element()? {
111+
list.push_front(item)
112+
}
113+
list.reverse();
114+
115+
Ok(list)
116+
},
117+
@in_place(this) => match impl {
118+
Seq(s) => {
119+
this.clear();
120+
121+
while let Some(item) = s.next_element()? {
122+
this.push_front(item)
123+
}
124+
this.reverse();
125+
126+
Ok(())
127+
},
128+
},
129+
}
130+
}
131+
132+
serialize_impl!(StrV, Sequence(iter) => iter);
133+
134+
#[cfg(test)]
135+
mod tests {
136+
use serde_json::json;
137+
138+
use crate::{gformat, Bytes, List, PtrSlice, SList, Slice};
139+
140+
#[test]
141+
fn serialization_equality() {
142+
let bytes = gformat!("Lorem ipsum dolor sit amet").into_bytes();
143+
144+
let slice = Slice::from([
145+
Bytes::from_owned(bytes[..].to_vec()),
146+
Bytes::from_owned(bytes[1..].to_vec()),
147+
Bytes::from_owned(bytes[2..].to_vec()),
148+
Bytes::from_owned(bytes[3..].to_vec()),
149+
]);
150+
151+
let ptr_slice = PtrSlice::from([
152+
Bytes::from_owned(bytes[..].to_vec()),
153+
Bytes::from_owned(bytes[1..].to_vec()),
154+
Bytes::from_owned(bytes[2..].to_vec()),
155+
Bytes::from_owned(bytes[3..].to_vec()),
156+
]);
157+
158+
let mut list = List::<Bytes>::new();
159+
list.push_front(Bytes::from_owned(bytes[..].to_vec()));
160+
list.push_front(Bytes::from_owned(bytes[1..].to_vec()));
161+
list.push_front(Bytes::from_owned(bytes[2..].to_vec()));
162+
list.push_front(Bytes::from_owned(bytes[3..].to_vec()));
163+
list.reverse();
164+
165+
let mut slist = SList::<Bytes>::new();
166+
slist.push_front(Bytes::from_owned(bytes[..].to_vec()));
167+
slist.push_front(Bytes::from_owned(bytes[1..].to_vec()));
168+
slist.push_front(Bytes::from_owned(bytes[2..].to_vec()));
169+
slist.push_front(Bytes::from_owned(bytes[3..].to_vec()));
170+
slist.reverse();
171+
172+
assert_eq!(json!(&slice), json!(&list));
173+
assert_eq!(json!(&slice), json!(&slist));
174+
assert_eq!(json!(&ptr_slice), json!(&list));
175+
assert_eq!(json!(&ptr_slice), json!(&slist));
176+
assert_eq!(json!(&slice), json!(&ptr_slice));
177+
assert_eq!(json!(&list), json!(&slist));
178+
}
179+
180+
#[test]
181+
fn deserialization() {
182+
let json_str = r#"
183+
[
184+
[76,111,114,101,109,32,105,112,115,117,109,32,100,111,108,111,114,32,115,105,116,32,97,109,101],
185+
[111,114,101,109,32,105,112,115,117,109,32,100,111,108,111,114,32,115,105,116,32,97,109,101],
186+
[114,101,109,32,105,112,115,117,109,32,100,111,108,111,114,32,115,105,116,32,97,109,101],
187+
[101,109,32,105,112,115,117,109,32,100,111,108,111,114,32,115,105,116,32,97,109,101]
188+
]"#;
189+
serde_json::from_str::<Slice<Bytes>>(json_str).unwrap();
190+
serde_json::from_str::<PtrSlice<Bytes>>(json_str).unwrap();
191+
serde_json::from_str::<List<Bytes>>(json_str).unwrap();
192+
serde_json::from_str::<SList<Bytes>>(json_str).unwrap();
193+
}
194+
}

0 commit comments

Comments
 (0)