From 5727e2cb9d45e6bbc3a7270585c760055b806169 Mon Sep 17 00:00:00 2001 From: Leonid Tyurin Date: Fri, 15 Mar 2024 19:07:09 +0400 Subject: [PATCH] Add support for repeated messages generation --- actix-prost-build/src/conversions.rs | 26 +++++++++++++ tests/proto/conversions.proto | 46 +++++++++++++++------- tests/src/conversions.rs | 16 +++++--- tests/src/proto/conversions.rs | 57 ++++++++++++++++++++-------- 4 files changed, 111 insertions(+), 34 deletions(-) diff --git a/actix-prost-build/src/conversions.rs b/actix-prost-build/src/conversions.rs index 59bc358..7a8a76a 100644 --- a/actix-prost-build/src/conversions.rs +++ b/actix-prost-build/src/conversions.rs @@ -309,6 +309,32 @@ impl ConversionsGenerator { ) -> Option { self.try_process_option(m_type, f, convert_field, res) .or(self.try_process_map(m_type, f, convert_field, res)) + .or(self.try_process_array(m_type, f, convert_field, res)) + } + + fn try_process_array( + &mut self, + m_type: MessageType, + f: &Field, + convert_field: Option<&ConvertFieldOptions>, + res: &mut Vec, + ) -> Option { + let name = f.ident.as_ref().unwrap(); + + let field_desc = convert_field.map(|cf| &cf.field)?; + let el_type = match (field_desc.cardinality(), field_desc.kind()) { + (Cardinality::Repeated, Kind::Message(m)) if !m.is_map_entry() => Some(m), + _ => None, + }?; + // TODO: Proto name might not be the same as Rust struct name + let rust_struct_name = self.messages.get(el_type.name())?.ident.clone(); + + let new_struct_name = self.build_internal_nested_struct(m_type, &rust_struct_name, res); + + let convert = &self.convert_prefix; + let ty = quote!(::prost::alloc::vec::Vec<#new_struct_name>); + let conversion = quote!(#convert::try_convert(from.#name)?); + Some((ty, conversion)) } fn try_process_option( diff --git a/tests/proto/conversions.proto b/tests/proto/conversions.proto index c6544cb..ab42b0a 100644 --- a/tests/proto/conversions.proto +++ b/tests/proto/conversions.proto @@ -5,34 +5,54 @@ import "convert_options.proto"; option go_package = "github.com/blockscout/actix-prost/tests"; -service ConversionsRPC { rpc ConvertRPC(ConversionsRequest) returns (ConversionsResponse); } - -message Nested { - string address = 3 [ (convert_options.convert) = { type : "ethers::types::Address" } ]; +service ConversionsRPC { + rpc ConvertRPC(ConversionsRequest) returns (ConversionsResponse); } message MapValue { - string address = 1 [ (convert_options.convert) = { type : "ethers::types::Address" } ]; + string address = 1 + [ (convert_options.convert) = {type : "ethers::types::Address"} ]; +} + +message OuterMessage { + string address = 3 + [ (convert_options.convert) = {type : "ethers::types::Address"} ]; +} + +message RepeatedValue { + string address = 1 + [ (convert_options.convert) = {type : "ethers::types::Address"} ]; } message ConversionsRequest { - option (convert_options.extra_fields) = { name: "field1", type: "String" }; - option (convert_options.extra_fields) = { name: "field2", type: "i32" }; + option (convert_options.extra_fields) = { + name : "field1", + type : "String" + }; + option (convert_options.extra_fields) = { + name : "field2", + type : "i32" + }; map map_field = 1; + string query = 2 + [ (convert_options.convert) = {override : "Default::default()"} ]; + repeated string addresses = 3 [ (convert_options.convert) = { + type : "std::collections::HashSet" + } ]; + enum NestedEnum { NESTED_OK = 0; NESTED_ERROR = 1; } - - string query = 2 [ (convert_options.convert) = { override : "Default::default()" } ]; - repeated string addresses = 3 [ (convert_options.convert) = { type : "std::collections::HashSet" } ]; NestedEnum nested_enum = 4; - Nested nested = 5 [ (convert_options.convert) = { required : true } ]; + OuterMessage outer = 5 [ (convert_options.convert) = {required : true} ]; + repeated RepeatedValue repeated = 6; } message ConversionsResponse { - string address = 1 [ (convert_options.convert) = { type : "ethers::types::Address" } ]; - Nested nested = 2; + string address = 1 + [ (convert_options.convert) = {type : "ethers::types::Address"} ]; + OuterMessage outer = 2; map map_field = 3; } diff --git a/tests/src/conversions.rs b/tests/src/conversions.rs index f9a928c..81c1753 100644 --- a/tests/src/conversions.rs +++ b/tests/src/conversions.rs @@ -4,7 +4,7 @@ use crate::{ proto::conversions::{ conversions_rpc_actix::route_conversions_rpc, conversions_rpc_server::ConversionsRpc, ConversionsRequest, ConversionsRequestInternal, ConversionsResponse, - ConversionsResponseInternal, MapValue, Nested, + ConversionsResponseInternal, MapValue, OuterMessage, RepeatedValue, }, test, }; @@ -28,7 +28,7 @@ impl ConversionsRpc for ConversionsServer { let internal_response = ConversionsResponseInternal { address: Address::from_low_u64_be(0), - nested: Some(internal_request.nested), + outer: Some(internal_request.outer), map_field: internal_request.map_field, }; @@ -75,9 +75,12 @@ async fn conversions() { query: "some_string".to_string(), addresses: vec!["".to_string()], nested_enum: 1, - nested: Some(Nested { + outer: Some(OuterMessage { address: "".to_string(), }), + repeated: vec![RepeatedValue { + address: "".to_string(), + }], }; let res = send_post(&addr, "/conversions", serde_json::to_value(req).unwrap()).await; @@ -100,14 +103,17 @@ async fn conversions() { query: "some_string".to_string(), addresses: vec![test_address.clone()], nested_enum: 1, - nested: Some(Nested { + outer: Some(OuterMessage { address: test_address.clone(), }), + repeated: vec![RepeatedValue { + address: test_address.clone(), + }], }; let res = send_post(&addr, "/conversions", serde_json::to_value(req).unwrap()).await; let res: ConversionsResponse = serde_json::from_str(&res).unwrap(); - assert_eq!(res.nested.unwrap().address, test_address); + assert_eq!(res.outer.unwrap().address, test_address); assert_eq!(res.map_field.get("key").unwrap().address, test_address); } diff --git a/tests/src/proto/conversions.rs b/tests/src/proto/conversions.rs index 3e6190e..eee7bdc 100644 --- a/tests/src/proto/conversions.rs +++ b/tests/src/proto/conversions.rs @@ -1,14 +1,21 @@ #[actix_prost_macros::serde] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct Nested { +pub struct MapValue { + #[prost(string, tag = "1")] + pub address: ::prost::alloc::string::String, +} +#[actix_prost_macros::serde] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OuterMessage { #[prost(string, tag = "3")] pub address: ::prost::alloc::string::String, } #[actix_prost_macros::serde] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct MapValue { +pub struct RepeatedValue { #[prost(string, tag = "1")] pub address: ::prost::alloc::string::String, } @@ -25,7 +32,9 @@ pub struct ConversionsRequest { #[prost(enumeration = "conversions_request::NestedEnum", tag = "4")] pub nested_enum: i32, #[prost(message, optional, tag = "5")] - pub nested: ::core::option::Option, + pub outer: ::core::option::Option, + #[prost(message, repeated, tag = "6")] + pub repeated: ::prost::alloc::vec::Vec, } /// Nested message and enum types in `ConversionsRequest`. pub mod conversions_request { @@ -74,7 +83,7 @@ pub struct ConversionsResponse { #[prost(string, tag = "1")] pub address: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] - pub nested: ::core::option::Option, + pub outer: ::core::option::Option, #[prost(map = "string, message", tag = "3")] pub map_field: ::std::collections::HashMap<::prost::alloc::string::String, MapValue>, } @@ -99,7 +108,9 @@ pub mod conversions_rpc_actix { #[prost(enumeration = "conversions_request::NestedEnum", tag = "4")] pub nested_enum: i32, #[prost(message, optional, tag = "5")] - pub nested: ::core::option::Option, + pub outer: ::core::option::Option, + #[prost(message, repeated, tag = "6")] + pub repeated: ::prost::alloc::vec::Vec, } async fn call_convert_rpc( service: ::actix_web::web::Data, @@ -121,7 +132,8 @@ pub mod conversions_rpc_actix { query: json.query, addresses: json.addresses, nested_enum: json.nested_enum, - nested: json.nested, + outer: json.outer, + repeated: json.repeated, }; let request = ::actix_prost::new_request(request, &http_request); let response = service.convert_rpc(request).await?; @@ -148,11 +160,22 @@ impl convert_trait::TryConvert for MapValueInternal { } } #[derive(Debug)] -pub struct NestedInternal { +pub struct OuterMessageInternal { + pub address: ethers::types::Address, +} +impl convert_trait::TryConvert for OuterMessageInternal { + fn try_convert(from: OuterMessage) -> Result { + Ok(Self { + address: convert_trait::TryConvert::try_convert(from.address)?, + }) + } +} +#[derive(Debug)] +pub struct RepeatedValueInternal { pub address: ethers::types::Address, } -impl convert_trait::TryConvert for NestedInternal { - fn try_convert(from: Nested) -> Result { +impl convert_trait::TryConvert for RepeatedValueInternal { + fn try_convert(from: RepeatedValue) -> Result { Ok(Self { address: convert_trait::TryConvert::try_convert(from.address)?, }) @@ -167,7 +190,8 @@ pub struct ConversionsRequestInternal { pub query: ::prost::alloc::string::String, pub addresses: std::collections::HashSet, pub nested_enum: conversions_request::NestedEnum, - pub nested: NestedInternal, + pub outer: OuterMessageInternal, + pub repeated: ::prost::alloc::vec::Vec, pub field1: Option, pub field2: Option, } @@ -178,16 +202,17 @@ impl convert_trait::TryConvert for ConversionsRequestInterna query: Default::default(), addresses: convert_trait::TryConvert::try_convert(from.addresses)?, nested_enum: conversions_request::NestedEnum::try_from(from.nested_enum)?, - nested: convert_trait::TryConvert::try_convert( - from.nested.ok_or("field nested is required")?, + outer: convert_trait::TryConvert::try_convert( + from.outer.ok_or("field outer is required")?, )?, + repeated: convert_trait::TryConvert::try_convert(from.repeated)?, field1: None, field2: None, }) } } -impl convert_trait::TryConvert for Nested { - fn try_convert(from: NestedInternal) -> Result { +impl convert_trait::TryConvert for OuterMessage { + fn try_convert(from: OuterMessageInternal) -> Result { Ok(Self { address: convert_trait::TryConvert::try_convert(from.address)?, }) @@ -203,7 +228,7 @@ impl convert_trait::TryConvert for MapValue { #[derive(Debug)] pub struct ConversionsResponseInternal { pub address: ethers::types::Address, - pub nested: ::core::option::Option, + pub outer: ::core::option::Option, pub map_field: ::std::collections::HashMap< ::prost::alloc::string::String, MapValueInternal, @@ -213,7 +238,7 @@ impl convert_trait::TryConvert for ConversionsRespo fn try_convert(from: ConversionsResponseInternal) -> Result { Ok(Self { address: convert_trait::TryConvert::try_convert(from.address)?, - nested: convert_trait::TryConvert::try_convert(from.nested)?, + outer: convert_trait::TryConvert::try_convert(from.outer)?, map_field: convert_trait::TryConvert::try_convert(from.map_field)?, }) }