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}