diff --git a/iterable b/iterable deleted file mode 160000 index efa07f1c9..000000000 --- a/iterable +++ /dev/null @@ -1 +0,0 @@ -Subproject commit efa07f1c9fdcab42bd2991c3e99bda5932d8e4f4 diff --git a/iterable/Cargo.lock b/iterable/Cargo.lock new file mode 100644 index 000000000..582ac585d --- /dev/null +++ b/iterable/Cargo.lock @@ -0,0 +1,60 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "struct_iterable" +version = "0.1.2" +dependencies = [ + "struct_iterable_derive", + "struct_iterable_internal", +] + +[[package]] +name = "struct_iterable_derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "struct_iterable_internal", + "syn", +] + +[[package]] +name = "struct_iterable_internal" +version = "0.1.1" + +[[package]] +name = "syn" +version = "2.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/iterable/Cargo.toml b/iterable/Cargo.toml new file mode 100644 index 000000000..ba00c67ad --- /dev/null +++ b/iterable/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "struct_iterable" +version = "0.1.2" +authors = ["André de Moraes "] +edition = "2021" +description = "A Rust library providing a proc macro to make a struct iterable." +license = "MIT" +repository = "https://github.com/decomoraes/rust_struct_iterable" +readme = "README.md" +keywords = ["proc-macro", "struct", "iterable"] +categories = ["development-tools::cargo-plugins"] +homepage = "https://github.com/decomoraes/rust_struct_iterable" +documentation = "https://docs.rs/struct_iterable" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +struct_iterable_derive = { path = "./struct_iterable_derive" } +struct_iterable_internal = { path = "./struct_iterable_internal" } + +[lib] +name = "struct_iterable" +path = "src/lib.rs" + +[package.metadata.docs.rs] +all-features = true diff --git a/iterable/README.md b/iterable/README.md new file mode 100644 index 000000000..b7cecc86a --- /dev/null +++ b/iterable/README.md @@ -0,0 +1,105 @@ +# Struct Iterable + +`Struct Iterable` is a Rust library that provides a proc macro to make a struct iterable. This allows you to iterate over the fields of your struct in a generic way, with each iteration returning a tuple containing the name of the field as a static string and a reference to the field's value as a `dyn Any`. + +## How to Use + +First, add `Struct Iterable` to your `Cargo.toml`: + +```toml +[dependencies] +struct_iterable = "0.1.1" +``` + +Next, include the library at the top of your Rust file: + +```rust +use struct_iterable::Iterable; +``` + +Finally, add the `#[derive(Iterable)]` attribute to your struct: + +```rust +#[derive(Iterable)] +struct MyStruct { + field1: u32, + field2: String, + // etc. +} +``` + +Now, you can iterate over the fields of an instance of your struct: + +```rust +let my_instance = MyStruct { + field1: 42, + field2: "Hello, world!".to_string(), +}; + +for (field_name, field_value) in my_instance.iter() { + println!("{}: {:?}", field_name, field_value); +} +``` + +## Limitations +- Only structs with named fields are supported. +- Only structs are supported, not enums or unions. + +## Implementation + +Here is the implementation of the proc macro: + +```rust +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; +use iterable_structs::Iterable; + +#[proc_macro_derive(Iterable)] +pub fn derive_iterable(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let struct_name = input.ident; + let fields = match input.data { + Data::Struct(data_struct) => match data_struct.fields { + Fields::Named(fields_named) => fields_named.named, + _ => panic!("Only structs with named fields are supported"), + }, + _ => panic!("Only structs are supported"), + }; + + let fields_iter = fields.iter().map(|field| { + let field_ident = &field.ident; + let field_name = field_ident.as_ref().unwrap().to_string(); + quote! { + (#field_name, &(self.#field_ident) as &dyn std::any::Any) + } + }); + + let expanded = quote! { + impl Iterable for #struct_name { + fn iter<'a>(&'a self) -> std::vec::IntoIter<(&'static str, &'a dyn std::any::Any)> { + vec![ + #(#fields_iter),* + ].into_iter() + } + } + }; + + TokenStream::from(expanded) +} +``` + +The macro takes in the TokenStream of a struct and expands it into an implementation of the Iterable trait for that struct. This trait provides an iter method that returns an iterator over tuples of field names and values. + +## Contributing and License + +`Struct Iterable` is an open-source project, and contributions are warmly welcomed. Whether you're fixing bugs, improving the documentation, or proposing new features, your efforts are highly appreciated! + +If you're interested in contributing, please feel free to submit a pull request. For major changes, please open an issue first to discuss what you would like to change. + +Please note that this project is released with a Contributor Code of Conduct. By participating in this project, you agree to abide by its terms. + +`Struct Iterable` is distributed under the terms of the MIT license. As such, you're free to use, modify, distribute, and privately use it in any way you see fit, in accordance with the terms of the license. diff --git a/iterable/src/lib.rs b/iterable/src/lib.rs new file mode 100644 index 000000000..c1fb55ed6 --- /dev/null +++ b/iterable/src/lib.rs @@ -0,0 +1,91 @@ +/// The `Iterable` proc macro. +/// +/// This macro provides a convenient way to make a struct iterable. +/// The struct fields' names are returned as static strings and their values as `dyn Any`. +/// This allows to iterate over the struct fields in a generic way. +/// +/// Note that only structs with named fields are supported. +/// +/// # Example +/// +/// ``` +/// use struct_iterable::Iterable; +/// +/// #[derive(Iterable)] +/// struct MyStruct { +/// field1: i32, +/// field2: String, +/// // etc. +/// } +/// +/// let my_instance = MyStruct { +/// field1: 42, +/// field2: "Hello, world!".to_string(), +/// }; +/// +/// for (field_name, field_value) in my_instance.iter() { +/// println!("{}: {:?}", field_name, field_value); +/// } +/// ``` +pub use struct_iterable_derive::Iterable; + +/// The `Iterable` trait. +/// +/// This trait is implemented by the struct once the `Iterable` proc macro is derived. +/// It provides an `iter` method that returns an iterator over tuples of field names and values. +/// +/// # Example +/// +/// ``` +/// use struct_iterable::Iterable; +/// +/// #[derive(Iterable)] +/// struct MyStruct { +/// field1: i32, +/// field2: String, +/// // etc. +/// } +/// +/// let my_instance = MyStruct { +/// field1: 42, +/// field2: "Hello, world!".to_string(), +/// }; +/// +/// for (field_name, field_value) in my_instance.iter() { +/// println!("{}: {:?}", field_name, field_value); +/// } +/// ``` +pub use struct_iterable_internal::Iterable; + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Iterable)] + struct MyStruct { + field1: i32, + field2: String, + // etc. + } + + #[test] + fn it_works() { + let mut my_instance = MyStruct { + field1: 42, + field2: "Hello, world!".to_string(), + }; + + for (field_name, field_value) in my_instance.iter() { + dbg!("{}: {:?}", field_name, field_value); + } + + for (field_name, field_value) in my_instance.iter_mut() { + dbg!("{}: {:?}", field_name, &field_value); + if let Some(i32) = field_value.downcast_mut::() { + *i32 += 1; + dbg!(i32); + } + dbg!("{}: {:?}", field_name, &field_value); + } + } +} diff --git a/iterable/struct_iterable_derive/Cargo.toml b/iterable/struct_iterable_derive/Cargo.toml new file mode 100644 index 000000000..95d4be924 --- /dev/null +++ b/iterable/struct_iterable_derive/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "struct_iterable_derive" +version = "0.1.0" +authors = ["André de Moraes "] +edition = "2021" +description = "An internal crate for struct_iterable" +license = "MIT" + +[lib] +proc-macro = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = "2.0.85" +quote = "1.0.37" +proc-macro2 = "1.0.89" +struct_iterable_internal = { path = "../struct_iterable_internal" } diff --git a/iterable/struct_iterable_derive/README.md b/iterable/struct_iterable_derive/README.md new file mode 100644 index 000000000..5703ba278 --- /dev/null +++ b/iterable/struct_iterable_derive/README.md @@ -0,0 +1,7 @@ +# Struct Iterable Derive + +This crate is a supporting library for the `struct_iterable` crate. It provides the proc macro `Iterable` which is used in conjunction with the `struct_iterable_internal` crate to provide an easy way to make a struct iterable in Rust. + +**Please note:** This crate is not intended to be used directly. If you want to make your structs iterable, please use the `struct_iterable` crate instead. + +Please visit the [`struct_iterable` crate on crates.io](https://crates.io/crates/struct_iterable) for more information and usage examples. diff --git a/iterable/struct_iterable_derive/src/lib.rs b/iterable/struct_iterable_derive/src/lib.rs new file mode 100644 index 000000000..db2a7a01c --- /dev/null +++ b/iterable/struct_iterable_derive/src/lib.rs @@ -0,0 +1,88 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; + +/// The `Iterable` proc macro. +/// +/// Deriving this macro for your struct will make it "iterable". An iterable struct allows you to iterate over its fields, returning a tuple containing the field name as a static string and a reference to the field's value as `dyn Any`. +/// +/// # Limitations +/// +/// - Only structs are supported, not enums or unions. +/// - Only structs with named fields are supported. +/// +/// # Usage +/// +/// Add the derive attribute (`#[derive(Iterable)]`) above your struct definition. +/// +/// ``` +/// use struct_iterable::Iterable; +/// +/// #[derive(Iterable)] +/// struct MyStruct { +/// field1: i32, +/// field2: String, +/// } +/// ``` +/// +/// You can now call the `iter` method on instances of your struct to get an iterator over its fields: +/// +/// ``` +/// let my_instance = MyStruct { +/// field1: 42, +/// field2: "Hello, world!".to_string(), +/// }; +/// +/// for (field_name, field_value) in my_instance.iter() { +/// println!("{}: {:?}", field_name, field_value); +/// } +/// ``` +#[proc_macro_derive(Iterable)] +pub fn derive_iterable(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let struct_name = input.ident; + let fields = match input.data { + Data::Struct(data_struct) => match data_struct.fields { + Fields::Named(fields_named) => fields_named.named, + _ => panic!("Only structs with named fields are supported"), + }, + _ => panic!("Only structs are supported"), + }; + + let fields_iter = fields.iter().map(|field| { + let field_ident = &field.ident; + let field_name = field_ident.as_ref().unwrap().to_string(); + quote! { + (#field_name, &(self.#field_ident) as &dyn std::any::Any) + } + }); + + let fields_iter_mut = fields.iter().map(|field| { + let field_ident = &field.ident; + let field_name = field_ident.as_ref().unwrap().to_string(); + quote! { + (#field_name, &mut (self.#field_ident) as &mut dyn std::any::Any) + } + }); + + let expanded = quote! { + impl Iterable for #struct_name { + fn iter<'a>(&'a self) -> std::vec::IntoIter<(&'static str, &'a dyn std::any::Any)> { + vec![ + #(#fields_iter),* + ].into_iter() + } + + fn iter_mut<'a>(&'a mut self) -> std::vec::IntoIter<(&'static str, &'a mut dyn std::any::Any)> { + vec![ + #(#fields_iter_mut),* + ].into_iter() + } + } + }; + + TokenStream::from(expanded) +} diff --git a/iterable/struct_iterable_internal/Cargo.toml b/iterable/struct_iterable_internal/Cargo.toml new file mode 100644 index 000000000..cdf06702c --- /dev/null +++ b/iterable/struct_iterable_internal/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "struct_iterable_internal" +version = "0.1.1" +authors = ["André de Moraes "] +edition = "2021" +description = "An internal crate for struct_iterable" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/iterable/struct_iterable_internal/README.md b/iterable/struct_iterable_internal/README.md new file mode 100644 index 000000000..462811fe4 --- /dev/null +++ b/iterable/struct_iterable_internal/README.md @@ -0,0 +1,7 @@ +# Struct Iterable Internal + +This crate is a supporting library for the `struct_iterable` crate. It provides the `Iterable` trait which is used in conjunction with the `struct_iterable_derive` crate to provide an easy way to make a struct iterable in Rust. + +**Please note:** This crate is not intended to be used directly. If you want to make your structs iterable, please use the `struct_iterable` crate instead. + +Please visit the [`struct_iterable` crate on crates.io](https://crates.io/crates/struct_iterable) for more information and usage examples. diff --git a/iterable/struct_iterable_internal/src/lib.rs b/iterable/struct_iterable_internal/src/lib.rs new file mode 100644 index 000000000..1b5c7bb1c --- /dev/null +++ b/iterable/struct_iterable_internal/src/lib.rs @@ -0,0 +1,58 @@ +/// The `Iterable` trait. +/// +/// This trait is implemented for structs that derive the `Iterable` proc macro. +/// It provides the `iter` method which returns an iterator over the struct's fields as tuples, containing the field name as a static string and a reference to the field's value as `dyn Any`. +/// +/// You usually don't need to implement this trait manually, as it is automatically derived when using the `#[derive(Iterable)]` proc macro. +/// +/// # Example +/// +/// ``` +/// use struct_iterable::Iterable; +/// +/// #[derive(Iterable)] +/// struct MyStruct { +/// field1: i32, +/// field2: String, +/// } +/// +/// let my_instance = MyStruct { +/// field1: 42, +/// field2: "Hello, world!".to_string(), +/// }; +/// +/// // Iterate over the fields of `my_instance`: +/// for (field_name, field_value) in my_instance.iter() { +/// println!("{}: {:?}", field_name, field_value); +/// } +/// ``` +pub trait Iterable { + /// Returns an iterator over the struct's fields as tuples. + /// + /// Each tuple contains a field's name as a static string and a reference to the field's value as `dyn Any`. + /// + /// # Example + /// + /// ``` + /// use struct_iterable::Iterable; + /// + /// #[derive(Iterable)] + /// struct MyStruct { + /// field1: i32, + /// field2: String, + /// } + /// + /// let my_instance = MyStruct { + /// field1: 42, + /// field2: "Hello, world!".to_string(), + /// }; + /// + /// // Iterate over the fields of `my_instance`: + /// for (field_name, field_value) in my_instance.iter() { + /// println!("{}: {:?}", field_name, field_value); + /// } + /// ``` + fn iter(&self) -> std::vec::IntoIter<(&'static str, &'_ dyn std::any::Any)>; + + fn iter_mut(&mut self) -> std::vec::IntoIter<(&'static str, &'_ mut dyn std::any::Any)>; +} diff --git a/parser/Cargo.lock b/parser/Cargo.lock index 152b6a613..392c171fd 100644 --- a/parser/Cargo.lock +++ b/parser/Cargo.lock @@ -1784,7 +1784,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "struct_iterable" -version = "0.1.1" +version = "0.1.2" dependencies = [ "struct_iterable_derive", "struct_iterable_internal", diff --git a/server/Cargo.lock b/server/Cargo.lock index b76e44281..3c6f7a93f 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -2708,7 +2708,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "struct_iterable" -version = "0.1.1" +version = "0.1.2" dependencies = [ "struct_iterable_derive", "struct_iterable_internal",