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
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{parse2, Expr, Lit, LitStr, MetaNameValue, Token, Type, TypePath};

use crate::derive_raw_wrapper::WrappedRawStructInfo;
use crate::{ARG_INTERFACE, ARG_METHODS};

struct InterfaceAttr {
    methods: Type,
    interface: LitStr,
}

impl Parse for InterfaceAttr {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut methods_arg_value: Option<Type> = None;
        let mut interface_arg_value: Option<LitStr> = None;

        let key_value_list: Punctuated<MetaNameValue, Token![,]> =
            Punctuated::parse_terminated(input)?;
        for key_value in key_value_list {
            if key_value.path.is_ident(ARG_METHODS) {
                if let Expr::Path(path) = key_value.value {
                    methods_arg_value = Some(Type::Path(TypePath {
                        qself: path.qself,
                        path: path.path,
                    }));
                } else {
                    return Err(input.error("Methods struct type path expected"));
                }
            } else if key_value.path.is_ident(ARG_INTERFACE) {
                if let Expr::Lit(lit) = key_value.value {
                    if let Lit::Str(lit_str) = lit.lit {
                        interface_arg_value = Some(lit_str);
                    }
                }
            } else {
                return Err(input.error("Unexpected attribute name"));
            }
        }

        if methods_arg_value.is_none() {
            return Err(input.error("Methods meta attribute is missing"));
        }
        if interface_arg_value.is_none() {
            return Err(input.error("Interface meta attribute is missing"));
        }

        Ok(Self {
            methods: methods_arg_value.unwrap(),
            interface: interface_arg_value.unwrap(),
        })
    }
}

pub fn interface(attr: TokenStream, input: TokenStream) -> TokenStream {
    let struct_info = match parse2::<WrappedRawStructInfo>(input.clone()) {
        Ok(parsed) => parsed,
        Err(error) => return error.to_compile_error(),
    };
    let interface_attr = match parse2::<InterfaceAttr>(attr) {
        Ok(parsed) => parsed,
        Err(error) => return error.to_compile_error(),
    };

    let struct_ident = &struct_info.struct_ident;
    let raw_field_ident = &struct_info.raw_field.ident;
    let methods_type = interface_attr.methods;
    let interface_name = interface_attr.interface;
    let interface_name_ident = Ident::new(
        format!("{}_TYPE_INFO", interface_name.value().to_uppercase()).as_str(),
        Span::call_site(),
    );

    quote!(
        #input

        pub const #interface_name_ident: crate::core_api::type_info::TypeInfo = crate::interface_type!(#interface_name);

        impl crate::core_api::proxy::Proxied for #struct_ident {
            fn type_info() -> crate::core_api::type_info::TypeInfo<'static> {
                #interface_name_ident
            }
        }

        impl crate::wrapper::SpaInterface for #struct_ident {
            type Methods = #methods_type;

            fn spa_interface(&self) -> &crate::spa::interface::InterfaceRef {
                use crate::wrapper::RawWrapper;
                unsafe {
                    assert_eq!(0, std::mem::size_of::<#struct_ident>(),
                        "Objects with pw_interface should not have any data, they are just zero-size \
                        pointers. Probably #[spa_interface(...)] should be used here");
                    crate::spa::interface::InterfaceRef::from_raw_ptr(
                        std::ptr::addr_of!(self.#raw_field_ident).cast())
                }
            }
        }
    )
}