├── .cargo └── config.toml ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── AUTHORS.txt ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── anyinput-core ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── lib.rs │ └── tests.rs ├── anyinput-derive ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ └── lib.rs ├── src └── lib.rs ├── tests ├── integration_test.rs └── ui │ ├── anyarray.rs │ ├── anyarray.stderr │ ├── anyiter.rs │ ├── anyiter.stderr │ ├── anyiter_paren.rs │ ├── anyiter_paren.stderr │ ├── anyiterconst.rs │ ├── anyiterconst.stderr │ ├── anyitertwo.rs │ ├── anyitertwo.stderr │ ├── anyndarray.rs │ ├── anyndarray.stderr │ ├── anypath.rs │ ├── anypath.stderr │ ├── anystring.rs │ ├── anystring.stderr │ ├── args.rs │ ├── args.stderr │ ├── misapply.rs │ └── misapply.stderr └── useful.md /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.wasm32-wasip1] 2 | runner = "wasmtime run --dir ." -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | schedule: 5 | - cron: '0 0 1 * *' 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | ci: 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: [ubuntu-latest, windows-latest, macOS-latest] 16 | runs-on: ${{ matrix.os }} 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Set up Rust 21 | uses: dtolnay/rust-toolchain@stable 22 | - name: Test Rust 23 | run: cargo test --verbose 24 | - name: Test Rust ndarray 25 | run: cargo test --verbose --all-features 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | anyinput-derive/target/ 4 | anyinput-helper/target/ 5 | 6 | anyinput-helper/target/debug/incremental/input_like_helper-tvfgvkj4hob2/s-ge2u6ohwvo-1hkxezd-3di1dx2wxvdzw/5659epb3vkm179vu.o 7 | -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Carl Kadie 2 | Christian Widmer -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | version = "0.1.8" 3 | name = "anyinput" 4 | description = "A macro for easier writing of functions that accept any string-, path-, iterator-, array-, or ndarray-like input" 5 | repository = "https://github.com/CarlKCarlK/anyinput" 6 | readme = "README.md" 7 | documentation = "https://docs.rs/anyinput/latest/anyinput/" 8 | authors = ["Carl Kadie"] 9 | exclude = [] 10 | license = "MIT OR Apache-2.0" 11 | keywords = ["macros", "function", "generics", "ndarray", "iterator"] 12 | categories = ["development-tools", "rust-patterns"] 13 | homepage = "https://github.com/CarlKCarlK/anyinput" 14 | edition = "2021" 15 | 16 | [lib] 17 | 18 | [features] 19 | ndarray = ["anyinput-derive/ndarray"] 20 | 21 | [workspace] 22 | members = ["anyinput-derive", "anyinput-core"] 23 | 24 | [dev-dependencies] 25 | anyhow = "1.0.92" 26 | ndarray = { version = "0.16.1" } 27 | trybuild = "1.0.101" 28 | 29 | [dependencies] 30 | anyinput-derive = { version = "0.1.8", path = "anyinput-derive" } 31 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONSs -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | anyinput 2 | ========== 3 | 4 | [![github](https://img.shields.io/badge/github-anyinput-8da0cb?style=flat&labelColor=555555&logo=github)](https://github.com/CarlKCarlK/anyinput) 5 | [![crates.io](https://img.shields.io/crates/v/anyinput.svg?flat&color=fc8d62&logo=rust")](https://crates.io/crates/anyinput) 6 | [![docs.rs](https://img.shields.io/badge/docs.rs-anyinput-66c2a5?flat&labelColor=555555&logoColor=white&logo=core:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K)](https://docs.rs/anyinput)[![CI](https://github.com/CarlKCarlK/anyinput/actions/workflows/ci.yml/badge.svg)](https://github.com/CarlKCarlK/anyinput/actions/workflows/ci.yml) 7 | 8 | A macro for easier writing of functions that accept any string-, path-, iterator-, array-, or ndarray-like input 9 | 10 | Do you know how to write a function that accepts any kind of string as input? (Are you sure it accepts, for example, a borrowed reference to `String`?) Do you know how to accept any kind of iterator? How about an iterator of any kind of path? How about a function that accepts a `Vec` as a `ndarray::ArrayView1`? If yes, you don't need this crate. 11 | 12 | Rust functions can accept all these inputs, but the syntax can be hard to remember and read. 13 | This crate provides syntactic sugar that makes writing and reading such functions easier. 14 | 15 | The AnyInputs are `AnyString`, `AnyPath`, `AnyIter`, `AnyArray`, and (optionally) `AnyNdArray`. They may be nested. 16 | 17 | Contents 18 | -------- 19 | 20 | - [Usage](#usage) 21 | - [Examples](#examples) 22 | - [The AnyInputs](#the-anyinputs) 23 | - [Notes & Features](#notes--features) 24 | - [How It Works](#how-it-works) 25 | - [Project Links](#project-links) 26 | 27 | Usage 28 | ----- 29 | 30 | Add this to your `Cargo.toml`: 31 | 32 | ```toml 33 | [dependencies] 34 | anyinput = { version = "0.1.6", features = ["ndarray"] } 35 | ``` 36 | 37 | If you don't need `NdArray` support, omit the `ndarray` feature. 38 | 39 | Examples 40 | -------- 41 | 42 | We'll start with examples that are so simple that you may not need the macro. 43 | We want to show that simple examples stay simple. 44 | 45 | Create a function that adds `2` to the length of any string-like thing. 46 | 47 | ```rust 48 | use anyinput::anyinput; 49 | 50 | #[anyinput] 51 | fn len_plus_2(s: AnyString) -> usize { 52 | s.len()+2 53 | } 54 | 55 | // By using AnyString, len_plus_2 works with 56 | // &str, String, or &String -- borrowed or moved. 57 | assert_eq!(len_plus_2("Hello"), 7); // move a &str 58 | let input: &str = "Hello"; 59 | assert_eq!(len_plus_2(&input), 7); // borrow a &str 60 | let input: String = "Hello".to_string(); 61 | assert_eq!(len_plus_2(&input), 7); // borrow a String 62 | let input2: &String = &input; 63 | assert_eq!(len_plus_2(&input2), 7); // borrow a &String 64 | assert_eq!(len_plus_2(input2), 7); // move a &String 65 | assert_eq!(len_plus_2(input), 7); // move a String 66 | ``` 67 | 68 | Another simple example: Create a function that counts the components of any path-like thing. 69 | 70 | ```rust 71 | use anyinput::anyinput; 72 | use std::path::Path; 73 | 74 | #[anyinput] 75 | fn component_count(path: AnyPath) -> usize { 76 | path.iter().count() 77 | } 78 | 79 | // By using AnyPath, component_count works with any 80 | // string-like or path-like thing, borrowed or moved. 81 | assert_eq!(component_count("usr/files/home"), 3); 82 | let path = Path::new("usr/files/home"); 83 | assert_eq!(component_count(&path), 3); 84 | let pathbuf = path.to_path_buf(); 85 | assert_eq!(component_count(pathbuf), 3); 86 | ``` 87 | 88 | As we add nesting and multiple inputs, the macro becomes more useful. 89 | Here we create a function with two inputs. One input accepts any iterator-like 90 | thing of `usize`. The second input accepts any iterator-like thing of string-like things. The function returns the sum of the numbers and string lengths. 91 | 92 | We apply the function to the range `1..=10` and a slice of `&str`'s. 93 | 94 | ```rust 95 | use anyinput::anyinput; 96 | 97 | #[anyinput] 98 | fn two_iterator_sum(iter1: AnyIter, iter2: AnyIter) -> usize { 99 | let mut sum = iter1.sum(); 100 | for any_string in iter2 { 101 | // Needs .as_ref to turn the nested AnyString into a &str. 102 | sum += any_string.as_ref().len(); 103 | } 104 | sum 105 | } 106 | 107 | assert_eq!(two_iterator_sum(1..=10, ["a", "bb", "ccc"]), 61); 108 | ``` 109 | 110 | Create a function that accepts an array-like thing of path-like things. 111 | Return the number of path components at an index. 112 | 113 | ```rust 114 | use anyinput::anyinput; 115 | use anyhow::Result; 116 | 117 | #[anyinput] 118 | fn indexed_component_count( 119 | array: AnyArray, 120 | index: usize, 121 | ) -> Result { 122 | // Needs .as_ref to turn the nested AnyPath into a &Path. 123 | let path = array[index].as_ref(); 124 | let count = path.iter().count(); 125 | Ok(count) 126 | } 127 | 128 | assert_eq!( 129 | indexed_component_count(vec!["usr/files/home", "usr/data"], 1)?, 130 | 2 131 | ); 132 | # // '# OK...' needed for doctest 133 | # Ok::<(), anyhow::Error>(()) 134 | ``` 135 | 136 | You can easily apply `NdArray` functions to any array-like thing of numbers. For example, 137 | here we create a function that accepts an `NdArray`-like thing of `f32` and returns the mean. 138 | We apply the function to both a `Vec` and an `Array1`. 139 | 140 | Support for `NdArray` is provided by the optional feature `ndarray`. 141 | 142 | ```rust 143 | use anyinput::anyinput; 144 | use anyhow::Result; 145 | 146 | # // '#[cfg...' needed for doctest 147 | # #[cfg(feature = "ndarray")] 148 | #[anyinput] 149 | fn any_mean(array: AnyNdArray) -> Result { 150 | if let Some(mean) = array.mean() { 151 | Ok(mean) 152 | } else { 153 | Err(anyhow::anyhow!("empty array")) 154 | } 155 | } 156 | 157 | // 'AnyNdArray' works with any 1-D array-like thing, but must be borrowed. 158 | # #[cfg(feature = "ndarray")] 159 | assert_eq!(any_mean(&vec![10.0, 20.0, 30.0, 40.0])?, 25.0); 160 | # #[cfg(feature = "ndarray")] 161 | assert_eq!(any_mean(&ndarray::array![10.0, 20.0, 30.0, 40.0])?, 25.0); 162 | # // '# OK...' needed for doctest 163 | # Ok::<(), anyhow::Error>(()) 164 | ``` 165 | 166 | The AnyInputs 167 | --------- 168 | 169 | | AnyInput | Description | Creates Concrete Type | 170 | | ---------- | -------------------------------------- | ------------------------------- | 171 | | AnyString | Any string-like thing | `&str` | 172 | | AnyPath | Any path-like or string-like thing | `&Path` | 173 | | AnyIter | Any iterator-like thing | `::IntoIter` | 174 | | AnyArray | Any array-like thing | `&[T]` | 175 | | AnyNdArray | Any 1-D array-like thing (borrow-only) | `ndarray::ArrayView1` | 176 | 177 | Notes & Features 178 | -------- 179 | 180 | - Suggestions, feature requests, and contributions are welcome. 181 | - Works with nesting, multiple inputs, and generics. 182 | - Automatically and efficiently converts an top-level AnyInput into a concrete type. 183 | - Elements of AnyArray, AnyIter, and AnyNdArray must be a single type. So, `AnyArray` 184 | accepts a vector of all `&str` or all `String`, but not mixed. 185 | - When nesting, efficiently convert the nested AnyInput to the concrete type with 186 | - `.as_ref()` -- AnyString, AnyPath, AnyArray 187 | - `.into_iter()` -- AnyIter 188 | - `.into()` -- AnyNdArray 189 | 190 | (The iterator and array examples above show this.) 191 | 192 | - Let's you easily apply `NdArray` functions to regular Rust arrays, slices, and `Vec`s. 193 | - Used by [bed-reader](https://docs.rs/bed-reader/latest/bed_reader/) (genomics crate) and [fetch-data](https://crates.io/crates/fetch-data) (sample-file download crate). 194 | 195 | How It Works 196 | -------- 197 | 198 | The `#[anyinput]` macro uses standard Rust generics to support multiple input types. To do this, it 199 | rewrites your function with the appropriate generics. It also adds lines to your function to efficiently convert from any top-level generic to a concrete type. For example, the macro transforms `len_plus_2` from: 200 | 201 | ```rust 202 | use anyinput::anyinput; 203 | 204 | #[anyinput] 205 | fn len_plus_2(s: AnyString) -> usize { 206 | s.len()+2 207 | } 208 | ``` 209 | 210 | into 211 | 212 | ```rust 213 | fn len_plus_2>(s: AnyString0) -> usize { 214 | let s = s.as_ref(); 215 | s.len() + 2 216 | } 217 | ``` 218 | 219 | Here `AnyString0` is the generic type. The line `let s = s.as_ref()` converts from generic type `AnyString0` to concrete type `&str`. 220 | 221 | As with all Rust generics, the compiler creates a separate function for each combination of concrete types used by the calling code. 222 | 223 | Project Links 224 | ----- 225 | 226 | - [**Installation**](https://crates.io/crates/anyinput) 227 | - [**Documentation**](https://docs.rs/anyinput/) 228 | - [**Source code**](https://github.com/CarlKCarlK/anyinput) 229 | - [**Discussion**](https://github.com/CarlKCarlK/anyinput/discussions/) 230 | - [**Bug Reports and Feature Requests**](https://github.com/CarlKCarlK/anyinput/issues) 231 | -------------------------------------------------------------------------------- /anyinput-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anyinput-core" 3 | version = "0.1.8" 4 | description = "An internal helper library of anyinput" 5 | repository = "https://github.com/CarlKCarlK/anyinput" 6 | readme = "README.md" 7 | documentation = "https://docs.rs/anyinput/latest/anyinput/" 8 | authors = ["Carl Kadie"] 9 | exclude = [] 10 | license = "MIT OR Apache-2.0" 11 | keywords = ["macros", "function", "generics", "ndarray", "iterator"] 12 | categories = ["development-tools", "rust-patterns"] 13 | homepage = "https://github.com/CarlKCarlK/anyinput" 14 | edition = "2021" 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [lib] 19 | 20 | [features] 21 | ndarray = ["dep:ndarray"] 22 | 23 | 24 | [dev-dependencies] 25 | colored-diff = "0.2.3" 26 | anyhow = "1.0" 27 | 28 | [dependencies] 29 | syn = { version = "2.0.87", features = ["extra-traits", "full", "fold"] } 30 | quote = "1.0.9" 31 | proc-macro2 = "1.0.24" 32 | strum = { version = "0.26", features = ["derive"] } 33 | ndarray = { version = "0.16.1", optional = true } 34 | proc-macro-error = "1.0" 35 | -------------------------------------------------------------------------------- /anyinput-core/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONSs -------------------------------------------------------------------------------- /anyinput-core/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /anyinput-core/README.md: -------------------------------------------------------------------------------- 1 | anyinput-core 2 | ========== 3 | 4 | [![github](https://img.shields.io/badge/github-anyinput-8da0cb?style=flat&labelColor=555555&logo=github)](https://github.com/CarlKCarlK/anyinput) 5 | [![crates.io](https://img.shields.io/crates/v/anyinput.svg?flat&color=fc8d62&logo=rust")](https://crates.io/crates/anyinput) 6 | [![docs.rs](https://img.shields.io/badge/docs.rs-anyinput-66c2a5?flat&labelColor=555555&logoColor=white&logo=core:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K)](https://docs.rs/anyinput) 7 | 8 | **Important Note**: 9 | 10 | * You are probably looking for the [`anyinput`](https://docs.rs/anyinput/) crate, which wraps this crate and is much more ergonomic to use. 11 | 12 | Purpose 13 | ------- 14 | 15 | This is an internal helper library of [`anyinput`](https://docs.rs/anyinput/). 16 | -------------------------------------------------------------------------------- /anyinput-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | 3 | mod tests; 4 | 5 | use proc_macro2::TokenStream; 6 | use proc_macro_error::{abort, SpanRange}; 7 | use quote::quote; 8 | use std::str::FromStr; 9 | use strum::{Display, EnumString}; 10 | use syn::fold::Fold; 11 | use syn::WhereClause; 12 | use syn::{ 13 | parse2, parse_quote, parse_str, punctuated::Punctuated, token::Comma, Block, FnArg, 14 | GenericArgument, GenericParam, Generics, Ident, ItemFn, Lifetime, Pat, PatIdent, PatType, 15 | PathArguments, Signature, Stmt, Type, TypePath, WherePredicate, 16 | }; 17 | 18 | pub fn anyinput_core(args: TokenStream, input: TokenStream) -> TokenStream { 19 | if !args.is_empty() { 20 | abort!(args, "anyinput does not take any arguments.") 21 | } 22 | 23 | // proc_marco2 version of "parse_macro_input!(input as ItemFn)" 24 | let old_item_fn = match parse2::(input) { 25 | Ok(syntax_tree) => syntax_tree, 26 | Err(error) => return error.to_compile_error(), 27 | }; 28 | 29 | let new_item_fn = transform_fn(old_item_fn); 30 | 31 | quote!(#new_item_fn) 32 | } 33 | 34 | pub fn anyinput_core_sample(args: TokenStream, input: TokenStream) -> TokenStream { 35 | if !args.is_empty() { 36 | abort!(args, "anyinput does not take any arguments.") 37 | } 38 | 39 | // proc_marco2 version of "parse_macro_input!(input as ItemFn)" 40 | let old_item_fn = match parse2::(input) { 41 | Ok(syntax_tree) => syntax_tree, 42 | Err(error) => return error.to_compile_error(), 43 | }; 44 | 45 | let new_item_fn = transform_fn_sample(old_item_fn); 46 | 47 | quote!(#new_item_fn) 48 | } 49 | 50 | fn transform_fn_sample(_item_fn: ItemFn) -> ItemFn { 51 | println!("input code : {}", quote!(#_item_fn)); 52 | println!("input syntax: {:?}", _item_fn); 53 | parse_quote! { 54 | fn hello_world() { 55 | println!("Hello, world!"); 56 | } 57 | } 58 | } 59 | 60 | fn transform_fn(item_fn: ItemFn) -> ItemFn { 61 | let mut suffix_iter = simple_suffix_iter_factory(); 62 | let delta_fn_arg_new = |fn_arg| DeltaFnArg::new(fn_arg, &mut suffix_iter); 63 | 64 | // Transform each old argument of the function, accumulating: the new argument, new generics, wheres, and statements 65 | // Then, turn the accumulation into a new function. 66 | item_fn 67 | .sig 68 | .inputs 69 | .iter() 70 | .map(delta_fn_arg_new) 71 | .fold(ItemFnAcc::init(&item_fn), ItemFnAcc::fold) 72 | .to_item_fn() 73 | } 74 | 75 | struct ItemFnAcc<'a> { 76 | old_fn: &'a ItemFn, 77 | fn_args: Punctuated, 78 | generic_params: Punctuated, 79 | where_predicates: Punctuated, 80 | stmts: Vec, 81 | } 82 | 83 | impl ItemFnAcc<'_> { 84 | fn init(item_fn: &ItemFn) -> ItemFnAcc { 85 | // Start with 1. no function arguments, 2. the old function's generics, wheres, and statements 86 | ItemFnAcc { 87 | old_fn: item_fn, 88 | fn_args: Punctuated::::new(), 89 | generic_params: item_fn.sig.generics.params.clone(), 90 | where_predicates: ItemFnAcc::extract_where_predicates(item_fn), 91 | stmts: item_fn.block.stmts.clone(), 92 | } 93 | } 94 | 95 | // Even if the where clause is None, we still need to return an empty Punctuated 96 | fn extract_where_predicates(item_fn: &ItemFn) -> Punctuated { 97 | if let Some(WhereClause { predicates, .. }) = &item_fn.sig.generics.where_clause { 98 | predicates.clone() 99 | } else { 100 | parse_quote!() 101 | } 102 | } 103 | 104 | fn fold(mut self, delta: DeltaFnArg) -> Self { 105 | self.fn_args.push(delta.fn_arg); 106 | self.generic_params.extend(delta.generic_params); 107 | self.where_predicates.extend(delta.where_predicates); 108 | for (index, element) in delta.stmt.into_iter().enumerate() { 109 | self.stmts.insert(index, element); 110 | } 111 | self 112 | } 113 | 114 | // Use Rust's struct update syntax (https://www.reddit.com/r/rust/comments/pchp8h/media_struct_update_syntax_in_rust/) 115 | fn to_item_fn(&self) -> ItemFn { 116 | ItemFn { 117 | sig: Signature { 118 | generics: self.to_generics(), 119 | inputs: self.fn_args.clone(), 120 | ..self.old_fn.sig.clone() 121 | }, 122 | block: Box::new(Block { 123 | stmts: self.stmts.clone(), 124 | ..*self.old_fn.block.clone() 125 | }), 126 | ..self.old_fn.clone() 127 | } 128 | } 129 | 130 | fn to_generics(&self) -> Generics { 131 | Generics { 132 | lt_token: parse_quote!(<), 133 | params: self.generic_params.clone(), 134 | gt_token: parse_quote!(>), 135 | where_clause: self.to_where_clause(), 136 | } 137 | } 138 | 139 | fn to_where_clause(&self) -> Option { 140 | if self.where_predicates.is_empty() { 141 | None 142 | } else { 143 | Some(WhereClause { 144 | where_token: parse_quote!(where), 145 | predicates: self.where_predicates.clone(), 146 | }) 147 | } 148 | } 149 | } 150 | 151 | // Define a generator for suffixes of generic types. "0", "1", "2", ... 152 | // This is used to create unique names for generic types. 153 | // Could switch to one based on UUIDs, but this is easier to read. 154 | fn simple_suffix_iter_factory() -> impl Iterator + 'static { 155 | (0usize..).map(|i| format!("{i}")) 156 | } 157 | 158 | // Define the Specials and their properties. 159 | #[derive(Debug, Clone, EnumString, Display)] 160 | #[allow(clippy::enum_variant_names)] 161 | enum Special { 162 | AnyArray, 163 | AnyString, 164 | AnyPath, 165 | AnyIter, 166 | AnyNdArray, 167 | } 168 | 169 | impl Special { 170 | fn special_to_where_predicate( 171 | &self, 172 | generic: &TypePath, // for example: AnyArray0 173 | maybe_sub_type: Option, 174 | maybe_lifetime: Option, 175 | span_range: &SpanRange, 176 | ) -> WherePredicate { 177 | match &self { 178 | Special::AnyString => { 179 | if maybe_sub_type.is_some() { 180 | abort!(span_range,"AnyString should not have a generic parameter, so 'AnyString', not 'AnyString<_>'.") 181 | }; 182 | if maybe_lifetime.is_some() { 183 | abort!(span_range, "AnyString should not have a lifetime.") 184 | }; 185 | parse_quote! { 186 | #generic : AsRef 187 | } 188 | } 189 | Special::AnyPath => { 190 | if maybe_sub_type.is_some() { 191 | abort!(span_range,"AnyPath should not have a generic parameter, so 'AnyPath', not 'AnyPath<_>'.") 192 | }; 193 | if maybe_lifetime.is_some() { 194 | abort!(span_range, "AnyPath should not have a lifetime.") 195 | }; 196 | parse_quote! { 197 | #generic : AsRef 198 | } 199 | } 200 | Special::AnyArray => { 201 | let sub_type = match maybe_sub_type { 202 | Some(sub_type) => sub_type, 203 | None => { 204 | abort!(span_range,"AnyArray expects a generic parameter, for example, AnyArray or AnyArray.") 205 | } 206 | }; 207 | if maybe_lifetime.is_some() { 208 | abort!(span_range, "AnyArray should not have a lifetime.") 209 | }; 210 | parse_quote! { 211 | #generic : AsRef<[#sub_type]> 212 | } 213 | } 214 | Special::AnyIter => { 215 | let sub_type = match maybe_sub_type { 216 | Some(sub_type) => sub_type, 217 | None => { 218 | abort!(span_range,"AnyIter expects a generic parameter, for example, AnyIter or AnyIter.") 219 | } 220 | }; 221 | if maybe_lifetime.is_some() { 222 | abort!(span_range, "AnyIter should not have a lifetime.") 223 | }; 224 | parse_quote! { 225 | #generic : IntoIterator 226 | } 227 | } 228 | Special::AnyNdArray => { 229 | let sub_type = match maybe_sub_type { 230 | Some(sub_type) => sub_type, 231 | None => { 232 | abort!(span_range,"AnyNdArray expects a generic parameter, for example, AnyNdArray or AnyNdArray.") 233 | } 234 | }; 235 | let lifetime = 236 | maybe_lifetime.expect("Internal error: AnyNdArray should be given a lifetime."); 237 | parse_quote! { 238 | #generic: Into> 239 | } 240 | } 241 | } 242 | } 243 | 244 | fn ident_to_stmt(&self, name: &Ident) -> Stmt { 245 | match &self { 246 | Special::AnyArray | Special::AnyString | Special::AnyPath => { 247 | parse_quote! { 248 | let #name = #name.as_ref(); 249 | } 250 | } 251 | Special::AnyIter => { 252 | parse_quote! { 253 | let #name = #name.into_iter(); 254 | } 255 | } 256 | Special::AnyNdArray => { 257 | parse_quote! { 258 | let #name = #name.into(); 259 | } 260 | } 261 | } 262 | } 263 | 264 | fn should_add_lifetime(&self) -> bool { 265 | match self { 266 | Special::AnyArray | Special::AnyString | Special::AnyPath | Special::AnyIter => false, 267 | Special::AnyNdArray => true, 268 | } 269 | } 270 | 271 | fn maybe_new(type_path: &TypePath, span_range: &SpanRange) -> Option<(Special, Option)> { 272 | // A special type path has exactly one segment and a name from the Special enum. 273 | if type_path.qself.is_none() { 274 | if let Some(segment) = first_and_only(type_path.path.segments.iter()) { 275 | if let Ok(special) = Special::from_str(segment.ident.to_string().as_ref()) { 276 | let maybe_sub_type = 277 | Special::create_maybe_sub_type(&segment.arguments, span_range); 278 | return Some((special, maybe_sub_type)); 279 | } 280 | } 281 | } 282 | None 283 | } 284 | 285 | fn create_maybe_sub_type(args: &PathArguments, span_range: &SpanRange) -> Option { 286 | match args { 287 | PathArguments::None => None, 288 | PathArguments::AngleBracketed(ref args) => { 289 | let arg = first_and_only(args.args.iter()).unwrap_or_else(|| { 290 | abort!(span_range, "Expected at exactly one generic parameter.") 291 | }); 292 | if let GenericArgument::Type(sub_type2) = arg { 293 | Some(sub_type2.clone()) 294 | } else { 295 | abort!(span_range, "Expected generic parameter to be a type.") 296 | } 297 | } 298 | PathArguments::Parenthesized(_) => { 299 | abort!(span_range, "Expected <..> generic parameter.") 300 | } 301 | } 302 | } 303 | 304 | // Utility that turns camel case into snake case. 305 | // For example, "AnyString" -> "any_string". 306 | fn to_snake_case(&self) -> String { 307 | let mut snake_case_string = String::new(); 308 | for (index, ch) in self.to_string().chars().enumerate() { 309 | if index > 0 && ch.is_uppercase() { 310 | snake_case_string.push('_'); 311 | } 312 | snake_case_string.push(ch.to_ascii_lowercase()); 313 | } 314 | snake_case_string 315 | } 316 | } 317 | 318 | #[derive(Debug)] 319 | // The new function input, any statements to add, and any new generic definitions. 320 | struct DeltaFnArg { 321 | fn_arg: FnArg, 322 | generic_params: Vec, 323 | where_predicates: Vec, 324 | stmt: Option, 325 | } 326 | 327 | impl DeltaFnArg { 328 | // If a function argument contains a special type(s), re-write it/them. 329 | fn new(fn_arg: &FnArg, suffix_iter: &mut impl Iterator) -> DeltaFnArg { 330 | // If the function input is normal (not self, not a macro, etc) ... 331 | if let Some((pat_ident, pat_type)) = DeltaFnArg::is_normal_fn_arg(fn_arg) { 332 | // Replace any specials in the type with generics. 333 | DeltaFnArg::replace_any_specials(pat_type.clone(), pat_ident, suffix_iter) 334 | } else { 335 | // if input is not normal, return it unchanged. 336 | DeltaFnArg { 337 | fn_arg: fn_arg.clone(), 338 | generic_params: vec![], 339 | where_predicates: vec![], 340 | stmt: None, 341 | } 342 | } 343 | } 344 | 345 | // A function argument is normal if it is not self, not a macro, etc. 346 | fn is_normal_fn_arg(fn_arg: &FnArg) -> Option<(&PatIdent, &PatType)> { 347 | if let FnArg::Typed(pat_type) = fn_arg { 348 | if let Pat::Ident(pat_ident) = &*pat_type.pat { 349 | if let Type::Path(_) = &*pat_type.ty { 350 | return Some((pat_ident, pat_type)); 351 | } 352 | } 353 | } 354 | None 355 | } 356 | 357 | // Search type and its (sub)subtypes for specials starting at the deepest level. 358 | // When one is found, replace it with a generic. 359 | // Finally, return the new type and a list of the generic definitions. 360 | // Also, if the top-level type was special, return the special type. 361 | #[allow(clippy::ptr_arg)] 362 | fn replace_any_specials( 363 | old_pat_type: PatType, 364 | pat_ident: &PatIdent, 365 | suffix_iter: &mut impl Iterator, 366 | ) -> DeltaFnArg { 367 | let mut delta_pat_type = DeltaPatType::new(suffix_iter); 368 | let new_pat_type = delta_pat_type.fold_pat_type(old_pat_type); 369 | 370 | // Return the new function input, any statements to add, and any new generic definitions. 371 | DeltaFnArg { 372 | fn_arg: FnArg::Typed(new_pat_type), 373 | stmt: delta_pat_type.generate_any_stmt(pat_ident), 374 | generic_params: delta_pat_type.generic_params, 375 | where_predicates: delta_pat_type.where_predicates, 376 | } 377 | } 378 | } 379 | 380 | struct DeltaPatType<'a> { 381 | generic_params: Vec, 382 | where_predicates: Vec, 383 | suffix_iter: &'a mut dyn Iterator, 384 | last_special: Option, 385 | } 386 | 387 | impl Fold for DeltaPatType<'_> { 388 | fn fold_type_path(&mut self, type_path_old: TypePath) -> TypePath { 389 | let span_range = SpanRange::from_tokens(&type_path_old); // used by abort! 390 | 391 | // Apply "fold" recursively to process specials in subtypes, for example, Vec. 392 | let type_path_middle = syn::fold::fold_type_path(self, type_path_old); 393 | 394 | // If this type is special, replace it with a generic. 395 | if let Some((special, maybe_sub_types)) = Special::maybe_new(&type_path_middle, &span_range) 396 | { 397 | self.last_special = Some(special.clone()); // remember the special found (used for stmt generation) 398 | self.create_and_define_generic(special, maybe_sub_types, &span_range) 399 | } else { 400 | self.last_special = None; 401 | type_path_middle 402 | } 403 | } 404 | } 405 | 406 | impl<'a> DeltaPatType<'a> { 407 | fn new(suffix_iter: &'a mut dyn Iterator) -> Self { 408 | DeltaPatType { 409 | generic_params: vec![], 410 | where_predicates: vec![], 411 | suffix_iter, 412 | last_special: None, 413 | } 414 | } 415 | 416 | // If the top-level type is a special, add a statement to convert 417 | // from its generic type to to a concrete type. 418 | // For example, "let x = x.into_iter();" for AnyIter. 419 | fn generate_any_stmt(&self, pat_ident: &PatIdent) -> Option { 420 | if let Some(special) = &self.last_special { 421 | let stmt = special.ident_to_stmt(&pat_ident.ident); 422 | Some(stmt) 423 | } else { 424 | None 425 | } 426 | } 427 | 428 | // Define the generic type, for example, "AnyString3: AsRef", and remember the definition. 429 | fn create_and_define_generic( 430 | &mut self, 431 | special: Special, 432 | maybe_sub_type: Option, 433 | span_range: &SpanRange, 434 | ) -> TypePath { 435 | let generic = self.create_generic(&special); // for example, "AnyString3" 436 | let maybe_lifetime = self.create_maybe_lifetime(&special); 437 | let where_predicate = special.special_to_where_predicate( 438 | &generic, 439 | maybe_sub_type, 440 | maybe_lifetime, 441 | span_range, 442 | ); 443 | let generic_param: GenericParam = parse_quote!(#generic); 444 | self.generic_params.push(generic_param); 445 | self.where_predicates.push(where_predicate); 446 | generic 447 | } 448 | 449 | // create a lifetime if needed, for example, Some('any_nd_array_3) or None 450 | fn create_maybe_lifetime(&mut self, special: &Special) -> Option { 451 | if special.should_add_lifetime() { 452 | let lifetime = self.create_lifetime(special); 453 | let generic_param: GenericParam = parse_quote!(#lifetime); 454 | self.generic_params.push(generic_param); 455 | 456 | Some(lifetime) 457 | } else { 458 | None 459 | } 460 | } 461 | 462 | // Create a new generic type, for example, "AnyString3" 463 | fn create_generic(&mut self, special: &Special) -> TypePath { 464 | let suffix = self.create_suffix(); 465 | let generic_name = format!("{}{}", &special, suffix); 466 | parse_str(&generic_name).expect("Internal error: failed to parse generic name") 467 | } 468 | 469 | // Create a new lifetime, for example, "'any_nd_array_4" 470 | fn create_lifetime(&mut self, special: &Special) -> Lifetime { 471 | let lifetime_name = format!("'{}{}", special.to_snake_case(), self.create_suffix()); 472 | parse_str(&lifetime_name).expect("Internal error: failed to parse lifetime name") 473 | } 474 | 475 | // Create a new suffix, for example, "4" 476 | fn create_suffix(&mut self) -> String { 477 | self.suffix_iter 478 | .next() 479 | .expect("Internal error: ran out of generic suffixes") 480 | } 481 | } 482 | 483 | // Utility that tells if an iterator contains exactly one element. 484 | fn first_and_only>(mut iter: I) -> Option { 485 | let first = iter.next()?; 486 | if iter.next().is_some() { 487 | None 488 | } else { 489 | Some(first) 490 | } 491 | } 492 | 493 | // todo later could nested .as_ref(), .into_iter(), and .into() be replaced with a single method or macro? 494 | // todo later do something interesting with 2d ndarray/views 495 | // todo later when does the std lib use where clauses? Is there an informal rule? Should there be an option? 496 | -------------------------------------------------------------------------------- /anyinput-core/src/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use crate::{anyinput_core, anyinput_core_sample}; 4 | use proc_macro2::TokenStream; 5 | use quote::quote; 6 | use syn::{ 7 | fold::Fold, 8 | parse2, parse_quote, parse_str, 9 | punctuated::Punctuated, 10 | token::{Comma, Lt}, 11 | ItemFn, Stmt, WherePredicate, 12 | }; 13 | #[cfg(feature = "ndarray")] 14 | use syn::{GenericParam, Lifetime}; 15 | 16 | #[test] 17 | fn first() { 18 | let before = quote! { 19 | fn hello_universe() { 20 | println!("Hello, universe!"); 21 | } 22 | }; 23 | 24 | let after = anyinput_core_sample(quote!(), before); 25 | assert_eq!( 26 | after.to_string(), 27 | "fn hello_world () { println ! (\"Hello, world!\") ; }" 28 | ); 29 | } 30 | 31 | fn assert_tokens_eq(expected: &TokenStream, actual: &TokenStream) { 32 | let expected = expected.to_string(); 33 | let actual = actual.to_string(); 34 | 35 | if expected != actual { 36 | println!( 37 | "{}", 38 | colored_diff::PrettyDifference { 39 | expected: &expected, 40 | actual: &actual, 41 | } 42 | ); 43 | println!("expected: {}", &expected); 44 | println!("actual : {}", &actual); 45 | panic!("expected != actual"); 46 | } 47 | } 48 | 49 | #[test] 50 | fn one_input() { 51 | let before = quote! { 52 | fn any_str_len(s: AnyString) -> usize 53 | { 54 | s.len() 55 | } 56 | }; 57 | 58 | let expected = quote! { 59 | fn any_str_len(s: AnyString0) -> usize 60 | where 61 | AnyString0: AsRef 62 | { 63 | let s = s.as_ref(); 64 | s.len() 65 | } 66 | }; 67 | 68 | let after = anyinput_core(quote!(), before); 69 | assert_tokens_eq(&expected, &after); 70 | 71 | fn any_str_len(s: AnyString0) -> usize 72 | where 73 | AnyString0: AsRef, 74 | { 75 | let s = s.as_ref(); 76 | s.len() 77 | } 78 | assert_eq!(any_str_len("abc"), 3); 79 | } 80 | 81 | #[test] 82 | fn two_inputs() -> anyhow::Result<()> { 83 | let before = quote! { 84 | pub fn any_str_len(a: AnyString, b: AnyString) -> Result { 85 | let len = a.len() + b.len(); 86 | Ok(len) 87 | } 88 | }; 89 | let expected = quote! { 90 | pub fn any_str_len( 91 | a: AnyString0, 92 | b: AnyString1 93 | ) -> Result 94 | where 95 | AnyString0: AsRef, 96 | AnyString1: AsRef 97 | { 98 | let b = b.as_ref(); 99 | let a = a.as_ref(); 100 | let len = a.len() + b.len(); 101 | Ok(len) 102 | } 103 | }; 104 | 105 | let after = anyinput_core(quote!(), before); 106 | assert_tokens_eq(&expected, &after); 107 | 108 | pub fn any_str_len( 109 | a: AnyString0, 110 | b: AnyString1, 111 | ) -> Result 112 | where 113 | AnyString0: AsRef, 114 | AnyString1: AsRef, 115 | { 116 | let b = b.as_ref(); 117 | let a = a.as_ref(); 118 | let len = a.len() + b.len(); 119 | Ok(len) 120 | } 121 | let s = "1234".to_string(); 122 | assert_eq!(any_str_len("abc", s)?, 7); 123 | Ok(()) 124 | } 125 | 126 | #[test] 127 | fn zero_inputs() { 128 | let before = quote! { 129 | pub fn any_str_len0() -> Result { 130 | let len = 0; 131 | Ok(len) 132 | }}; 133 | let expected = quote! { 134 | pub fn any_str_len0 () -> Result { 135 | let len = 0; 136 | Ok(len) 137 | }}; 138 | 139 | let after = anyinput_core(quote!(), before); 140 | assert_tokens_eq(&expected, &after); 141 | } 142 | 143 | #[test] 144 | fn one_plus_two_input() -> anyhow::Result<()> { 145 | let before = quote! { 146 | pub fn any_str_len_plus2(a: usize, s: AnyString, b: usize) -> Result { 147 | let len = s.len()+a+b; 148 | Ok(len) 149 | } 150 | }; 151 | let expected = quote! { 152 | pub fn any_str_len_plus2( 153 | a: usize, 154 | s: AnyString0, 155 | b: usize 156 | ) -> Result 157 | where 158 | AnyString0: AsRef 159 | { 160 | let s = s.as_ref(); 161 | let len = s.len() + a + b; 162 | Ok(len) 163 | } 164 | }; 165 | 166 | let after = anyinput_core(quote!(), before); 167 | assert_tokens_eq(&expected, &after); 168 | 169 | pub fn any_str_len_plus2( 170 | a: usize, 171 | s: AnyString0, 172 | b: usize, 173 | ) -> Result 174 | where 175 | AnyString0: AsRef, 176 | { 177 | let s = s.as_ref(); 178 | let len = s.len() + a + b; 179 | Ok(len) 180 | } 181 | assert_eq!(any_str_len_plus2(1, "abc", 2)?, 6); 182 | Ok(()) 183 | } 184 | 185 | #[test] 186 | fn one_path_input() { 187 | let before = quote! { 188 | pub fn any_count_path(p: AnyPath) -> Result { 189 | let count = p.iter().count(); 190 | Ok(count) 191 | } 192 | }; 193 | 194 | let expected = quote! { 195 | pub fn any_count_path(p: AnyPath0) -> Result 196 | where 197 | AnyPath0: AsRef 198 | { 199 | let p = p.as_ref(); 200 | let count = p.iter().count(); 201 | Ok(count) 202 | } 203 | }; 204 | 205 | let after = anyinput_core(quote!(), before); 206 | assert_tokens_eq(&expected, &after); 207 | 208 | pub fn any_count_path(p: AnyPath0) -> Result 209 | where 210 | AnyPath0: AsRef, 211 | { 212 | let p = p.as_ref(); 213 | let count = p.iter().count(); 214 | Ok(count) 215 | } 216 | assert_eq!(any_count_path("abc/ed").unwrap(), 2); 217 | } 218 | 219 | #[test] 220 | fn one_iter_usize_input() { 221 | let before = quote! { 222 | pub fn any_count_iter(i: AnyIter) -> Result { 223 | let count = i.count(); 224 | Ok(count) 225 | } 226 | }; 227 | let expected = quote! { 228 | pub fn any_count_iter(i: AnyIter0) -> Result 229 | where 230 | AnyIter0: IntoIterator 231 | { 232 | let i = i.into_iter(); 233 | let count = i.count(); 234 | Ok(count) 235 | } 236 | }; 237 | 238 | let after = anyinput_core(quote!(), before); 239 | assert_tokens_eq(&expected, &after); 240 | 241 | pub fn any_count_iter(i: AnyIter0) -> Result 242 | where 243 | AnyIter0: IntoIterator, 244 | { 245 | let i = i.into_iter(); 246 | let count = i.count(); 247 | Ok(count) 248 | } 249 | assert_eq!(any_count_iter([1, 2, 3]).unwrap(), 3); 250 | } 251 | 252 | #[test] 253 | fn one_iter_i32() { 254 | let before = quote! { 255 | pub fn any_count_iter(i: AnyIter) -> Result { 256 | let count = i.count(); 257 | Ok(count) 258 | } 259 | }; 260 | let expected = quote! { 261 | pub fn any_count_iter(i: AnyIter0) -> Result 262 | where 263 | AnyIter0: IntoIterator 264 | { 265 | let i = i.into_iter(); 266 | let count = i.count(); 267 | Ok(count) 268 | } 269 | }; 270 | 271 | let after = anyinput_core(quote!(), before); 272 | assert_tokens_eq(&expected, &after); 273 | 274 | pub fn any_count_iter(i: AnyIter0) -> Result 275 | where 276 | AnyIter0: IntoIterator, 277 | { 278 | let i = i.into_iter(); 279 | let count = i.count(); 280 | Ok(count) 281 | } 282 | assert_eq!(any_count_iter([1, 2, 3]).unwrap(), 3); 283 | } 284 | 285 | #[test] 286 | fn one_iter_t() { 287 | let before = quote! { 288 | pub fn any_count_iter(i: AnyIter) -> Result { 289 | let count = i.count(); 290 | Ok(count) 291 | } 292 | }; 293 | let expected = quote! { 294 | pub fn any_count_iter(i: AnyIter0) -> Result 295 | where 296 | AnyIter0: IntoIterator 297 | { 298 | let i = i.into_iter(); 299 | let count = i.count(); 300 | Ok(count) 301 | } 302 | }; 303 | 304 | let after = anyinput_core(quote!(), before); 305 | assert_tokens_eq(&expected, &after); 306 | 307 | pub fn any_count_iter(i: AnyIter0) -> Result 308 | where 309 | AnyIter0: IntoIterator, 310 | { 311 | let i = i.into_iter(); 312 | let count = i.count(); 313 | Ok(count) 314 | } 315 | assert_eq!(any_count_iter([1, 2, 3]).unwrap(), 3); 316 | } 317 | 318 | #[test] 319 | fn one_iter_t_where() { 320 | let before = quote! { 321 | pub fn any_count_iter(i: AnyIter) -> Result 322 | where T: Copy 323 | { 324 | let count = i.count(); 325 | Ok(count) 326 | } 327 | }; 328 | let expected = quote! { 329 | pub fn any_count_iter(i: AnyIter0) -> Result 330 | where 331 | T: Copy, 332 | AnyIter0: IntoIterator 333 | { 334 | let i = i.into_iter(); 335 | let count = i.count(); 336 | Ok(count) 337 | } }; 338 | 339 | let after = anyinput_core(quote!(), before); 340 | assert_tokens_eq(&expected, &after); 341 | 342 | pub fn any_count_iter(i: AnyIter0) -> Result 343 | where 344 | T: Copy, 345 | AnyIter0: IntoIterator, 346 | { 347 | let i = i.into_iter(); 348 | let count = i.count(); 349 | Ok(count) 350 | } 351 | assert_eq!(any_count_iter([1, 2, 3]).unwrap(), 3); 352 | } 353 | #[test] 354 | fn one_iter_path() { 355 | let before = quote! { 356 | pub fn any_count_iter(i: AnyIter) -> Result { 357 | let sum_count = i.map(|x| x.as_ref().iter().count()).sum(); 358 | Ok(sum_count) 359 | } 360 | }; 361 | let expected = quote! { 362 | pub fn any_count_iter(i: AnyIter1) -> Result 363 | where 364 | AnyPath0: AsRef, 365 | AnyIter1: IntoIterator 366 | { 367 | let i = i.into_iter(); 368 | let sum_count = i.map(|x| x.as_ref().iter().count()).sum(); 369 | Ok(sum_count) 370 | } 371 | }; 372 | 373 | let after = anyinput_core(quote!(), before); 374 | assert_tokens_eq(&expected, &after); 375 | 376 | pub fn any_count_iter(i: AnyIter1) -> Result 377 | where 378 | AnyPath0: AsRef, 379 | AnyIter1: IntoIterator, 380 | { 381 | let i = i.into_iter(); 382 | let sum_count = i.map(|x| x.as_ref().iter().count()).sum(); 383 | Ok(sum_count) 384 | } 385 | assert_eq!(any_count_iter(["a/b", "d"]).unwrap(), 3); 386 | } 387 | 388 | #[test] 389 | fn one_vec_path() { 390 | let before = quote! { 391 | pub fn any_count_vec( 392 | i: Vec, 393 | ) -> Result { 394 | let sum_count = i.iter().map(|x| x.as_ref().iter().count()).sum(); 395 | Ok(sum_count) 396 | } 397 | }; 398 | let expected = quote! { 399 | pub fn any_count_vec(i: Vec) -> Result 400 | where 401 | AnyPath0: AsRef 402 | { 403 | let sum_count = i.iter().map(|x| x.as_ref().iter().count()).sum(); 404 | Ok(sum_count) 405 | } 406 | }; 407 | 408 | let after = anyinput_core(quote!(), before); 409 | assert_tokens_eq(&expected, &after); 410 | 411 | pub fn any_count_vec(i: Vec) -> Result 412 | where 413 | AnyPath0: AsRef, 414 | { 415 | let sum_count = i.iter().map(|x| x.as_ref().iter().count()).sum(); 416 | Ok(sum_count) 417 | } 418 | assert_eq!(any_count_vec(vec!["a/b", "d"]).unwrap(), 3); 419 | } 420 | 421 | #[test] 422 | fn one_array_usize_input() { 423 | let before = quote! { 424 | pub fn any_array_len(a: AnyArray) -> Result { 425 | let len = a.len(); 426 | Ok(len) 427 | } 428 | }; 429 | let expected = quote! { 430 | pub fn any_array_len(a: AnyArray0) -> Result 431 | where 432 | AnyArray0: AsRef<[usize]> 433 | { 434 | let a = a.as_ref(); 435 | let len = a.len(); 436 | Ok(len) 437 | } 438 | }; 439 | 440 | let after = anyinput_core(quote!(), before); 441 | assert_tokens_eq(&expected, &after); 442 | 443 | pub fn any_array_len(a: AnyArray0) -> Result 444 | where 445 | AnyArray0: AsRef<[usize]>, 446 | { 447 | let a = a.as_ref(); 448 | let len = a.len(); 449 | Ok(len) 450 | } 451 | assert_eq!(any_array_len([1, 2, 3]).unwrap(), 3); 452 | } 453 | 454 | #[cfg(feature = "ndarray")] 455 | #[test] 456 | fn understand_lifetime_parse() { 457 | let a = Lifetime::new("'a", syn::__private::Span::call_site()); 458 | println!("a: {}", quote!(#a)); 459 | let b: Lifetime = parse_quote!('a); 460 | println!("b: {}", quote!(#b)); 461 | 462 | let _generic_param: GenericParam = parse_quote!(S1: Into>); 463 | println!("gp: {}", quote!(#_generic_param)); 464 | println!("done"); 465 | } 466 | 467 | #[cfg(feature = "ndarray")] 468 | #[test] 469 | fn one_ndarray_usize_input() { 470 | let before = quote! { 471 | pub fn any_array_len(a: AnyNdArray) -> Result { 472 | let len = a.len(); 473 | Ok(len) 474 | } }; 475 | let expected = quote! { 476 | pub fn any_array_len<'any_nd_array1, AnyNdArray0>( 477 | a: AnyNdArray0 478 | ) -> Result 479 | where 480 | AnyNdArray0: Into > 481 | { 482 | let a = a.into(); 483 | let len = a.len(); 484 | Ok(len) 485 | } 486 | }; 487 | 488 | let after = anyinput_core(quote!(), before); 489 | assert_tokens_eq(&expected, &after); 490 | 491 | // The lines are long enough that Clippy would like a comma after 492 | // a:AnyNdArray0, but the macro doesn't do that because 493 | // it doesn't know the line length. 494 | pub fn any_array_len<'any_nd_array1, AnyNdArray0>( 495 | a: AnyNdArray0, 496 | ) -> Result 497 | where 498 | AnyNdArray0: Into>, 499 | { 500 | let a = a.into(); 501 | let len = a.len(); 502 | Ok(len) 503 | } 504 | assert_eq!(any_array_len([1, 2, 3].as_ref()).unwrap(), 3); 505 | } 506 | 507 | #[test] 508 | #[cfg(feature = "ndarray")] 509 | fn complex() { 510 | let before = quote! { 511 | pub fn complex_total( 512 | a: usize, 513 | b: AnyIter>>, 514 | c: AnyNdArray 515 | ) -> Result { 516 | let mut total = a + c.sum(); 517 | for vec in b { 518 | for any_array in vec { 519 | let any_array = any_array.as_ref(); 520 | for any_path in any_array.iter() { 521 | let any_path = any_path.as_ref(); 522 | total += any_path.iter().count(); 523 | } 524 | } 525 | } 526 | Ok(total) 527 | } 528 | }; 529 | let expected = quote! { 530 | pub fn complex_total<'any_nd_array4, AnyPath0, AnyArray1, AnyIter2, AnyNdArray3>( 531 | a: usize, 532 | b: AnyIter2, 533 | c: AnyNdArray3 534 | ) -> Result 535 | where 536 | AnyPath0: AsRef, 537 | AnyArray1: AsRef<[AnyPath0]>, 538 | AnyIter2: IntoIterator >, 539 | AnyNdArray3: Into > 540 | { 541 | let c = c.into(); 542 | let b = b.into_iter(); 543 | let mut total = a + c.sum(); 544 | for vec in b { 545 | for any_array in vec { 546 | let any_array = any_array.as_ref(); 547 | for any_path in any_array.iter() { 548 | let any_path = any_path.as_ref(); 549 | total += any_path.iter().count(); 550 | } 551 | } 552 | } 553 | Ok(total) 554 | } 555 | }; 556 | 557 | let after = anyinput_core(quote!(), before); 558 | assert_tokens_eq(&expected, &after); 559 | 560 | pub fn complex_total<'any_nd_array4, AnyPath0, AnyArray1, AnyIter2, AnyNdArray3>( 561 | a: usize, 562 | b: AnyIter2, 563 | c: AnyNdArray3, 564 | ) -> Result 565 | where 566 | AnyPath0: AsRef, 567 | AnyArray1: AsRef<[AnyPath0]>, 568 | AnyIter2: IntoIterator>, 569 | AnyNdArray3: Into>, 570 | { 571 | let c = c.into(); 572 | let b = b.into_iter(); 573 | let mut total = a + c.sum(); 574 | for vec in b { 575 | for any_array in vec { 576 | let any_array = any_array.as_ref(); 577 | for any_path in any_array.iter() { 578 | let any_path = any_path.as_ref(); 579 | total += any_path.iter().count(); 580 | } 581 | } 582 | } 583 | Ok(total) 584 | } 585 | 586 | assert_eq!( 587 | complex_total(17, [vec![["one"]]], [1, 2, 3].as_ref()).unwrap(), 588 | 24 589 | ); 590 | } 591 | 592 | #[test] 593 | fn doc_write() -> Result<(), anyhow::Error> { 594 | let before = quote! { 595 | fn len_plus_2(s: AnyString) -> Result { 596 | Ok(s.len()+2) 597 | } }; 598 | let after = anyinput_core(quote!(), before); 599 | println!("after: {}", quote! { #after}); 600 | let expected = quote! { 601 | fn len_plus_2(s: AnyString0) -> Result 602 | where 603 | AnyString0: AsRef 604 | { 605 | let s = s.as_ref(); 606 | Ok(s.len() + 2) 607 | } 608 | }; 609 | assert_tokens_eq(&expected, &after); 610 | 611 | fn len_plus_2(s: AnyString0) -> Result 612 | where 613 | AnyString0: AsRef, 614 | { 615 | let s = s.as_ref(); 616 | Ok(s.len() + 2) 617 | } 618 | assert_eq!(len_plus_2("hello")?, 7); 619 | 620 | Ok(()) 621 | } 622 | 623 | #[test] 624 | #[should_panic( 625 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 626 | )] 627 | fn one_bad_input_1() { 628 | let before = quote! { 629 | pub fn any_str_len(s: AnyIter) -> Result { 630 | let len = s.len(); 631 | Ok(len) 632 | } 633 | }; 634 | let _after = anyinput_core(quote!(), before); 635 | } 636 | 637 | #[test] 638 | #[should_panic( 639 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 640 | )] 641 | fn one_bad_input_2() { 642 | let before = quote! { 643 | pub fn any_str_len(s: AnyIter<3>) -> Result { 644 | let len = s.len(); 645 | Ok(len) 646 | } 647 | }; 648 | let _after = anyinput_core(quote!(), before); 649 | } 650 | 651 | #[test] 652 | #[should_panic( 653 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 654 | )] 655 | fn one_bad_input_3() { 656 | let before = quote! { 657 | pub fn any_str_len(s: AnyIter(AnyString)) { 658 | s.len() 659 | } 660 | }; 661 | let _after = anyinput_core(quote!(), before); 662 | } 663 | 664 | #[test] 665 | #[should_panic( 666 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 667 | )] 668 | fn one_bad_input_4() { 669 | let before = quote! { 670 | pub fn any_str_len(s: AnyArray) -> Result { 671 | let len = s.len(); 672 | Ok(len) 673 | } 674 | }; 675 | let _after = anyinput_core(quote!(), before); 676 | } 677 | 678 | #[test] 679 | #[should_panic( 680 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 681 | )] 682 | fn one_bad_input_5() { 683 | let before = quote! { 684 | pub fn any_str_len(s: AnyIter) -> Result { 685 | let len = s.len(); 686 | Ok(len) 687 | } 688 | }; 689 | let _after = anyinput_core(quote!(), before); 690 | } 691 | #[test] 692 | #[should_panic( 693 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 694 | )] 695 | fn one_bad_input_6() { 696 | let before = quote! { 697 | pub fn any_str_len(s: AnyNdArray) -> Result { 698 | let len = s.len(); 699 | Ok(len) 700 | } 701 | }; 702 | let _after = anyinput_core(quote!(), before); 703 | } 704 | 705 | #[test] 706 | #[should_panic( 707 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 708 | )] 709 | fn one_bad_input_7() { 710 | let before = quote! { 711 | pub fn any_str_len(s: AnyString) -> Result { 712 | let len = s.len(); 713 | Ok(len) 714 | } 715 | }; 716 | let _after = anyinput_core(quote!(), before); 717 | } 718 | 719 | #[test] 720 | #[should_panic( 721 | expected = "proc-macro-error API cannot be used outside of `entry_point` invocation, perhaps you forgot to annotate your #[proc_macro] function with `#[proc_macro_error]" 722 | )] 723 | fn one_bad_input_8() { 724 | let before = quote! { 725 | pub fn any_str_len(s: AnyPath) -> Result { 726 | let len = s.len(); 727 | Ok(len) 728 | } 729 | }; 730 | let _after = anyinput_core(quote!(), before); 731 | } 732 | 733 | #[test] 734 | fn see_bed_reader() { 735 | let before = quote! { 736 | pub fn iid(mut self, iid: AnyIter) -> Self { 737 | // Unwrap will always work because BedBuilder starting with some metadata 738 | self.metadata.as_mut().unwrap().set_iid(iid); 739 | self 740 | } 741 | }; 742 | let after = anyinput_core(quote!(), before); 743 | println!("after: {}", quote! { #after}); 744 | 745 | // pub fn iid, AnyIter1: IntoIterator>( 746 | // mut self, 747 | // iid: AnyIter1, 748 | // ) -> Self { 749 | // let iid = iid.into_iter(); 750 | // self.metadata.as_mut().unwrap().set_iid(iid); 751 | // self 752 | // } 753 | } 754 | 755 | #[test] 756 | fn understand_token_stream() { 757 | let token_stream = quote!( 758 | pub fn hello() { 759 | println!("hello world") 760 | } 761 | ); 762 | println!("{:#?}", token_stream); 763 | } 764 | 765 | #[test] 766 | fn understand_parse_quote() { 767 | let item_fn: ItemFn = parse_quote!( 768 | pub fn hello() { 769 | println!("hello world") 770 | } 771 | ); 772 | 773 | // println!("{}", &item_fn); 774 | println!("{:?}", &item_fn); 775 | println!("{:#?}", &item_fn); 776 | 777 | use quote::ToTokens; 778 | let token_stream: proc_macro2::TokenStream = item_fn.clone().into_token_stream(); 779 | let _token_stream2: proc_macro2::TokenStream = quote!(#item_fn); 780 | println!("{}", &token_stream); 781 | println!("{:?}", &token_stream); 782 | println!("{:#?}", &token_stream); 783 | } 784 | 785 | /// Also see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7ae7d8fd405a2af5afc60ef3de9b2dad 786 | #[test] 787 | fn conversion_combinations() { 788 | // Literal code to tokens, syntax, and string-of-code 789 | let tokens1 = quote! { 790 | fn hello() { 791 | println!("hello world") 792 | } 793 | }; 794 | let syntax1: ItemFn = parse_quote! { 795 | fn hello() { 796 | println!("hello world") 797 | } 798 | }; 799 | let string_of_code1 = stringify!( 800 | fn hello() { 801 | println!("hello world") 802 | } 803 | ); 804 | assert_eq!(string_of_code1, "fn hello() { println! (\"hello world\") }"); 805 | 806 | // Tokens to string-of-code & string-of-tokens 807 | assert_eq!( 808 | tokens1.to_string(), 809 | "fn hello () { println ! (\"hello world\") }" 810 | ); 811 | // -- use {:#?} for the pretty-printed version 812 | let string_of_tokens1 = format!("{:?}", tokens1); 813 | assert!(string_of_tokens1.starts_with("TokenStream [Ident { sym: fn }")); 814 | 815 | // Syntax to string-of-code & string-of-syntax 816 | assert_eq!( 817 | quote!(#syntax1).to_string(), 818 | "fn hello () { println ! (\"hello world\") }" 819 | ); 820 | // -- use {:#?} for the pretty-printed version 821 | let string_of_syntax1 = format!("{:?}", syntax1); 822 | assert!(string_of_syntax1.starts_with("ItemFn { attrs: [], ")); 823 | 824 | // Tokens <--> syntax 825 | let syntax2_result: Result = parse2::(tokens1); 826 | let syntax2: ItemFn = syntax2_result.expect("todo: need better error"); 827 | let _tokens2 = quote!(#syntax2); // or .into_token_stream() 828 | 829 | // String of code to syntax or tokens 830 | let syntax3_result: Result = 831 | parse_str("fn hello () { println ! (\"hello world\") }"); 832 | let _syntax3 = syntax3_result.expect("todo: need better error"); 833 | let tokens3_result: Result = 834 | parse_str("fn hello () { println ! (\"hello world\") }"); 835 | let _tokens3 = tokens3_result.expect("todo: need better error"); 836 | 837 | // Literal code to string-of-tokens and string-of-syntax 838 | assert!(format!( 839 | "{:?}", 840 | quote! { 841 | fn hello() { 842 | println!("hello world") 843 | } 844 | } 845 | ) 846 | .starts_with("TokenStream [Ident { sym: fn }")); 847 | assert!(format!( 848 | "{:?}", 849 | parse2::(quote! { 850 | fn hello() { 851 | println!("hello world") 852 | } 853 | }) 854 | .expect("todo: need better error") 855 | ) 856 | .starts_with("ItemFn { attrs: [], ")); 857 | } 858 | 859 | /// Also see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=acce21f0c87ce66cb90c4cf12103e247 860 | struct StmtCounter { 861 | count: usize, 862 | } 863 | 864 | impl Fold for StmtCounter { 865 | fn fold_stmt(&mut self, stmt_old: Stmt) -> Stmt { 866 | let stmt_middle = syn::fold::fold_stmt(self, stmt_old); 867 | 868 | println!("stmt #{}: {}", self.count, quote!(#stmt_middle)); 869 | self.count += 1; 870 | 871 | if quote!(#stmt_middle).to_string().contains("galaxy") { 872 | parse_quote!(println!("hello universe");) 873 | } else { 874 | stmt_middle 875 | } 876 | } 877 | } 878 | 879 | #[test] 880 | fn count_statements() { 881 | let mut stmt_counter = StmtCounter { count: 0 }; 882 | let item_fn_old: ItemFn = parse_quote! { 883 | fn hello() { 884 | println!("hello world"); 885 | { 886 | println!("hello solar system"); 887 | println!("hello galaxy"); 888 | } 889 | } 890 | }; 891 | let item_fn_new = stmt_counter.fold_item_fn(item_fn_old); 892 | println!("item_fn_new: {}", quote!(#item_fn_new)); 893 | println!("count: {}", stmt_counter.count); 894 | } 895 | 896 | #[test] 897 | fn parse_quote_sample() { 898 | let _lt: Lt = parse_quote!(<); 899 | let _where_list: Punctuated = parse_quote!(); 900 | } 901 | -------------------------------------------------------------------------------- /anyinput-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anyinput-derive" 3 | version = "0.1.8" 4 | description = "An internal helper library of anyinput" 5 | repository = "https://github.com/CarlKCarlK/anyinput" 6 | readme = "README.md" 7 | documentation = "https://docs.rs/anyinput/latest/anyinput/" 8 | authors = ["Carl Kadie"] 9 | exclude = [] 10 | license = "MIT OR Apache-2.0" 11 | keywords = ["macros", "function", "generics", "ndarray", "iterator"] 12 | categories = ["development-tools", "rust-patterns"] 13 | homepage = "https://github.com/CarlKCarlK/anyinput" 14 | edition = "2021" 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [features] 20 | ndarray = ["anyinput-core/ndarray"] 21 | 22 | [dependencies] 23 | anyinput-core = { version = "0.1.8", path = "../anyinput-core" } 24 | proc-macro-error = "1.0" 25 | proc-macro2 = "1.0" 26 | -------------------------------------------------------------------------------- /anyinput-derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONSs -------------------------------------------------------------------------------- /anyinput-derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /anyinput-derive/README.md: -------------------------------------------------------------------------------- 1 | anyinput-derive 2 | ========== 3 | 4 | [![github](https://img.shields.io/badge/github-anyinput-8da0cb?style=flat&labelColor=555555&logo=github)](https://github.com/CarlKCarlK/anyinput) 5 | [![crates.io](https://img.shields.io/crates/v/anyinput.svg?flat&color=fc8d62&logo=rust")](https://crates.io/crates/anyinput) 6 | [![docs.rs](https://img.shields.io/badge/docs.rs-anyinput-66c2a5?flat&labelColor=555555&logoColor=white&logo=core:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K)](https://docs.rs/anyinput) 7 | 8 | **Important Note**: 9 | 10 | * You are probably looking for the [`anyinput`](https://docs.rs/anyinput/) crate, which wraps this crate and is much more ergonomic to use. 11 | 12 | Purpose 13 | ------- 14 | 15 | This is an internal helper library of [`anyinput`](https://docs.rs/anyinput/). 16 | -------------------------------------------------------------------------------- /anyinput-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | 3 | use anyinput_core::anyinput_core; 4 | use proc_macro::TokenStream; 5 | use proc_macro_error::proc_macro_error; 6 | 7 | #[proc_macro_error] 8 | #[proc_macro_attribute] 9 | pub fn anyinput(args: TokenStream, input: TokenStream) -> TokenStream { 10 | anyinput_core(args.into(), input.into()).into() 11 | } 12 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs)] 2 | #![doc = include_str!("../README.md")] 3 | 4 | /// A macro for easier writing of functions that accept any string-, path-, iterator-, array-, or ndarray-like input. 5 | /// The AnyInputs are `AnyString`, `AnyPath`, `AnyIter`, `AnyArray`, and (optionally) `AnyNdArray`. 6 | /// 7 | /// See the [documentation](https://docs.rs/anyinput/) for for details. 8 | /// 9 | /// # Example 10 | /// ``` 11 | /// use anyinput::anyinput; 12 | /// 13 | /// #[anyinput] 14 | /// fn len_plus_2(s: AnyString) -> usize { 15 | /// s.len()+2 16 | /// } 17 | /// 18 | /// // By using AnyString, len_plus_2 works with 19 | /// // &str, String, or &String -- borrowed or moved. 20 | /// assert_eq!(len_plus_2("Hello"), 7); // move a &str 21 | /// let input: &str = "Hello"; 22 | /// assert_eq!(len_plus_2(&input), 7); // borrow a &str 23 | /// let input: String = "Hello".to_string(); 24 | /// assert_eq!(len_plus_2(&input), 7); // borrow a String 25 | /// let input2: &String = &input; 26 | /// assert_eq!(len_plus_2(&input2), 7); // borrow a &String 27 | /// assert_eq!(len_plus_2(input2), 7); // move a &String 28 | /// assert_eq!(len_plus_2(input), 7); // move a String 29 | /// ``` 30 | pub use anyinput_derive::anyinput; 31 | -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | use std::path::PathBuf; 3 | 4 | #[test] 5 | fn one_input() { 6 | #[anyinput] 7 | pub fn any_str_len(s: AnyString) -> usize { 8 | s.len() 9 | } 10 | assert!(any_str_len("123") == 3); 11 | } 12 | 13 | #[test] 14 | fn two_inputs() -> Result<(), anyhow::Error> { 15 | #[anyinput] 16 | pub fn any_str_len2(a: AnyString, b: AnyString) -> Result { 17 | let len = a.len() + b.len(); 18 | Ok(len) 19 | } 20 | let s = "Hello".to_string(); 21 | assert!(any_str_len2("123", s)? == 8); 22 | Ok(()) 23 | } 24 | 25 | #[test] 26 | fn zero_inputs() -> Result<(), anyhow::Error> { 27 | #[anyinput] 28 | pub fn any_str_len0() -> Result { 29 | let len = 0; 30 | Ok(len) 31 | } 32 | assert!(any_str_len0()? == 0); 33 | Ok(()) 34 | } 35 | 36 | #[test] 37 | fn one_plus_two_input() -> Result<(), anyhow::Error> { 38 | #[anyinput] 39 | pub fn any_str_len1plus2(a: usize, s: AnyString, b: usize) -> Result { 40 | let len = s.len() + a + b; 41 | Ok(len) 42 | } 43 | assert!(any_str_len1plus2(1, "123", 2)? == 6); 44 | Ok(()) 45 | } 46 | 47 | #[test] 48 | fn one_path_input() -> Result<(), anyhow::Error> { 49 | #[anyinput] 50 | pub fn any_count_path(p: AnyPath) -> Result { 51 | let count = p.iter().count(); 52 | Ok(count) 53 | } 54 | assert!(any_count_path(PathBuf::from("one/two/three"))? == 3); 55 | Ok(()) 56 | } 57 | 58 | #[test] 59 | fn one_iter_usize_input() -> Result<(), anyhow::Error> { 60 | #[anyinput] 61 | pub fn any_count_iter(i: AnyIter) -> Result { 62 | let count = i.count(); 63 | Ok(count) 64 | } 65 | assert_eq!(any_count_iter([1, 2, 3])?, 3); 66 | Ok(()) 67 | } 68 | 69 | #[test] 70 | fn one_iter_i32() -> Result<(), anyhow::Error> { 71 | #[anyinput] 72 | pub fn any_count_iter(i: AnyIter) -> Result { 73 | let count = i.count(); 74 | Ok(count) 75 | } 76 | assert_eq!(any_count_iter([1, 2, 3])?, 3); 77 | Ok(()) 78 | } 79 | 80 | #[test] 81 | fn one_iter_t() -> Result<(), anyhow::Error> { 82 | #[anyinput] 83 | pub fn any_count_iter(i: AnyIter) -> Result { 84 | let count = i.count(); 85 | Ok(count) 86 | } 87 | assert_eq!(any_count_iter([1, 2, 3])?, 3); 88 | Ok(()) 89 | } 90 | 91 | #[test] 92 | fn one_iter_path() -> Result<(), anyhow::Error> { 93 | #[anyinput] 94 | pub fn any_count_iter(i: AnyIter) -> Result { 95 | let sum_count = i.map(|x| x.as_ref().iter().count()).sum(); 96 | Ok(sum_count) 97 | } 98 | assert_eq!(any_count_iter(["a/b", "d"])?, 3); 99 | Ok(()) 100 | } 101 | 102 | #[test] 103 | fn one_vec_path() -> Result<(), anyhow::Error> { 104 | #[anyinput] 105 | pub fn any_count_vec(i: Vec) -> Result { 106 | let sum_count = i.iter().map(|x| x.as_ref().iter().count()).sum(); 107 | Ok(sum_count) 108 | } 109 | assert_eq!(any_count_vec(vec!["a/b", "d"])?, 3); 110 | Ok(()) 111 | } 112 | 113 | #[cfg(feature = "ndarray")] 114 | #[test] 115 | fn one_array_usize_input() -> Result<(), anyhow::Error> { 116 | #[anyinput] 117 | pub fn any_array_len(a: AnyArray) -> Result { 118 | let len = a.len(); 119 | Ok(len) 120 | } 121 | assert_eq!(any_array_len([1, 2, 3])?, 3); 122 | Ok(()) 123 | } 124 | 125 | #[cfg(feature = "ndarray")] 126 | #[test] 127 | fn one_ndarray_usize_input() -> Result<(), anyhow::Error> { 128 | use ndarray; 129 | 130 | #[anyinput] 131 | pub fn any_array_len(a: AnyNdArray) -> Result { 132 | let len = a.len(); 133 | Ok(len) 134 | } 135 | assert_eq!(any_array_len([1, 2, 3].as_ref())?, 3); 136 | Ok(()) 137 | } 138 | 139 | #[cfg(feature = "ndarray")] 140 | #[test] 141 | fn complex() -> Result<(), anyhow::Error> { 142 | use ndarray; 143 | 144 | #[anyinput] 145 | pub fn complex_total( 146 | a: usize, 147 | b: AnyIter>>, 148 | c: AnyNdArray, 149 | ) -> Result { 150 | let mut total = a + c.sum(); 151 | for vec in b { 152 | for any_array in vec { 153 | let any_array = any_array.as_ref(); 154 | for any_path in any_array.iter() { 155 | let any_path = any_path.as_ref(); 156 | total += any_path.iter().count(); 157 | } 158 | } 159 | } 160 | Ok(total) 161 | } 162 | assert_eq!(complex_total(17, [vec![["one"]]], [1, 2, 3].as_ref())?, 24); 163 | Ok(()) 164 | } 165 | 166 | #[test] 167 | #[cfg(feature = "ndarray")] 168 | fn doc_ndarray() -> Result<(), anyhow::Error> { 169 | use anyhow::Result; 170 | use ndarray; 171 | 172 | #[anyinput] 173 | fn any_mean(array: AnyNdArray) -> Result { 174 | if let Some(mean) = array.mean() { 175 | Ok(mean) 176 | } else { 177 | Err(anyhow::anyhow!("empty array")) 178 | } 179 | } 180 | 181 | // 'AnyNdArray' works with any 1-D array-like thing, but must be borrowed. 182 | assert_eq!(any_mean(&[10.0, 20.0, 30.0, 40.0])?, 25.0); 183 | assert_eq!(any_mean(&ndarray::array![10.0, 20.0, 30.0, 40.0])?, 25.0); 184 | 185 | Ok(()) 186 | } 187 | 188 | #[test] 189 | fn doc_path() -> Result<(), anyhow::Error> { 190 | use anyinput::anyinput; 191 | use std::path::Path; 192 | 193 | #[anyinput] 194 | fn component_count(path: AnyPath) -> usize { 195 | path.iter().count() 196 | } 197 | 198 | // By using AnyPath, component_count works with any 199 | // string-like or path-like thing, borrowed or moved. 200 | assert_eq!(component_count("usr/files/home"), 3); 201 | let path = Path::new("usr/files/home"); 202 | assert_eq!(component_count(path), 3); 203 | let pathbuf = path.to_path_buf(); 204 | assert_eq!(component_count(pathbuf), 3); 205 | 206 | Ok(()) 207 | } 208 | 209 | #[test] 210 | fn doc_iter() -> Result<(), anyhow::Error> { 211 | use anyinput::anyinput; 212 | 213 | #[anyinput] 214 | fn two_iterator_sum(iter1: AnyIter, iter2: AnyIter) -> usize { 215 | let mut sum = iter1.sum(); 216 | for any_string in iter2 { 217 | // Needs .as_ref to turn the nested AnyString into a &str. 218 | sum += any_string.as_ref().len(); 219 | } 220 | sum 221 | } 222 | 223 | assert_eq!(two_iterator_sum(1..=10, ["a", "bb", "ccc"]), 61); 224 | Ok(()) 225 | } 226 | 227 | #[test] 228 | fn doc_array() -> Result<(), anyhow::Error> { 229 | use crate::anyinput; 230 | use anyhow::Result; 231 | 232 | #[anyinput] 233 | fn indexed_component_count( 234 | array: AnyArray, 235 | index: usize, 236 | ) -> Result { 237 | // Needs .as_ref to turn the nested AnyPath into a &Path. 238 | let path = array[index].as_ref(); 239 | let count = path.iter().count(); 240 | Ok(count) 241 | } 242 | assert_eq!( 243 | indexed_component_count(vec!["usr/files/home", "usr/data"], 1)?, 244 | 2 245 | ); 246 | Ok(()) 247 | } 248 | 249 | #[test] 250 | #[cfg(feature = "ndarray")] 251 | fn doc_ndarray2() -> Result<(), anyhow::Error> { 252 | use crate::anyinput; 253 | use anyhow::Result; 254 | use ndarray; 255 | 256 | #[anyinput] 257 | fn any_mean(array: AnyNdArray) -> Result { 258 | if let Some(mean) = array.mean() { 259 | Ok(mean) 260 | } else { 261 | Err(anyhow::anyhow!("empty array")) 262 | } 263 | } 264 | 265 | // 'AnyNdArray' works with any 1-D array-like thing, but must be borrowed. 266 | assert_eq!(any_mean(&[10.0, 20.0, 30.0, 40.0])?, 25.0); 267 | assert_eq!(any_mean(&ndarray::array![10.0, 20.0, 30.0, 40.0])?, 25.0); 268 | Ok(()) 269 | } 270 | 271 | #[test] 272 | fn more_path() -> Result<(), anyhow::Error> { 273 | use crate::anyinput; 274 | use anyhow::Result; 275 | use std::path::Path; 276 | 277 | fn component_count1(path: impl AsRef) -> Result { 278 | let path = path.as_ref(); 279 | Ok(path.iter().count()) 280 | } 281 | assert_eq!(component_count1("usr/files/home")?, 3); 282 | 283 | #[anyinput] 284 | fn component_count2(path: AnyPath) -> Result { 285 | Ok(path.iter().count()) 286 | } 287 | assert_eq!(component_count2("usr/files/home")?, 3); 288 | 289 | Ok(()) 290 | } 291 | 292 | #[test] 293 | fn more_string() -> Result<(), anyhow::Error> { 294 | use std::borrow::Borrow; 295 | 296 | pub fn len_plus_2(s: impl Borrow) -> usize { 297 | s.borrow().len() + 2 298 | } 299 | 300 | assert_eq!(len_plus_2("Hello"), 7); // move a &str 301 | // let input: &str = "Hello"; 302 | // assert_eq!(len_plus_2(&input), 7); // borrow a &str 303 | // let input: String = "Hello".to_string(); 304 | // assert_eq!(len_plus_2(&input), 7); // borrow a String 305 | // let input2: &String = &input; 306 | // assert_eq!(len_plus_2(&input2), 7); // borrow a &String 307 | // assert_eq!(len_plus_2(input2), 7); // move a &String 308 | // assert_eq!(len_plus_2(input), 7); // move a String 309 | Ok(()) 310 | } 311 | 312 | #[test] 313 | fn more_iter() -> Result<(), anyhow::Error> { 314 | use crate::anyinput; 315 | use std::borrow::Borrow; 316 | 317 | pub fn two_iterator_sum( 318 | iter1: impl IntoIterator, 319 | iter2: impl IntoIterator>, 320 | ) -> usize { 321 | let mut sum = iter1.into_iter().sum(); 322 | 323 | for string in iter2 { 324 | sum += string.borrow().len(); 325 | } 326 | 327 | sum 328 | } 329 | 330 | two_iterator_sum(1..=10, ["a", "bb", "ccc"]); 331 | let s0 = "bb".to_string(); 332 | // two_iterator_sum(1..=10, [&s0]); 333 | 334 | #[anyinput] 335 | pub fn two_iterator_sum2(iter1: AnyIter, iter2: AnyIter) -> usize { 336 | let mut sum = iter1.sum(); 337 | 338 | for string in iter2 { 339 | sum += string.as_ref().len(); 340 | } 341 | 342 | sum 343 | } 344 | two_iterator_sum2(1..=10, [&s0]); 345 | 346 | Ok(()) 347 | } 348 | 349 | #[test] 350 | fn more_iter2() -> Result<(), anyhow::Error> { 351 | use crate::anyinput; 352 | 353 | #[anyinput] 354 | fn iid0(iid: AnyIter) -> usize { 355 | let mut sum = 0; 356 | for s in iid { 357 | sum += s.as_ref().len(); 358 | } 359 | sum 360 | } 361 | 362 | fn iid1, T: AsRef>(iid: I) -> usize { 363 | let mut sum = 0; 364 | for s in iid { 365 | sum += s.as_ref().len(); 366 | } 367 | sum 368 | } 369 | 370 | fn iid2(iid: I) -> usize 371 | where 372 | ::Item: AsRef, 373 | { 374 | let mut sum = 0; 375 | for s in iid { 376 | sum += s.as_ref().len(); 377 | } 378 | sum 379 | } 380 | 381 | //let fa = iid0; 382 | 383 | assert_eq!(iid0::<&str, [&str; 3]>(["a", "bb", "ccc"]), 6); 384 | 385 | // assert_eq!(iid0::<_, _>(["a", "bb", "ccc"]), 6); 386 | assert_eq!(iid1::<[&str; 3], &str>(["a", "bb", "ccc"]), 6); 387 | assert_eq!(iid2::<[&str; 3]>(["a", "bb", "ccc"]), 6); 388 | assert_eq!(iid1(["a", "bb", "ccc"]), 6); 389 | assert_eq!(iid2(["a", "bb", "ccc"]), 6); 390 | 391 | Ok(()) 392 | } 393 | 394 | #[cfg(not(target_arch = "wasm32"))] 395 | #[test] 396 | fn ui() { 397 | let t = trybuild::TestCases::new(); 398 | t.compile_fail("tests/ui/*.rs"); 399 | } 400 | 401 | // todo later should their be a warning/error if there is no Anyinput on a function to which this has been applied? 402 | // todo must test badly-formed functions to see that the error messages make sense. 403 | -------------------------------------------------------------------------------- /tests/ui/anyarray.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | fn empty_array(array: AnyArray) { 5 | println!(array[0]); 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /tests/ui/anyarray.stderr: -------------------------------------------------------------------------------- 1 | error: AnyArray expects a generic parameter, for example, AnyArray or AnyArray. 2 | --> tests/ui/anyarray.rs:4:23 3 | | 4 | 4 | fn empty_array(array: AnyArray) { 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/anyiter.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | pub fn any_str_len(s: AnyIter(AnyString)) -> Result { 5 | let len = s.len(); 6 | Ok(len) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/anyiter.stderr: -------------------------------------------------------------------------------- 1 | error: expected `,` 2 | --> tests/ui/anyiter.rs:4:30 3 | | 4 | 4 | pub fn any_str_len(s: AnyIter(AnyString)) -> Result { 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/anyiter_paren.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | pub fn any_str_len(s: AnyIter(AnyString)) -> usize { 5 | s.len() 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /tests/ui/anyiter_paren.stderr: -------------------------------------------------------------------------------- 1 | error: expected `,` 2 | --> tests/ui/anyiter_paren.rs:4:30 3 | | 4 | 4 | pub fn any_str_len(s: AnyIter(AnyString)) -> usize { 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/anyiterconst.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | pub fn any_str_len(s: AnyIter<3>) -> Result { 5 | let len = s.len(); 6 | Ok(len) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/anyiterconst.stderr: -------------------------------------------------------------------------------- 1 | error: Expected generic parameter to be a type. 2 | --> tests/ui/anyiterconst.rs:4:23 3 | | 4 | 4 | pub fn any_str_len(s: AnyIter<3>) -> Result { 5 | | ^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/anyitertwo.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | pub fn any_str_len(s: AnyIter) -> Result { 5 | let len = s.len(); 6 | Ok(len) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/anyitertwo.stderr: -------------------------------------------------------------------------------- 1 | error: Expected at exactly one generic parameter. 2 | --> tests/ui/anyitertwo.rs:4:23 3 | | 4 | 4 | pub fn any_str_len(s: AnyIter) -> Result { 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/anyndarray.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | pub fn any_str_len(s: AnyIter(AnyString)) -> Result { 5 | let len = s.len(); 6 | Ok(len) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/anyndarray.stderr: -------------------------------------------------------------------------------- 1 | error: expected `,` 2 | --> tests/ui/anyndarray.rs:4:30 3 | | 4 | 4 | pub fn any_str_len(s: AnyIter(AnyString)) -> Result { 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/anypath.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | pub fn any_str_len(s: AnyPath) -> Result { 5 | let len = s.len(); 6 | Ok(len) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/anypath.stderr: -------------------------------------------------------------------------------- 1 | error: AnyPath should not have a generic parameter, so 'AnyPath', not 'AnyPath<_>'. 2 | --> tests/ui/anypath.rs:4:23 3 | | 4 | 4 | pub fn any_str_len(s: AnyPath) -> Result { 5 | | ^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/anystring.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | fn full_string(s: AnyString) { 5 | println!("{}", s); 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /tests/ui/anystring.stderr: -------------------------------------------------------------------------------- 1 | error: AnyString should not have a generic parameter, so 'AnyString', not 'AnyString<_>'. 2 | --> tests/ui/anystring.rs:4:19 3 | | 4 | 4 | fn full_string(s: AnyString) { 5 | | ^^^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/args.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput(not_empty)] 4 | pub fn any_str_len(s: AnyIter(AnyString)) -> Result { 5 | let len = s.len(); 6 | Ok(len) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/args.stderr: -------------------------------------------------------------------------------- 1 | error: anyinput does not take any arguments. 2 | --> tests/ui/args.rs:3:12 3 | | 4 | 3 | #[anyinput(not_empty)] 5 | | ^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/misapply.rs: -------------------------------------------------------------------------------- 1 | use anyinput::anyinput; 2 | 3 | #[anyinput] 4 | struct Test { 5 | a: AnyString, 6 | b: AnyString, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/ui/misapply.stderr: -------------------------------------------------------------------------------- 1 | error: expected `fn` 2 | --> tests/ui/misapply.rs:4:1 3 | | 4 | 4 | struct Test { 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /useful.md: -------------------------------------------------------------------------------- 1 | # Useful Commands 2 | 3 | ## Updating 4 | 5 | In all 3 `cargo.toml` files, update the version number of the crate you want to update. 6 | --------------------------------------------------------------------------------