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
//! This crate provides macros for deriving some useful methods and traits for the MatterDB.

#![recursion_limit = "128"]
#![deny(unsafe_code, bare_trait_objects)]
#![warn(missing_docs, missing_debug_implementations)]

extern crate proc_macro;

mod db_traits;

use proc_macro::TokenStream;
use syn::{Attribute, NestedMeta};

/// Derives `BinaryValue` trait. The target type must implement (de)serialization logic,
/// which should be provided externally.
///
/// The trait currently supports the following codecs:
///
/// - `bincode` serialization via the eponymous crate. Switched on by the
///   `#[binary_value(codec = "bincode")]` attribute.
///
/// # Container Attributes
///
/// ## `codec`
///
/// Selects the serialization codec to use. Allowed values are `protobuf` (used by default)
/// and `bincode`.
///
/// # Examples
///
/// With Protobuf serialization:
///
/// With `bincode` serialization:
///
/// ```ignore
/// #[derive(Clone, Debug, Serialize, Deserialize, BinaryValue)]
/// #[binary_value(codec = "bincode")]
/// pub struct Wallet {
///     pub username: PublicKey,
///     /// Current balance of the wallet.
///     pub balance: u64,
/// }
///
/// let wallet = Wallet {
///     username: "Alice".to_owned(),
///     balance: 100,
/// };
/// let bytes = wallet.to_bytes();
/// ```
#[proc_macro_derive(BinaryValue, attributes(binary_value))]
pub fn binary_value(input: TokenStream) -> TokenStream {
    db_traits::impl_binary_value(input)
}

/// Derives `FromAccess` trait.
///
/// This macro can be applied only to `struct`s, each field of which implements `FromAccess`
/// itself (e.g., indexes, `Group`s, or `Lazy` indexes). The macro instantiates each field
/// using the address created by appending a dot `.` and the name of the field or its override
/// (see [below](#rename)) to the root address where the struct is created. For example,
/// if the struct is created at the address `"foo"` and has fields `"list"` and `"map"`, they
/// will be instantiated at addresses `"foo.list"` and `"foo.map"`, respectively.
///
/// The struct must have at least one type param, which will correspond to the `Access` type.
/// The derive logic will determine this param as the first param with `T: Access` bound.
/// If there are no such params, but there is a single type param, it will be used.
///
/// # Container Attributes
///
/// ## `transparent`
///
/// ```text
/// #[from_access(transparent)]`
/// ```
///
/// Switches to the *transparent* layout similarly to `#[repr(transparent)]`
/// or `#[serde(transparent)]`.
/// A struct with the transparent layout must have a single field. The field will be created at
/// the same address as the struct itself (i.e., no suffix will be added).
///
/// # Field Attributes
///
/// ## `rename`
///
/// ```text
/// #[from_access(rename = "name")]
/// ```
///
/// Changes the suffix appended to the address when creating a field. The name should follow
/// conventions for index names.
#[proc_macro_derive(FromAccess, attributes(from_access))]
pub fn from_access(input: TokenStream) -> TokenStream {
    db_traits::impl_from_access(input)
}

pub(crate) fn find_meta_attrs(name: &str, args: &[Attribute]) -> Option<NestedMeta> {
    args.as_ref()
        .iter()
        .filter_map(|a| a.parse_meta().ok())
        .find(|m| m.path().is_ident(name))
        .map(NestedMeta::from)
}