1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) Facebook, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate proc_macro;

use proc_macro::TokenStream;
use syn::DeriveInput;

mod helper;
mod qenum;
mod queriable;

/// Implements std::string::ToString for enum, which must only contain unit
/// variants or variants with single unnamed field, e.g. Some(T). Unit variants
/// are converted to their snake case representations. Nested variants works
/// similarly by joining the variant name and field representation with dot ".".
/// For example, None => "none", and Some(None) => "some.none".
#[proc_macro_derive(EnumToString)]
pub fn enum_to_string_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input as DeriveInput);
    qenum::enum_to_string_derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implements std::str::FromStr for enum, which has same constraints as
/// EnumToString and works in the opposite direction.
#[proc_macro_derive(EnumFromStr)]
pub fn enum_from_str_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input as DeriveInput);
    qenum::enum_from_str_derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implements unit_variant_iter() and all_variant_iter(), both of which return
/// impl Iterator<Item = Self>. The unit version iters over unit variants, while
/// the all version iters over both unit variants and expanded nested variants,
/// effectively doing a DFS over the enum tree. The nested variant field type
/// must also have all_variant_iter() defined. Currently no trait is defined to
/// capture the two methods.
#[proc_macro_derive(EnumIter)]
pub fn enum_iter_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input as DeriveInput);
    qenum::enum_iter_derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}

/// Implements the Queriable trait for a model. An enum with variants that map
/// to its fields are created with auto derive above: EnumToString, EnumFromStr
/// and EnumIter. That enum is used as Queriable::FieldId. Subquery fields are
/// accessed by delegating the subquery field_id to the corresponding sub-models.
///
/// Struct attributes:
///
/// #[queriable(field_id_name = MyCgroupModelFieldId)]
///     Alternative name for the created enum. If not provided, It will be
///     `{model_name}FieldId`, i.e. CgroupModelFieldId for struct CgroupModel.
///
/// Field attributes:
///
/// #[queriable(ignore)]
///     Ignore field when implementing Queriable trait.
///
/// #[queriable(subquery)]
///     Mark field for subquery processing, i.e. its value is a Queriable to
///     which we delegate the subquery.
///     For example, a `cpu` field with type CgroupCpuModel annotated with
///     subquery will generate a corresponding variant in the created enum as
///     Cpu(<CgroupCpuModel as Queriable>::FieldId).
///
/// #[queriable(preferred_name = mem)]
///     Name used for generating enum variant instead of the original one. Must
///     be a valid field name for struct (not quoted).
///
/// Example:
///
/// #[derive(::below_derive::Queriable)]
/// #[queriable(field_id_name = MyFooFieldId)]
/// struct Foo {
///     a: u64,
///     b: Option<String>,
///     #[queriable(subquery)]
///     c: Option<Bar>,
///     #[queriable(ignore)]
///     d: f64,
/// }
///
/// Generated code:
///
/// #[derive(
///     Clone,
///     Debug,
///     PartialEq,
///     ::below_derive::EnumIter,
///     ::below_derive::EnumFromStr,
///     ::below_derive::EnumToString
/// )]
/// enum MyFooFieldId {
///     A,
///     B,
///     C(<Bar as Queriable>::FieldId),
/// }
///
/// impl Queriable for Foo {
///     type FieldId = MyFooFieldId;
///     fn query(&self, field_id: &Self::FieldId) -> ::std::option::Option<Field> {
///         match field_id {
///             A => std::option::Option::Some(Field::from(&self.a)),
///             B => self.b.as_ref().map(Field::from),
///             C(field_id) => self.c.query(field_id),
///         }
///     }
/// }
#[proc_macro_derive(Queriable, attributes(queriable))]
pub fn queriable_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input as DeriveInput);
    queriable::queriable_derive_impl(&ast)
        .unwrap_or_else(|err| err.to_compile_error())
        .into()
}