Skip to content

Commit a1a86f4

Browse files
Context clients + more integration tests (#10)
* Heavily refactor the context(s) to implement specific traits, makes the code accessing context features more reusable for users. Also re-shaped the call/send to a builder-like approach, to prepare it for codegen. * Macro generated context clients
1 parent e674c93 commit a1a86f4

File tree

12 files changed

+732
-367
lines changed

12 files changed

+732
-367
lines changed

macros/src/ast.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use syn::parse::{Parse, ParseStream};
1616
use syn::spanned::Spanned;
1717
use syn::token::Comma;
1818
use syn::{
19-
braced, parenthesized, Attribute, Error, Expr, ExprLit, FnArg, GenericArgument, Ident, Lit,
20-
Pat, PatType, Path, PathArguments, Result, ReturnType, Token, Type, Visibility,
19+
braced, parenthesized, parse_quote, Attribute, Error, Expr, ExprLit, FnArg, GenericArgument,
20+
Ident, Lit, Pat, PatType, Path, PathArguments, Result, ReturnType, Token, Type, Visibility,
2121
};
2222

2323
/// Accumulates multiple errors into a result.
@@ -145,7 +145,7 @@ pub(crate) struct Handler {
145145
pub(crate) restate_name: String,
146146
pub(crate) ident: Ident,
147147
pub(crate) arg: Option<PatType>,
148-
pub(crate) output: ReturnType,
148+
pub(crate) output: Type,
149149
}
150150

151151
impl Parse for Handler {
@@ -189,20 +189,24 @@ impl Parse for Handler {
189189
errors?;
190190

191191
// Parse return type
192-
let output: ReturnType = input.parse()?;
192+
let return_type: ReturnType = input.parse()?;
193193
input.parse::<Token![;]>()?;
194194

195-
match &output {
196-
ReturnType::Default => {}
195+
let output: Type = match &return_type {
196+
ReturnType::Default => {
197+
parse_quote!(())
198+
}
197199
ReturnType::Type(_, ty) => {
198-
if handler_result_parameter(ty).is_none() {
200+
if let Some(ty) = extract_handler_result_parameter(ty) {
201+
ty
202+
} else {
199203
return Err(Error::new(
200-
output.span(),
204+
return_type.span(),
201205
"Only restate_sdk::prelude::HandlerResult is supported as return type",
202206
));
203207
}
204208
}
205-
}
209+
};
206210

207211
// Process attributes
208212
let mut is_shared = false;
@@ -259,7 +263,7 @@ fn read_literal_attribute_name(attr: &Attribute) -> Result<Option<String>> {
259263
.transpose()
260264
}
261265

262-
fn handler_result_parameter(ty: &Type) -> Option<&Type> {
266+
fn extract_handler_result_parameter(ty: &Type) -> Option<Type> {
263267
let path = match ty {
264268
Type::Path(ty) => &ty.path,
265269
_ => return None,
@@ -280,7 +284,7 @@ fn handler_result_parameter(ty: &Type) -> Option<&Type> {
280284
}
281285

282286
match &bracketed.args[0] {
283-
GenericArgument::Type(arg) => Some(arg),
287+
GenericArgument::Type(arg) => Some(arg.clone()),
284288
_ => None,
285289
}
286290
}

macros/src/gen.rs

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ use crate::ast::{Handler, Object, Service, ServiceInner, ServiceType, Workflow};
22
use proc_macro2::TokenStream as TokenStream2;
33
use proc_macro2::{Ident, Literal};
44
use quote::{format_ident, quote, ToTokens};
5-
use syn::{parse_quote, Attribute, ReturnType, Type, Visibility};
5+
use syn::{Attribute, PatType, Visibility};
66

77
pub(crate) struct ServiceGenerator<'a> {
88
pub(crate) service_ty: ServiceType,
99
pub(crate) restate_name: &'a str,
1010
pub(crate) service_ident: &'a Ident,
11+
pub(crate) client_ident: Ident,
1112
pub(crate) serve_ident: Ident,
1213
pub(crate) vis: &'a Visibility,
1314
pub(crate) attrs: &'a [Attribute],
@@ -20,6 +21,7 @@ impl<'a> ServiceGenerator<'a> {
2021
service_ty,
2122
restate_name: &s.restate_name,
2223
service_ident: &s.ident,
24+
client_ident: format_ident!("{}Client", s.ident),
2325
serve_ident: format_ident!("Serve{}", s.ident),
2426
vis: &s.vis,
2527
attrs: &s.attrs,
@@ -50,8 +52,6 @@ impl<'a> ServiceGenerator<'a> {
5052
..
5153
} = self;
5254

53-
let unit_type: &Type = &parse_quote!(());
54-
5555
let handler_fns = handlers
5656
.iter()
5757
.map(
@@ -66,13 +66,9 @@ impl<'a> ServiceGenerator<'a> {
6666
(ServiceType::Workflow, false) => quote! { ::restate_sdk::prelude::WorkflowContext },
6767
};
6868

69-
let output = match output {
70-
ReturnType::Type(_, ref ty) => ty.as_ref(),
71-
ReturnType::Default => unit_type,
72-
};
7369
quote! {
7470
#( #attrs )*
75-
fn #ident(&self, context: #ctx, #( #args ),*) -> impl std::future::Future<Output=#output> + ::core::marker::Send;
71+
fn #ident(&self, context: #ctx, #( #args ),*) -> impl std::future::Future<Output=::restate_sdk::prelude::HandlerResult<#output>> + ::core::marker::Send;
7672
}
7773
},
7874
);
@@ -223,6 +219,123 @@ impl<'a> ServiceGenerator<'a> {
223219
}
224220
}
225221
}
222+
223+
fn struct_client(&self) -> TokenStream2 {
224+
let &Self {
225+
vis,
226+
ref client_ident,
227+
// service_ident,
228+
ref service_ty,
229+
..
230+
} = self;
231+
232+
let key_field = match service_ty {
233+
ServiceType::Service => quote! {},
234+
ServiceType::Object | ServiceType::Workflow => quote! {
235+
key: String,
236+
},
237+
};
238+
239+
let into_client_impl = match service_ty {
240+
ServiceType::Service => {
241+
quote! {
242+
impl<'ctx> ::restate_sdk::context::IntoServiceClient<'ctx> for #client_ident<'ctx> {
243+
fn create_client(ctx: &'ctx ::restate_sdk::endpoint::ContextInternal) -> Self {
244+
Self { ctx }
245+
}
246+
}
247+
}
248+
}
249+
ServiceType::Object => quote! {
250+
impl<'ctx> ::restate_sdk::context::IntoObjectClient<'ctx> for #client_ident<'ctx> {
251+
fn create_client(ctx: &'ctx ::restate_sdk::endpoint::ContextInternal, key: String) -> Self {
252+
Self { ctx, key }
253+
}
254+
}
255+
},
256+
ServiceType::Workflow => quote! {
257+
impl<'ctx> ::restate_sdk::context::IntoWorkflowClient<'ctx> for #client_ident<'ctx> {
258+
fn create_client(ctx: &'ctx ::restate_sdk::endpoint::ContextInternal, key: String) -> Self {
259+
Self { ctx, key }
260+
}
261+
}
262+
},
263+
};
264+
265+
quote! {
266+
/// Struct exposing the client to invoke [#service_ident] from another service.
267+
#vis struct #client_ident<'ctx> {
268+
ctx: &'ctx ::restate_sdk::endpoint::ContextInternal,
269+
#key_field
270+
}
271+
272+
#into_client_impl
273+
}
274+
}
275+
276+
fn impl_client(&self) -> TokenStream2 {
277+
let &Self {
278+
vis,
279+
ref client_ident,
280+
service_ident,
281+
handlers,
282+
restate_name,
283+
service_ty,
284+
..
285+
} = self;
286+
287+
let service_literal = Literal::string(restate_name);
288+
289+
let handlers_fns = handlers.iter().map(|handler| {
290+
let handler_ident = &handler.ident;
291+
let handler_literal = Literal::string(&handler.restate_name);
292+
293+
let argument = match &handler.arg {
294+
None => quote! {},
295+
Some(PatType {
296+
ty, ..
297+
}) => quote! { req: #ty }
298+
};
299+
let argument_ty = match &handler.arg {
300+
None => quote! { () },
301+
Some(PatType {
302+
ty, ..
303+
}) => quote! { #ty }
304+
};
305+
let res_ty = &handler.output;
306+
let input = match &handler.arg {
307+
None => quote! { () },
308+
Some(_) => quote! { req }
309+
};
310+
let request_target = match service_ty {
311+
ServiceType::Service => quote! {
312+
::restate_sdk::context::RequestTarget::service(#service_literal, #handler_literal)
313+
},
314+
ServiceType::Object => quote! {
315+
::restate_sdk::context::RequestTarget::object(#service_literal, &self.key, #handler_literal)
316+
},
317+
ServiceType::Workflow => quote! {
318+
::restate_sdk::context::RequestTarget::workflow(#service_literal, &self.key, #handler_literal)
319+
}
320+
};
321+
322+
quote! {
323+
#vis fn #handler_ident(&self, #argument) -> ::restate_sdk::context::Request<'ctx, #argument_ty, #res_ty> {
324+
self.ctx.request(#request_target, #input)
325+
}
326+
}
327+
});
328+
329+
let doc_msg = format!(
330+
"Struct exposing the client to invoke [`{service_ident}`] from another service."
331+
);
332+
quote! {
333+
#[doc = #doc_msg]
334+
impl<'ctx> #client_ident<'ctx> {
335+
#( #handlers_fns )*
336+
}
337+
}
338+
}
226339
}
227340

228341
impl<'a> ToTokens for ServiceGenerator<'a> {
@@ -232,6 +345,8 @@ impl<'a> ToTokens for ServiceGenerator<'a> {
232345
self.struct_serve(),
233346
self.impl_service_for_serve(),
234347
self.impl_discoverable(),
348+
self.struct_client(),
349+
self.impl_client(),
235350
]);
236351
}
237352
}

0 commit comments

Comments
 (0)