iced_layershell_macros/
lib.rs

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use darling::{
    ast::{Data, NestedMeta},
    util::{Flag, Ignored},
    FromDeriveInput, FromMeta,
};
use proc_macro2::{Span, TokenStream as TokenStream2};
use syn::{DeriveInput, Generics, Ident, LitStr, Variant, Visibility};

use quote::quote;

#[manyhow::manyhow]
#[proc_macro_attribute]
pub fn to_layer_message(attr: TokenStream2, input: TokenStream2) -> manyhow::Result<TokenStream2> {
    let meta = NestedMeta::parse_meta_list(attr)?;

    let ToLayerMessageAttr { multi, info_name } = ToLayerMessageAttr::from_list(&meta)?;

    let is_multi = multi.is_present();

    let derive_input = syn::parse2::<DeriveInput>(input)?;
    let attrs = &derive_input.attrs;
    let MessageEnum {
        vis,
        ident,
        generics,
        data,
    } = MessageEnum::from_derive_input(&derive_input)?;

    let (impl_gen, ty_gen, where_gen) = generics.split_for_impl();
    let variants = data.take_enum().unwrap();

    let (additional_variants, try_into_impl) = match is_multi {
        true => {
            let info_name = info_name.expect("Should set the info_name").value();
            let info = Ident::new(&info_name, Span::call_site());

            let additional_variants = quote! {
                AnchorChange{id: iced::window::Id, anchor: iced_layershell::reexport::Anchor},
                AnchorSizeChange{id: iced::window::Id, anchor:iced_layershell::reexport::Anchor, size: (u32, u32)},
                LayerChange{id: iced::window::Id, layer:iced_layershell::reexport::Layer},
                MarginChange{id: iced::window::Id, margin: (i32, i32, i32, i32)},
                SizeChange{id: iced::window::Id, size: (u32, u32)},
                VirtualKeyboardPressed {
                    time: u32,
                    key: u32,
                },
                NewLayerShell { settings: iced_layershell::reexport::NewLayerShellSettings, info: #info },
                NewPopUp { settings: iced_layershell::actions::IcedNewPopupSettings, info: #info },
                NewMenu { settings: iced_layershell::actions::IcedNewMenuSettings, info: #info },
                RemoveWindow(iced::window::Id),
                ForgetLastOutput,
            };

            let try_into_impl = quote! {
                impl #impl_gen TryInto<iced_layershell::actions::LayershellCustomActionsWithIdAndInfo<#info>> for #ident #ty_gen #where_gen {
                    type Error = Self;

                    fn try_into(self) -> Result<iced_layershell::actions::LayershellCustomActionsWithIdAndInfo<#info>, Self::Error> {
                        type InnerLayerActionId = iced_layershell::actions::LayershellCustomActionsWithIdAndInfo<#info>;
                        type InnerLayerAction = iced_layershell::actions::LayershellCustomActionsWithInfo<#info>;

                        match self {
                            Self::AnchorChange { id, anchor } => Ok(InnerLayerActionId::new(Some(id), InnerLayerAction::AnchorChange(anchor))),
                            Self::AnchorSizeChange { id, anchor, size } => Ok(InnerLayerActionId::new(Some(id), InnerLayerAction::AnchorSizeChange(anchor, size))),
                            Self::LayerChange { id, layer } => Ok(InnerLayerActionId::new(Some(id), InnerLayerAction::LayerChange(layer))),
                            Self::MarginChange { id, margin } => Ok(InnerLayerActionId::new(Some(id), InnerLayerAction::MarginChange(margin))),
                            Self::SizeChange { id, size } => Ok(InnerLayerActionId::new(Some(id), InnerLayerAction::SizeChange(size))),
                            Self::VirtualKeyboardPressed { time, key } => Ok(InnerLayerActionId::new(
                                None,
                                InnerLayerAction::VirtualKeyboardPressed { time, key })
                            ),
                            Self::NewLayerShell {settings, info } => Ok(InnerLayerActionId::new(None, InnerLayerAction::NewLayerShell((settings, info)))),
                            Self::NewPopUp { settings, info } => Ok(InnerLayerActionId::new(None, InnerLayerAction::NewPopUp((settings, info)))),
                            Self::NewMenu { settings, info } =>  Ok(InnerLayerActionId::new(None, InnerLayerAction::NewMenu((settings, info)))),
                            Self::RemoveWindow(id) => Ok(InnerLayerActionId::new(None, InnerLayerAction::RemoveWindow(id))),
                            Self::ForgetLastOutput => Ok(InnerLayerActionId::new(None, InnerLayerAction::ForgetLastOutput)),
                            _ => Err(self)
                        }
                    }
                }
            };

            (additional_variants, try_into_impl)
        }
        false => {
            let additional_variants = quote! {
                AnchorChange(iced_layershell::reexport::Anchor),
                AnchorSizeChange(iced_layershell::reexport::Anchor, (u32, u32)),
                LayerChange(iced_layershell::reexport::Layer),
                MarginChange((i32, i32, i32, i32)),
                SizeChange((u32, u32)),
                VirtualKeyboardPressed {
                    time: u32,
                    key: u32,
                },
            };
            let try_into_impl = quote! {
                impl #impl_gen TryInto<iced_layershell::actions::LayershellCustomActions> for #ident #ty_gen #where_gen {
                    type Error = Self;

                    fn try_into(self) -> Result<iced_layershell::actions::LayershellCustomActions, Self::Error> {
                        use iced_layershell::actions::LayershellCustomActions;

                        match self {
                            Self::AnchorChange(anchor) => Ok(LayershellCustomActions::AnchorChange(anchor)),
                            Self::AnchorSizeChange(anchor, size) => Ok(LayershellCustomActions::AnchorSizeChange(anchor, size)),
                            Self::LayerChange(layer) => Ok(LayershellCustomActions::LayerChange(layer)),

                            Self::MarginChange(margin) => Ok(LayershellCustomActions::MarginChange(margin)),
                            Self::SizeChange(size) => Ok(LayershellCustomActions::SizeChange(size)),
                            Self::VirtualKeyboardPressed { time, key } => Ok(LayershellCustomActions::VirtualKeyboardPressed {
                                time,
                                key
                            }),
                            _ => Err(self)
                        }
                    }
                }
            };

            (additional_variants, try_into_impl)
        }
    };

    Ok(quote! {
        #(#attrs)*
        #vis enum #ident #ty_gen #where_gen {
            #(#variants,)*
            #additional_variants
        }

        #try_into_impl
    })
}

#[derive(FromMeta)]
struct ToLayerMessageAttr {
    multi: Flag,
    info_name: Option<LitStr>,
}

#[derive(FromDeriveInput)]
#[darling(supports(enum_any))]
struct MessageEnum {
    vis: Visibility,
    ident: Ident,
    generics: Generics,
    data: Data<Variant, Ignored>,
}