Skip to content

Commit

Permalink
TryFromRpcValue: Add support for generic structs
Browse files Browse the repository at this point in the history
  • Loading branch information
syyyr committed Jul 10, 2024
1 parent 1514c97 commit c770d29
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 10 deletions.
24 changes: 17 additions & 7 deletions libshvproto-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ fn get_type(ty: &syn::Type) -> Option<String> {
pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::DeriveInput);
let struct_identifier = &input.ident;
let struct_generics = &input.generics;
let struct_generics_without_bounds_vec = input.generics.params.clone().into_iter().map(|generic_param|
if let syn::GenericParam::Type(mut type_param) = generic_param {
type_param.bounds.clear();
syn::GenericParam::Type(type_param)
} else {
generic_param
}).collect::<Vec<_>>();
let struct_generics_without_bounds = quote!(<#(#struct_generics_without_bounds_vec),*>);

match &input.data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
Expand Down Expand Up @@ -75,8 +84,9 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream {
});
}
}

quote!{
impl TryFrom<shvproto::RpcValue> for #struct_identifier {
impl #struct_generics TryFrom<shvproto::RpcValue> for #struct_identifier #struct_generics_without_bounds {
type Error = String;
fn try_from(value: shvproto::RpcValue) -> Result<Self, Self::Error> {
if let shvproto::Value::Map(value) = value.value() {
Expand All @@ -87,7 +97,7 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream {
}
}

impl TryFrom<&shvproto::RpcValue> for #struct_identifier {
impl #struct_generics TryFrom<&shvproto::RpcValue> for #struct_identifier #struct_generics_without_bounds {
type Error = String;
fn try_from(value: &shvproto::RpcValue) -> Result<Self, Self::Error> {
if let shvproto::Value::Map(value) = value.value() {
Expand All @@ -98,14 +108,14 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream {
}
}

impl TryFrom<&Box<shvproto::Map>> for #struct_identifier {
impl #struct_generics TryFrom<&Box<shvproto::Map>> for #struct_identifier #struct_generics_without_bounds {
type Error = String;
fn try_from(value: &Box<shvproto::Map>) -> Result<Self, Self::Error> {
value.as_ref().try_into()
}
}

impl TryFrom<&shvproto::Map> for #struct_identifier {
impl #struct_generics TryFrom<&shvproto::Map> for #struct_identifier #struct_generics_without_bounds {
type Error = String;
fn try_from(value: &shvproto::Map) -> Result<Self, Self::Error> {
let get_key = |key_name| value.get(key_name).ok_or_else(|| "Missing ".to_string() + key_name + " key");
Expand All @@ -115,15 +125,15 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream {
}
}

impl TryFrom<shvproto::Map> for #struct_identifier {
impl #struct_generics TryFrom<shvproto::Map> for #struct_identifier #struct_generics_without_bounds {
type Error = String;
fn try_from(value: shvproto::Map) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}

impl From<#struct_identifier> for shvproto::RpcValue {
fn from(value: #struct_identifier) -> Self {
impl #struct_generics From<#struct_identifier #struct_generics_without_bounds> for shvproto::RpcValue {
fn from(value: #struct_identifier #struct_generics_without_bounds) -> Self {
let mut map = shvproto::rpcvalue::Map::new();
#rpcvalue_inserts
map.into()
Expand Down
19 changes: 16 additions & 3 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
mod test {
use std::collections::BTreeMap;

use libshvproto_macros::TryFromRpcValue;
use shvproto::RpcValue;
use shvproto::{RpcValue, TryFromRpcValue};

#[derive(Clone,Debug,PartialEq,TryFromRpcValue)]
pub struct EmptyStruct {
Expand Down Expand Up @@ -71,7 +70,7 @@ mod test {
"mapIntField" => [("aaa".to_string(), 111)].into_iter().collect::<BTreeMap<_,_>>(),
"imapField" => [(420, 111)].into_iter().collect::<BTreeMap<_,_>>(),
).into();

let y: TestStruct = x.clone().try_into().expect("Failed to parse");
assert_eq!(x, y.into());
}
Expand Down Expand Up @@ -135,4 +134,18 @@ mod test {
let input: RpcValue = String::new().into();
let _output: EnumWithUserStruct = input.try_into().expect("Expected failure");
}

#[derive(Clone,Debug,PartialEq,TryFromRpcValue)]
pub struct GenericStruct<T: Into<RpcValue> + for<'a> TryFrom<&'a RpcValue, Error = String>> {
x: T
}

#[test]
fn generic_struct() {
let int_struct_in: GenericStruct::<i64> = shvproto::make_map!("x" => 123).try_into().expect("Failed to parse");
let int_struct_rpcvalue: RpcValue = int_struct_in.clone().into();
let int_struct_out: GenericStruct::<i64> = int_struct_rpcvalue.clone().try_into().expect("Failed to parse");
assert_eq!(int_struct_in, int_struct_out);

}
}

0 comments on commit c770d29

Please sign in to comment.