ambient_package_macro_common/
enums.rs

1use ambient_package_semantic::{Item, ItemMap, Scope};
2use proc_macro2::TokenStream;
3use quote::quote;
4
5use crate::{make_path, Context};
6
7pub fn generate(context: Context, items: &ItemMap, scope: &Scope) -> anyhow::Result<TokenStream> {
8    let enums = scope
9        .types
10        .values()
11        .filter_map(|id| context.extract_item_if_relevant(items, *id))
12        .filter(|ty| ty.inner.as_enum().is_some())
13        .map(|ty| {
14            let (data, enumeration) = (ty.data(), ty.inner.as_enum().unwrap());
15            let id = data.id.as_str();
16            let doc_comment = if let Some(desc) = &enumeration.description {
17                format!("**{}**: {}", id, desc)
18            } else {
19                format!("**{}**", id)
20            };
21
22            let enum_name = make_path(id);
23            let members = enumeration.members.iter().map(|(id, _)| make_path(id.as_str())).collect::<Vec<_>>();
24            let enum_fields = enumeration.members.iter().map(|(id, comment)| {
25                let name = make_path(id.as_str());
26                quote! {
27                    #[doc = #comment]
28                    #name
29                }
30            });
31
32            let ecs_prefix = context.guest_api_path()
33                .map(|path| quote! { #path::ecs:: })
34                .unwrap_or_else(|| quote! { crate:: });
35
36            let main = quote! {
37                #[derive(Copy, Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
38                #[serde(crate = "self::serde")]
39                #[doc = #doc_comment]
40                pub enum #enum_name {
41                    #[default]
42                    #(#enum_fields,)*
43                }
44
45                impl #ecs_prefix EnumComponent for #enum_name {
46                    fn to_u32(&self) -> u32 {
47                        match self {
48                            #(
49                                Self::#members => #enum_name::#members as u32,
50                            )*
51                        }
52                    }
53
54                    fn from_u32(value: u32) -> Option<Self> {
55                        #(
56                            if value == #enum_name::#members as u32 {
57                                return Some(Self::#members);
58                            }
59                        )*
60
61                        None
62                    }
63                }
64            };
65
66            let supported_value = if let Some(guest_api_path) = context.guest_api_path() {
67                quote! {
68                    impl #guest_api_path::ecs::SupportedValue for #enum_name {
69                        fn from_result(result: #guest_api_path::ecs::WitComponentValue) -> Option<Self> {
70                            use #ecs_prefix EnumComponent;
71                            u32::from_result(result).and_then(Self::from_u32)
72                        }
73
74                        fn into_result(self) -> #guest_api_path::ecs::WitComponentValue {
75                            use #ecs_prefix EnumComponent;
76                            self.to_u32().into_result()
77                        }
78
79                        fn from_value(value: #guest_api_path::ecs::ComponentValue) -> Option<Self> {
80                            use #ecs_prefix EnumComponent;
81                            u32::from_value(value).and_then(Self::from_u32)
82                        }
83
84                        fn into_value(self) -> #guest_api_path::ecs::ComponentValue {
85                            use #ecs_prefix EnumComponent;
86                            self.to_u32().into_value()
87                        }
88                    }
89                    // Code generation for Vec/Option is disabled as you cannot implement foreign traits on foreign types
90                    // Need to think about how to best solve this
91                    // impl #guest_api_path::ecs::SupportedValue for Vec<#enum_name> {
92                    //     fn from_result(result: #guest_api_path::ecs::WitComponentValue) -> Option<Self> {
93                    //         use #ecs_prefix EnumComponent;
94                    //         (Vec :: <u32> :: from_result(result)).and_then(|v| {
95                    //             v.into_iter().map(|v| #enum_name::from_u32(v)).collect::<Option<Vec<_>>>()
96                    //         })
97                    //     }
98
99                    //     fn into_result(self) -> #guest_api_path::ecs::WitComponentValue {
100                    //         use #ecs_prefix EnumComponent;
101                    //         self.into_iter().map(|v| v.to_u32()).collect::<Vec<_>>().into_result()
102                    //     }
103                    // }
104                    // impl #guest_api_path::ecs::SupportedValue for Option<#enum_name> {
105                    //     fn from_result(result: #guest_api_path::ecs::WitComponentValue) -> Option<Self> {
106                    //         use #ecs_prefix EnumComponent;
107                    //         u32::from_result(result).map(|v| #enum_name ::from_u32(v))
108                    //     }
109
110                    //     fn into_result(self) -> #guest_api_path::ecs::WitComponentValue {
111                    //         use #ecs_prefix EnumComponent;
112                    //         self.map(|v| v.to_u32()).into_result()
113                    //     }
114                    // }
115                }
116            } else {
117                quote! {}
118            };
119
120            let message_serde_impl = quote! {
121                impl MessageSerde for #enum_name {
122                    fn serialize_message_part(
123                        &self,
124                        output: &mut Vec<u8>,
125                    ) -> Result<(), MessageSerdeError> {
126                        #ecs_prefix EnumComponent::to_u32(self).serialize_message_part(output)
127                    }
128
129                    fn deserialize_message_part(
130                        input: &mut dyn std::io::Read,
131                    ) -> Result<Self, MessageSerdeError> {
132                        #ecs_prefix EnumComponent::from_u32(u32::deserialize_message_part(input)?)
133                            .ok_or(MessageSerdeError::InvalidValue)
134                    }
135                }
136            };
137
138            Ok(quote! {
139                #main
140                #supported_value
141                #message_serde_impl
142            })
143        })
144        .collect::<anyhow::Result<Vec<_>>>()?;
145
146    if enums.is_empty() {
147        return Ok(quote! {});
148    }
149
150    let includes = context
151        .guest_api_path()
152        .map(|s| quote! { use #s::{global::serde, message::*}; })
153        .unwrap_or(quote! { use serde; use ambient_package_rt::message_serde::*; });
154
155    Ok(quote! {
156        /// Auto-generated type definitions.
157        pub mod types {
158            #includes
159            #(#enums)*
160        }
161    })
162}