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
// Copyright 2020-2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! Proc macros for Stronghold.

mod comm;

use comm::{build_plain, impl_permission, impl_to_permissioned};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput};

/// A version of the derive [`Debug`] trait that blocks parsing the data inside of a struct or enum.
/// Use [`GuardDebug`] to block reading and inspection of a data structure via the [`Debug`] trait.
#[proc_macro_derive(GuardDebug)]
pub fn derive_guard_debug(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let ident = input.ident;

    let (generics, types, _) = input.generics.split_for_impl();

    let generated = quote! {
        impl #generics core::fmt::Debug for #ident #types {
            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
                f.debug_struct(concat!(stringify!(#ident), "(guarded)")).finish()
            }
        }
    };

    generated.into()
}

/// Implements the `VariantPermission` for struct/ unions with PermissionValue(1).
/// For enums, it creates an analogous new enum <Ident>Permission with Unit variants, and implements `VariantPermission`
/// by assigning different `PermissionValue` for each variant. The permission value is the "index" in the enum as
/// exponent for the power of 2, thus from top to bottom 1, 2, 4, 8...
/// Additionally, it implements `Borrow` from the new original enum to the new enum, to satisfy the trait bounds of
/// `StrongholdP2p`.
#[proc_macro_derive(RequestPermissions)]
pub fn permissions(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let derive_input = syn::parse_macro_input!(input as DeriveInput);
    let name = format!("{}Permission", derive_input.ident);
    let name = syn::Ident::new(&name, derive_input.ident.span());
    let gen = match derive_input.data {
        Data::Enum(data_enum) => {
            let unit_enum = build_plain(&name, &data_enum);
            let impl_permission = impl_permission(&name, &data_enum);
            let to_permissioned = impl_to_permissioned(&derive_input.ident, &name, &data_enum);

            quote! {
                #unit_enum
                #impl_permission
                #to_permissioned
            }
        }
        Data::Struct(_) | Data::Union(_) => {
            let ident = derive_input.ident.clone();
            quote! {
                impl VariantPermission for #ident {
                    fn permission(&self) -> PermissionValue {
                        // Only panics for values > 31.
                        PermissionValue::new(0).unwrap()
                    }
                }

            }
        }
    };
    gen.into()
}