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}