1- use std :: fmt :: Write ;
1+ use quote :: { format_ident , quote } ;
22
33/// Generates twirp services for protobuf rpc service definitions.
44///
@@ -11,123 +11,205 @@ pub fn service_generator() -> Box<ServiceGenerator> {
1111 Box :: new ( ServiceGenerator { } )
1212}
1313
14+ struct Service {
15+ /// The name of the server trait, as parsed into a Rust identifier.
16+ server_name : syn:: Ident ,
17+
18+ /// The name of the client trait, as parsed into a Rust identifier.
19+ client_name : syn:: Ident ,
20+
21+ /// The fully qualified protobuf name of this Service.
22+ fqn : String ,
23+
24+ /// The methods that make up this service.
25+ methods : Vec < Method > ,
26+ }
27+
28+ struct Method {
29+ /// The name of the method, as parsed into a Rust identifier.
30+ name : syn:: Ident ,
31+
32+ /// The name of the method as it appears in the protobuf definition.
33+ proto_name : String ,
34+
35+ /// The input type of this method.
36+ input_type : syn:: Type ,
37+
38+ /// The output type of this method.
39+ output_type : syn:: Type ,
40+ }
41+
42+ impl Service {
43+ fn from_prost ( s : prost_build:: Service ) -> Self {
44+ let fqn = format ! ( "{}.{}" , s. package, s. proto_name) ;
45+ let server_name = format_ident ! ( "{}" , & s. name) ;
46+ let client_name = format_ident ! ( "{}Client" , & s. name) ;
47+ let methods = s
48+ . methods
49+ . into_iter ( )
50+ . map ( |m| Method :: from_prost ( & s. package , & s. proto_name , m) )
51+ . collect ( ) ;
52+
53+ Self {
54+ server_name,
55+ client_name,
56+ fqn,
57+ methods,
58+ }
59+ }
60+ }
61+
62+ impl Method {
63+ fn from_prost ( pkg_name : & str , svc_name : & str , m : prost_build:: Method ) -> Self {
64+ let as_type = |s| -> syn:: Type {
65+ let Ok ( typ) = syn:: parse_str :: < syn:: Type > ( s) else {
66+ panic ! (
67+ "twirp-build failed generated invalid Rust while processing {pkg}.{svc}/{name}). this is a bug in twirp-build, please file a GitHub issue" ,
68+ pkg = pkg_name,
69+ svc = svc_name,
70+ name = m. proto_name,
71+ ) ;
72+ } ;
73+ typ
74+ } ;
75+
76+ let input_type = as_type ( & m. input_type ) ;
77+ let output_type = as_type ( & m. output_type ) ;
78+ let name = format_ident ! ( "{}" , m. name) ;
79+ let message = m. proto_name ;
80+
81+ Self {
82+ name,
83+ proto_name : message,
84+ input_type,
85+ output_type,
86+ }
87+ }
88+ }
89+
1490pub struct ServiceGenerator ;
1591
1692impl prost_build:: ServiceGenerator for ServiceGenerator {
1793 fn generate ( & mut self , service : prost_build:: Service , buf : & mut String ) {
18- let service_name = service. name ;
19- let service_fqn = format ! ( "{}.{}" , service. package, service. proto_name) ;
20- writeln ! ( buf) . unwrap ( ) ;
94+ let service = Service :: from_prost ( service) ;
2195
22- writeln ! ( buf, "pub use twirp;" ) . unwrap ( ) ;
23- writeln ! ( buf) . unwrap ( ) ;
24- writeln ! ( buf, "pub const SERVICE_FQN: &str = \" /{service_fqn}\" ;" ) . unwrap ( ) ;
25-
26- //
2796 // generate the twirp server
28- //
29- writeln ! ( buf, "#[twirp::async_trait::async_trait]" ) . unwrap ( ) ;
30- writeln ! ( buf, "pub trait {} {{" , service_name) . unwrap ( ) ;
31- writeln ! ( buf, " type Error;" ) . unwrap ( ) ;
32- for m in & service. methods {
33- writeln ! (
34- buf,
35- " async fn {}(&self, ctx: twirp::Context, req: {}) -> Result<{}, Self::Error>;" ,
36- m. name, m. input_type, m. output_type,
37- )
38- . unwrap ( ) ;
39- }
40- writeln ! ( buf, "}}" ) . unwrap ( ) ;
41-
42- writeln ! ( buf, "#[twirp::async_trait::async_trait]" ) . unwrap ( ) ;
43- writeln ! ( buf, "impl<T> {service_name} for std::sync::Arc<T>" ) . unwrap ( ) ;
44- writeln ! ( buf, "where" ) . unwrap ( ) ;
45- writeln ! ( buf, " T: {service_name} + Sync + Send" ) . unwrap ( ) ;
46- writeln ! ( buf, "{{" ) . unwrap ( ) ;
47- writeln ! ( buf, " type Error = T::Error;\n " ) . unwrap ( ) ;
97+ let mut trait_methods = Vec :: with_capacity ( service. methods . len ( ) ) ;
98+ let mut proxy_methods = Vec :: with_capacity ( service. methods . len ( ) ) ;
4899 for m in & service. methods {
49- writeln ! (
50- buf,
51- " async fn {}(&self, ctx: twirp::Context, req: {}) -> Result<{}, Self::Error> {{" ,
52- m. name, m. input_type, m. output_type,
53- )
54- . unwrap ( ) ;
55- writeln ! ( buf, " T::{}(&*self, ctx, req).await" , m. name) . unwrap ( ) ;
56- writeln ! ( buf, " }}" ) . unwrap ( ) ;
100+ let name = & m. name ;
101+ let input_type = & m. input_type ;
102+ let output_type = & m. output_type ;
103+
104+ trait_methods. push ( quote ! {
105+ async fn #name( & self , ctx: twirp:: Context , req: #input_type) -> Result <#output_type, Self :: Error >;
106+ } ) ;
107+
108+ proxy_methods. push ( quote ! {
109+ async fn #name( & self , ctx: twirp:: Context , req: #input_type) -> Result <#output_type, Self :: Error > {
110+ T :: #name( & * self , ctx, req) . await
111+ }
112+ } ) ;
57113 }
58- writeln ! ( buf, "}}" ) . unwrap ( ) ;
59-
60- // add_service
61- writeln ! (
62- buf,
63- r#"pub fn router<T>(api: T) -> twirp::Router
64- where
65- T: {service_name} + Clone + Send + Sync + 'static,
66- <T as {service_name}>::Error: twirp::IntoTwirpResponse,
67- {{
68- twirp::details::TwirpRouterBuilder::new(api)"# ,
69- )
70- . unwrap ( ) ;
114+
115+ let server_name = & service. server_name ;
116+ let server_trait = quote ! {
117+ #[ twirp:: async_trait:: async_trait]
118+ pub trait #server_name {
119+ type Error ;
120+
121+ #( #trait_methods) *
122+ }
123+
124+ #[ twirp:: async_trait:: async_trait]
125+ impl <T > #server_name for std:: sync:: Arc <T >
126+ where
127+ T : #server_name + Sync + Send
128+ {
129+ type Error = T :: Error ;
130+
131+ #( #proxy_methods) *
132+ }
133+ } ;
134+
135+ // generate the router
136+ let mut route_calls = Vec :: with_capacity ( service. methods . len ( ) ) ;
71137 for m in & service. methods {
72- let uri = & m. proto_name ;
73- let req_type = & m. input_type ;
74- let rust_method_name = & m. name ;
75- writeln ! (
76- buf,
77- r#" .route("/{uri}", |api: T, ctx: twirp::Context, req: {req_type}| async move {{
78- api.{rust_method_name}(ctx, req).await
79- }})"# ,
80- )
81- . unwrap ( ) ;
138+ let name = & m. name ;
139+ let input_type = & m. input_type ;
140+ let path = format ! ( "/{uri}" , uri = m. proto_name) ;
141+
142+ route_calls. push ( quote ! {
143+ . route( #path, |api: T , ctx: twirp:: Context , req: #input_type| async move {
144+ api. #name( ctx, req) . await
145+ } )
146+ } ) ;
82147 }
83- writeln ! (
84- buf,
85- r#"
86- .build()
87- }}"#
88- )
89- . unwrap ( ) ;
148+ let router = quote ! {
149+ pub fn router<T >( api: T ) -> twirp:: Router
150+ where
151+ T : #server_name + Clone + Send + Sync + ' static ,
152+ <T as #server_name>:: Error : twirp:: IntoTwirpResponse
153+ {
154+ twirp:: details:: TwirpRouterBuilder :: new( api)
155+ #( #route_calls) *
156+ . build( )
157+ }
158+ } ;
90159
91160 //
92161 // generate the twirp client
93162 //
94- writeln ! ( buf) . unwrap ( ) ;
95- writeln ! ( buf, "#[twirp::async_trait::async_trait]" ) . unwrap ( ) ;
96- writeln ! ( buf, "pub trait {service_name}Client: Send + Sync {{" , ) . unwrap ( ) ;
97- for m in & service. methods {
98- // Define: <METHOD>
99- writeln ! (
100- buf,
101- " async fn {}(&self, req: {}) -> Result<{}, twirp::ClientError>;" ,
102- m. name, m. input_type, m. output_type,
103- )
104- . unwrap ( ) ;
105- }
106- writeln ! ( buf, "}}" ) . unwrap ( ) ;
107-
108- // Implement the rpc traits for: `twirp::client::Client`
109- writeln ! ( buf, "#[twirp::async_trait::async_trait]" ) . unwrap ( ) ;
110- writeln ! (
111- buf,
112- "impl {service_name}Client for twirp::client::Client {{" ,
113- )
114- . unwrap ( ) ;
163+
164+ let client_name = service. client_name ;
165+ let mut client_trait_methods = Vec :: with_capacity ( service. methods . len ( ) ) ;
166+ let mut client_methods = Vec :: with_capacity ( service. methods . len ( ) ) ;
115167 for m in & service. methods {
116- // Define the rpc `<METHOD>`
117- writeln ! (
118- buf ,
119- " async fn {}(&self, req: {}) -> Result<{}, twirp::ClientError> {{" ,
120- m . name , m . input_type , m . output_type ,
121- )
122- . unwrap ( ) ;
123- writeln ! (
124- buf ,
125- r#" self.request("{}/{}", req).await"# ,
126- service_fqn , m . proto_name
127- )
128- . unwrap ( ) ;
129- writeln ! ( buf , " }}" ) . unwrap ( ) ;
168+ let name = & m . name ;
169+ let input_type = & m . input_type ;
170+ let output_type = & m . output_type ;
171+ let request_path = format ! ( "{}/{}" , service . fqn , m . proto_name ) ;
172+
173+ client_trait_methods . push ( quote ! {
174+ async fn #name ( & self , req : #input_type ) -> Result <#output_type , twirp :: ClientError > ;
175+ } ) ;
176+
177+ client_methods . push ( quote ! {
178+ async fn #name ( & self , req : #input_type ) -> Result <#output_type , twirp :: ClientError > {
179+ self . request ( #request_path , req ) . await
180+ }
181+ } )
130182 }
131- writeln ! ( buf, "}}" ) . unwrap ( ) ;
183+ let client_trait = quote ! {
184+ #[ twirp:: async_trait:: async_trait]
185+ pub trait #client_name: Send + Sync {
186+ #( #client_trait_methods) *
187+ }
188+
189+ #[ twirp:: async_trait:: async_trait]
190+ impl #client_name for twirp:: client:: Client {
191+ #( #client_methods) *
192+ }
193+ } ;
194+
195+ // generate the service and client as a single file. run it through
196+ // prettyplease before outputting it.
197+ let service_fqn_path = format ! ( "/{}" , service. fqn) ;
198+ let generated = quote ! {
199+ pub use twirp;
200+
201+ pub const SERVICE_FQN : & str = #service_fqn_path;
202+
203+ #server_trait
204+
205+ #router
206+
207+ #client_trait
208+ } ;
209+
210+ let ast: syn:: File = syn:: parse2 ( generated)
211+ . expect ( "twirp-build generated invalid Rust. this is a bug in twirp-build, please file an issue" ) ;
212+ let code = prettyplease:: unparse ( & ast) ;
213+ buf. push_str ( & code) ;
132214 }
133215}
0 commit comments