summaryrefslogtreecommitdiffstats
path: root/third_party/rust/enum-map-derive/src/derive_struct.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/enum-map-derive/src/derive_struct.rs')
-rw-r--r--third_party/rust/enum-map-derive/src/derive_struct.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/third_party/rust/enum-map-derive/src/derive_struct.rs b/third_party/rust/enum-map-derive/src/derive_struct.rs
new file mode 100644
index 0000000000..94f3f97250
--- /dev/null
+++ b/third_party/rust/enum-map-derive/src/derive_struct.rs
@@ -0,0 +1,129 @@
+// SPDX-FileCopyrightText: 2021 - 2022 Kamila Borowska <kamila@borowska.pw>
+// SPDX-FileCopyrightText: 2021 Bruno CorrĂȘa Zimmermann <brunoczim@gmail.com>
+//
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use crate::type_length;
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{DataStruct, Fields, FieldsNamed, FieldsUnnamed, Ident, Index};
+
+pub fn generate(name: Ident, data_struct: DataStruct) -> TokenStream {
+ StructGenerator::from_fields(&data_struct.fields).finish(&name)
+}
+
+/// Total length is the product of each member's length. To represent a struct, one can
+/// think of this as representing a little-endian number. First member is simply added, but
+/// next members are multiplied before being added.
+#[derive(Debug)]
+struct StructGenerator {
+ length: TokenStream,
+ from_usize: TokenStream,
+ into_usize: TokenStream,
+}
+
+impl StructGenerator {
+ fn from_fields(fields: &Fields) -> Self {
+ match fields {
+ Fields::Unit => Self::from_unit_fields(),
+ Fields::Unnamed(fields_data) => Self::from_unnamed_fields(fields_data),
+ Fields::Named(fields_data) => Self::from_named_fields(fields_data),
+ }
+ }
+
+ fn from_unit_fields() -> Self {
+ Self {
+ length: quote! { 1usize },
+ from_usize: quote! { Self },
+ into_usize: quote! { 0usize },
+ }
+ }
+
+ fn from_unnamed_fields(fields: &FieldsUnnamed) -> Self {
+ let mut params_from = quote! {};
+ let mut into_usize = quote! { 0usize };
+ let mut length = quote! { 1usize };
+ for (i, field) in fields.unnamed.iter().enumerate() {
+ let ty = &field.ty;
+ let index_ident = Index::from(i);
+ let field_length = type_length(ty);
+
+ into_usize = quote! {
+ (#into_usize + #length * ::enum_map::Enum::into_usize(self.#index_ident))
+ };
+
+ params_from = quote! {
+ #params_from <#ty as ::enum_map::Enum>::from_usize(
+ value / #length % #field_length
+ ),
+ };
+
+ length = quote! { (#length * #field_length) };
+ }
+
+ let from_usize = quote! { Self(#params_from) };
+ Self {
+ length,
+ from_usize,
+ into_usize,
+ }
+ }
+
+ fn from_named_fields(fields: &FieldsNamed) -> Self {
+ let mut params_from = quote! {};
+ let mut into_usize = quote! { 0usize };
+ let mut length = quote! { 1usize };
+ for field in fields.named.iter() {
+ let ty = &field.ty;
+ let ident = field.ident.as_ref().unwrap();
+ let field_length = type_length(ty);
+
+ into_usize = quote! {
+ (#into_usize + #length * ::enum_map::Enum::into_usize(self.#ident))
+ };
+
+ params_from = quote! {
+ #params_from #ident: <#ty as ::enum_map::Enum>::from_usize(
+ value / #length % #field_length
+ ),
+ };
+
+ length = quote! { (#field_length * #length) };
+ }
+
+ let from_usize = quote! { Self { #params_from } };
+ Self {
+ length,
+ from_usize,
+ into_usize,
+ }
+ }
+
+ fn finish(&self, name: &Ident) -> TokenStream {
+ let length = &self.length;
+ let from_usize = &self.from_usize;
+ let into_usize = &self.into_usize;
+
+ quote! {
+ #[automatically_derived]
+ impl ::enum_map::Enum for #name {
+ const LENGTH: ::enum_map::usize = #length;
+
+ #[inline]
+ fn from_usize(value: ::enum_map::usize) -> Self {
+ #from_usize
+ }
+
+ #[inline]
+ fn into_usize(self) -> ::enum_map::usize {
+ #into_usize
+ }
+ }
+
+ #[automatically_derived]
+ impl<V> ::enum_map::EnumArray<V> for #name {
+ type Array = [V; #length];
+ }
+ }
+ }
+}