Skip to main content

protify_proc_macro/
lib.rs

1#![allow(
2	clippy::single_match,
3	clippy::collapsible_if,
4	clippy::collapsible_else_if
5)]
6#![cfg_attr(docsrs, feature(doc_cfg))]
7
8use std::{
9	borrow::Cow,
10	fmt::Display,
11	ops::{Deref, Range},
12};
13
14use bool_enum::bool_enum;
15use parsing::*;
16use proc_macro::TokenStream;
17use proc_macro2::{Span, TokenStream as TokenStream2};
18use quote::{ToTokens, format_ident, quote, quote_spanned};
19use syn::{
20	Attribute, Error, Expr, Field, Fields, Ident, ItemEnum, ItemStruct, Lit, LitBool, LitStr, Meta,
21	Path, RangeLimits, Token, Type, Variant, Visibility, bracketed,
22	meta::ParseNestedMeta,
23	parse::{Parse, Parser},
24	parse_macro_input, parse_quote, parse_quote_spanned,
25	spanned::Spanned,
26	token,
27};
28use syn_utils::*;
29
30use crate::{
31	enum_proc_macro::*, extension_macro::*, file_macro::*, impls::*, internals::*,
32	message_proc_macro::*, oneof_proc_macro::*, package_macro::*, service_macro::*,
33};
34
35#[cfg(feature = "cel")]
36mod cel_try_into;
37mod enum_proc_macro;
38mod extension_macro;
39mod file_macro;
40mod impls;
41mod internals;
42mod message_proc_macro;
43mod oneof_proc_macro;
44mod package_macro;
45mod parsing;
46#[cfg(feature = "reflection")]
47mod reflection;
48mod service_macro;
49
50#[doc(hidden)]
51#[proc_macro_derive(__AttrForwarding, attributes(forward))]
52pub fn attr_forwarding_derive_test(_: TokenStream) -> TokenStream {
53	TokenStream::new()
54}
55
56/// Implements the [`CelOneof`](protify::CelOneof) trait on an enum.
57///
58/// Automatically implemented by the [`proto_oneof`](protify::proto_oneof) macro when the `cel` feature is enabled.
59#[cfg(feature = "cel")]
60#[proc_macro_derive(CelOneof, attributes(cel))]
61pub fn cel_oneof_derive(input: TokenStream) -> TokenStream {
62	let item = parse_macro_input!(input as ItemEnum);
63
64	match cel_try_into::derive_cel_value_oneof(&item) {
65		Ok(tokens) => tokens.into(),
66		Err(e) => e.into_compile_error().into(),
67	}
68}
69
70/// Implements the [`CelValue`](protify::CelValue) trait on a struct.
71///
72/// Automatically implemented by the [`proto_message`](protify::proto_message) macro when the `cel` feature is enabled.
73#[cfg(feature = "cel")]
74#[proc_macro_derive(CelValue, attributes(cel))]
75pub fn cel_struct_derive(input: TokenStream) -> TokenStream {
76	let item = parse_macro_input!(input as ItemStruct);
77
78	match cel_try_into::derive_cel_value_struct(&item) {
79		Ok(tokens) => tokens.into(),
80		Err(e) => e.into_compile_error().into(),
81	}
82}
83
84/// Implements [`ValidatedOneof`](protify::ValidatedOneof) on an enum.
85///
86/// Automatically implemented by the [`proto_oneof`](protify::proto_oneof) macro.
87#[cfg(feature = "reflection")]
88#[proc_macro_derive(ValidatedOneof, attributes(proto))]
89pub fn validated_oneof_derive(input: TokenStream) -> TokenStream {
90	let mut item = parse_macro_input!(input as ItemEnum);
91
92	reflection::reflection_oneof_derive(&mut item).into()
93}
94
95/// Implements [`ProtoEnum`](protify::ProtoEnum).
96///
97/// Automatically implemented by the [`proto_enum`](protify::proto_enum) macro.
98#[cfg(feature = "reflection")]
99#[proc_macro_derive(ProtoEnum, attributes(proto))]
100pub fn enum_derive(input: TokenStream) -> TokenStream {
101	let item = parse_macro_input!(input as ItemEnum);
102
103	reflection::enum_reflection_derive(&item).into()
104}
105
106/// Implements [`ValidatedMessage`](protify::ValidatedMessage) on a struct.
107///
108/// Automatically implemented by the [`proto_message`](protify::proto_message) macro.
109#[cfg(feature = "reflection")]
110#[proc_macro_derive(ValidatedMessage, attributes(proto))]
111pub fn validated_message_derive(input: TokenStream) -> TokenStream {
112	let mut item = parse_macro_input!(input as ItemStruct);
113
114	reflection::reflection_message_derive(&mut item).into()
115}
116
117#[doc(hidden)]
118#[proc_macro]
119pub fn impl_known_type(input: TokenStream) -> TokenStream {
120	match well_known_type_impl_macro(input.into()) {
121		Ok(output) => output.into(),
122		Err(e) => e.into_compile_error().into(),
123	}
124}
125
126#[doc(hidden)]
127#[proc_macro]
128pub fn builder_state_macro(input: TokenStream) -> TokenStream {
129	match builder_macro(input.into()) {
130		Ok(output) => output.into(),
131		Err(e) => e.into_compile_error().into(),
132	}
133}
134
135#[doc = include_str!("../docs/file_macro.md")]
136#[proc_macro]
137pub fn define_proto_file(input: TokenStream) -> TokenStream {
138	match process_file_macro(input.into()) {
139		Ok(output) => output.into(),
140		Err(e) => e.into_compile_error().into(),
141	}
142}
143
144#[allow(clippy::doc_overindented_list_items)]
145/// Creates a new package handle, which is used to collect the proto schemas in a crate.
146///
147/// For a comprehensive guide of how to set up a package, visit the [`package setup`](protify::guide::package_setup) section.
148///
149/// The **first parameter** of the macro is the ident that will be used for the generated constant that will hold the package handle, which will be used to generate the package and its proto files.
150///
151/// To distinguish the package handle from a normal rust type, it is advised to use SCREAMING_CASE casing.
152///
153///The other parameters are not positional and are as follows:
154///
155/// - `name` (required)
156///   - Type: string
157///   - Example: `proto_package!(MY_PKG, name = "my_pkg")`
158///   - Description:
159///       The name of the package.
160///
161///
162///   - `no_cel_test`
163///   - Type: Ident
164///   - Example: `proto_package!(MY_PKG, name = "my_pkg", no_cel_test)`
165///   - Description:
166///       By default, if the `cel` feature is enabled, the macro will automatically generate a test that will check for collisions of CEL rules with the same ID within the same message. You can use this ident to disable this behaviour. The [`check_unique_cel_rules`](crate::Package::check_unique_cel_rules) method will still be available if you want to call it manually inside a test.
167///
168/// As explained in the [`package setup`](protify::guide::package_setup) section, when the `inventory` feature is disabled, the files of a given package must be added manually, inside a bracketed list.
169///
170/// # Examples
171/// ```
172/// use protify::*;
173///
174/// // If we want to skip the automatically generated
175/// // Test for conflicting CEL rules in the same scope
176/// proto_package!(WITHOUT_TEST, name = "without_test", no_cel_test);
177///
178/// // If the `inventory` feature is disabled, we add the files manually
179/// proto_package!(MY_PKG, name = "my_pkg", files = [ MY_FILE ]);
180/// define_proto_file!(MY_FILE, name = "my_file.proto", package = MY_PKG);
181/// ```
182#[proc_macro]
183pub fn proto_package(input: TokenStream) -> TokenStream {
184	match package_macro_impl(input.into()) {
185		Ok(output) => output.into(),
186		Err(e) => e.into_compile_error().into(),
187	}
188}
189
190#[doc = include_str!("../docs/message_macro.md")]
191///
192/// # Field attributes
193#[doc = include_str!("../docs/field_ref.md")]
194#[proc_macro_attribute]
195pub fn proto_message(args: TokenStream, input: TokenStream) -> TokenStream {
196	let item = parse_macro_input!(input as ItemStruct);
197
198	message_proc_macro(item, args.into()).into()
199}
200
201#[doc(hidden)]
202#[proc_macro_derive(__Message, attributes(proto))]
203pub fn message_derive(_input: TokenStream) -> TokenStream {
204	TokenStream::new()
205}
206
207#[doc = include_str!("../docs/extension_macro.md")]
208#[proc_macro_attribute]
209pub fn proto_extension(args: TokenStream, input: TokenStream) -> TokenStream {
210	let mut item = parse_macro_input!(input as ItemStruct);
211
212	let extra_tokens = match process_extension_derive(args.into(), &mut item) {
213		Ok(output) => output,
214		Err(e) => e.to_compile_error(),
215	};
216
217	quote! {
218	  #[derive(::protify::macros::__Extension)]
219	  #item
220
221	  #extra_tokens
222	}
223	.into()
224}
225
226#[doc(hidden)]
227#[proc_macro_derive(__Extension, attributes(proto))]
228pub fn extension_derive(_input: TokenStream) -> TokenStream {
229	TokenStream::new()
230}
231
232#[doc = include_str!("../docs/service_macro.md")]
233#[proc_macro_attribute]
234pub fn proto_service(_args: TokenStream, input: TokenStream) -> TokenStream {
235	let item = parse_macro_input!(input as ItemEnum);
236
237	let output = match process_service_derive(&item) {
238		Ok(output) => output,
239		Err(e) => return e.to_compile_error().into(),
240	};
241
242	output.into()
243}
244
245#[doc(hidden)]
246#[proc_macro_derive(__Service, attributes(proto))]
247pub fn service_derive(_input: TokenStream) -> TokenStream {
248	TokenStream::new()
249}
250
251#[doc = include_str!("../docs/enum_macro.md")]
252#[proc_macro_attribute]
253pub fn proto_enum(_args: TokenStream, input: TokenStream) -> TokenStream {
254	let item = parse_macro_input!(input as ItemEnum);
255
256	enum_proc_macro(item).into()
257}
258
259#[doc(hidden)]
260#[proc_macro_derive(__Enum, attributes(proto))]
261pub fn enum_empty_derive(_input: TokenStream) -> TokenStream {
262	TokenStream::new()
263}
264
265#[doc = include_str!("../docs/oneof_macro.md")]
266///
267/// # Variant attributes
268#[doc = include_str!("../docs/field_ref.md")]
269#[proc_macro_attribute]
270pub fn proto_oneof(args: TokenStream, input: TokenStream) -> TokenStream {
271	let item = parse_macro_input!(input as ItemEnum);
272
273	process_oneof_proc_macro(item, args.into()).into()
274}
275
276#[doc(hidden)]
277#[proc_macro_derive(__Oneof, attributes(proto))]
278pub fn oneof_derive(_input: TokenStream) -> TokenStream {
279	TokenStream::new()
280}