Skip to content

Commit

Permalink
glib: Add optional support for serialization and deserialization with…
Browse files Browse the repository at this point in the history
… serde

This feature is gated as `serde`

Supports both serialization and deserialization:
- glib::Bytes
- glib::GString
- glib::PtrSlice<_>
- glib::Slice<_>
- glib::List<_>
- glib::SList<_>

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 + _>
  • Loading branch information
rmnscnce committed Apr 5, 2023
1 parent 7f9a20b commit 3479d6a
Show file tree
Hide file tree
Showing 3 changed files with 365 additions and 0 deletions.
2 changes: 2 additions & 0 deletions glib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ smallvec = "1.0"
thiserror = "1"
gio_ffi = { package = "gio-sys", path = "../gio/sys", optional = true }
memchr = "2.5.0"
serde = { version = "1.0", optional = true }

[dev-dependencies]
tempfile = "3"
Expand All @@ -59,6 +60,7 @@ log_macros = ["log"]
dox = ["ffi/dox", "gobject_ffi/dox", "log_macros"]
compiletests = []
gio = ["gio_ffi"]
serde = ["dep:serde"]

[package.metadata.docs.rs]
features = ["dox"]
Expand Down
3 changes: 3 additions & 0 deletions glib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ pub use self::thread_pool::{ThreadHandle, ThreadPool};

pub mod thread_guard;

#[cfg(feature = "serde")]
mod serde;

// rustdoc-stripper-ignore-next
/// This is the log domain used by the [`clone!`][crate::clone!] macro. If you want to use a custom
/// logger (it prints to stdout by default), you can set your own logger using the corresponding
Expand Down
360 changes: 360 additions & 0 deletions glib/src/serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
use serde::{
de::{self, Visitor},
Deserialize, Serialize,
};

mod byte_array {
use crate::ByteArray;

use super::*;

impl Serialize for ByteArray {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(self)
}
}
}

mod bytes {
use crate::Bytes;

use super::*;

impl Serialize for Bytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(self)
}
}

struct BytesVisitor;

impl<'a> Visitor<'a> for BytesVisitor {
type Value = Bytes;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an array of bytes")
}

fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Bytes::from_owned(v.to_owned()))
}

fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Bytes::from_owned(v))
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'a>,
{
let mut byte_vec = vec![];

while let Some(_size @ 1..) = seq.size_hint() {
match seq.next_element()? {
Some(byte) => byte_vec.push(byte),
None => break,
}
}

Ok(Bytes::from_owned(byte_vec))
}
}

impl<'de> Deserialize<'de> for Bytes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_seq(BytesVisitor)
}
}
}

mod gstring {
use super::*;

use std::fmt;

use crate::{GStr, GString, GStringPtr};

impl Serialize for GStr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}

impl Serialize for GString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}

struct GStringVisitor;

impl<'a> Visitor<'a> for GStringVisitor {
type Value = GString;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a GString")
}

fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
GString::from_string_checked(v).map_err(|e| de::Error::custom(e))
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(GString::from(v))
}
}

impl<'de> Deserialize<'de> for GString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
// String to GString conversion is pretty much a zero-alloc op, so this gets used instead
deserializer.deserialize_string(GStringVisitor)
}
}

impl Serialize for GStringPtr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.to_str())
}
}
}

mod collections {
use super::*;

use crate::{
translate::{TransparentPtrType, TransparentType},
List, PtrSlice, SList, Slice, StrV,
};

impl<T: Serialize + TransparentPtrType> Serialize for PtrSlice<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(self)
}
}

struct PtrSliceVisitor<'v, T: Deserialize<'v> + TransparentPtrType>(
std::marker::PhantomData<&'v T>,
);

impl<'a, T: Deserialize<'a> + TransparentPtrType> Visitor<'a> for PtrSliceVisitor<'a, T> {
type Value = PtrSlice<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sequence of GLib transparent pointer values")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'a>,
{
let mut ret = PtrSlice::new();

while let Some(_size @ 1..) = seq.size_hint() {
ret.push(match seq.next_element()? {
Some(item) => item,
None => break,
})
}

Ok(ret)
}
}

impl<'de: 'a, 'a: 'de, T: 'de + Deserialize<'a> + TransparentPtrType> Deserialize<'de>
for PtrSlice<T>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_seq(PtrSliceVisitor(Default::default()))
}
}

impl<T: Serialize + TransparentType> Serialize for Slice<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(self)
}
}

struct SliceVisitor<'v, T: Deserialize<'v>>(std::marker::PhantomData<&'v T>);

impl<'a, T: Deserialize<'a> + TransparentType> Visitor<'a> for SliceVisitor<'a, T> {
type Value = Slice<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sequence of GLib transparent values")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'a>,
{
let mut ret = Slice::new();

while let Some(_size @ 1..) = seq.size_hint() {
ret.push(match seq.next_element()? {
Some(item) => item,
None => break,
})
}

Ok(ret)
}
}

impl<'de: 'a, 'a: 'de, T: 'de + Deserialize<'a> + TransparentType> Deserialize<'de> for Slice<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_seq(SliceVisitor(Default::default()))
}
}

impl<T: Serialize + TransparentPtrType> Serialize for List<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(self.iter())
}
}

struct ListVisitor<'v, T: Deserialize<'v> + TransparentPtrType>(
std::marker::PhantomData<&'v T>,
);

impl<'a, T: Deserialize<'a> + TransparentPtrType> Visitor<'a> for ListVisitor<'a, T> {
type Value = List<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sequence of GLib transparent pointer values")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'a>,
{
let mut ret = List::new();

while let Some(_size @ 1..) = seq.size_hint() {
ret.push_front(match seq.next_element()? {
Some(item) => item,
None => break,
})
}

ret.reverse();

Ok(ret)
}
}

impl<'de: 'a, 'a: 'de, T: 'de + Deserialize<'a> + TransparentPtrType> Deserialize<'de> for List<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_seq(ListVisitor(Default::default()))
}
}

impl<T: Serialize + TransparentPtrType> Serialize for SList<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(self.iter())
}
}

struct SListVisitor<'v, T: Deserialize<'v> + TransparentPtrType>(
std::marker::PhantomData<&'v T>,
);

impl<'a, T: Deserialize<'a> + TransparentPtrType> Visitor<'a> for SListVisitor<'a, T> {
type Value = SList<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a sequence of GLib transparent pointer values")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'a>,
{
let mut ret = SList::new();

while let Some(_size @ 1..) = seq.size_hint() {
ret.push_front(match seq.next_element()? {
Some(item) => item,
None => break,
})
}

ret.reverse();

Ok(ret)
}
}

impl<'de: 'a, 'a: 'de, T: 'de + Deserialize<'a> + TransparentPtrType> Deserialize<'de>
for SList<T>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_seq(SListVisitor(Default::default()))
}
}

impl Serialize for StrV {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(self)
}
}
}

0 comments on commit 3479d6a

Please sign in to comment.