Skip to content

Commit

Permalink
Merge pull request #547 from danieldg/proxy-dynamic-tuples
Browse files Browse the repository at this point in the history
Add SerializeTuple and use it for code generated by #[dbus_proxy]
  • Loading branch information
zeenix authored Jan 27, 2024
2 parents 590ee0c + 5696d5f commit 4dbcb15
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 9 deletions.
4 changes: 2 additions & 2 deletions zbus/src/blocking/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl<'a> Proxy<'a> {
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
R: serde::de::DeserializeOwned + zvariant::Type,
R: for<'d> zvariant::DynamicDeserialize<'d>,
{
block_on(self.inner().call(method_name, body))
}
Expand All @@ -251,7 +251,7 @@ impl<'a> Proxy<'a> {
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
R: serde::de::DeserializeOwned + zvariant::Type,
R: for<'d> zvariant::DynamicDeserialize<'d>,
{
block_on(self.inner().call_with_flags(method_name, flags, body))
}
Expand Down
4 changes: 2 additions & 2 deletions zbus/src/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ impl<'a> Proxy<'a> {
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
R: serde::de::DeserializeOwned + zvariant::Type,
R: for<'d> zvariant::DynamicDeserialize<'d>,
{
let reply = self.call_method(method_name, body).await?;

Expand All @@ -866,7 +866,7 @@ impl<'a> Proxy<'a> {
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
R: serde::de::DeserializeOwned + zvariant::Type,
R: for<'d> zvariant::DynamicDeserialize<'d>,
{
let flags = flags.iter().map(Flags::from).collect::<BitFlags<_>>();
match self
Expand Down
6 changes: 3 additions & 3 deletions zbus_macros/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ fn gen_proxy_method_call(
let object_path: #zbus::zvariant::OwnedObjectPath =
self.0.call(
#method_name,
&(#(#args),*),
&#zbus::zvariant::DynamicTuple((#(#args,)*)),
)
#wait?;
#proxy_path::builder(&self.0.connection())
Expand All @@ -574,11 +574,11 @@ fn gen_proxy_method_call(
// the '()' from the signature that we add and not the actual intended ones.
let arg = &args[0];
quote! {
&(#arg,)
&#zbus::zvariant::DynamicTuple((#arg,))
}
} else {
quote! {
&(#(#args),*)
&#zbus::zvariant::DynamicTuple((#(#args),*))
}
};

Expand Down
11 changes: 10 additions & 1 deletion zbus_macros/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ mod param {
}

mod test {
use zbus::fdo;
use zbus::{
fdo,
zvariant::{OwnedStructure, Structure},
};

#[zbus_macros::dbus_proxy(
assume_defaults = false,
Expand All @@ -34,6 +37,12 @@ mod test {
/// which is useful to pass in a proxy as a param. It serializes it as an `ObjectPath`.
fn some_method<T>(&self, object_path: &T) -> zbus::Result<()>;

/// A call accepting an argument that only implements DynamicType and Serialize.
fn test_dyn_type(&self, arg: Structure<'_>, arg2: u32) -> zbus::Result<()>;

/// A call returning an type that only implements DynamicDeserialize
fn test_dyn_ret(&self) -> zbus::Result<OwnedStructure>;

#[dbus_proxy(name = "CheckRENAMING")]
fn check_renaming(&self) -> zbus::Result<Vec<u8>>;

Expand Down
3 changes: 3 additions & 0 deletions zvariant/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ pub use error::*;
mod r#type;
pub use r#type::*;

mod tuple;
pub use tuple::*;

mod from_value;

mod into_value;
Expand Down
56 changes: 55 additions & 1 deletion zvariant/src/structure.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(unknown_lints)]
use serde::{
de::{DeserializeSeed, Deserializer, SeqAccess, Visitor},
de::{DeserializeSeed, Deserializer, Error, SeqAccess, Visitor},
ser::{Serialize, SerializeTupleStruct, Serializer},
};
use static_assertions::assert_impl_all;
Expand Down Expand Up @@ -405,3 +405,57 @@ fn create_signature_from_fields(fields: &[Value<'_>]) -> Signature<'static> {

Signature::from_string_unchecked(signature)
}

/// Owned [`Structure`]
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct OwnedStructure(pub Structure<'static>);

/// Use this to deserialize an [`OwnedStructure`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OwnedStructureSeed(Signature<'static>);

impl DynamicType for OwnedStructure {
fn dynamic_signature(&self) -> Signature<'_> {
self.0.dynamic_signature()
}
}

impl DynamicType for OwnedStructureSeed {
fn dynamic_signature(&self) -> Signature<'_> {
self.0.clone()
}
}

impl<'de> DynamicDeserialize<'de> for OwnedStructure {
type Deserializer = OwnedStructureSeed;

fn deserializer_for_signature<S>(signature: S) -> zvariant::Result<Self::Deserializer>
where
S: TryInto<Signature<'de>>,
S::Error: Into<zvariant::Error>,
{
Structure::deserializer_for_signature(signature)
.map(|StructureSeed(s)| OwnedStructureSeed(s.to_owned()))
}
}

impl<'de> DeserializeSeed<'de> for OwnedStructureSeed {
type Value = OwnedStructure;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer
.deserialize_seq(StructureVisitor { signature: self.0 })
.and_then(|s| match s.try_to_owned() {
Ok(s) => Ok(OwnedStructure(s)),
Err(e) => Err(D::Error::custom(e)),
})
}
}

impl Serialize for OwnedStructure {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
161 changes: 161 additions & 0 deletions zvariant/src/tuple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use crate::{
signature_parser::SignatureParser, utils::*, DynamicDeserialize, DynamicType, Signature,
};
use serde::{
de::{Deserialize, DeserializeSeed, Deserializer, Error, Visitor},
Serialize, Serializer,
};
use std::marker::PhantomData;

/// A helper type to serialize or deserialize a tuple whose elements implement [DynamicType] but
/// not [Type].
///
/// This is required because tuples already have an implementation of [DynamicType] via the blanket
/// implementation of [DynamicType] where `T: Type`, but that results in a bound of [Type] on each
/// element, which is stronger than needed for serializing.
///
/// [Type]: trait.Type.html
#[derive(Debug, Copy, Clone)]
pub struct DynamicTuple<T>(pub T);

impl DynamicType for DynamicTuple<()> {
fn dynamic_signature(&self) -> Signature<'_> {
Signature::from_static_str_unchecked("")
}
}

impl<T: Serialize> Serialize for DynamicTuple<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for DynamicTuple<()> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
<()>::deserialize(deserializer).map(DynamicTuple)
}
}

/// A helper type for [DynamicTuple]'s [DynamicDeserialize] implementation.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TupleSeed<'a, T, S> {
sig: Signature<'a>,
seeds: S,
marker: PhantomData<T>,
}

impl<'a, T, S> DynamicType for TupleSeed<'a, T, S> {
fn dynamic_signature(&self) -> Signature<'_> {
self.sig.clone()
}
}

struct TupleVisitor<T, S> {
seeds: S,
marker: PhantomData<T>,
}

macro_rules! tuple_impls {
($($len:expr => ($($n:tt $name:ident)+))+) => {
$(
impl<$($name),+> DynamicType for DynamicTuple<($($name,)+)>
where
$($name: DynamicType,)+
{
fn dynamic_signature(&self) -> Signature<'_> {
let mut sig = String::with_capacity(255);
sig.push(STRUCT_SIG_START_CHAR);
$(
sig.push_str(DynamicType::dynamic_signature(&self.0.$n).as_str());
)+
sig.push(STRUCT_SIG_END_CHAR);

Signature::from_string_unchecked(sig)
}
}

impl<'de, $($name),+> DeserializeSeed<'de> for TupleSeed<'de, ($($name,)+), ($(<$name as DynamicDeserialize<'de>>::Deserializer,)+)>
where
$($name: DynamicDeserialize<'de>,)+
{
type Value = DynamicTuple<($($name,)+)>;

fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
deserializer.deserialize_tuple($len, TupleVisitor { seeds: self.seeds, marker: self.marker })
}
}

impl<'de, $($name),+> Visitor<'de> for TupleVisitor<($($name,)+), ($(<$name as DynamicDeserialize<'de>>::Deserializer,)+)>
where
$($name: DynamicDeserialize<'de>,)+
{
type Value = DynamicTuple<($($name,)+)>;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a tuple")
}

fn visit_seq<V>(self, mut visitor: V) -> Result<DynamicTuple<($($name,)+)>, V::Error>
where
V: serde::de::SeqAccess<'de>,
{
Ok(DynamicTuple(($({
match visitor.next_element_seed(self.seeds.$n) {
Ok(Some(elt)) => elt,
Ok(None) => return Err(V::Error::invalid_length($len, &"")),
Err(e) => return Err(e),
}
},)+)))
}
}

impl<'de, $($name),+> DynamicDeserialize<'de> for DynamicTuple<($($name,)+)>
where
$($name: DynamicDeserialize<'de>,)+
{
type Deserializer = TupleSeed<'de, ($($name,)+), ($(<$name as DynamicDeserialize<'de>>::Deserializer,)+)>;

fn deserializer_for_signature<S>(signature: S) -> zvariant::Result<Self::Deserializer>
where S: TryInto<Signature<'de>>, S::Error: Into<zvariant::Error>
{
let sig = signature.try_into().map_err(Into::into)?;
if !sig.starts_with(zvariant::STRUCT_SIG_START_CHAR) {
return Err(zvariant::Error::IncorrectType);
}
if !sig.ends_with(zvariant::STRUCT_SIG_END_CHAR) {
return Err(zvariant::Error::IncorrectType);
}

let end = sig.len() - 1;
let mut sig_parser = SignatureParser::new(sig.slice(1..end));

let seeds = ($({
let elt_sig = sig_parser.parse_next_signature()?;
$name::deserializer_for_signature(elt_sig)?
},)+);

Ok(TupleSeed { sig, seeds, marker: PhantomData })
}
}
)+
}
}

tuple_impls! {
1 => (0 T0)
2 => (0 T0 1 T1)
3 => (0 T0 1 T1 2 T2)
4 => (0 T0 1 T1 2 T2 3 T3)
5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
}

0 comments on commit 4dbcb15

Please sign in to comment.