From e50ded4442f0d30946ae33fa4e8d53fb0071d75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 4 Jul 2024 13:59:31 +0200 Subject: [PATCH 01/11] Improve TryFromRpcValue Now you can also convert back to RpcValue! --- libshvproto-macros/src/lib.rs | 12 +++++++++++- tests/test.rs | 34 +++------------------------------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index cc9f3b3..20c6eae 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -13,6 +13,7 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { match &input.data { syn::Data::Struct(syn::DataStruct { fields, .. }) => { let mut struct_initializers = quote!{}; + let mut rpcvalue_inserts = quote!{}; for field in fields { let identifier = field.ident.as_ref().unwrap(); let field_name = field @@ -26,7 +27,9 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { struct_initializers.extend(quote!{ #identifier: get_key(#field_name).and_then(|x| x.try_into())?, }); - + rpcvalue_inserts.extend(quote!{ + map.insert(#field_name.into(), value.#identifier.into()); + }); } quote!{ impl TryFrom for #struct_identifier { @@ -78,6 +81,13 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { } } + impl From<#struct_identifier> for RpcValue { + fn from(value: #struct_identifier) -> Self { + let mut map = shvproto::rpcvalue::Map::new(); + #rpcvalue_inserts + map.into() + } + } } } _ => panic!("This macro can only be used on a struct.") diff --git a/tests/test.rs b/tests/test.rs index 6e6b73a..bf0fba5 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -5,28 +5,16 @@ mod test { use libshvproto_macros::TryFromRpcValue; use shvproto::RpcValue; - #[derive(Debug,PartialEq,TryFromRpcValue)] + #[derive(Clone,Debug,PartialEq,TryFromRpcValue)] struct EmptyStruct { } - impl From for RpcValue { - fn from(_value: EmptyStruct) -> Self { - shvproto::make_map!().into() - } - } - - #[derive(Debug,PartialEq,TryFromRpcValue)] + #[derive(Clone,Debug,PartialEq,TryFromRpcValue)] struct OneFieldStruct { x: i32 } - impl From for RpcValue { - fn from(value: OneFieldStruct) -> Self { - shvproto::make_map!("x" => value.x).into() - } - } - - #[derive(Debug,PartialEq,TryFromRpcValue)] + #[derive(Clone,Debug,PartialEq,TryFromRpcValue)] struct TestStruct { int_field: i32, #[field_name = "my_custom_field_name"] int_field_with_custom_field_name: i32, @@ -39,22 +27,6 @@ mod test { map_int_field: BTreeMap, } - impl From 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() - } - } - #[test] fn derive_struct() { let x: RpcValue = shvproto::make_map!( From e5f2d6c95572ef02940d1f5cd370850927f03065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 4 Jul 2024 14:02:12 +0200 Subject: [PATCH 02/11] Fix RpcValue paths --- libshvproto-macros/src/lib.rs | 2 +- src/rpcvalue.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index 20c6eae..4fd670d 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -81,7 +81,7 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { } } - impl From<#struct_identifier> for RpcValue { + impl From<#struct_identifier> for shvproto::RpcValue { fn from(value: #struct_identifier) -> Self { let mut map = shvproto::rpcvalue::Map::new(); #rpcvalue_inserts diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index 988915b..2bd220f 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -25,7 +25,7 @@ static EMPTY_METAMAP: OnceLock = OnceLock::new(); macro_rules! make_map { ($( $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 }} } From e1c07cc65bbd5acec07ce61ad13aa6ae57bbb66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 4 Jul 2024 14:02:49 +0200 Subject: [PATCH 03/11] Fix error message --- tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index bf0fba5..efa6af3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -64,6 +64,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"); } } From f2a13ee647f7b096cf8f1abd25b712e914321e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 4 Jul 2024 14:05:05 +0200 Subject: [PATCH 04/11] make_map: Allow trailing comma --- libshvproto-macros/Cargo.toml | 2 +- src/rpcvalue.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libshvproto-macros/Cargo.toml b/libshvproto-macros/Cargo.toml index a3b5450..893a803 100644 --- a/libshvproto-macros/Cargo.toml +++ b/libshvproto-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libshvproto-macros" -version = "0.1.0" +version = "0.1.1" edition = "2021" [lib] diff --git a/src/rpcvalue.rs b/src/rpcvalue.rs index 2bd220f..f8fe9c5 100644 --- a/src/rpcvalue.rs +++ b/src/rpcvalue.rs @@ -23,7 +23,7 @@ static EMPTY_METAMAP: OnceLock = 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(), $crate::RpcValue::from($val)); )* map From 067bc335366d7a03c14446364a3256469de58b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 4 Jul 2024 15:11:35 +0200 Subject: [PATCH 05/11] Rmove code redundancy --- libshvproto-macros/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index 4fd670d..9e7c434 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -74,10 +74,7 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { impl TryFrom for #struct_identifier { type Error = String; fn try_from(value: shvproto::Map) -> Result { - 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) } } From add78fb4d2b2292ad262d6f5941088440a964f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Thu, 4 Jul 2024 15:56:10 +0200 Subject: [PATCH 06/11] Improve variable names --- libshvproto-macros/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index 9e7c434..74cb653 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -18,10 +18,10 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { 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!{ From 8b3b2fbb4ad480060c063f9f4ee7537860515a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Mon, 8 Jul 2024 15:15:26 +0200 Subject: [PATCH 07/11] Remove unnecessary Clone derivations --- tests/test.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index efa6af3..5c10c8d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -5,16 +5,16 @@ mod test { use libshvproto_macros::TryFromRpcValue; use shvproto::RpcValue; - #[derive(Clone,Debug,PartialEq,TryFromRpcValue)] + #[derive(Debug,PartialEq,TryFromRpcValue)] struct EmptyStruct { } - #[derive(Clone,Debug,PartialEq,TryFromRpcValue)] + #[derive(Debug,PartialEq,TryFromRpcValue)] struct OneFieldStruct { x: i32 } - #[derive(Clone,Debug,PartialEq,TryFromRpcValue)] + #[derive(Debug,PartialEq,TryFromRpcValue)] struct TestStruct { int_field: i32, #[field_name = "my_custom_field_name"] int_field_with_custom_field_name: i32, From a5b159c3d22ad007a0ab4976237472c34b7390b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Mon, 8 Jul 2024 15:15:44 +0200 Subject: [PATCH 08/11] TryFromRpcValue: Add IMap test --- tests/test.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 5c10c8d..46b08ff 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -25,6 +25,7 @@ mod test { vec_int_field: Vec, vec_empty_struct_field: Vec, map_int_field: BTreeMap, + imap_field: BTreeMap, } #[test] @@ -40,7 +41,8 @@ mod test { "oneFieldStruct" => shvproto::make_map!("x" => 4565), "vecIntField" => vec![1_i32, 2_i32].into_iter().map(RpcValue::from).collect::>(), "vecEmptyStructField" => vec![shvproto::make_map!(), shvproto::make_map!()].into_iter().map(RpcValue::from).collect::>(), - "mapIntField" => [("aaa".to_string(), 111)].into_iter().collect::>() + "mapIntField" => [("aaa".to_string(), 111)].into_iter().collect::>(), + "imapField" => [(420, 111)].into_iter().collect::>(), ).into(); let y: TestStruct = x.clone().try_into().expect("Failed to parse"); @@ -55,6 +57,7 @@ mod test { 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::>(), + imap_field: [(420, 111)].into_iter().collect::>(), }); assert_eq!(x, y.into()); } From 75f2f7e75a5bb766be3a4582d274abcddde8c3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Mon, 8 Jul 2024 15:17:05 +0200 Subject: [PATCH 09/11] TryFromRpcValue: Remove explicit test --- tests/test.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 46b08ff..35cc0d2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -46,19 +46,6 @@ mod test { ).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::>(), - imap_field: [(420, 111)].into_iter().collect::>(), - }); assert_eq!(x, y.into()); } From 6d7218cf9d7cacdde44fc900f7a8edb240447acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Mon, 8 Jul 2024 15:44:18 +0200 Subject: [PATCH 10/11] TryFromRpcValue: Change custom field name The previous one was too confusing because of the snake_case. --- tests/test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 35cc0d2..9fdfa6f 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -17,7 +17,7 @@ mod test { #[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, @@ -32,7 +32,7 @@ mod 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 From 8bdcf65394d08603f5a979fcd4e7260e9ef312e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Kubern=C3=A1t?= Date: Mon, 8 Jul 2024 15:50:17 +0200 Subject: [PATCH 11/11] TryFromRpcValue: Use tuples to initialize map --- libshvproto-macros/src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libshvproto-macros/src/lib.rs b/libshvproto-macros/src/lib.rs index 74cb653..199199b 100644 --- a/libshvproto-macros/src/lib.rs +++ b/libshvproto-macros/src/lib.rs @@ -13,7 +13,7 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { match &input.data { syn::Data::Struct(syn::DataStruct { fields, .. }) => { let mut struct_initializers = quote!{}; - let mut rpcvalue_inserts = quote!{}; + let mut map_initializers = quote!{}; for field in fields { let identifier = field.ident.as_ref().unwrap(); let field_name = field @@ -27,8 +27,8 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { struct_initializers.extend(quote!{ #identifier: get_key(#field_name).and_then(|x| x.try_into())?, }); - rpcvalue_inserts.extend(quote!{ - map.insert(#field_name.into(), value.#identifier.into()); + map_initializers.extend(quote!{ + (#field_name.into(), value.#identifier.into()), }); } quote!{ @@ -80,9 +80,7 @@ pub fn derive_from_rpcvalue(item: TokenStream) -> TokenStream { impl From<#struct_identifier> for shvproto::RpcValue { fn from(value: #struct_identifier) -> Self { - let mut map = shvproto::rpcvalue::Map::new(); - #rpcvalue_inserts - map.into() + [#map_initializers].into_iter().collect::().into() } } }