protify/decl_macros.rs
1// For docs
2#[allow(unused)]
3use crate::*;
4
5macro_rules! pluralize {
6 ($count:expr) => {
7 if $count != 1 { "s" } else { "" }
8 };
9}
10
11macro_rules! custom_error_messages_method {
12 ($kind:ident) => {
13 paste! {
14 /// Adds a map with custom error messages to the underlying validator.
15 ///
16 /// If a violation has no custom error message attached to it, it uses the default error message.
17 #[inline]
18 pub fn with_error_messages(
19 mut self,
20 error_messages: impl IntoIterator<Item = ([< $kind Violation >], impl Into<FixedStr>)>,
21 ) -> [< $kind ValidatorBuilder >]<SetErrorMessages<S>>
22 where
23 S::ErrorMessages: IsUnset,
24 {
25 self.data.error_messages = Some(collect_error_messages(error_messages));
26
27 [< $kind ValidatorBuilder >] {
28 _state: PhantomData,
29 data: self.data,
30 }
31 }
32 }
33 };
34}
35
36#[doc(hidden)]
37#[cfg(feature = "inventory")]
38#[macro_export]
39macro_rules! register_proto_data {
40 ($($tokens:tt)*) => {
41 $crate::inventory::submit! { $($tokens)* }
42 };
43}
44
45#[doc(hidden)]
46#[cfg(not(feature = "inventory"))]
47#[macro_export]
48macro_rules! register_proto_data {
49 ($($tokens:tt)*) => {};
50}
51
52/// This macro can be used to generate a [`ProtoOption`] with a concise syntax.
53///
54/// The input can be a single `key => value`, where the key should support [`Into`] [`FixedStr`] and the value should support [`Into`] [`OptionValue`], or a bracketed series or key-value pairs to generate an [`OptionValue::Message`] for the value.
55///
56/// # Examples
57///
58/// ```
59/// use protify::*;
60///
61/// let option = proto_option!("is_cool" => true);
62/// assert_eq!(option, ProtoOption { name: "is_cool".into(), value: true.into() });
63///
64/// let object_like_option = proto_option!("is_cool" => { "answer" => true });
65/// assert_eq!(object_like_option.name, "is_cool");
66/// assert_eq!(object_like_option.value, OptionValue::Message(option_message!("answer" => true)));
67/// ```
68#[macro_export]
69macro_rules! proto_option {
70 ( $name:expr => { $($key:expr => $val:expr),* $(,)? } ) => {
71 $crate::ProtoOption {
72 name: $name.into(),
73 value: $crate::OptionValue::Message($crate::option_message!($($key => $val),*)),
74 }
75 };
76
77 ($name:expr => $val:expr) => {
78 $crate::ProtoOption {
79 name: $name.into(),
80 value: $val.into(),
81 }
82 };
83}
84
85/// This macro can be used to create an [`OptionValue::List`] from an iterator of items that implement [`Into`] [`OptionValue`].
86///
87/// # Examples
88///
89/// ```
90/// use protify::*;
91///
92/// let list = option_list!([ 1, 2 ]);
93/// assert!(matches!(list, OptionValue::List(_)));
94/// ```
95#[macro_export]
96macro_rules! option_list {
97 ($list:expr) => {
98 $crate::OptionValue::new_list($list)
99 };
100}
101
102/// This macro can be used to create an object-like protobuf option. It follows the syntax of crates like maplit, for creating key-value pairs.
103///
104/// # Examples
105///
106/// ```
107/// use protify::*;
108///
109/// let option = option_message!("is_cool" => true);
110/// let value = option.get("is_cool").unwrap();
111///
112/// assert_eq!(value, &OptionValue::Bool(true));
113/// ```
114#[macro_export]
115macro_rules! option_message {
116 ($($key:expr => $val:expr),* $(,)?) => {
117 {
118 let mut builder = $crate::OptionMessageBuilder::new();
119 $(
120 builder.set($key, $val);
121 )*
122 builder.build()
123 }
124 };
125
126 ($msg:expr) => {
127 $crate::OptionValue::new_message($msg)
128 }
129}
130
131macro_rules! length_rule_value {
132 ($name:literal, $value:expr) => {
133 &LengthRuleValue {
134 name: $name,
135 value: $value,
136 }
137 };
138}
139
140/// Brings a pre-defined proto file handle in scope so that it can be picked up by the proto items defined in the module where it's called.
141///
142/// It **retains** the `extern_path` of the original file, so it should be used only if the items are meant to be re-exported by the module where the original file is defined.
143///
144/// For more information about how to manage proto files, visit the [dedicated section](crate::guide::managing_files).
145///
146/// # Examples
147///
148/// ```rust
149/// use protify::*;
150/// mod example {
151/// use super::*;
152///
153/// proto_package!(MY_PKG, name = "my_pkg");
154/// define_proto_file!(MY_FILE, name = "my_file.proto", package = MY_PKG);
155///
156/// pub use re_exported::Msg;
157///
158/// pub fn mod_path() -> &'static str {
159/// module_path!()
160/// }
161///
162/// mod re_exported {
163/// use super::MY_FILE;
164/// use protify::*;
165///
166/// // The file is now in scope, and will be picked up automatically by all items defined in this module
167/// // The items will have the extern path of the parent, so `::cratename::example`
168/// inherit_proto_file!(MY_FILE);
169///
170/// // This will have the extern path `::cratename::example::Msg`
171/// #[proto_message]
172/// pub struct Msg {
173/// pub id: i32
174/// }
175/// }
176/// }
177///
178/// fn main() {
179/// assert_eq!(example::Msg::proto_schema().rust_path, &format!("::{}::Msg", example::mod_path()));
180/// }
181/// ```
182#[macro_export]
183macro_rules! inherit_proto_file {
184 ($file:path) => {
185 #[doc(hidden)]
186 #[allow(unused)]
187 const __PROTO_FILE: $crate::FileReference = ::protify::FileReference {
188 name: <$file as ::protify::FileSchema>::NAME,
189 package: <$file as ::protify::FileSchema>::PACKAGE,
190 extern_path: <$file as ::protify::FileSchema>::EXTERN_PATH,
191 };
192 };
193}
194
195/// Brings a pre-defined proto file handle in scope so that it can be picked up by the proto items defined in the module where it's called.
196///
197/// The items defined in the module where this is called will have the `extern_path` set to the output of `module_path!()`. For re-exported items that are meant to inherit the same path as the parent module, use the [`inherit_proto_file`] macro instead.
198///
199/// For more information about how to manage proto files, visit the [dedicated section](crate::guide::managing_files).
200///
201/// # Examples
202///
203/// ```rust
204/// use protify::*;
205///
206/// mod example {
207/// use super::*;
208/// proto_package!(MY_PKG, name = "my_pkg");
209/// define_proto_file!(MY_FILE, name = "my_file.proto", package = MY_PKG);
210///
211/// pub mod submod {
212/// use super::MY_FILE;
213/// use protify::*;
214///
215/// pub fn mod_path() -> &'static str {
216/// module_path!()
217/// }
218///
219/// // The file is now in scope, and will be picked up automatically by all items defined in this module
220/// // The items will have the extern path of the `module_path!()` output in here, so `::cratename::example::submod`
221/// use_proto_file!(MY_FILE);
222///
223/// // This will have the extern path `::cratename::example::submod::Msg`
224/// #[proto_message]
225/// pub struct Msg {
226/// pub id: i32
227/// }
228/// }
229/// }
230///
231/// fn main() {
232/// assert_eq!(example::submod::Msg::proto_schema().rust_path, &format!("::{}::Msg", example::submod::mod_path()));
233/// }
234/// ```
235#[macro_export]
236macro_rules! use_proto_file {
237 ($file:path) => {
238 #[doc(hidden)]
239 #[allow(unused)]
240 const __PROTO_FILE: $crate::FileReference = ::protify::FileReference {
241 name: <$file as ::protify::FileSchema>::NAME,
242 package: <$file as ::protify::FileSchema>::PACKAGE,
243 extern_path: ::core::module_path!(),
244 };
245 };
246}
247
248macro_rules! handle_ignore_always {
249 ($ignore:expr) => {
250 if matches!($ignore, Ignore::Always) {
251 return Ok(IsValid::Yes);
252 }
253 };
254}
255
256macro_rules! handle_ignore_if_zero_value {
257 ($ignore:expr, $condition:expr) => {
258 if matches!($ignore, Ignore::IfZeroValue) && $condition {
259 return Ok(IsValid::Yes);
260 }
261 };
262}
263
264macro_rules! impl_testing_methods {
265 () => {
266 #[cfg(feature = "cel")]
267 #[inline(never)]
268 #[cold]
269 fn check_cel_programs_with(&self, val: Self::Target) -> Result<(), Vec<CelError>> {
270 if !self.cel.is_empty() {
271 test_programs(&self.cel, val)
272 } else {
273 Ok(())
274 }
275 }
276
277 #[cfg(feature = "cel")]
278 #[inline(never)]
279 #[cold]
280 #[doc(hidden)]
281 fn __check_cel_programs(&self) -> Result<(), Vec<CelError>> {
282 self.check_cel_programs_with(Self::Target::default())
283 }
284
285 #[doc(hidden)]
286 #[inline(never)]
287 #[cold]
288 fn __cel_rules(&self) -> Vec<CelRule> {
289 self.cel
290 .iter()
291 .map(|p| p.rule().clone())
292 .collect()
293 }
294 };
295}
296
297/// Defines a new [`CelProgram`].
298///
299/// The inputs, in positional order, are:
300/// - id (expr, Into<[`FixedStr`]>): The id of the specific CEL rule. It should be unique within the same message scope.
301/// - msg (expr, Into<[`FixedStr`]>): The error message associated with the given rule.
302/// - expr (expr, Into<[`FixedStr`]>): The actual CEL expression to use when validating the target.
303#[macro_export]
304macro_rules! cel_program {
305 (id = $id:expr, msg = $msg:expr, expr = $expr:expr) => {
306 $crate::CelRule {
307 id: $id.into(),
308 message: $msg.into(),
309 expression: $expr.into(),
310 }
311 .into()
312 };
313}
314
315macro_rules! impl_proto_type {
316 ($rust_type:ty, $proto_type:ident) => {
317 impl AsProtoType for $rust_type {
318 #[inline]
319 fn proto_type() -> ProtoType {
320 ProtoType::Scalar(ProtoScalar::$proto_type)
321 }
322 }
323 };
324}
325
326macro_rules! impl_proto_map_key {
327 ($rust_type:ty, $enum_ident:ident) => {
328 impl AsProtoMapKey for $rust_type {
329 #[doc(hidden)]
330 #[allow(private_interfaces)]
331 const SEALED: crate::Sealed = crate::Sealed;
332
333 #[doc(hidden)]
334 #[cold]
335 #[inline]
336 fn as_proto_map_key() -> ProtoMapKey {
337 ProtoMapKey::$enum_ident
338 }
339 }
340 };
341}