Skip to content

Commit d95e941

Browse files
committed
glib: Add optional support for serialization and deserialization with serde
This feature is gated as `serialize` Supports both serialization and deserialization: - glib::Bytes - glib::GString Supports serialization only: - glib::ByteArray - glib::GStr - glib::StrV Collection types are also supported as long as the type parameters implement the necessary traits: - glib::PtrSlice<T: TransparentPtrType + _> - glib::Slice<T: TransparentType + _> - glib::List<T: TransparentPtrType + _> - glib::SList<T: TransparentPtrType + _>
1 parent 7f9a20b commit d95e941

File tree

4 files changed

+263
-0
lines changed

4 files changed

+263
-0
lines changed

glib/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ smallvec = "1.0"
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"
@@ -59,6 +60,7 @@ log_macros = ["log"]
5960
dox = ["ffi/dox", "gobject_ffi/dox", "log_macros"]
6061
compiletests = []
6162
gio = ["gio_ffi"]
63+
serialize = ["dep:serde"]
6264

6365
[package.metadata.docs.rs]
6466
features = ["dox"]

glib/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ glib = "0.13"
9696
glib = { git = "https://github.com/gtk-rs/gtk-rs-core.git", package = "glib" }
9797
```
9898

99+
### Serialization with Serde
100+
101+
If you want to serialize (and deserialize) some GLib types (e.g. `GString`) with Serde, you can enable the `serialize` feature:
102+
103+
```toml
104+
glib = { version = "0.18", features = ["serialize"] }
105+
```
106+
107+
This library implements serialization and deserialization as described in the `serde@^1.0` API.
108+
99109
## License
100110

101111
__glib__ is available under the MIT License, please refer to it.

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 = "serialize")]
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

glib/src/serde.rs

+248
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
macro_rules! serialize_impl {
4+
($ty:ty, Bytes($bind:ident) => $expr:expr) => {
5+
impl ::serde::Serialize for $ty {
6+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
7+
where
8+
S: ::serde::Serializer,
9+
{
10+
let $bind = self;
11+
12+
serializer.serialize_bytes($expr)
13+
}
14+
}
15+
};
16+
($ty:ty, Str($bind:ident) => $expr:expr) => {
17+
impl ::serde::Serialize for $ty {
18+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
19+
where
20+
S: ::serde::Serializer,
21+
{
22+
let $bind = self;
23+
24+
serializer.serialize_str($expr)
25+
}
26+
}
27+
};
28+
($ty:ident$(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, Iterator($bind:ident) => $expr:expr) => {
29+
impl$(<$($generic $(: ::serde::Serialize + $bound $(+ $bound2)*)?),+>)? ::serde::Serialize for $ty$(<$($generic),+>)? {
30+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31+
where
32+
S: ::serde::Serializer
33+
{
34+
let $bind = self;
35+
serializer.collect_seq($expr)
36+
}
37+
}
38+
};
39+
}
40+
41+
macro_rules! deserialize_impl {
42+
($ty:ty, Bytes($arg:ident) => $body:block) => {
43+
impl<'de> ::serde::Deserialize<'de> for $ty {
44+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
45+
where
46+
D: ::serde::Deserializer<'de>,
47+
{
48+
struct Visitor;
49+
50+
impl<'a> ::serde::de::Visitor<'a> for Visitor {
51+
type Value = $ty;
52+
53+
fn expecting(
54+
&self,
55+
formatter: &mut ::std::fmt::Formatter,
56+
) -> ::std::fmt::Result {
57+
formatter.write_str("an array of bytes")
58+
}
59+
60+
fn visit_seq<A>(self, mut $arg: A) -> Result<Self::Value, A::Error>
61+
where
62+
A: ::serde::de::SeqAccess<'a>,
63+
$body
64+
}
65+
66+
deserializer.deserialize_seq(Visitor)
67+
}
68+
}
69+
};
70+
($ty:ty, String($arg:ident) => $body:block) => {
71+
impl<'de> ::serde::Deserialize<'de> for $ty {
72+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73+
where
74+
D: ::serde::Deserializer<'de>,
75+
{
76+
struct Visitor;
77+
78+
impl<'a> ::serde::de::Visitor<'a> for Visitor {
79+
type Value = $ty;
80+
81+
fn expecting(
82+
&self,
83+
formatter: &mut ::std::fmt::Formatter,
84+
) -> ::std::fmt::Result {
85+
formatter.write_str("a valid UTF-8 string")
86+
}
87+
88+
fn visit_str<E>(self, _: &str) -> Result<Self::Value, E>
89+
where
90+
E: de::Error,
91+
{
92+
todo!()
93+
}
94+
95+
fn visit_string<E>(self, $arg: String) -> Result<Self::Value, E>
96+
where
97+
E: ::serde::de::Error,
98+
$body
99+
}
100+
101+
deserializer.deserialize_string(Visitor)
102+
}
103+
}
104+
};
105+
106+
($ty:ident$(<$($generic:ident$(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $expect:literal, Collection($arg:ident) => $body:block) => {
107+
impl<'de: 'a, 'a: 'de, $($($generic $(: ::serde::Deserialize<'de> + $bound $(+ $bound2)*)?),+)?> ::serde::Deserialize<'de> for $ty$(<$($generic),+>)? {
108+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109+
where
110+
D: ::serde::Deserializer<'de>,
111+
{
112+
struct Visitor<'v, $($($generic $(: ::serde::Deserialize<'v> + $bound $(+ $bound2)*)?),+)?>(::std::marker::PhantomData<&'v ()>, $($(::std::marker::PhantomData<fn() -> $generic>),+)?);
113+
114+
impl<'a, $($($generic $(: ::serde::Deserialize<'a> + $bound $(+ $bound2)*)?),+)?> ::serde::de::Visitor<'a> for Visitor<'a, $($($generic),+)?> {
115+
type Value = $ty$(<$($generic),+>)?;
116+
117+
fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
118+
formatter.write_str($expect)
119+
}
120+
121+
fn visit_seq<A>(self, mut $arg: A) -> Result<Self::Value, A::Error>
122+
where
123+
A: ::serde::de::SeqAccess<'a>,
124+
$body
125+
}
126+
127+
deserializer.deserialize_seq(Visitor(::std::marker::PhantomData, $($(::std::marker::PhantomData::<fn() -> $generic>::default()),+)?))
128+
}
129+
}
130+
};
131+
}
132+
133+
mod byte_array {
134+
use crate::ByteArray;
135+
136+
serialize_impl!(ByteArray, Bytes(b) => b);
137+
}
138+
139+
mod bytes {
140+
use crate::Bytes;
141+
142+
serialize_impl!(Bytes, Bytes(b) => b);
143+
144+
deserialize_impl!(
145+
Bytes,
146+
Bytes(seq) => {
147+
let mut byte_vec = vec![];
148+
149+
while let Some(_size @ 1..) = seq.size_hint() {
150+
match seq.next_element()? {
151+
Some(byte) => byte_vec.push(byte),
152+
None => break,
153+
}
154+
}
155+
156+
Ok(Bytes::from_owned(byte_vec))
157+
}
158+
);
159+
}
160+
161+
mod gstring {
162+
use crate::{GStr, GString, GStringPtr};
163+
use serde::de;
164+
165+
serialize_impl!(GStr, Str(s) => s.as_str());
166+
167+
serialize_impl!(GString, Str(s) => s.as_str());
168+
169+
deserialize_impl!(
170+
GString,
171+
String(s) => {GString::from_string_checked(s).map_err(|e| de::Error::custom(e))}
172+
);
173+
174+
serialize_impl!(GStringPtr, Str(s) => s.to_str());
175+
}
176+
177+
mod collections {
178+
use crate::{
179+
translate::{TransparentPtrType, TransparentType},
180+
List, PtrSlice, SList, Slice, StrV,
181+
};
182+
183+
serialize_impl!(PtrSlice<T: TransparentPtrType>, Iterator(iter) => iter);
184+
185+
deserialize_impl!(PtrSlice<T: TransparentPtrType>, "a sequence of GLib transparent pointer values", Collection(seq) => {
186+
let mut ret = PtrSlice::new();
187+
188+
while let Some(_size @ 1..) = seq.size_hint() {
189+
ret.push(match seq.next_element()? {
190+
Some(item) => item,
191+
None => break,
192+
})
193+
}
194+
195+
Ok(ret)
196+
});
197+
198+
serialize_impl!(Slice<T: TransparentType>, Iterator(iter) => iter);
199+
200+
deserialize_impl!(Slice<T: TransparentType>, "a sequence of GLib transparent values", Collection(seq) => {
201+
let mut ret = Slice::new();
202+
203+
while let Some(_size @ 1..) = seq.size_hint() {
204+
ret.push(match seq.next_element()? {
205+
Some(item) => item,
206+
None => break,
207+
})
208+
}
209+
210+
Ok(ret)
211+
});
212+
213+
serialize_impl!(List<T: TransparentPtrType>, Iterator(iter) => iter.iter());
214+
215+
deserialize_impl!(List<T: TransparentPtrType>, "a sequence of GLib transparent pointer values", Collection(seq) => {
216+
let mut ret = List::new();
217+
218+
while let Some(_size @ 1..) = seq.size_hint() {
219+
ret.push_front(match seq.next_element()? {
220+
Some(item) => item,
221+
None => break,
222+
})
223+
}
224+
225+
ret.reverse();
226+
227+
Ok(ret)
228+
});
229+
230+
serialize_impl!(SList<T: TransparentPtrType>, Iterator(iter) => iter.iter());
231+
232+
deserialize_impl!(SList<T: TransparentPtrType>, "a sequence of GLib transparent pointer values", Collection(seq) => {
233+
let mut ret = SList::new();
234+
235+
while let Some(_size @ 1..) = seq.size_hint() {
236+
ret.push_front(match seq.next_element()? {
237+
Some(item) => item,
238+
None => break,
239+
})
240+
}
241+
242+
ret.reverse();
243+
244+
Ok(ret)
245+
});
246+
247+
serialize_impl!(StrV, Iterator(iter) => iter);
248+
}

0 commit comments

Comments
 (0)