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
#![no_std]
#![doc = include_str!("../README.md")]

extern crate alloc;
use alloc::format;
use alloc::string::ToString;

use crate::constants::CTOR_WORD;
use crate::enums::create_enum_token_stream;
use proc_macro::TokenStream;
use proc_macro2::Delimiter;
use quote::ToTokens;
use structs::create_struct_token_stream;
use syn::parse::discouraged::AnyDelimiter;
use syn::parse::Parse;
use syn::parse::ParseStream;
use syn::parse_macro_input;
use syn::spanned::Spanned;
use syn::Attribute;
use syn::Data;
use syn::DeriveInput;
use syn::Error;
use syn::Type;

pub(crate) mod constants;
pub(crate) mod enums;
pub(crate) mod fields;
pub(crate) mod structs;

#[proc_macro_derive(ctor, attributes(ctor))]
pub fn derive_ctor(input: TokenStream) -> TokenStream {
    let derive_input = parse_macro_input!(input as DeriveInput);

    match &derive_input.data {
        Data::Struct(_) => create_struct_token_stream(derive_input),
        Data::Enum(_) => create_enum_token_stream(derive_input),
        Data::Union(_) => TokenStream::from(
            Error::new(derive_input.span(), "Unions are not yet supported by ctor").to_compile_error(),
        ),
    }
}

pub(crate) fn try_parse_attributes_with_default<T: Parse, F: Fn() -> T>(
    attributes: &[Attribute],
    default: F,
) -> Result<T, Error> {
    for attribute in attributes {
        if attribute.path().is_ident(CTOR_WORD) {
            return attribute.parse_args::<T>();
        }
    }
    Ok(default())
}

pub(crate) fn try_parse_attributes<T: Parse>(attributes: &[Attribute]) -> Result<Option<T>, Error> {
    for attribute in attributes {
        if attribute.path().is_ident(CTOR_WORD) {
            return attribute.parse_args::<T>().map(|t| Some(t));
        }
    }
    Ok(None)
}

pub(crate) fn is_phantom_data(typ: &Type) -> bool {
    for token in typ.to_token_stream() {
        if token.to_string() == "PhantomData" {
            return true;
        }
    }
    false
}

pub(crate) fn consume_delimited<T, F>(
    stream: ParseStream,
    expected: Delimiter,
    expression: F,
) -> Result<T, Error>
where
    F: Fn(ParseStream) -> Result<T, Error>,
{
    let (delimiter, span, buffer) = stream.parse_any_delimiter()?;
    if delimiter != expected {
        return Err(Error::new(span.span(),
            format!("Expected enclosing {:?}", expected),
        ));
    }
    expression(&buffer)
}

#[test]
fn test_is_phantom_data() {
    assert!(is_phantom_data(&syn::parse_str::<Type>("PhantomData").unwrap()));
    assert!(is_phantom_data(&syn::parse_str::<Type>("&mut PhantomData<&'static str>").unwrap()));
    assert!(!is_phantom_data(&syn::parse_str::<Type>("i32").unwrap()));
}