Skip to content

Commit

Permalink
Merge pull request #5 from syyyr/misc-improvements
Browse files Browse the repository at this point in the history
Misc improvements
  • Loading branch information
fvacek authored Jul 8, 2024
2 parents c5540a1 + 8bdcf65 commit ff25cb7
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 56 deletions.
2 changes: 1 addition & 1 deletion libshvproto-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libshvproto-macros"
version = "0.1.0"
version = "0.1.1"
edition = "2021"

[lib]
Expand Down
23 changes: 14 additions & 9 deletions libshvproto-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream {
match &input.data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
let mut struct_initializers = quote!{};
let mut map_initializers = quote!{};
for field in fields {
let identifier = field.ident.as_ref().unwrap();
let field_name = field
.attrs.first()
.and_then(|x| x.meta.require_name_value().ok())
.filter(|x| x.path.is_ident("field_name"))
.map(|x| if let syn::Expr::Lit(expr) = &x.value { expr } else { panic!("Expected a string literal for 'field_name'") })
.map(|x| if let syn::Lit::Str(expr) = &x.lit { expr.value() } else { panic!("Expected a string literal for 'field_name'") })
.and_then(|attr| attr.meta.require_name_value().ok())
.filter(|meta_name_value| meta_name_value.path.is_ident("field_name"))
.map(|meta_name_value| if let syn::Expr::Lit(expr) = &meta_name_value.value { expr } else { panic!("Expected a string literal for 'field_name'") })
.map(|literal| if let syn::Lit::Str(expr) = &literal.lit { expr.value() } else { panic!("Expected a string literal for 'field_name'") })
.unwrap_or_else(|| identifier.to_string().to_case(Case::Camel));

struct_initializers.extend(quote!{
#identifier: get_key(#field_name).and_then(|x| x.try_into())?,
});

map_initializers.extend(quote!{
(#field_name.into(), value.#identifier.into()),
});
}
quote!{
impl TryFrom<shvproto::RpcValue> for #struct_identifier {
Expand Down Expand Up @@ -71,13 +74,15 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream {
impl TryFrom<shvproto::Map> for #struct_identifier {
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");
Ok(Self {
#struct_initializers
})
Self::try_from(&value)
}
}

impl From<#struct_identifier> for shvproto::RpcValue {
fn from(value: #struct_identifier) -> Self {
[#map_initializers].into_iter().collect::<shvproto::rpcvalue::Map>().into()
}
}
}
}
_ => panic!("This macro can only be used on a struct.")
Expand Down
4 changes: 2 additions & 2 deletions src/rpcvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ static EMPTY_METAMAP: OnceLock<MetaMap> = OnceLock::new();

#[macro_export(local_inner_macros)]
macro_rules! make_map {
($( $key: expr => $val: expr ),*) => {{
($( $key: expr => $val: expr ),* $(,)?) => {{
let mut map = $crate::rpcvalue::Map::new();
$( map.insert($key.to_string(), RpcValue::from($val)); )*
$( map.insert($key.to_string(), $crate::RpcValue::from($val)); )*
map
}}
}
Expand Down
50 changes: 6 additions & 44 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,30 @@ mod test {
struct EmptyStruct {
}

impl From<EmptyStruct> for RpcValue {
fn from(_value: EmptyStruct) -> Self {
shvproto::make_map!().into()
}
}

#[derive(Debug,PartialEq,TryFromRpcValue)]
struct OneFieldStruct {
x: i32
}

impl From<OneFieldStruct> for RpcValue {
fn from(value: OneFieldStruct) -> Self {
shvproto::make_map!("x" => value.x).into()
}
}

#[derive(Debug,PartialEq,TryFromRpcValue)]
struct TestStruct {
int_field: i32,
#[field_name = "my_custom_field_name"] int_field_with_custom_field_name: i32,
#[field_name = "myCustomFieldName"] int_field_with_custom_field_name: i32,
string_field: String,
map_field: shvproto::Map,
empty_struct_field: EmptyStruct,
one_field_struct: OneFieldStruct,
vec_int_field: Vec<i32>,
vec_empty_struct_field: Vec<EmptyStruct>,
map_int_field: BTreeMap<String, i32>,
}

impl From<TestStruct> for RpcValue {
fn from(value: TestStruct) -> Self {
shvproto::make_map!(
"intField" => value.int_field,
"my_custom_field_name" => value.int_field_with_custom_field_name,
"stringField" => value.string_field,
"mapField" => value.map_field,
"emptyStructField" => value.empty_struct_field,
"oneFieldStruct" => value.one_field_struct,
"vecIntField" => value.vec_int_field,
"vecEmptyStructField" => value.vec_empty_struct_field,
"mapIntField" => value.map_int_field
).into()
}
imap_field: BTreeMap<i32, i32>,
}

#[test]
fn derive_struct() {
let x: RpcValue = shvproto::make_map!(
"intField" => 123,
"my_custom_field_name" => 1234,
"myCustomFieldName" => 1234,
"stringField" => "some_string",
"mapField" => shvproto::make_map!(
"some_key" => 123
Expand All @@ -68,22 +41,11 @@ mod test {
"oneFieldStruct" => shvproto::make_map!("x" => 4565),
"vecIntField" => vec![1_i32, 2_i32].into_iter().map(RpcValue::from).collect::<Vec<_>>(),
"vecEmptyStructField" => vec![shvproto::make_map!(), shvproto::make_map!()].into_iter().map(RpcValue::from).collect::<Vec<_>>(),
"mapIntField" => [("aaa".to_string(), 111)].into_iter().collect::<BTreeMap<_,_>>()
"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!(y, TestStruct {
int_field: 123,
string_field: "some_string".to_owned(),
int_field_with_custom_field_name: 1234,
map_field: shvproto::make_map!("some_key" => 123),
one_field_struct: OneFieldStruct {x: 4565},
empty_struct_field: EmptyStruct{},
vec_int_field: vec![1_i32, 2_i32],
vec_empty_struct_field: vec![EmptyStruct{}, EmptyStruct{}],
map_int_field: [("aaa".to_string(), 111)].into_iter().collect::<BTreeMap<_,_>>(),
});
assert_eq!(x, y.into());
}

Expand All @@ -92,6 +54,6 @@ mod test {
fn missing_fields() {
let _x: TestStruct = shvproto::make_map!(
"my_custom_field_name" => 1234
).try_into().expect("Asd");
).try_into().expect("Expected parse failure");
}
}

0 comments on commit ff25cb7

Please sign in to comment.