below_derive/
lib.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
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
15#![deny(clippy::all)]
16
17extern crate proc_macro;
18
19use proc_macro::TokenStream;
20use syn::DeriveInput;
21
22mod helper;
23mod qenum;
24mod queriable;
25
26/// Implements std::fmt::Display for enum, which must only contain unit
27/// variants or variants with single unnamed field, e.g. Some(T). Unit variants
28/// are converted to their snake case representations. Nested variants works
29/// similarly by joining the variant name and field representation with dot ".".
30/// For example, None => "none", and Some(None) => "some.none".
31#[proc_macro_derive(EnumToString)]
32pub fn enum_to_string_derive(input: TokenStream) -> TokenStream {
33    let ast = syn::parse_macro_input!(input as DeriveInput);
34    qenum::enum_to_string_derive_impl(&ast)
35        .unwrap_or_else(|err| err.to_compile_error())
36        .into()
37}
38
39/// Implements std::str::FromStr for enum, which has same constraints as
40/// EnumToString and works in the opposite direction.
41#[proc_macro_derive(EnumFromStr)]
42pub fn enum_from_str_derive(input: TokenStream) -> TokenStream {
43    let ast = syn::parse_macro_input!(input as DeriveInput);
44    qenum::enum_from_str_derive_impl(&ast)
45        .unwrap_or_else(|err| err.to_compile_error())
46        .into()
47}
48
49/// Implements the Queriable trait for a model. An enum with variants that map
50/// to its fields are created with auto derive above: EnumToString, EnumFromStr.
51/// That enum is used as Queriable::FieldId. Subquery fields are accessed by
52/// delegating the subquery field_id to the corresponding sub-models.
53///
54/// Struct attributes:
55///
56/// #[queriable(field_id_name = MyCgroupModelFieldId)]
57///     Alternative name for the created enum. If not provided, It will be
58///     `{model_name}FieldId`, i.e. CgroupModelFieldId for struct CgroupModel.
59///
60/// Field attributes:
61///
62/// #[queriable(ignore)]
63///     Ignore field when implementing Queriable trait.
64///
65/// #[queriable(subquery)]
66///     Mark field for subquery processing, i.e. its value is a Queriable to
67///     which we delegate the subquery.
68///     For example, a `cpu` field with type CgroupCpuModel annotated with
69///     subquery will generate a corresponding variant in the created enum as
70///     Cpu(<CgroupCpuModel as Queriable>::FieldId).
71///
72/// #[queriable(preferred_name = mem)]
73///     Name used for generating enum variant instead of the original one. Must
74///     be a valid field name for struct (not quoted).
75///
76/// Example:
77///
78/// #[derive(::below_derive::Queriable)]
79/// #[queriable(field_id_name = MyFooFieldId)]
80/// struct Foo {
81///     a: u64,
82///     b: Option<String>,
83///     #[queriable(subquery)]
84///     c: Option<Bar>,
85///     #[queriable(ignore)]
86///     d: f64,
87/// }
88///
89/// Generated code:
90///
91/// #[derive(
92///     Clone,
93///     Debug,
94///     PartialEq,
95///     ::below_derive::EnumFromStr,
96///     ::below_derive::EnumToString
97///     ::enum_iterator::Sequence
98/// )]
99/// enum MyFooFieldId {
100///     A,
101///     B,
102///     C(<Bar as Queriable>::FieldId),
103/// }
104///
105/// impl Queriable for Foo {
106///     type FieldId = MyFooFieldId;
107///     fn query(&self, field_id: &Self::FieldId) -> ::std::option::Option<Field> {
108///         match field_id {
109///             A => std::option::Option::Some(Field::from(&self.a)),
110///             B => self.b.as_ref().map(Field::from),
111///             C(field_id) => self.c.query(field_id),
112///         }
113///     }
114/// }
115#[proc_macro_derive(Queriable, attributes(queriable))]
116pub fn queriable_derive(input: TokenStream) -> TokenStream {
117    let ast = syn::parse_macro_input!(input as DeriveInput);
118    queriable::queriable_derive_impl(&ast)
119        .unwrap_or_else(|err| err.to_compile_error())
120        .into()
121}
122
123/// Shorthand to add #[derive] for all traits necessary for a Queriable model.
124/// Example:
125///
126/// #[below_derive::queriable_derives]
127/// struct Foo {
128///    ...
129/// }
130///
131/// Generates:
132///
133/// #[derive(
134///     Clone,
135///     Debug,
136///     Default,
137///     PartialEq,
138///     Serialize,
139///     Deserialize,
140///     ::below_derive::Queriable,
141/// )]
142/// struct Foo {
143///   ...
144/// }
145#[proc_macro_attribute]
146pub fn queriable_derives(_: TokenStream, input: TokenStream) -> TokenStream {
147    queriable::queriable_derives_impl(input.into()).into()
148}