Skip to main content

restate_sdk_macros/
lib.rs

1// Copyright (c) 2023 -  Restate Software, Inc., Restate GmbH.
2// All rights reserved.
3//
4// Use of this software is governed by the Business Source License
5// included in the LICENSE file.
6//
7// As of the Change Date specified in that file, in accordance with
8// the Business Source License, use of this software will be governed
9// by the Apache License, Version 2.0.
10
11// Some parts of this codebase were taken from https://github.com/google/tarpc/blob/b826f332312d3702667880a464e247556ad7dbfe/plugins/src/lib.rs
12// License MIT
13
14extern crate proc_macro;
15
16mod ast;
17mod generator;
18mod struct_ast;
19mod struct_generator;
20
21use crate::ast::{Object, Service, ServiceType, Workflow};
22use crate::generator::ServiceGenerator;
23use crate::struct_ast::StructService;
24use proc_macro::TokenStream;
25use quote::ToTokens;
26use syn::{Item, parse_macro_input};
27
28#[proc_macro_attribute]
29pub fn service(attr: TokenStream, input: TokenStream) -> TokenStream {
30    dispatch(ServiceType::Service, attr, input)
31}
32
33#[proc_macro_attribute]
34pub fn object(attr: TokenStream, input: TokenStream) -> TokenStream {
35    dispatch(ServiceType::Object, attr, input)
36}
37
38#[proc_macro_attribute]
39pub fn workflow(attr: TokenStream, input: TokenStream) -> TokenStream {
40    dispatch(ServiceType::Workflow, attr, input)
41}
42
43/// Marks a method inside a `#[restate_sdk::service]`/`#[object]`/`#[workflow]` impl block as a
44/// Restate handler.
45///
46/// This attribute is consumed by the enclosing service macro; on its own it is a no-op.
47#[proc_macro_attribute]
48pub fn handler(_: TokenStream, input: TokenStream) -> TokenStream {
49    input
50}
51
52/// Dispatch a service/object/workflow attribute macro to either the (deprecated) trait-based API or
53/// the struct-based API, depending on whether it is applied to a `trait` or an `impl` block.
54fn dispatch(service_ty: ServiceType, attr: TokenStream, input: TokenStream) -> TokenStream {
55    let item = parse_macro_input!(input as Item);
56    match item {
57        Item::Impl(item_impl) => {
58            let args = match struct_ast::parse_service_args(attr.into(), service_ty) {
59                Ok(args) => args,
60                Err(e) => return e.to_compile_error().into(),
61            };
62            match StructService::from_impl(service_ty, args, item_impl) {
63                Ok(svc) => struct_generator::generate(&svc).into(),
64                Err(e) => e.to_compile_error().into(),
65            }
66        }
67        Item::Trait(item_trait) => {
68            // Deprecated trait-based API. Re-parse the trait tokens through the legacy parser.
69            let tokens = item_trait.into_token_stream();
70            let result = match service_ty {
71                ServiceType::Service => syn::parse2::<Service>(tokens)
72                    .map(|s| ServiceGenerator::new_service(&s).into_token_stream()),
73                ServiceType::Object => syn::parse2::<Object>(tokens)
74                    .map(|s| ServiceGenerator::new_object(&s).into_token_stream()),
75                ServiceType::Workflow => syn::parse2::<Workflow>(tokens)
76                    .map(|s| ServiceGenerator::new_workflow(&s).into_token_stream()),
77            };
78            match result {
79                Ok(ts) => ts.into(),
80                Err(e) => e.to_compile_error().into(),
81            }
82        }
83        other => syn::Error::new_spanned(
84            other,
85            "#[restate_sdk::service]/#[object]/#[workflow] can only be applied to a trait \
86             (deprecated) or an inherent impl block",
87        )
88        .to_compile_error()
89        .into(),
90    }
91}