From d056be0b657eb43925a762248258dfa1ecb31427 Mon Sep 17 00:00:00 2001 From: Leonid Tyurin Date: Wed, 22 Oct 2025 13:21:58 +0200 Subject: [PATCH 1/2] feat: use internal types for external messages --- actix-prost-build/src/conversions.rs | 23 ++++++++++++---- actix-prost-build/src/helpers.rs | 39 +++++++++++++++++++++++----- tests/proto/conversions.proto | 3 +++ tests/proto/errors.proto | 9 ++++++- tests/proto/types.proto | 7 ----- tests/proto/types.swagger.yaml | 2 -- tests/src/conversions.rs | 1 + tests/src/proto/conversions.rs | 4 +++ tests/src/proto/errors.rs | 4 +++ tests/src/proto/types.rs | 4 --- 10 files changed, 70 insertions(+), 26 deletions(-) diff --git a/actix-prost-build/src/conversions.rs b/actix-prost-build/src/conversions.rs index 17de22d..5eb2c91 100644 --- a/actix-prost-build/src/conversions.rs +++ b/actix-prost-build/src/conversions.rs @@ -6,7 +6,7 @@ use std::{ rc::Rc, }; -use crate::helpers::extract_type_from_option; +use crate::helpers::{extract_type_from_option, try_construct_external_type_path}; use proc_macro2::{Ident, TokenStream}; use prost_build::Service; use prost_reflect::{ @@ -443,10 +443,23 @@ impl ConversionsGenerator { match extract_type_from_option(&f.ty) { Some(Type::Path(ty)) => { - let ty = ty.path.segments.first()?; - let rust_struct_name = self.messages.get(&ty.ident.to_string())?.ident.clone(); - let new_struct_name = - self.build_internal_nested_struct(m_type, &rust_struct_name, res); + let maybe_local_message = ty + .path + .segments + .first() + .and_then(|s| self.messages.get(&s.ident.to_string())); + + let new_struct_name = if let Some(item_struct) = maybe_local_message { + let rust_struct_name = item_struct.ident.clone(); + let new_struct_name = + self.build_internal_nested_struct(m_type, &rust_struct_name, res); + quote!(#new_struct_name) + } else if let Some(path) = try_construct_external_type_path(&ty.path) { + quote!(#path) + } else { + return None; + }; + let convert = &self.convert_prefix; let (ty, conversion) = match convert_field { Some(ConvertFieldOptions { required: true, .. }) => { diff --git a/actix-prost-build/src/helpers.rs b/actix-prost-build/src/helpers.rs index 6c3e484..6235a20 100644 --- a/actix-prost-build/src/helpers.rs +++ b/actix-prost-build/src/helpers.rs @@ -1,13 +1,6 @@ use syn::{GenericArgument, Path, PathArguments, PathSegment}; pub fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> { - fn extract_type_path(ty: &syn::Type) -> Option<&Path> { - match *ty { - syn::Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path), - _ => None, - } - } - fn extract_option_segment(path: &Path) -> Option<&PathSegment> { let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| { acc.push_str(&v.ident.to_string()); @@ -35,3 +28,35 @@ pub fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> { _ => None, }) } + +pub fn try_construct_external_type_path(path: &Path) -> Option { + let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| { + acc.push_str(&v.ident.to_string()); + acc.push('|'); + acc + }); + + // ignore conventional external imported types + let ignore_segments = ["super|google|protobuf|"]; + if ignore_segments.iter().any(|s| idents_of_path.contains(s)) { + return None; + } + + if !idents_of_path.starts_with("super|") { + return None; + } + + let mut new_path = path.clone(); + + let ty = new_path.segments.last_mut()?; + ty.ident = quote::format_ident!("{}Internal", ty.ident); + + Some(new_path) +} + +fn extract_type_path(ty: &syn::Type) -> Option<&Path> { + match *ty { + syn::Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path), + _ => None, + } +} diff --git a/tests/proto/conversions.proto b/tests/proto/conversions.proto index 7b63364..f78b3bc 100644 --- a/tests/proto/conversions.proto +++ b/tests/proto/conversions.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package conversions; import "convert_options.proto"; +import "errors.proto"; option go_package = "github.com/blockscout/actix-prost/tests"; @@ -95,6 +96,8 @@ message ConversionsResponse { string response_naive_datetime = 8 [ (convert_options.convert) = { type : "chrono::NaiveDateTime" } ]; string response_uuid = 9 [ (convert_options.convert) = { type : "uuid::Uuid" } ]; string response_decimal = 10 [ (convert_options.convert) = { type : "rust_decimal::Decimal" } ]; + + errors.ErrorResponse error = 11; } diff --git a/tests/proto/errors.proto b/tests/proto/errors.proto index cb35422..ddc82b5 100644 --- a/tests/proto/errors.proto +++ b/tests/proto/errors.proto @@ -1,6 +1,8 @@ syntax = "proto3"; package errors; +import "convert_options.proto"; + option go_package = "github.com/blockscout/actix-prost/tests"; service ErrorsRPC { rpc Error(ErrorRequest) returns (ErrorResponse); } @@ -11,4 +13,9 @@ message ErrorRequest { string message = 3; } -message ErrorResponse {} \ No newline at end of file +message ErrorResponse { + option (convert_options.derive) = { name: "serde::Serialize" }; + option (convert_options.derive) = { name: "serde::Deserialize" }; + option (convert_options.derive) = { name: "PartialEq" }; + option (convert_options.derive) = { name: "Eq" }; +} \ No newline at end of file diff --git a/tests/proto/types.proto b/tests/proto/types.proto index 4f0ea37..e00b2c6 100644 --- a/tests/proto/types.proto +++ b/tests/proto/types.proto @@ -38,18 +38,12 @@ message OneOfs { } } -// message Google { -// google.protobuf.Timestamp foo = 1; -// google.protobuf.Any bar = 2; -// } - message Complex { Scalars scalars = 1; Enums enums = 2; Repeated repeated = 3; Maps maps = 4; OneOfs oneofs = 5; - // Google google = 6; } service TypesRPC { @@ -59,6 +53,5 @@ service TypesRPC { rpc RepeatedRPC(Repeated) returns (Repeated); rpc MapsRPC(Maps) returns (Maps); rpc OneOfsRPC(OneOfs) returns (OneOfs); - // rpc GoogleRPC(Google) returns (Google); rpc ComplexRPC(Complex) returns (Complex); } diff --git a/tests/proto/types.swagger.yaml b/tests/proto/types.swagger.yaml index 62a192f..7d1fad1 100644 --- a/tests/proto/types.swagger.yaml +++ b/tests/proto/types.swagger.yaml @@ -11,7 +11,6 @@ produces: paths: /types/complex: post: - summary: rpc GoogleRPC(Google) returns (Google); operationId: TypesRPC_ComplexRPC responses: "200": @@ -183,7 +182,6 @@ definitions: $ref: '#/definitions/typesMaps' oneofs: $ref: '#/definitions/typesOneOfs' - title: Google google = 6; typesEnums: type: object properties: diff --git a/tests/src/conversions.rs b/tests/src/conversions.rs index 90c31a5..16d85ec 100644 --- a/tests/src/conversions.rs +++ b/tests/src/conversions.rs @@ -39,6 +39,7 @@ impl ConversionsRpc for ConversionsServer { response_naive_datetime: internal_request.naive_datetime, response_uuid: internal_request.uuid_field, response_decimal: internal_request.decimal_field, + error: None, }; let response = ConversionsResponse::try_convert(internal_response) diff --git a/tests/src/proto/conversions.rs b/tests/src/proto/conversions.rs index f355a3d..5d7255e 100644 --- a/tests/src/proto/conversions.rs +++ b/tests/src/proto/conversions.rs @@ -121,6 +121,8 @@ pub struct ConversionsResponse { pub response_uuid: ::prost::alloc::string::String, #[prost(string, tag = "10")] pub response_decimal: ::prost::alloc::string::String, + #[prost(message, optional, tag = "11")] + pub error: ::core::option::Option, } #[actix_prost_macros::serde] #[derive(Clone, Copy, PartialEq, ::prost::Message)] @@ -409,6 +411,7 @@ pub struct ConversionsResponseInternal { pub response_naive_datetime: chrono::NaiveDateTime, pub response_uuid: uuid::Uuid, pub response_decimal: rust_decimal::Decimal, + pub error: ::core::option::Option, } impl convert_trait::TryConvert for ConversionsResponse { fn try_convert(from: ConversionsResponseInternal) -> Result { @@ -430,6 +433,7 @@ impl convert_trait::TryConvert for ConversionsRespo response_decimal: convert_trait::TryConvert::try_convert( from.response_decimal, )?, + error: convert_trait::TryConvert::try_convert(from.error)?, }) } } diff --git a/tests/src/proto/errors.rs b/tests/src/proto/errors.rs index 71b81dc..4d97306 100644 --- a/tests/src/proto/errors.rs +++ b/tests/src/proto/errors.rs @@ -108,6 +108,10 @@ impl convert_trait::TryConvert for ErrorRequestInternal { }) } } +#[derive(serde::Serialize)] +#[derive(serde::Deserialize)] +#[derive(PartialEq)] +#[derive(Eq)] #[derive(Clone, Debug)] pub struct ErrorResponseInternal {} impl convert_trait::TryConvert for ErrorResponse { diff --git a/tests/src/proto/types.rs b/tests/src/proto/types.rs index 5edbe54..a3e574c 100644 --- a/tests/src/proto/types.rs +++ b/tests/src/proto/types.rs @@ -75,7 +75,6 @@ pub struct Complex { pub repeated: ::core::option::Option, #[prost(message, optional, tag = "4")] pub maps: ::core::option::Option, - /// Google google = 6; #[prost(message, optional, tag = "5")] pub oneofs: ::core::option::Option, } @@ -175,7 +174,6 @@ pub mod types_rpc_actix { pub repeated: ::core::option::Option, #[prost(message, optional, tag = "4")] pub maps: ::core::option::Option, - /// Google google = 6; #[prost(message, optional, tag = "5")] pub oneofs: ::core::option::Option, } @@ -762,7 +760,6 @@ pub mod types_rpc_client { req.extensions_mut().insert(GrpcMethod::new("types.TypesRPC", "OneOfsRPC")); self.inner.unary(req, path, codec).await } - /// rpc GoogleRPC(Google) returns (Google); pub async fn complex_rpc( &mut self, request: impl tonic::IntoRequest, @@ -822,7 +819,6 @@ pub mod types_rpc_server { &self, request: tonic::Request, ) -> std::result::Result, tonic::Status>; - /// rpc GoogleRPC(Google) returns (Google); async fn complex_rpc( &self, request: tonic::Request, From a60ac7e4726df4fd082a1d77e731bae522b27499 Mon Sep 17 00:00:00 2001 From: Leonid Tyurin Date: Wed, 22 Oct 2025 13:26:41 +0200 Subject: [PATCH 2/2] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d0c2b19..7b5e08a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ ] [workspace.package] -version = "0.2.3" +version = "0.2.4" edition = "2021" license = "MIT" repository = "https://github.com/blockscout/actix-prost"