1
- use std :: fmt :: Write ;
1
+ use quote :: { format_ident , quote } ;
2
2
3
3
/// Generates twirp services for protobuf rpc service definitions.
4
4
///
@@ -11,123 +11,205 @@ pub fn service_generator() -> Box<ServiceGenerator> {
11
11
Box :: new ( ServiceGenerator { } )
12
12
}
13
13
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
+
14
90
pub struct ServiceGenerator ;
15
91
16
92
impl prost_build:: ServiceGenerator for ServiceGenerator {
17
93
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) ;
21
95
22
- writeln ! ( buf, "pub use twirp;" ) . unwrap ( ) ;
23
- writeln ! ( buf) . unwrap ( ) ;
24
- writeln ! ( buf, "pub const SERVICE_FQN: &str = \" /{service_fqn}\" ;" ) . unwrap ( ) ;
25
-
26
- //
27
96
// 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 ( ) ) ;
48
99
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
+ } ) ;
57
113
}
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 ( ) ) ;
71
137
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
+ } ) ;
82
147
}
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
+ } ;
90
159
91
160
//
92
161
// generate the twirp client
93
162
//
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 ( ) ) ;
115
167
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
+ } )
130
182
}
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) ;
132
214
}
133
215
}
0 commit comments