witchcraft_server_macros/
lib.rs

1// Copyright 2022 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14#![allow(clippy::needless_doctest_main)]
15use proc_macro::TokenStream;
16use proc_macro2::Span;
17use quote::quote;
18use syn::{Error, ItemFn};
19
20/// Marks the entrypoint of a Witchcraft server.
21///
22/// This macro should be applied to a function taking 3 arguments: the server's install config, a `Refreshable` of the
23/// server's runtime config, and a mutable reference to the `Witchcraft` context object. It is a simple convenience
24/// function that wraps the annotated function in one that passes it to the `witchcraft_server::init` function.
25///
26/// # Examples
27///
28/// ```ignore
29/// use conjure_error::Error;
30/// use witchcraft_server::config::install::InstallConfig;
31/// use witchcraft_server::config::runtime::RuntimeConfig;
32/// use witchcraft_server::Witchcraft;
33/// use refreshable::Refreshable;
34///
35/// #[witchcraft_server::main]
36/// fn main(
37///     install: InstallConfig,
38///     runtime: Refreshable<RuntimeConfig, Error>,
39///     wc: &mut Witchcraft,
40/// ) -> Result<(), Error> {
41///     // initialization code...
42///     Ok(())
43/// }
44/// ```
45///
46/// Expands to:
47///
48/// ```ignore
49/// use conjure_error::Error;
50/// use witchcraft_server::config::install::InstallConfig;
51/// use witchcraft_server::config::runtime::RuntimeConfig;
52/// use witchcraft_server::Witchcraft;
53/// use refreshable::Refreshable;
54///
55/// fn main() {
56///     fn inner_main(
57///         install: InstallConfig,
58///         runtime: Refreshable<RuntimeConfig, Error>,
59///         wc: &mut Witchcraft,
60///     ) -> Result<(), Error> {
61///         // initialization code...
62///         Ok(())
63///     }
64///
65///     witchcraft_server::init(inner_main)
66/// }
67/// ```
68#[proc_macro_attribute]
69pub fn main(args: TokenStream, input: TokenStream) -> TokenStream {
70    if !args.is_empty() {
71        return with_error(
72            input,
73            Error::new(
74                Span::call_site(),
75                "#[witchcraft_server::main] does not take arguments",
76            ),
77        );
78    }
79
80    let function = match syn::parse::<ItemFn>(input.clone()) {
81        Ok(function) => function,
82        Err(e) => return with_error(input, e),
83    };
84    let vis = &function.vis;
85    let name = &function.sig.ident;
86
87    quote! {
88        #vis fn #name() {
89            #function
90
91            witchcraft_server::init(#name)
92        }
93    }
94    .into()
95}
96
97fn with_error(mut tokens: TokenStream, error: Error) -> TokenStream {
98    tokens.extend(TokenStream::from(error.into_compile_error()));
99    tokens
100}