├── .github ├── FUNDING.yml └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── example ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── src ├── lib.rs ├── parse_attribute.rs ├── parse_decorator.rs ├── parse_derive_arraygen.rs ├── parse_gen_array.rs ├── parse_in_array.rs ├── transform_context.rs ├── types.rs └── utils.rs └── tests ├── arraygen_main_tests.rs ├── casting_tests.rs ├── compile-fail ├── declaration-missing-fn.rs ├── declaration-wrong-type.rs ├── derive-on-non-struct.rs ├── derive-on-struct-non-braced.rs ├── implicit-select-all-conflicting-wildcards.rs ├── implicit-select-all-duplicated-fields.rs ├── implicit-select-all-duplicated-types.rs ├── implicit-select-all-duplicated-wildcards.rs ├── implicit-select-all-on-gen-array-with-override-implicit.rs ├── inclusion-bad-tokens.rs ├── inclusion-decorator-empty.rs ├── inclusion-decorator-wrong-kind.rs ├── inclusion-decorator-wrong-method.rs ├── inclusion-decorator-wrong-place.rs ├── inclusion-duplicated-method-for-field.rs ├── inclusion-method-not-declared.rs └── inclusion-wrong-syntax.rs ├── compile-warning ├── derive-alone-test.rs └── empty-method.rs ├── compile_fail_tests.rs ├── implicit_select_all_tests.rs └── trait_objects_tests.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: theypsilon 2 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Build 13 | run: cargo build --verbose 14 | - name: Run tests 15 | run: cargo test --verbose 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | /.idea 13 | *.iml -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | 7 | ## Version 0.3 - 2021-06-17 8 | 9 | ### Added 10 | - *Type Wildcards* for `implicit_select_all` clause. You now can use the character `_` to match all types within the struct. 11 | - Decorators `cast` and `unsafe_transmute` that can be used in the `in_array` attributes and the `implicit_select_all` clause, for converting field types to other types. 12 | 13 | ## Version 0.2 - 2021-05-09 14 | 15 | ### Added 16 | - `implicit_select_all` clause for the `gen_array` attribute. With it, you can select fields by type, without having to use `in_array`. 17 | 18 | ## Version 0.1 - 2019-11-24 19 | 20 | ### Added 21 | - `gen_array`, and `in_array` attributes. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "arraygen" 3 | version = "0.3.2" 4 | authors = ["José Manuel Barroso Galindo "] 5 | license = "MIT" 6 | edition = "2021" 7 | 8 | description = "Derive macro for generating arrays from struct fields." 9 | readme = "README.md" 10 | documentation = "https://docs.rs/arraygen" 11 | 12 | homepage = "https://github.com/theypsilon/arraygen" 13 | repository = "https://github.com/theypsilon/arraygen" 14 | keywords = ["array", "struct", "derive", "field", "iter"] 15 | categories = ["rust-patterns"] 16 | 17 | [lib] 18 | proc-macro = true 19 | 20 | [dependencies] 21 | quote = "1.0.9" 22 | proc-macro2 = "1.0.26" 23 | syn = { version = "1.0.72", features = ["extra-traits"] } 24 | 25 | [dev-dependencies] 26 | compiletest_rs = "0.7.0" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 José Manuel Barroso Galindo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arraygen 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/arraygen.svg)](https://crates.io/crates/arraygen) 4 | [![Docs](https://docs.rs/arraygen/badge.svg)](https://docs.rs/arraygen) 5 | [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/josembarroso.svg?style=social&label=Follow%20%40josembarroso)](https://twitter.com/josembarroso) 6 | Buy Me a Coffee at ko-fi.com' 7 | 8 | This crate provides `Arraygen` derive macro for structs, which generates methods returning arrays filled with the selected struct fields. 9 | 10 | #### Complete example: 11 | 12 | ```rust 13 | #[derive(Arraygen, Debug)] 14 | #[gen_array(fn get_names: &mut String)] 15 | struct Person { 16 | #[in_array(get_names)] 17 | first_name: String, 18 | #[in_array(get_names)] 19 | last_name: String, 20 | } 21 | 22 | let mut person = Person { 23 | first_name: "Ada".into(), 24 | last_name: "Lovelace".into() 25 | }; 26 | 27 | for name in person.get_names() { 28 | *name = name.to_lowercase(); 29 | } 30 | 31 | assert_eq!( 32 | format!("{:?}", person), 33 | "Person { first_name: \"ada\", last_name: \"lovelace\" }" 34 | ); 35 | // PASSES ! 36 | // Notice how it was not lowercased on object creation 37 | // but now it is. 38 | ``` 39 | 40 | As you can see above, the `gen_array` attribute generates a new method returning an array of the indicated type. Additionally, the `in_array` attribute tells which fields are contained within the array returned by that method. 41 | 42 | What `Arraygen` does under the hood is simply generating the following impl: 43 | 44 | ```rust 45 | impl Person { 46 | #[inline(always)] 47 | fn get_names(&mut self) -> [&mut String; 2] { 48 | [&mut self.first_name, &mut self.last_name] 49 | } 50 | } 51 | ``` 52 | 53 | #### The attribute `gen_array`: 54 | 55 | For generating an `Arraygen` method you have to use the attribute `gen_array` on top of the struct. There you will indicate the method name and the return type. Optionally, you can also indicate the visibility or an `implicit_select_all` clause. In the following example you'll see how to tweak the visibility: 56 | 57 | ```rust 58 | #[derive(Arraygen)] 59 | #[gen_array(pub(crate) fn get_strings: &String)] 60 | struct Foo {...} 61 | ``` 62 | 63 | In the code above, the struct `Foo` would have a new method with the following signature: 64 | 65 | ```rust 66 | pub(crate) fn get_strings(&self) -> [&String; ?] {...} 67 | ``` 68 | 69 | #### The attribute `in_array`: 70 | 71 | To fill your `Arraygen` methods with struct fields, you have to use the attribute `in_array` in each struct field you want to include. 72 | 73 | ```rust 74 | // inside a struct 75 | #[in_array(get_strings)] 76 | id: String, 77 | 78 | #[in_array(get_strings, get_names)] 79 | name: String, 80 | ``` 81 | 82 | You have to match the method name used in `gen_array` and `in_array` to include these fields in each generated method. So in this example, assuming `gen_strings` and `get_names` are both generated by `gen_array`, the former will get populated with the fields `id` and `name`, and the latter will get populated with the field `name`. 83 | 84 | It is also possible to entirely omit the attribute `in_array` with the use of an `implicit_select_all` clause. Check the ["implicit_select_all" section in the documentation](https://docs.rs/arraygen/0.3.2/arraygen/derive.Arraygen.html#implicitly-selection-fields-by-their-types) to learn more about this possibility. 85 | 86 | 87 | #### Generating arrays of Trait Objects: 88 | 89 | Trait Objects are fully supported, check the [Trait Objects section in the documentation](https://docs.rs/arraygen/0.3.2/arraygen/derive.Arraygen.html#trait-objects) to see a few working examples. 90 | 91 | #### Implicit selection of Fields by their Types 92 | 93 | With the clause `implicit_select_all`, you may select fields without using `in_array`, check [this section in the documentation](https://docs.rs/arraygen/0.3.2/arraygen/derive.Arraygen.html#implicitly-selection-fields-by-their-types) to see an example. 94 | 95 | 96 | ## Documentation 97 | 98 | For more information, check the [documentation page](https://docs.rs/arraygen). 99 | 100 | ## Usage 101 | 102 | With Cargo, you can add this line to your Cargo.toml: 103 | 104 | ```toml 105 | [dependencies] 106 | arraygen = "0.3.2" 107 | ``` 108 | 109 | ## About the Syntax 110 | 111 | I'm open to change the syntax for the 1.0 version. Participate in the issue [Syntax Proposals](https://github.com/theypsilon/arraygen/issues/1) to give your opinion on this matter. 112 | 113 | ## Known Problems 114 | 115 | Error messages could be improved in a few cases. And there is no proper support for warnings yet. Arraygen prints standard messages instead of warnings at the moment. 116 | 117 | ## GettersByType 118 | 119 | This crate is heavily inspired by [GettersByType](https://github.com/theypsilon/getters-by-type-rs) which is another derive that allows you 120 | to do the same thing. But that one is more opinionated, less flexible, and less powerful, with the only advantage of being less verbose. That's 121 | why I decided to work on this new solution. 122 | 123 | ## License 124 | 125 | MIT 126 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | /.idea 13 | *.iml -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example" 3 | version = "0.1.0" 4 | authors = ["José Manuel Barroso Galindo "] 5 | edition = "2021" 6 | 7 | [dependencies.arraygen] 8 | path = ".." 9 | -------------------------------------------------------------------------------- /example/src/main.rs: -------------------------------------------------------------------------------- 1 | use arraygen::Arraygen; 2 | 3 | fn main() { 4 | read_prices(); 5 | to_lowercase(); 6 | call_trait_objects(); 7 | implicit_select_all(); 8 | casts(); 9 | } 10 | 11 | fn read_prices() { 12 | let prices = Prices { 13 | water: 1.0, 14 | oil: 3.0, 15 | tomato: 2.0, 16 | chocolate: 4.0, 17 | }; 18 | 19 | println!( 20 | "Sum of all prices: {}", 21 | prices.get_all_prices().into_iter().sum::() 22 | ); 23 | } 24 | 25 | #[derive(Arraygen)] 26 | #[gen_array(fn get_all_prices: f32)] 27 | struct Prices { 28 | #[in_array(get_all_prices)] 29 | pub water: f32, 30 | #[in_array(get_all_prices)] 31 | pub oil: f32, 32 | #[in_array(get_all_prices)] 33 | pub tomato: f32, 34 | #[in_array(get_all_prices)] 35 | pub chocolate: f32, 36 | } 37 | 38 | fn to_lowercase() { 39 | let mut person = Person { 40 | first_name: "ADa".into(), 41 | last_name: "LoVElaCE".into(), 42 | }; 43 | 44 | for name in person.get_names() { 45 | *name = name.to_lowercase(); 46 | } 47 | 48 | println!("Lowercase Ada name is: {}", person); 49 | } 50 | 51 | #[derive(Arraygen)] 52 | #[gen_array(fn get_names: &mut String)] 53 | struct Person { 54 | #[in_array(get_names)] 55 | pub first_name: String, 56 | #[in_array(get_names)] 57 | pub last_name: String, 58 | } 59 | 60 | impl std::fmt::Display for Person { 61 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { 62 | write!(fmt, "{} {}", self.first_name, self.last_name) 63 | } 64 | } 65 | 66 | fn call_trait_objects() { 67 | let animals = Animals { 68 | dogo: Dog {}, 69 | tiger: Cat {}, 70 | bacon: Pig {}, 71 | }; 72 | 73 | let talk = animals 74 | .get_animals() 75 | .into_iter() 76 | .map(|animal| animal.talk()) 77 | .collect::>() 78 | .join(", "); 79 | 80 | println!("Animals say: {}", talk); 81 | } 82 | 83 | trait Animal { 84 | fn talk(&self) -> &'static str; 85 | } 86 | struct Dog {} 87 | impl Animal for Dog { 88 | fn talk(&self) -> &'static str { 89 | "bark" 90 | } 91 | } 92 | struct Cat {} 93 | impl Animal for Cat { 94 | fn talk(&self) -> &'static str { 95 | "meow" 96 | } 97 | } 98 | struct Pig {} 99 | impl Animal for Pig { 100 | fn talk(&self) -> &'static str { 101 | "oink" 102 | } 103 | } 104 | 105 | #[derive(Arraygen)] 106 | #[gen_array(fn get_animals: &dyn Animal)] 107 | struct Animals { 108 | #[in_array(get_animals)] 109 | dogo: Dog, 110 | #[in_array(get_animals)] 111 | tiger: Cat, 112 | #[in_array(get_animals)] 113 | bacon: Pig, 114 | } 115 | 116 | fn implicit_select_all() { 117 | implicit_select_all_prices(); 118 | implicit_select_all_animals(); 119 | implicit_select_all_with_wildcards(); 120 | } 121 | 122 | fn implicit_select_all_prices() { 123 | let prices = ImplicitPrices { 124 | water: 2.0, 125 | oil: 4.0, 126 | tomato: 3.0, 127 | chocolate: 5.0, 128 | }; 129 | 130 | println!( 131 | "Sum of all implicit prices: {}", 132 | prices.get_all_prices().into_iter().sum::() 133 | ); 134 | } 135 | 136 | #[derive(Arraygen)] 137 | #[gen_array(fn get_all_prices: f32, implicit_select_all: f32)] 138 | struct ImplicitPrices { 139 | pub water: f32, 140 | pub oil: f32, 141 | pub tomato: f32, 142 | pub chocolate: f32, 143 | } 144 | 145 | fn implicit_select_all_animals() { 146 | let animals = ImplicitAnimals { 147 | dogo: Dog {}, 148 | tiger: Cat {}, 149 | bacon: Pig {}, 150 | }; 151 | 152 | let talk = animals 153 | .get_animals() 154 | .into_iter() 155 | .map(|animal| animal.talk()) 156 | .collect::>() 157 | .join(", "); 158 | 159 | println!("Implicit animals say: {}", talk); 160 | } 161 | 162 | #[derive(Arraygen)] 163 | #[gen_array(fn get_animals: &dyn Animal, implicit_select_all: Dog, Cat, Pig)] 164 | struct ImplicitAnimals { 165 | bacon: Pig, 166 | dogo: Dog, 167 | tiger: Cat, 168 | } 169 | 170 | fn implicit_select_all_with_wildcards() { 171 | let mut options = Options { 172 | a: Some(1), 173 | b: Some(true), 174 | c: 3.0, 175 | }; 176 | 177 | println!("Options before reset: {:?}", options); 178 | options.options().into_iter().for_each(|o| o.reset()); 179 | println!("Options after reset: {:?}", options); 180 | } 181 | 182 | impl ResetOption for Option { 183 | fn reset(&mut self) { 184 | *self = None; 185 | } 186 | } 187 | 188 | trait ResetOption { 189 | fn reset(&mut self); 190 | } 191 | 192 | #[derive(Arraygen, Debug)] 193 | #[gen_array(fn options: &mut dyn ResetOption, implicit_select_all: Option<_>)] 194 | struct Options { 195 | pub a: Option, 196 | pub b: Option, 197 | pub c: f32, 198 | } 199 | 200 | fn casts() { 201 | let numbers = Numbers { 202 | a: -1, 203 | b: 1.0, 204 | c: -1.0, 205 | d: -1.0, 206 | }; 207 | 208 | println!("{:?} casted to uints: {:?}", numbers, numbers.uints()); 209 | } 210 | 211 | #[derive(Arraygen, Debug)] 212 | #[gen_array(fn uints: u32, implicit_select_all { cast }: _)] 213 | struct Numbers { 214 | pub a: i32, 215 | pub b: f32, 216 | pub c: f32, 217 | #[in_array(uints { override_implicit, unsafe_transmute })] 218 | pub d: f32, 219 | } 220 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! This crate provides `Arraygen` derive macro for structs, which generates methods returning arrays filled with the selected struct fields. 3 | //! 4 | //! Complete example: 5 | //! 6 | //! ```rust 7 | //! use arraygen::Arraygen; 8 | //! 9 | //! #[derive(Arraygen, Debug)] 10 | //! #[gen_array(fn get_names: &mut String)] 11 | //! struct Person { 12 | //! #[in_array(get_names)] 13 | //! first_name: String, 14 | //! #[in_array(get_names)] 15 | //! last_name: String, 16 | //! } 17 | //! 18 | //! let mut person = Person { 19 | //! first_name: "Ada".into(), 20 | //! last_name: "Lovelace".into() 21 | //! }; 22 | //! 23 | //! for name in person.get_names() { 24 | //! *name = name.to_lowercase(); 25 | //! } 26 | //! 27 | //! assert_eq!( 28 | //! format!("{:?}", person), 29 | //! "Person { first_name: \"ada\", last_name: \"lovelace\" }" 30 | //! ); 31 | //! ``` 32 | //! 33 | //! As you can see above, the attribute `gen_array` generates a new method returning an array of the given type. 34 | //! And then, the attribute `in_array` indicates the fields to be included by that method. In this case, the 35 | //! generated method 'get_names' will return an array including references to all the fields of the struct. 36 | //! 37 | //! As you might have guessed, what `Arraygen` does under the hood is simply generating the following impl: 38 | //! 39 | //! ```rust 40 | //! # struct Person { 41 | //! # first_name: String, 42 | //! # last_name: String, 43 | //! # } 44 | //! impl Person { 45 | //! #[inline(always)] 46 | //! fn get_names(&mut self) -> [&mut String; 2] { 47 | //! [&mut self.first_name, &mut self.last_name] 48 | //! } 49 | //! } 50 | //! ``` 51 | 52 | #![allow(clippy::eval_order_dependence)] 53 | 54 | extern crate proc_macro; 55 | 56 | use proc_macro::TokenStream; 57 | 58 | const DERIVE_NAME: &str = "Arraygen"; 59 | const DECL_FN_NAME: &str = "gen_array"; 60 | const FIELD_SELECTOR_NAME: &str = "in_array"; 61 | const IMPLICIT_SELECT_ALL_NAME: &str = "implicit_select_all"; 62 | 63 | /// The `Arraygen` derive allows you to use the attribute `gen_array` at the struct level, and the attribute `in_array` in each contained field. 64 | /// 65 | /// 66 | /// # gen_array 67 | /// 68 | /// With `gen_array` you can declare your `Arraygen` methods in the following way: 69 | /// 70 | /// ```ignore 71 | /// #[gen_array(?visibility fn your_method_name: YourReturnType)] 72 | /// ``` 73 | /// 74 | /// * **?visibility**: This placeholder is optional. You can let it blank entirely. Or you can write `pub`, `pub(crate)`, or any other pub variant. 75 | /// * **your_method_name**: This is meant to be any valid method name, following the standard rules. You can't use a name taken by another method in the struct impl. This restriction also includes other `Arraygen` methods. 76 | /// * **YourReturnType**: The return type can be any Rust type that can appear in a struct field. Notice that if the `type` does not implement the trait `Copy`, you are better returning `&type` or `&mut type` instead, to avoid ownership errors. 77 | /// 78 | /// There is no limit to the number of methods you can declare. 79 | /// 80 | /// By default, these new `Arraygen` methods return arrays of length 0. That's not very useful, but that's why we also have the next attribute: `in_array`. 81 | /// 82 | /// 83 | /// # in_array 84 | /// 85 | /// With `in_array` you select which field is returned by which method generated by `gen_array`. 86 | /// 87 | /// ```ignore 88 | /// #[in_array(your_method_name)] 89 | /// ``` 90 | /// 91 | /// * `your_method_name`: This needs to match the name of some method declared in the same struct by the `gen_array` attribute. 92 | /// 93 | /// 94 | /// This is the way to fill up your `Arraygen` methods. The only thing you need to care about is that the type returned by `your_method_name` needs to be compatible with the type of the field with the `in_array` attribute. 95 | /// 96 | /// Notice that in Rust, non-reference field types can be returned as references, but not the other way around. Or in other words. This is good: 97 | /// 98 | /// ```rust 99 | /// # use arraygen::Arraygen; 100 | /// #[derive(Arraygen)] 101 | /// #[gen_array(fn references: &i32)] 102 | /// struct Test { 103 | /// #[in_array(references)] 104 | /// data: i32 105 | /// } 106 | /// ``` 107 | /// 108 | /// But this is bad: 109 | /// 110 | /// ```compile_fail 111 | /// # use arraygen::Arraygen; 112 | /// #[derive(Arraygen)] 113 | /// #[gen_array(fn non_references: i32)] 114 | /// struct Test<'a> { 115 | /// #[in_array(non_references)] 116 | /// data: &'a i32 117 | /// } 118 | /// ``` 119 | /// 120 | /// Is also good to know that the same field can be included in many `Arraygen` methods, not just in only one. 121 | /// You will see what I mean by checking the following example: 122 | /// 123 | /// ```rust 124 | /// # use arraygen::Arraygen; 125 | /// #[derive(Arraygen)] 126 | /// #[gen_array(fn odds: i32)] 127 | /// #[gen_array(fn evens: i32)] 128 | /// #[gen_array(fn primes: i32)] 129 | /// struct Numbers { 130 | /// #[in_array(odds)] 131 | /// one: i32, 132 | /// 133 | /// #[in_array(evens)] 134 | /// #[in_array(primes)] 135 | /// two: i32, 136 | /// 137 | /// #[in_array(odds, primes)] // This syntax is also valid, by the way. 138 | /// three: i32, 139 | /// 140 | /// #[in_array(evens)] 141 | /// four: i32, 142 | /// 143 | /// #[in_array(odds, primes)] 144 | /// five: i32 145 | /// } 146 | /// 147 | /// let numbers = Numbers { 148 | /// one: 1, 149 | /// two: 2, 150 | /// three: 3, 151 | /// four: 4, 152 | /// five: 5 153 | /// }; 154 | /// 155 | /// assert_eq!(numbers.odds(), [1, 3, 5]); 156 | /// assert_eq!(numbers.evens(), [2, 4]); 157 | /// assert_eq!(numbers.primes(), [2, 3, 5]); 158 | /// ``` 159 | /// 160 | /// Additionally, you may also add decorators to your `in_array` attribute. 161 | /// 162 | /// ```ignore 163 | /// #[in_array(your_method_name { comma_separated_decorators })] 164 | /// ``` 165 | /// 166 | /// Possible decorators are: 167 | /// 168 | /// * **cast** : This decorator casts the current field to the return type of the `gen_array` method where it will be included. 169 | /// * **unsafe_transmute** : This one uses [`unsafe { std::mem::transmute }`](https://doc.rust-lang.org/std/mem/fn.transmute.html) to force an unsafe cast of the current field to the return type of the `gen_array` method. 170 | /// * **override_implicit** : In case the current field is already selected by an `implicit_select_all` clause for this `gen_array` (more about this clause later), you may use `override_implicit` to apply different decorators to the current field. 171 | /// 172 | /// Casting example: 173 | /// 174 | /// ```rust 175 | /// # use arraygen::Arraygen; 176 | /// #[derive(Arraygen)] 177 | /// #[gen_array(fn all: i32)] 178 | /// struct Numbers { 179 | /// #[in_array(all { cast })] 180 | /// one: f32, 181 | /// 182 | /// #[in_array(all { cast })] 183 | /// two: u8, 184 | /// 185 | /// #[in_array(all { cast })] 186 | /// three: bool, 187 | /// } 188 | /// 189 | /// let numbers = Numbers { 190 | /// one: 1.0, 191 | /// two: 1, 192 | /// three: true 193 | /// }; 194 | /// 195 | /// assert_eq!(numbers.all(), [1, 1, 1]); 196 | /// 197 | /// ``` 198 | /// 199 | /// 200 | /// 201 | /// # Trait Objects 202 | /// 203 | /// A very good use-case for `Arraygen` consists of extracting [Trait Objects](https://doc.rust-lang.org/reference/types/trait-object.html) from different concrete types, so you can operate in all of them at once. 204 | /// 205 | /// ```rust 206 | /// # use arraygen::Arraygen; 207 | /// trait Animal { 208 | /// fn talk(&self) -> &'static str; 209 | /// } 210 | /// # struct Dog {} 211 | /// # impl Animal for Dog { 212 | /// # fn talk(&self) -> &'static str { 213 | /// # "bark" 214 | /// # } 215 | /// # } 216 | /// # struct Cat {} 217 | /// # impl Animal for Cat { 218 | /// # fn talk(&self) -> &'static str { 219 | /// # "meow" 220 | /// # } 221 | /// # } 222 | /// # struct Pig {} 223 | /// # impl Animal for Pig { 224 | /// # fn talk(&self) -> &'static str { 225 | /// # "oink" 226 | /// # } 227 | /// # } 228 | /// 229 | /// #[derive(Arraygen)] 230 | /// #[gen_array(fn get_animals: &dyn Animal)] 231 | /// struct Animals { 232 | /// #[in_array(get_animals)] 233 | /// dogo: Dog, 234 | /// #[in_array(get_animals)] 235 | /// tiger: Cat, 236 | /// #[in_array(get_animals)] 237 | /// bacon: Pig, 238 | /// } 239 | /// 240 | /// let animals = Animals { 241 | /// dogo: Dog {}, 242 | /// tiger: Cat {}, 243 | /// bacon: Pig {} 244 | /// }; 245 | /// 246 | /// let talk: Vec<&'static str> = animals 247 | /// .get_animals() 248 | /// .iter() 249 | /// .map(|animal| animal.talk()) 250 | /// .collect(); 251 | /// 252 | /// assert_eq!(talk, ["bark", "meow", "oink"]); 253 | /// ``` 254 | /// 255 | /// And a more realistic example could be this other one: 256 | /// 257 | /// ``` 258 | /// # use arraygen::Arraygen; 259 | /// trait SetNone { 260 | /// fn set_none(&mut self); 261 | /// } 262 | /// 263 | /// impl SetNone for Option { 264 | /// fn set_none(&mut self) { 265 | /// *self = None; 266 | /// } 267 | /// } 268 | /// 269 | /// #[derive(Arraygen)] 270 | /// #[gen_array(fn ephemeral_options: &mut dyn SetNone)] 271 | /// struct ManyOptions { 272 | /// #[in_array(ephemeral_options)] 273 | /// a: Option, 274 | /// #[in_array(ephemeral_options)] 275 | /// b: Option, 276 | /// c: Option, 277 | /// } 278 | /// 279 | /// let mut many = ManyOptions { 280 | /// a: Some(42), 281 | /// b: Some(String::from("foo")), 282 | /// c: Some(String::from("bar")) 283 | /// }; 284 | /// 285 | /// for option in many.ephemeral_options() { 286 | /// option.set_none(); 287 | /// } 288 | /// 289 | /// assert_eq!(many.a, None); 290 | /// assert_eq!(many.b, None); 291 | /// assert_eq!(many.c, Some(String::from("bar"))); 292 | /// ``` 293 | /// 294 | /// With ad-hoc traits and `Arraygen` is very easy to generalize common transformations with simple one-liners. 295 | /// 296 | /// 297 | /// # Implicit selection of Fields by their Types 298 | /// 299 | /// You may omit entirely the `in_array` attribute if you add the `implicit_select_all` clause at the end of your `gen_array` declarations. 300 | /// 301 | /// ```ignore 302 | /// #[gen_array(?visibility fn your_method_name: YourReturnType, implicit_select_all: Type1, Type2, Type3)] 303 | /// ``` 304 | /// 305 | /// You may place either a single type in your `implicit_select_all` clause or a list of comma-separated types. 306 | /// 307 | /// As an example, by adding "`implicit_select_all: f32`" to our `gen_array` method, we'll add all the fields that are of type `f32` in the current `struct`, as shown in the following code: 308 | /// 309 | /// ```rust 310 | /// # use arraygen::Arraygen; 311 | /// #[derive(Arraygen)] 312 | /// #[gen_array(fn get_all_prices: f32, implicit_select_all: f32)] 313 | /// struct ImplicitPrices { 314 | /// pub water: f32, 315 | /// pub oil: f32, 316 | /// pub tomato: f32, 317 | /// pub chocolate: f32, 318 | /// } 319 | /// 320 | /// let prices = ImplicitPrices { 321 | /// water: 2.0, 322 | /// oil: 4.0, 323 | /// tomato: 3.0, 324 | /// chocolate: 5.0, 325 | /// }; 326 | /// 327 | /// assert_eq!(prices.get_all_prices().iter().sum::(), 14.0); 328 | /// ``` 329 | /// 330 | /// The `implicit_select_all` clause may also include decorators: 331 | /// ```ignore 332 | /// #[gen_array(?visibility fn your_method_name: YourReturnType, implicit_select_all { comma_separated_decorators }: MatchingFieldTypes)] 333 | /// ``` 334 | /// 335 | /// See which decorators you may use in the previous `in_array` section. 336 | /// 337 | /// # Implicit selection of Fields with Type Wildcards 338 | /// 339 | /// You may use *Type Wildcards* (`_`) on the `implicit_select_all` clause. 340 | /// 341 | /// For example, the next expression will match all fields regardless of their type (this might be used in conjunction with decorators for casting between types): 342 | /// 343 | /// ```ignore 344 | /// #[gen_array(fn all_fields: f32, implicit_select_all: _)] 345 | /// ``` 346 | /// 347 | /// *Type Wildcards* may also be used within a more complex *Type* definition, like `Option < _ >` or `Result < f32, _ >`. 348 | /// 349 | /// Example: 350 | /// 351 | /// ```rust 352 | /// # use arraygen::Arraygen; 353 | /// #[derive(Arraygen, Debug)] 354 | /// #[gen_array(fn options: &mut dyn ResetOption, implicit_select_all: Option<_>)] 355 | /// struct Options { 356 | /// pub a: Option, 357 | /// pub b: Option 358 | /// } 359 | /// 360 | /// impl ResetOption for Option { 361 | /// fn reset(&mut self) { 362 | /// *self = None; 363 | /// } 364 | /// } 365 | 366 | /// trait ResetOption { 367 | /// fn reset(&mut self); 368 | /// } 369 | /// 370 | /// let mut options = Options { 371 | /// a: Some(1), 372 | /// b: Some(true) 373 | /// }; 374 | /// 375 | /// options.options().into_iter().for_each(|o| o.reset()); 376 | /// assert_eq!(format!("{:?}", options), "Options { a: None, b: None }"); 377 | /// ``` 378 | /// 379 | /// As you may see above, using *Type Wildcards* in conjuction with [Trait Objects](#trait-objects) allows you to accomplish very powerful constructs in a very succinct manner. 380 | /// 381 | 382 | #[proc_macro_derive(Arraygen, attributes(gen_array, in_array))] 383 | pub fn arraygen(input: TokenStream) -> TokenStream { 384 | transform_context::transform_ast(input) 385 | } 386 | 387 | mod parse_attribute; 388 | mod parse_decorator; 389 | mod parse_derive_arraygen; 390 | mod parse_gen_array; 391 | mod parse_in_array; 392 | mod transform_context; 393 | mod types; 394 | mod utils; 395 | -------------------------------------------------------------------------------- /src/parse_attribute.rs: -------------------------------------------------------------------------------- 1 | use syn::parse::{ParseStream, Result}; 2 | use syn::Token; 3 | use syn::{bracketed, AttrStyle, Attribute, Path}; 4 | 5 | pub fn single_parse_outer_attribute(input: ParseStream) -> Result<()> { 6 | let content; 7 | let _ = Attribute { 8 | pound_token: input.parse()?, 9 | style: AttrStyle::Outer, 10 | bracket_token: bracketed!(content in input), 11 | path: content.call(Path::parse_mod_style)?, 12 | tokens: content.parse()?, 13 | }; 14 | Ok(()) 15 | } 16 | 17 | pub fn parse_inner_attributes(input: ParseStream) -> Result<()> { 18 | while input.peek(Token![#]) && input.peek(Token![!]) { 19 | input.call(single_parse_inner_attribute)?; 20 | } 21 | Ok(()) 22 | } 23 | 24 | fn single_parse_inner_attribute(input: ParseStream) -> Result<()> { 25 | let content; 26 | let _ = Attribute { 27 | pound_token: input.parse()?, 28 | style: AttrStyle::Inner(input.parse()?), 29 | bracket_token: bracketed!(content in input), 30 | path: content.call(Path::parse_mod_style)?, 31 | tokens: content.parse()?, 32 | }; 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /src/parse_decorator.rs: -------------------------------------------------------------------------------- 1 | use syn::parse::{Parse, ParseStream, Result}; 2 | use syn::punctuated::Punctuated; 3 | use syn::token; 4 | use syn::{braced, Error, Ident, Token}; 5 | 6 | use crate::FIELD_SELECTOR_NAME; 7 | 8 | #[derive(Clone, PartialEq)] 9 | pub enum CastKind { 10 | SafeCast, 11 | UnsafeTransmute, 12 | } 13 | 14 | pub struct Decorator { 15 | pub override_implicit: bool, 16 | pub cast: Option, 17 | } 18 | 19 | impl Decorator { 20 | pub fn new() -> Decorator { 21 | Decorator { 22 | override_implicit: false, 23 | cast: None, 24 | } 25 | } 26 | } 27 | 28 | impl Parse for Decorator { 29 | fn parse(input: ParseStream) -> Result { 30 | let mut decorator = Decorator::new(); 31 | if input.peek(token::Brace) { 32 | let content; 33 | let _ = braced!(content in input); 34 | for ident in Punctuated::::parse_separated_nonempty(&content)?.iter() { 35 | match ident.to_string().as_ref() { 36 | "override_implicit" if !decorator.override_implicit => { 37 | decorator.override_implicit = true 38 | } 39 | "cast" if decorator.cast.is_none() => decorator.cast = Some(CastKind::SafeCast), 40 | "unsafe_transmute" if decorator.cast.is_none() => { 41 | decorator.cast = Some(CastKind::UnsafeTransmute) 42 | } 43 | decorator => { 44 | return Err(Error::new_spanned( 45 | ident, 46 | format!( 47 | "{} doesn't allow '{}' as decorator here", 48 | FIELD_SELECTOR_NAME, decorator 49 | ), 50 | )) 51 | } 52 | }; 53 | } 54 | } 55 | Ok(decorator) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/parse_derive_arraygen.rs: -------------------------------------------------------------------------------- 1 | use crate::DECL_FN_NAME; 2 | use std::collections::HashMap; 3 | use syn::parse::{Parse, ParseStream, Result}; 4 | use syn::token; 5 | use syn::{braced, Error, Generics, Ident, Token, Visibility, WhereClause}; 6 | 7 | use crate::parse_attribute::parse_inner_attributes; 8 | use crate::parse_gen_array::{parse_gen_arrays, GenArray}; 9 | use crate::parse_in_array::{ 10 | parse_in_array_fields, InArrayElement, InArrayElementKind, InArrayField, 11 | }; 12 | use crate::types::ty_inferred_by; 13 | 14 | pub struct DeriveArraygen { 15 | pub gen_arrays: HashMap, 16 | pub vis: Visibility, 17 | pub struct_name: Ident, 18 | pub generics: Generics, 19 | } 20 | 21 | impl Parse for DeriveArraygen { 22 | fn parse(input: ParseStream) -> Result { 23 | let mut gen_arrays = input.call(parse_gen_arrays)?; 24 | let vis = input.parse::()?; 25 | 26 | let lookahead = input.lookahead1(); 27 | if !lookahead.peek(Token![struct]) { 28 | return Err(input.error("derive 'Arraygen' should only be used with braced structs")); 29 | } 30 | 31 | let _ = input.parse::()?; 32 | let struct_name = input.parse::()?; 33 | let generics = input.parse::()?; 34 | let where_clause = parse_struct(input, &mut gen_arrays)?; 35 | 36 | Ok(DeriveArraygen { 37 | gen_arrays, 38 | vis, 39 | struct_name, 40 | generics: Generics { 41 | where_clause, 42 | ..generics 43 | }, 44 | }) 45 | } 46 | } 47 | 48 | pub fn parse_struct( 49 | input: ParseStream, 50 | gen_arrays: &mut HashMap, 51 | ) -> Result> { 52 | let mut lookahead = input.lookahead1(); 53 | let mut where_clause = None; 54 | if lookahead.peek(Token![where]) { 55 | where_clause = Some(input.parse()?); 56 | lookahead = input.lookahead1(); 57 | } 58 | 59 | if lookahead.peek(token::Brace) { 60 | parse_braced_struct(input, gen_arrays)?; 61 | Ok(where_clause) 62 | } else { 63 | Err(input.error("derive 'Arraygen' should only be used with braced structs")) 64 | } 65 | } 66 | 67 | pub(crate) fn parse_braced_struct( 68 | input: ParseStream, 69 | gen_arrays: &mut HashMap, 70 | ) -> Result<()> { 71 | let content; 72 | let _ = braced!(content in input); 73 | parse_inner_attributes(&content)?; 74 | for iaf in content 75 | .parse_terminated::(parse_in_array_fields)? 76 | .into_iter() 77 | { 78 | for (_, ga) in gen_arrays.iter_mut() { 79 | for implicit_ty in ga.implicit_select_all_tys.iter() { 80 | if ty_inferred_by(&iaf.ty, implicit_ty) { 81 | ga.fields.push(InArrayElement { 82 | ident: iaf.ident.clone(), 83 | ty: iaf.ty.clone(), 84 | cast: ga.implicit_select_all_decorator.cast.clone(), 85 | kind: InArrayElementKind::Implicit, 86 | }); 87 | } 88 | } 89 | } 90 | for attr in iaf.attrs.iter() { 91 | for entry in attr.entries.iter() { 92 | if let Some(ga) = gen_arrays.get_mut(&entry.ident) { 93 | let iae = ga.fields.iter().find(|iae| iae.ident == iaf.ident); 94 | if matches!(iae, Some(iae) if iae.kind != InArrayElementKind::Implicit || (iae.kind == InArrayElementKind::Implicit && !entry.decorator.override_implicit)) 95 | { 96 | return Err(Error::new_spanned( 97 | entry.ident.clone(), 98 | format!( 99 | "Field '{}' is already included in {} method '{}'", 100 | iaf.ident.to_string(), 101 | DECL_FN_NAME, 102 | entry.ident.to_string() 103 | ), 104 | )); 105 | } else { 106 | if let Some(ident) = iae.map(|iae| iae.ident.clone()) { 107 | ga.fields.retain(|field| field.ident != ident); 108 | } 109 | 110 | ga.fields.push(InArrayElement { 111 | ident: iaf.ident.clone(), 112 | ty: iaf.ty.clone(), 113 | cast: entry.decorator.cast.clone(), 114 | kind: InArrayElementKind::InArray, 115 | }); 116 | } 117 | } else { 118 | return Err(Error::new_spanned( 119 | entry.ident.clone(), 120 | format!( 121 | "{} method '{}' not present but used by field '{}'", 122 | DECL_FN_NAME, 123 | entry.ident.to_string(), 124 | iaf.ident.to_string() 125 | ), 126 | )); 127 | } 128 | } 129 | } 130 | } 131 | Ok(()) 132 | } 133 | -------------------------------------------------------------------------------- /src/parse_gen_array.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use std::collections::HashMap; 3 | use syn::parse::{Parse, ParseStream, Result}; 4 | use syn::token; 5 | use syn::{bracketed, parenthesized, Error, Ident, Path, Token, Type, Visibility}; 6 | 7 | use crate::parse_attribute::single_parse_outer_attribute; 8 | use crate::parse_decorator::{CastKind, Decorator}; 9 | use crate::parse_in_array::InArrayElement; 10 | use crate::types::are_matching_types; 11 | use crate::{DECL_FN_NAME, IMPLICIT_SELECT_ALL_NAME}; 12 | 13 | pub struct GenArray { 14 | pub vis: Visibility, 15 | pub fn_name: Ident, 16 | pub fn_ty: Type, 17 | pub is_mut: bool, 18 | pub is_ref: bool, 19 | pub implicit_select_all_tys: Vec, 20 | pub implicit_select_all_decorator: Decorator, 21 | pub casts: Vec<(syn::Ident, syn::Type, proc_macro2::TokenStream, CastKind)>, 22 | pub fields: Vec, 23 | } 24 | 25 | pub fn parse_gen_arrays(input: ParseStream) -> Result> { 26 | let mut gen_arrays = HashMap::new(); 27 | while input.peek(Token![#]) { 28 | if let Ok(true) = is_gen_array(&input.fork()) { 29 | let gen_array = input.call(single_parse_gen_array)?; 30 | gen_arrays.insert(gen_array.fn_name.clone(), gen_array); 31 | } else { 32 | input.call(single_parse_outer_attribute)?; 33 | } 34 | } 35 | Ok(gen_arrays) 36 | } 37 | 38 | pub fn single_parse_gen_array(input: ParseStream) -> Result { 39 | let content; 40 | let _: Token![#] = input.parse()?; 41 | let _: token::Bracket = bracketed!(content in input); 42 | let path: Path = content.call(Path::parse_mod_style)?; 43 | 44 | if path.segments.len() != 1 { 45 | return Err(input.error(format!("Wrong syntax for {}", DECL_FN_NAME))); 46 | } 47 | 48 | content.call(parse_gen_array_group) 49 | } 50 | 51 | pub fn is_gen_array(input: ParseStream) -> Result { 52 | let content; 53 | let _: Token![#] = input.parse()?; 54 | let _: token::Bracket = bracketed!(content in input); 55 | let path: Path = content.call(Path::parse_mod_style)?; 56 | 57 | Ok(!path.segments.is_empty() && path.segments[0].ident == DECL_FN_NAME) 58 | } 59 | 60 | pub fn parse_gen_array_group(input: ParseStream) -> Result { 61 | let content; 62 | let _ = parenthesized!(content in input); 63 | let vis: Visibility = content.parse()?; 64 | let _: Token![fn] = content.parse()?; 65 | let fn_name: Ident = content.parse()?; 66 | let _: Token![:] = content.parse()?; 67 | let fn_ty: Type = content.parse()?; 68 | 69 | let (is_ref, is_mut) = if let Type::Reference(ref reference) = fn_ty { 70 | (true, reference.mutability.is_some()) 71 | } else { 72 | (false, false) 73 | }; 74 | 75 | let mut implicit_select_all_tys = vec![]; 76 | let mut implicit_select_all_decorator = Decorator::new(); 77 | 78 | if content.peek(Token![,]) && content.peek2(syn::Ident) { 79 | let _: Token![,] = content.parse()?; 80 | let implicit: syn::Ident = content.parse()?; 81 | if implicit != IMPLICIT_SELECT_ALL_NAME { 82 | return Err(content.error(format!("clause '{}' not recognised", implicit))); 83 | } 84 | 85 | implicit_select_all_decorator = content.parse::()?; 86 | if implicit_select_all_decorator.override_implicit { 87 | return Err(Error::new_spanned( 88 | implicit, 89 | format!( 90 | "{} method '{}' contains {} clause with forbidden decorator 'override_implicit'", 91 | DECL_FN_NAME, 92 | fn_name, 93 | IMPLICIT_SELECT_ALL_NAME 94 | ), 95 | )); 96 | } 97 | 98 | let _: Token![:] = content.parse::()?; 99 | implicit_select_all_tys = content 100 | .parse_terminated::(Type::parse)? 101 | .into_iter() 102 | .collect::>(); 103 | 104 | if implicit_select_all_tys.is_empty() { 105 | return Err(content.error("missing type to select")); 106 | } 107 | 108 | for (i, ty_left) in implicit_select_all_tys.iter().enumerate() { 109 | for ty_right in implicit_select_all_tys.iter().skip(i + 1) { 110 | if are_matching_types(ty_left, ty_right) { 111 | return Err(Error::new_spanned( 112 | ty_right, 113 | format!( 114 | "{} method '{}' contains {} clause with duplicated '{}' type", 115 | DECL_FN_NAME, 116 | fn_name, 117 | IMPLICIT_SELECT_ALL_NAME, 118 | quote! { #ty_right }.to_string() 119 | ), 120 | )); 121 | } 122 | } 123 | } 124 | } 125 | 126 | Ok(GenArray { 127 | vis, 128 | fn_name, 129 | fn_ty, 130 | is_mut, 131 | is_ref, 132 | implicit_select_all_tys, 133 | implicit_select_all_decorator, 134 | casts: vec![], 135 | fields: vec![], 136 | }) 137 | } 138 | -------------------------------------------------------------------------------- /src/parse_in_array.rs: -------------------------------------------------------------------------------- 1 | use syn::parse::{ParseStream, Result}; 2 | use syn::token; 3 | use syn::{bracketed, parenthesized, Ident, Path, Token, Type, Visibility}; 4 | 5 | use crate::parse_attribute::single_parse_outer_attribute; 6 | use crate::parse_decorator::{CastKind, Decorator}; 7 | use crate::FIELD_SELECTOR_NAME; 8 | 9 | #[derive(PartialEq)] 10 | pub enum InArrayElementKind { 11 | Implicit, 12 | InArray, 13 | } 14 | 15 | pub struct InArrayElement { 16 | pub ident: Ident, 17 | pub ty: Type, 18 | pub cast: Option, 19 | pub kind: InArrayElementKind, 20 | } 21 | 22 | pub struct InArrayAttributeEntry { 23 | pub ident: Ident, 24 | pub decorator: Decorator, 25 | } 26 | 27 | pub struct InArrayAttribute { 28 | pub entries: Vec, 29 | } 30 | 31 | pub struct InArrayField { 32 | pub attrs: Vec, 33 | pub ident: Ident, 34 | pub ty: Type, 35 | } 36 | 37 | pub fn parse_in_array_fields(input: ParseStream) -> Result { 38 | let attrs: Vec = input.call(parse_in_array_attributes)?; 39 | let _: Visibility = input.parse()?; 40 | let ident: Ident = input.parse()?; 41 | let _: Option = Some(input.parse()?); 42 | let ty: Type = input.parse()?; 43 | Ok(InArrayField { attrs, ident, ty }) 44 | } 45 | 46 | pub fn is_in_array_attribute(input: ParseStream) -> Result { 47 | let content; 48 | let _: Token![#] = input.parse()?; 49 | let _: token::Bracket = bracketed!(content in input); 50 | let path: Path = content.call(Path::parse_mod_style)?; 51 | 52 | Ok(!path.segments.is_empty() && path.segments[0].ident == FIELD_SELECTOR_NAME) 53 | } 54 | 55 | pub fn parse_in_array_attributes(input: ParseStream) -> Result> { 56 | let mut ret = vec![]; 57 | while input.peek(Token![#]) { 58 | if let Ok(true) = is_in_array_attribute(&input.fork()) { 59 | ret.push(input.call(parse_single_in_array_attribute_header)?); 60 | } else { 61 | input.call(single_parse_outer_attribute)?; 62 | } 63 | } 64 | Ok(ret) 65 | } 66 | 67 | pub fn parse_single_in_array_attribute_header(input: ParseStream) -> Result { 68 | let content; 69 | let _: Token![#] = input.parse()?; 70 | let _: token::Bracket = bracketed!(content in input); 71 | let path: Path = content.call(Path::parse_mod_style)?; 72 | 73 | if path.segments.len() != 1 { 74 | return Err(input.error(format!("Wrong syntax for {}", FIELD_SELECTOR_NAME))); 75 | } 76 | 77 | content.call(parse_single_in_array_attribute_body) 78 | } 79 | 80 | pub fn parse_single_in_array_attribute_body(input: ParseStream) -> Result { 81 | let content; 82 | let _ = parenthesized!(content in input); 83 | Ok(InArrayAttribute { 84 | entries: content 85 | .parse_terminated::(parse_attribute_entry)? 86 | .into_iter() 87 | .collect(), 88 | }) 89 | } 90 | 91 | pub fn parse_attribute_entry(input: ParseStream) -> Result { 92 | Ok(InArrayAttributeEntry { 93 | ident: input.parse()?, 94 | decorator: input.parse()?, 95 | }) 96 | } 97 | -------------------------------------------------------------------------------- /src/transform_context.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use proc_macro2::TokenTree; 3 | use quote::quote; 4 | use std::collections::HashMap; 5 | use syn::parse_macro_input; 6 | use syn::{Ident, Type}; 7 | 8 | use crate::parse_decorator::CastKind; 9 | use crate::parse_derive_arraygen::DeriveArraygen; 10 | use crate::parse_gen_array::GenArray; 11 | use crate::{DECL_FN_NAME, DERIVE_NAME}; 12 | 13 | pub fn transform_ast(input: TokenStream) -> TokenStream { 14 | let arraygen = parse_macro_input!(input as DeriveArraygen); 15 | 16 | let struct_name = arraygen.struct_name; 17 | 18 | if arraygen.gen_arrays.is_empty() { 19 | eprintln!( 20 | //struct_name.span(), @TODO emit warning 21 | "warning (Arraygen): The struct '{}' derives '{}' but does not contain any '{}' attribute, so '{}' does nothing.", 22 | struct_name, 23 | DERIVE_NAME, 24 | DECL_FN_NAME, 25 | DERIVE_NAME 26 | ); 27 | } 28 | 29 | let impl_fns = make_impl_fns(arraygen.gen_arrays, &struct_name); 30 | let (impl_generics, ty_generics, where_clause) = arraygen.generics.split_for_impl(); 31 | 32 | let tokens = quote! { 33 | impl #impl_generics #struct_name #ty_generics 34 | #where_clause 35 | { 36 | #(#impl_fns) 37 | * 38 | } 39 | }; 40 | 41 | tokens.into() 42 | } 43 | 44 | fn make_impl_fns(methods: HashMap, struct_name: &Ident) -> Vec { 45 | methods 46 | .into_iter() 47 | .fold(Vec::::new(), |mut acc, (name, method)| { 48 | if method.fields.is_empty() { 49 | eprintln!( 50 | //method.fn_name.span(), @TODO emit warning 51 | "warning (Arraygen): Method '{}' from struct '{}' returns an empty array.", 52 | name, 53 | struct_name 54 | ); 55 | } 56 | let tokens = make_method_tokens(&method); 57 | acc.extend(tokens); 58 | acc 59 | }) 60 | } 61 | 62 | fn make_method_tokens(props: &GenArray) -> proc_macro2::TokenStream { 63 | let field_idents = &props.fields; 64 | let count = field_idents.len(); 65 | let return_type = &props.fn_ty; 66 | let vis = &props.vis; 67 | let fn_name = &props.fn_name; 68 | let refa = if props.is_ref { 69 | if props.is_mut { 70 | quote! {&mut} 71 | } else { 72 | quote! {&} 73 | } 74 | } else { 75 | quote! {} 76 | }; 77 | let muta = if props.is_mut { 78 | quote! {mut} 79 | } else { 80 | quote! {} 81 | }; 82 | let field_idents = field_idents 83 | .iter() 84 | .map(|iae| { 85 | let ident = iae.ident.clone(); 86 | match iae.cast { 87 | Some(CastKind::SafeCast) => quote! { #refa self.#ident as #return_type }, 88 | Some(CastKind::UnsafeTransmute) => { 89 | let source_ty = &iae.ty; 90 | let refb = match source_ty { 91 | Type::Reference(_) if props.is_ref => quote! {}, 92 | _ => quote! { #refa } 93 | }; 94 | quote ! { unsafe { std::mem::transmute::<#refb #source_ty, #return_type>(#refa self.#ident) } } 95 | }, 96 | None => quote! { #refa self.#ident } 97 | } 98 | }); 99 | 100 | quote! { 101 | #[inline(always)] 102 | #vis fn #fn_name (& #muta self) -> [#return_type; #count] { 103 | [#(#field_idents),*] 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{token_stream::IntoIter, TokenTree, TokenTree::*}; 2 | use quote::quote; 3 | use syn::Type; 4 | 5 | pub fn are_matching_types(left_ty: &Type, right_ty: &Type) -> bool { 6 | compare_types(left_ty, right_ty, true) 7 | } 8 | 9 | pub fn ty_inferred_by(field_ty: &Type, implicit_ty: &Type) -> bool { 10 | compare_types(field_ty, implicit_ty, false) 11 | } 12 | 13 | fn compare_types(left_ty: &Type, right_ty: &Type, wildcards_on_left: bool) -> bool { 14 | if *left_ty == *right_ty { 15 | return true; 16 | } 17 | if let Type::Infer(_) = right_ty { 18 | return true; 19 | } 20 | if let (true, Type::Infer(_)) = (wildcards_on_left, left_ty) { 21 | return true; 22 | } 23 | 24 | let mut right_tokens = quote! { #right_ty }.into_iter(); 25 | let mut left_tokens = quote! { #left_ty }.into_iter(); 26 | 27 | let mut last_group = 'Z'; 28 | 29 | loop { 30 | let (left_t, right_t) = match (left_tokens.next(), right_tokens.next()) { 31 | (Some(lt), Some(rt)) => (lt, rt), 32 | (Some(_), None) | (None, Some(_)) => return false, 33 | (None, None) => return true, 34 | }; 35 | 36 | match right_t { 37 | Punct(ref p) if p.as_char() == '(' || p.as_char() == '<' || p.as_char() == '[' => { 38 | last_group = p.as_char() 39 | } 40 | _ => {} 41 | } 42 | 43 | match (&left_t, &right_t) { 44 | (Punct(p1), Punct(p2)) if p1.as_char() == p2.as_char() => continue, 45 | (Ident(i1), Ident(i2)) if i1 == i2 => continue, 46 | (Literal(l1), Literal(l2)) if l1.to_string() == l2.to_string() => continue, 47 | (Group(g1), Group(g2)) if g1.to_string() == g2.to_string() => continue, 48 | _ => {} 49 | } 50 | 51 | let mut termination = AdvanceTermination { 52 | wildcard_ended: false, 53 | other_ended: false, 54 | }; 55 | 56 | if advance_if_wildcard( 57 | &right_t, 58 | &mut right_tokens, 59 | &mut left_tokens, 60 | last_group, 61 | &mut termination, 62 | ) || (wildcards_on_left 63 | && advance_if_wildcard( 64 | &left_t, 65 | &mut left_tokens, 66 | &mut right_tokens, 67 | last_group, 68 | &mut termination, 69 | )) 70 | { 71 | match (termination.wildcard_ended, termination.other_ended) { 72 | (true, true) => return true, 73 | (true, false) | (false, true) => return false, 74 | (false, false) => continue, 75 | } 76 | } 77 | return false; 78 | } 79 | } 80 | 81 | struct AdvanceTermination { 82 | pub wildcard_ended: bool, 83 | pub other_ended: bool, 84 | } 85 | 86 | fn advance_if_wildcard( 87 | wildcard_token: &TokenTree, 88 | wildcard_iter: &mut IntoIter, 89 | other_iter: &mut IntoIter, 90 | last_group: char, 91 | termination: &mut AdvanceTermination, 92 | ) -> bool { 93 | if !matches!(wildcard_token, Ident(ref p) if p == "_") { 94 | return false; 95 | } 96 | 97 | match wildcard_iter.next() { 98 | Some(_) => {} 99 | None => { 100 | termination.wildcard_ended = true; 101 | } 102 | } 103 | 104 | for other_token in other_iter { 105 | match other_token { 106 | Punct(ref p) 107 | if (p.as_char() == ')' && last_group == '(') 108 | || (p.as_char() == '>' && last_group == '<') 109 | || (p.as_char() == ']' && last_group == '[') 110 | || p.as_char() == ',' => 111 | { 112 | return true; 113 | } 114 | _ => {} 115 | } 116 | } 117 | termination.other_ended = true; 118 | true 119 | } 120 | 121 | #[allow(non_snake_case)] 122 | #[cfg(test)] 123 | mod test { 124 | use super::*; 125 | 126 | macro_rules! ty_inferred_by_tests { 127 | ($($name:ident: $str1:expr, $str2:expr, $expected:expr)*) => { 128 | $( 129 | #[test] 130 | fn $name() { 131 | assert_eq!( 132 | ty_inferred_by( 133 | &syn::parse_str($str1).unwrap(), 134 | &syn::parse_str($str2).unwrap() 135 | ), 136 | $expected 137 | ); 138 | } 139 | )* 140 | } 141 | } 142 | 143 | ty_inferred_by_tests! { 144 | ty_inferred_by___with_exactly_same_option___returns_true: "Option", "Option", true 145 | ty_inferred_by___with_exactly_same_complex_ty___returns_true: "Option>", "Option>", true 146 | ty_inferred_by___with_slightly_different_complex_ty_1___returns_false: "Option>", "Option>", false 147 | ty_inferred_by___with_slightly_different_complex_ty_2___returns_false: "Option>", "Option>", false 148 | ty_inferred_by___compared_to_wildcard_1___returns_true: "Option", "_", true 149 | ty_inferred_by___compared_to_wildcard_2___returns_true: "Option", "Option<_>", true 150 | ty_inferred_by___compared_to_wildcard_3___returns_true: "Option>", "Option>", true 151 | ty_inferred_by___compared_to_wildcard_4___returns_true: "Option>", "Option>", true 152 | ty_inferred_by___compared_to_wildcard_5___returns_false: "Option>", "Option>", false 153 | ty_inferred_by___compared_to_wildcard_6___returns_true: "Result>", "Result<_, Option>", true 154 | ty_inferred_by___with_matching_wildcards_in_both_sides_1___returns_true: "Result<_, i32>", "Result<_, i32>", true 155 | ty_inferred_by___with_matching_wildcards_in_both_sides_2___returns_true: "Result", "Result", true 156 | } 157 | 158 | macro_rules! are_matching_types_tests { 159 | ($($name:ident: $str1:expr, $str2:expr, $expected:expr)*) => { 160 | $( 161 | #[test] 162 | fn $name() { 163 | assert_eq!( 164 | are_matching_types( 165 | &syn::parse_str($str1).unwrap(), 166 | &syn::parse_str($str2).unwrap() 167 | ), 168 | $expected 169 | ); 170 | } 171 | )* 172 | } 173 | } 174 | 175 | are_matching_types_tests! { 176 | are_matching_types___with_matching_wildcards_in_both_sides___returns_true: "Result", "Result<_, i32>", true 177 | are_matching_types___between_wildcard_and_any_other_type___returns_true: "_", "Option", true 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | pub trait OptionExtensions { 2 | fn insert_stable(&mut self, value: T) -> &mut T; 3 | } 4 | 5 | impl OptionExtensions for Option { 6 | fn insert_stable(&mut self, value: T) -> &mut T { 7 | *self = Some(value); 8 | match self { 9 | Some(value) => value, 10 | None => unreachable!(), 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/arraygen_main_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate arraygen; 2 | 3 | #[allow(non_snake_case)] 4 | mod tests { 5 | use arraygen::Arraygen; 6 | 7 | #[test] 8 | fn test_arraygen___with_proper_declarations_and_includes___generates_correct_length_methods() { 9 | #[derive(Arraygen)] 10 | #[gen_array(fn booleans: &bool)] 11 | struct Sut<'a> { 12 | #[in_array(booleans)] 13 | first: &'a bool, 14 | 15 | #[in_array(booleans)] 16 | second: &'a mut bool, 17 | 18 | #[in_array(booleans)] 19 | third: bool, 20 | } 21 | 22 | let foo = true; 23 | let mut bar = true; 24 | let actual = Sut { 25 | first: &foo, 26 | second: &mut bar, 27 | third: true, 28 | }; 29 | 30 | assert_eq!(actual.booleans().len(), 3); 31 | } 32 | 33 | #[test] 34 | fn test_arraygen___with_proper_declarations_and_duplicated_includes___generates_correct_length_methods( 35 | ) { 36 | #[derive(Arraygen)] 37 | #[gen_array(pub(crate) fn my_bools: &bool)] 38 | #[gen_array(fn booleans: bool)] 39 | struct Sut { 40 | #[in_array(my_bools)] 41 | first: bool, 42 | 43 | #[in_array(my_bools)] 44 | #[in_array(booleans)] 45 | second: bool, 46 | } 47 | 48 | let actual = Sut { 49 | first: true, 50 | second: false, 51 | }; 52 | 53 | assert_eq!(actual.booleans().len(), 1); 54 | assert_eq!(actual.my_bools().len(), 2); 55 | } 56 | 57 | #[test] 58 | fn test_arraygen___with_proper_declarations_and_multi_includes___generates_correct_length_methods( 59 | ) { 60 | #[derive(Arraygen)] 61 | #[gen_array(pub(crate) fn my_bools: &bool)] 62 | #[gen_array(fn booleans: bool)] 63 | struct Sut { 64 | #[in_array(my_bools)] 65 | first: bool, 66 | 67 | #[in_array(booleans, my_bools)] 68 | second: bool, 69 | } 70 | 71 | let actual = Sut { 72 | first: true, 73 | second: false, 74 | }; 75 | 76 | assert_eq!(actual.booleans().len(), 1); 77 | assert_eq!(actual.my_bools().len(), 2); 78 | } 79 | 80 | #[test] 81 | fn test_arraygen___with_proper_declarations_and_includes___can_return_generic_types() { 82 | #[derive(Arraygen)] 83 | #[gen_array(fn some: Option)] 84 | struct Sut { 85 | #[in_array(some)] 86 | a: Option, 87 | } 88 | 89 | let actual = Sut { a: None }; 90 | assert_eq!(actual.some().len(), 1); 91 | } 92 | 93 | #[test] 94 | fn test_arraygen___with_proper_declarations_and_includes___can_return_fn_types() { 95 | #[derive(Arraygen)] 96 | #[gen_array(fn functions: &fn() -> () )] 97 | struct Sut { 98 | #[in_array(functions)] 99 | a: fn() -> (), 100 | } 101 | 102 | fn foo() {} 103 | 104 | let actual = Sut { a: foo }; 105 | assert_eq!(actual.functions().len(), 1); 106 | } 107 | 108 | #[test] 109 | fn test_arraygen___without_includes___generates_0_length_arrays() { 110 | #[derive(Arraygen)] 111 | #[gen_array(fn foo: i32)] 112 | struct Sut { 113 | bar: i32, 114 | } 115 | 116 | let test = Sut { bar: 2 }; 117 | let _ = test.bar; // Avoiding warning 118 | 119 | assert_eq!(test.foo().len(), 0); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /tests/casting_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate arraygen; 2 | 3 | #[allow(non_snake_case)] 4 | mod tests { 5 | use arraygen::Arraygen; 6 | 7 | #[test] 8 | fn test_casting_on_in_array___returns_expected_result() { 9 | #[derive(Arraygen)] 10 | #[gen_array(fn bytes: u8)] 11 | struct Sut { 12 | #[in_array(bytes { cast })] 13 | pub a: i32, 14 | } 15 | 16 | let actual = Sut { a: -1 }; 17 | 18 | assert_eq!(actual.bytes()[0], 255); 19 | } 20 | 21 | #[test] 22 | fn test_unsafe_transmute_on_in_array___returns_expected_result() { 23 | #[derive(Arraygen)] 24 | #[gen_array(fn numbers: i32)] 25 | struct Sut { 26 | #[in_array(numbers { unsafe_transmute })] 27 | pub a: f32, 28 | } 29 | 30 | let actual = Sut { a: -1.23456789 }; 31 | 32 | assert_eq!(actual.numbers()[0], -1080162734); 33 | } 34 | 35 | #[test] 36 | fn test_unsafe_transmute_on_in_array___between_refs___returns_expected_result() { 37 | #[derive(Arraygen)] 38 | #[gen_array(fn number_refs: &i32)] 39 | struct Sut<'a> { 40 | #[in_array(number_refs { unsafe_transmute })] 41 | pub a: &'a f32, 42 | } 43 | 44 | let foo = -1.23456789; 45 | 46 | let actual = Sut { a: &foo }; 47 | 48 | assert_eq!(*actual.number_refs()[0], -1080162734); 49 | } 50 | 51 | #[test] 52 | fn test_unsafe_transmute_on_in_array___from_number_to_ref___compiles_correctly() { 53 | #[derive(Arraygen)] 54 | #[gen_array(fn number_refs: &i32)] 55 | struct Sut { 56 | #[in_array(number_refs { unsafe_transmute })] 57 | pub a: f32, 58 | } 59 | 60 | let actual = Sut { a: -1.23456789 }; 61 | 62 | assert_eq!(*actual.number_refs()[0], -1080162734); 63 | } 64 | 65 | #[test] 66 | fn test_casting_on_implicit_select_all___returns_expepcted_sum() { 67 | #[derive(Arraygen)] 68 | #[gen_array(fn number_refs: f64, implicit_select_all { cast }: i8, u8, i16, u16, i32, u32, f32, f64)] 69 | struct Sut { 70 | pub a: i32, 71 | pub b: u32, 72 | pub c: i16, 73 | pub d: u16, 74 | pub e: f32, 75 | } 76 | 77 | let actual = Sut { 78 | a: 1, 79 | b: 1, 80 | c: 1, 81 | d: 1, 82 | e: 1.0, 83 | }; 84 | 85 | assert_eq!(actual.number_refs().into_iter().sum::(), 5.0); 86 | } 87 | 88 | #[test] 89 | fn test_overrideding_cast___from_implicit_select_all_with_no_decorator___compiles_correctly() { 90 | #[derive(Arraygen)] 91 | #[gen_array(fn my_array: f32, implicit_select_all: f32)] 92 | struct _Sut1 { 93 | #[in_array(my_array { override_implicit, cast })] 94 | pub a: i32, 95 | } 96 | 97 | #[derive(Arraygen)] 98 | #[gen_array(fn my_array: f32, implicit_select_all: f32)] 99 | struct _Sut2 { 100 | #[in_array(my_array { cast, override_implicit })] 101 | pub a: i32, 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /tests/compile-fail/declaration-missing-fn.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub my_array: i32)] //~ERROR 8:17: 8:25: expected `fn` 9 | struct Empty{} -------------------------------------------------------------------------------- /tests/compile-fail/declaration-wrong-type.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array)] //~ERROR 8:28: 8:29: expected `:` 9 | struct Empty{} -------------------------------------------------------------------------------- /tests/compile-fail/derive-on-non-struct.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | enum Empty1{} //~ERROR 8:1: 8:5: derive 'Arraygen' should only be used with braced structs 9 | 10 | #[derive(Arraygen)] 11 | union Empty2{a: i32} //~ERROR 11:1: 11:6: derive 'Arraygen' should only be used with braced structs -------------------------------------------------------------------------------- /tests/compile-fail/derive-on-struct-non-braced.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | struct Empty1; //~ERROR 8:14: 8:15: derive 'Arraygen' should only be used with braced structs 9 | 10 | #[derive(Arraygen)] 11 | struct Empty2(i32, f32); //~ERROR 11:14: 11:15: derive 'Arraygen' should only be used with braced structs 12 | -------------------------------------------------------------------------------- /tests/compile-fail/implicit-select-all-conflicting-wildcards.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(fn my_array: Option, implicit_select_all: _, Option<_>)] //~ERROR 8:63: 8:72: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'Option < _ >' type 9 | struct Implicit1 { 10 | pub value: Option, 11 | } 12 | 13 | #[derive(Arraygen)] 14 | #[gen_array(fn my_array: Option, implicit_select_all: Option<_>, _)] //~ERROR 14:71: 14:72: gen_array method 'my_array' contains implicit_select_all clause with duplicated '_' type 15 | struct Implicit2 { 16 | pub value: Option, 17 | } 18 | 19 | #[derive(Arraygen)] 20 | #[gen_array(fn my_array: Option, implicit_select_all: _, Option)] //~ERROR 20:63: 20:74: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'Option < f32 >' type 21 | struct Implicit3 { 22 | pub value: Option, 23 | } 24 | 25 | #[derive(Arraygen)] 26 | #[gen_array(fn my_array: Option, implicit_select_all: Option, _)] //~ERROR 26:74: 26:75: gen_array method 'my_array' contains implicit_select_all clause with duplicated '_' type 27 | struct Implicit4 { 28 | pub value: Option, 29 | } 30 | 31 | #[derive(Arraygen)] 32 | #[gen_array(fn my_array: Option, implicit_select_all: Option<_>, Option)] //~ERROR 32:71: 32:82: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'Option < f32 >' type 33 | struct Implicit5 { 34 | pub value: Option, 35 | } 36 | 37 | #[derive(Arraygen)] 38 | #[gen_array(fn my_array: Option, implicit_select_all: Option, Option<_>)] //~ERROR 38:74: 38:83: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'Option < _ >' type 39 | struct Implicit6 { 40 | pub value: Option, 41 | } 42 | 43 | #[derive(Arraygen)] 44 | #[gen_array(fn my_array: Result, implicit_select_all: Result<_, f32>, Result)] //~ERROR 44:82: 44:96: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'Result < f32, _ >' type 45 | struct Implicit7 { 46 | pub value: Result, 47 | } 48 | 49 | #[derive(Arraygen)] 50 | #[gen_array(fn my_array: Option>, implicit_select_all: Option>, Option>)] //~ERROR 50:98: 50:120: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'Option < Result < f32, _ > >' type 51 | struct Implicit8 { 52 | pub value: Option>, 53 | } 54 | 55 | #[derive(Arraygen)] 56 | #[gen_array(fn my_array: Result, Option>, implicit_select_all: Result, Option>, Result, Option<_>>)] //~ERROR 56:114: 56:144: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'Result < Option < f32 >, Option < _ > >' type 57 | struct Implicit9 { 58 | pub value: Result, Option>, 59 | } 60 | 61 | #[derive(Arraygen)] 62 | #[gen_array(fn my_array: i32, implicit_select_all: i32, f32, _, u16, u8, s8)] //~ERROR 62:62: 62:63: gen_array method 'my_array' contains implicit_select_all clause with duplicated '_' type 63 | struct Implicit10 { 64 | pub value: Option, 65 | } -------------------------------------------------------------------------------- /tests/compile-fail/implicit-select-all-duplicated-fields.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(fn my_array: f32, implicit_select_all: f32)] 9 | struct Implicit { 10 | #[in_array(my_array)] //~ERROR 10:16: 10:24: Field 'value' is already included in gen_array method 'my_array' 11 | pub value: f32, 12 | } 13 | -------------------------------------------------------------------------------- /tests/compile-fail/implicit-select-all-duplicated-types.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(fn my_array: f32, implicit_select_all: f32, f32)] //~ERROR 8:57: 8:60: gen_array method 'my_array' contains implicit_select_all clause with duplicated 'f32' type 9 | struct Implicit { 10 | pub value: f32, 11 | } 12 | -------------------------------------------------------------------------------- /tests/compile-fail/implicit-select-all-duplicated-wildcards.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(fn my_array: f32, implicit_select_all: _, _)] //~ERROR 8:55: 8:56: gen_array method 'my_array' contains implicit_select_all clause with duplicated '_' type 9 | struct Implicit { 10 | pub value: f32, 11 | } 12 | -------------------------------------------------------------------------------- /tests/compile-fail/implicit-select-all-on-gen-array-with-override-implicit.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(fn my_array: f32, implicit_select_all { override_implicit }: f32)] //~ERROR 8:31: 8:50: gen_array method 'my_array' contains implicit_select_all clause with forbidden decorator 'override_implicit' 9 | struct Implicit { 10 | #[in_array(my_array)] 11 | pub value: f32, 12 | } 13 | -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-bad-tokens.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | #[gen_array(pub fn another_array: i32)] 10 | struct Test{ 11 | #[in_array(my_array another_array)] //~ERROR 11:25: 11:38: expected `,` 12 | foo: i32 13 | } -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-decorator-empty.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | struct Test{ 10 | #[in_array(my_array2 { })] //~ERROR 10:28: 10:29: unexpected end of input, expected identifier 11 | foo: i32 12 | } -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-decorator-wrong-kind.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | struct Test{ 10 | #[in_array(my_array { blabla })] //~ERROR 10:27: 10:33: in_array doesn't allow 'blabla' as decorator 11 | foo: i32 12 | } -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-decorator-wrong-method.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | struct Test{ 10 | #[in_array(my_array2 { cast })] //~ERROR 10:16: 10:25: gen_array method 'my_array2' not present but used by field 'foo' 11 | foo: i32 12 | } -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-decorator-wrong-place.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | struct Test{ 10 | #[in_array({ cast } my_array )] //~ERROR 10:16: 10:17: expected identifier 11 | foo: i32 12 | } -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-duplicated-method-for-field.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | struct Test{ 10 | #[in_array(my_array, my_array)] //~ERROR 10:26: 10:34: Field 'foo' is already included in gen_array method 'my_array' 11 | foo: i32 12 | } -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-method-not-declared.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | struct Test{ 10 | #[in_array(another_array)] //~ERROR 10:16: 10:29: gen_array method 'another_array' not present but used by field 'foo' 11 | foo: i32 12 | } -------------------------------------------------------------------------------- /tests/compile-fail/inclusion-wrong-syntax.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | extern crate arraygen; 4 | 5 | use arraygen::Arraygen; 6 | 7 | #[derive(Arraygen)] 8 | #[gen_array(pub fn my_array: i32)] 9 | struct Test{ 10 | #[in_array(...)] //~ERROR 10:16: 10:19: expected identifier 11 | foo: i32 12 | } -------------------------------------------------------------------------------- /tests/compile-warning/derive-alone-test.rs: -------------------------------------------------------------------------------- 1 | extern crate arraygen; 2 | 3 | use arraygen::Arraygen; 4 | 5 | #[derive(Arraygen)] //~WARNING 'in_array' shouldn't contain these tokens. 6 | struct Empty{} 7 | 8 | fn main() { 9 | let _ = Empty{}; 10 | } 11 | 12 | // @TODO Not active, activate it when is possible on stable. -------------------------------------------------------------------------------- /tests/compile-warning/empty-method.rs: -------------------------------------------------------------------------------- 1 | extern crate arraygen; 2 | 3 | use arraygen::Arraygen; 4 | 5 | #[derive(Arraygen)] 6 | #[gen_array(fn empty: i32)] //~WARNING Method 'empty' returns an empty array. 7 | struct Test{} 8 | 9 | fn main() { 10 | let test = Test{}; 11 | assert_eq!(test.empty().len(), 0); 12 | } 13 | 14 | // @TODO Not active, activate it when is possible on stable. -------------------------------------------------------------------------------- /tests/compile_fail_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate compiletest_rs as compiletest; 2 | 3 | use std::path::PathBuf; 4 | 5 | fn run_mode(mode: &'static str) { 6 | let mut config = compiletest::Config::default(); 7 | 8 | config.mode = mode.parse().expect("Invalid mode"); 9 | config.src_base = PathBuf::from(format!("tests/{}", mode)); 10 | config.target_rustcflags = Some("-L target/debug -L target/debug/deps".to_string()); 11 | config.clean_rmeta(); 12 | 13 | compiletest::run_tests(&config); 14 | } 15 | 16 | #[test] 17 | fn compile_test() { 18 | run_mode("compile-fail"); 19 | } 20 | -------------------------------------------------------------------------------- /tests/implicit_select_all_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate arraygen; 2 | 3 | #[allow(dead_code)] 4 | #[allow(non_snake_case)] 5 | mod tests { 6 | use arraygen::Arraygen; 7 | 8 | #[test] 9 | fn test_implicit_select_all___given_f32___returns_all_f32_fields() { 10 | #[derive(Arraygen)] 11 | #[gen_array(fn get_all_prices: f32, implicit_select_all: f32)] 12 | struct ImplicitPrices { 13 | pub water: f32, 14 | pub oil: f32, 15 | pub tomato: f32, 16 | pub chocolate: f32, 17 | pub gold: f64, 18 | } 19 | 20 | let prices = ImplicitPrices { 21 | water: 2.0, 22 | oil: 4.0, 23 | tomato: 3.0, 24 | chocolate: 5.0, 25 | gold: 1000.0, 26 | }; 27 | 28 | assert_eq!(prices.get_all_prices().into_iter().sum::(), 14.0); 29 | } 30 | 31 | #[test] 32 | fn test_implicit_select_all___given_wildcard___returns_all_fields() { 33 | trait All {} 34 | 35 | struct A {} 36 | struct B {} 37 | struct C {} 38 | 39 | impl All for A {} 40 | impl All for B {} 41 | impl All for C {} 42 | 43 | #[derive(Arraygen)] 44 | #[gen_array(fn all: &dyn All, implicit_select_all: _)] 45 | struct Sut { 46 | pub a: A, 47 | pub b: B, 48 | pub c: C, 49 | } 50 | 51 | let actual = Sut { 52 | a: A {}, 53 | b: B {}, 54 | c: C {}, 55 | }; 56 | 57 | assert_eq!(actual.all().len(), 3); 58 | } 59 | 60 | #[test] 61 | fn test_implicit_select_all___given_option_with_wildcard___returns_all_option_fields() { 62 | trait OptionTrait {} 63 | 64 | impl OptionTrait for Option {} 65 | 66 | #[derive(Arraygen)] 67 | #[gen_array(fn options: &dyn OptionTrait, implicit_select_all: Option<_>)] 68 | struct Sut { 69 | pub a: Option, 70 | pub b: Option, 71 | pub c: f32, 72 | } 73 | 74 | let actual = Sut { 75 | a: Some(1), 76 | b: Some(false), 77 | c: 3.0, 78 | }; 79 | 80 | assert_eq!(actual.options().len(), 2); 81 | } 82 | 83 | #[test] 84 | fn test_implicit_select_all___given_result_with_wildcard___returns_all_result_fields() { 85 | #[derive(Arraygen)] 86 | #[gen_array(fn options: &Result, implicit_select_all: Result<_, std::fmt::Error>)] 87 | struct Sut { 88 | pub a: Result, 89 | pub b: f32, 90 | } 91 | 92 | let actual = Sut { a: Ok(1), b: 3.0 }; 93 | 94 | assert_eq!(actual.options().len(), 1); 95 | } 96 | 97 | #[test] 98 | fn test_implicit_select_all___with_display_sim_bug_scenario___compiles_as_expected() { 99 | impl ResetOption for Option { 100 | fn reset(&mut self) { 101 | *self = None; 102 | } 103 | } 104 | 105 | pub trait ResetOption { 106 | fn reset(&mut self); 107 | } 108 | 109 | pub trait Tracked {} 110 | impl Tracked for i32 {} 111 | impl Tracked for Result {} 112 | 113 | #[derive(Arraygen)] 114 | #[gen_array(pub fn options: &mut dyn ResetOption, implicit_select_all: Option<_>)] 115 | #[gen_array(pub fn tracked: &mut dyn Tracked, implicit_select_all: i32, Result)] 116 | pub struct Sut { 117 | // tracked 118 | pub a: Result, 119 | pub b: i32, 120 | 121 | // options 122 | pub c: Option, 123 | } 124 | 125 | let mut actual = Sut { 126 | a: Ok(1), 127 | b: 3, 128 | c: Some(1), 129 | }; 130 | assert_eq!(actual.options().len(), 1); 131 | assert_eq!(actual.tracked().len(), 2); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /tests/trait_objects_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate arraygen; 2 | 3 | #[allow(non_snake_case)] 4 | mod tests { 5 | use arraygen::Arraygen; 6 | 7 | #[test] 8 | fn test_trait_objects_return_type___with_proper_declarations_and_includes___compiles_as_expected( 9 | ) { 10 | struct A {} 11 | struct B {} 12 | trait C {} 13 | impl C for A {} 14 | impl C for B {} 15 | 16 | #[derive(Arraygen)] 17 | #[gen_array(fn trait_c: &dyn C)] 18 | struct Sut { 19 | #[in_array(trait_c)] 20 | a: A, 21 | 22 | #[in_array(trait_c)] 23 | b: B, 24 | } 25 | 26 | let actual = Sut { a: A {}, b: B {} }; 27 | assert_eq!(actual.trait_c().len(), 2); 28 | } 29 | 30 | #[test] 31 | fn test_trait_objects_return_type___with_display_sim_bug_scenario___compiles_as_expected() { 32 | trait UiController { 33 | fn give(&self) -> f32; 34 | } 35 | 36 | #[derive(Clone)] 37 | struct RgbRedR { 38 | pub value: f32, 39 | } 40 | impl UiController for RgbRedR { 41 | fn give(&self) -> f32 { 42 | self.value 43 | } 44 | } 45 | 46 | #[derive(Clone, Arraygen)] 47 | #[gen_array(pub fn get_ui_controllers: &dyn UiController)] 48 | #[gen_array(pub fn get_ui_controllers_mut: &mut dyn UiController)] 49 | struct DisplaySimBug { 50 | #[in_array(get_ui_controllers, get_ui_controllers_mut)] 51 | rgb_red_r: RgbRedR, 52 | } 53 | 54 | let mut actual = DisplaySimBug { 55 | rgb_red_r: RgbRedR { value: 3.0 }, 56 | }; 57 | 58 | assert_eq!(actual.get_ui_controllers_mut().len(), 1); 59 | assert_eq!(actual.get_ui_controllers().len(), 1); 60 | } 61 | } 62 | --------------------------------------------------------------------------------