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}