├── .gitignore ├── .travis.yml ├── CHANGES.md ├── Cargo.toml ├── LICENSE ├── Project.sublime-project ├── README.md ├── grabbag_macros ├── .gitignore ├── CHANGES.md ├── Cargo.toml ├── LICENSE ├── build.rs ├── src │ └── lib.rs └── tests │ ├── collect.rs │ ├── count_exprs.rs │ └── sequence_recurrence.rs ├── scripts └── test-matrix.py ├── src ├── iter │ ├── accumulate.rs │ ├── cartesian_product.rs │ ├── clone_each.rs │ ├── fold.rs │ ├── group_by.rs │ ├── intersperse.rs │ ├── keep_some.rs │ ├── mod.rs │ ├── pad_tail_to.rs │ ├── round_robin.rs │ ├── skip_exactly.rs │ ├── sorted.rs │ ├── stride.rs │ ├── take_exactly.rs │ ├── tee.rs │ └── zip_longest.rs └── lib.rs └── update-docs.py /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /*.sublime-workspace 4 | /gh-pages 5 | /local 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | script: > 3 | cargo build --verbose --manifest-path grabbag_macros/Cargo.toml 4 | && cargo build --verbose 5 | && cargo test --verbose --manifest-path grabbag_macros/Cargo.toml 6 | && cargo test --verbose 7 | rust: 8 | - 1.2.0 9 | - 1.3.0 10 | - 1.4.0 11 | - 1.5.0 12 | - 1.6.0 13 | - 1.7.0 14 | - 1.8.0 15 | - 1.9.0 16 | - stable 17 | - beta 18 | - nightly 19 | matrix: 20 | allow_failures: 21 | - rust: nightly 22 | branches: 23 | except: 24 | - /^issue-.*$/ 25 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # 0.1.1 2 | 3 | * Updated to fix Rust breaking change. 4 | 5 | # 0.1.0 6 | 7 | * Updated to recent rust nightly. 8 | 9 | # 0.0.4 10 | 11 | * Deprecated `CloneEach`; use `.cloned` from `Iterator`. 12 | 13 | # 0.0.3 14 | 15 | * Update to recent rust nightly. 16 | 17 | # 0.0.1 18 | 19 | * Renamed all `IteratorX` extension traits to `XIterator`. Also removed the `Items` suffix from actual iterator types. 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "grabbag" 4 | version = "0.1.3" 5 | authors = ["Daniel Keep "] 6 | 7 | description = "A random grab-bag of functionality." 8 | repository = "https://github.com/DanielKeep/rust-grabbag" 9 | documentation = "https://danielkeep.github.io/rust-grabbag/doc/grabbag/index.html" 10 | license = "MIT/Apache-2.0" 11 | 12 | exclude = [ 13 | "Project.sublime-project", 14 | "update-docs.py", 15 | "scripts/*", 16 | ".travis.yml", 17 | ] 18 | 19 | [dependencies.grabbag_macros] 20 | 21 | version = "0.1.0" 22 | path = "grabbag_macros" 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright ⓒ 2015, 2016 grabbag contributors. 2 | 3 | Licensed under either of: 4 | 5 | * MIT license, or 6 | * Apache License, Version 2.0 7 | 8 | at your option. 9 | 10 | Unless you explicitly state otherwise, any contribution intentionally 11 | submitted for inclusion in the work by you shall be dual licensed as 12 | above, without any additional terms or conditions. 13 | 14 | # MIT License 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining 17 | a copy of this software and associated documentation files (the 18 | "Software"), to deal in the Software without restriction, including 19 | without limitation the rights to use, copy, modify, merge, publish, 20 | distribute, sublicense, and/or sell copies of the Software, and to 21 | permit persons to whom the Software is furnished to do so, subject 22 | to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included 25 | in all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 28 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 30 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 31 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 32 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 33 | OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | # Apache License, Version 2.0 36 | 37 | Apache License 38 | Version 2.0, January 2004 39 | http://www.apache.org/licenses/ 40 | 41 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 42 | 43 | 1. Definitions. 44 | 45 | "License" shall mean the terms and conditions for use, reproduction, 46 | and distribution as defined by Sections 1 through 9 of this document. 47 | 48 | "Licensor" shall mean the copyright owner or entity authorized by 49 | the copyright owner that is granting the License. 50 | 51 | "Legal Entity" shall mean the union of the acting entity and all 52 | other entities that control, are controlled by, or are under common 53 | control with that entity. For the purposes of this definition, 54 | "control" means (i) the power, direct or indirect, to cause the 55 | direction or management of such entity, whether by contract or 56 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 57 | outstanding shares, or (iii) beneficial ownership of such entity. 58 | 59 | "You" (or "Your") shall mean an individual or Legal Entity 60 | exercising permissions granted by this License. 61 | 62 | "Source" form shall mean the preferred form for making modifications, 63 | including but not limited to software source code, documentation 64 | source, and configuration files. 65 | 66 | "Object" form shall mean any form resulting from mechanical 67 | transformation or translation of a Source form, including but 68 | not limited to compiled object code, generated documentation, 69 | and conversions to other media types. 70 | 71 | "Work" shall mean the work of authorship, whether in Source or 72 | Object form, made available under the License, as indicated by a 73 | copyright notice that is included in or attached to the work 74 | (an example is provided in the Appendix below). 75 | 76 | "Derivative Works" shall mean any work, whether in Source or Object 77 | form, that is based on (or derived from) the Work and for which the 78 | editorial revisions, annotations, elaborations, or other modifications 79 | represent, as a whole, an original work of authorship. For the purposes 80 | of this License, Derivative Works shall not include works that remain 81 | separable from, or merely link (or bind by name) to the interfaces of, 82 | the Work and Derivative Works thereof. 83 | 84 | "Contribution" shall mean any work of authorship, including 85 | the original version of the Work and any modifications or additions 86 | to that Work or Derivative Works thereof, that is intentionally 87 | submitted to Licensor for inclusion in the Work by the copyright owner 88 | or by an individual or Legal Entity authorized to submit on behalf of 89 | the copyright owner. For the purposes of this definition, "submitted" 90 | means any form of electronic, verbal, or written communication sent 91 | to the Licensor or its representatives, including but not limited to 92 | communication on electronic mailing lists, source code control systems, 93 | and issue tracking systems that are managed by, or on behalf of, the 94 | Licensor for the purpose of discussing and improving the Work, but 95 | excluding communication that is conspicuously marked or otherwise 96 | designated in writing by the copyright owner as "Not a Contribution." 97 | 98 | "Contributor" shall mean Licensor and any individual or Legal Entity 99 | on behalf of whom a Contribution has been received by Licensor and 100 | subsequently incorporated within the Work. 101 | 102 | 2. Grant of Copyright License. Subject to the terms and conditions of 103 | this License, each Contributor hereby grants to You a perpetual, 104 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 105 | copyright license to reproduce, prepare Derivative Works of, 106 | publicly display, publicly perform, sublicense, and distribute the 107 | Work and such Derivative Works in Source or Object form. 108 | 109 | 3. Grant of Patent License. Subject to the terms and conditions of 110 | this License, each Contributor hereby grants to You a perpetual, 111 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 112 | (except as stated in this section) patent license to make, have made, 113 | use, offer to sell, sell, import, and otherwise transfer the Work, 114 | where such license applies only to those patent claims licensable 115 | by such Contributor that are necessarily infringed by their 116 | Contribution(s) alone or by combination of their Contribution(s) 117 | with the Work to which such Contribution(s) was submitted. If You 118 | institute patent litigation against any entity (including a 119 | cross-claim or counterclaim in a lawsuit) alleging that the Work 120 | or a Contribution incorporated within the Work constitutes direct 121 | or contributory patent infringement, then any patent licenses 122 | granted to You under this License for that Work shall terminate 123 | as of the date such litigation is filed. 124 | 125 | 4. Redistribution. You may reproduce and distribute copies of the 126 | Work or Derivative Works thereof in any medium, with or without 127 | modifications, and in Source or Object form, provided that You 128 | meet the following conditions: 129 | 130 | (a) You must give any other recipients of the Work or 131 | Derivative Works a copy of this License; and 132 | 133 | (b) You must cause any modified files to carry prominent notices 134 | stating that You changed the files; and 135 | 136 | (c) You must retain, in the Source form of any Derivative Works 137 | that You distribute, all copyright, patent, trademark, and 138 | attribution notices from the Source form of the Work, 139 | excluding those notices that do not pertain to any part of 140 | the Derivative Works; and 141 | 142 | (d) If the Work includes a "NOTICE" text file as part of its 143 | distribution, then any Derivative Works that You distribute must 144 | include a readable copy of the attribution notices contained 145 | within such NOTICE file, excluding those notices that do not 146 | pertain to any part of the Derivative Works, in at least one 147 | of the following places: within a NOTICE text file distributed 148 | as part of the Derivative Works; within the Source form or 149 | documentation, if provided along with the Derivative Works; or, 150 | within a display generated by the Derivative Works, if and 151 | wherever such third-party notices normally appear. The contents 152 | of the NOTICE file are for informational purposes only and 153 | do not modify the License. You may add Your own attribution 154 | notices within Derivative Works that You distribute, alongside 155 | or as an addendum to the NOTICE text from the Work, provided 156 | that such additional attribution notices cannot be construed 157 | as modifying the License. 158 | 159 | You may add Your own copyright statement to Your modifications and 160 | may provide additional or different license terms and conditions 161 | for use, reproduction, or distribution of Your modifications, or 162 | for any such Derivative Works as a whole, provided Your use, 163 | reproduction, and distribution of the Work otherwise complies with 164 | the conditions stated in this License. 165 | 166 | 5. Submission of Contributions. Unless You explicitly state otherwise, 167 | any Contribution intentionally submitted for inclusion in the Work 168 | by You to the Licensor shall be under the terms and conditions of 169 | this License, without any additional terms or conditions. 170 | Notwithstanding the above, nothing herein shall supersede or modify 171 | the terms of any separate license agreement you may have executed 172 | with Licensor regarding such Contributions. 173 | 174 | 6. Trademarks. This License does not grant permission to use the trade 175 | names, trademarks, service marks, or product names of the Licensor, 176 | except as required for reasonable and customary use in describing the 177 | origin of the Work and reproducing the content of the NOTICE file. 178 | 179 | 7. Disclaimer of Warranty. Unless required by applicable law or 180 | agreed to in writing, Licensor provides the Work (and each 181 | Contributor provides its Contributions) on an "AS IS" BASIS, 182 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 183 | implied, including, without limitation, any warranties or conditions 184 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 185 | PARTICULAR PURPOSE. You are solely responsible for determining the 186 | appropriateness of using or redistributing the Work and assume any 187 | risks associated with Your exercise of permissions under this License. 188 | 189 | 8. Limitation of Liability. In no event and under no legal theory, 190 | whether in tort (including negligence), contract, or otherwise, 191 | unless required by applicable law (such as deliberate and grossly 192 | negligent acts) or agreed to in writing, shall any Contributor be 193 | liable to You for damages, including any direct, indirect, special, 194 | incidental, or consequential damages of any character arising as a 195 | result of this License or out of the use or inability to use the 196 | Work (including but not limited to damages for loss of goodwill, 197 | work stoppage, computer failure or malfunction, or any and all 198 | other commercial damages or losses), even if such Contributor 199 | has been advised of the possibility of such damages. 200 | 201 | 9. Accepting Warranty or Additional Liability. While redistributing 202 | the Work or Derivative Works thereof, You may choose to offer, 203 | and charge a fee for, acceptance of support, warranty, indemnity, 204 | or other liability obligations and/or rights consistent with this 205 | License. However, in accepting such obligations, You may act only 206 | on Your own behalf and on Your sole responsibility, not on behalf 207 | of any other Contributor, and only if You agree to indemnify, 208 | defend, and hold each Contributor harmless for any liability 209 | incurred by, or claims asserted against, such Contributor by reason 210 | of your accepting any such warranty or additional liability. 211 | 212 | END OF TERMS AND CONDITIONS 213 | 214 | APPENDIX: How to apply the Apache License to your work. 215 | 216 | To apply the Apache License to your work, attach the following 217 | boilerplate notice, with the fields enclosed by brackets "[]" 218 | replaced with your own identifying information. (Don't include 219 | the brackets!) The text should be enclosed in the appropriate 220 | comment syntax for the file format. We also recommend that a 221 | file or class name and description of purpose be included on the 222 | same "printed page" as the copyright notice for easier 223 | identification within third-party archives. 224 | 225 | Copyright [yyyy] [name of copyright owner] 226 | 227 | Licensed under the Apache License, Version 2.0 (the "License"); 228 | you may not use this file except in compliance with the License. 229 | You may obtain a copy of the License at 230 | 231 | http://www.apache.org/licenses/LICENSE-2.0 232 | 233 | Unless required by applicable law or agreed to in writing, software 234 | distributed under the License is distributed on an "AS IS" BASIS, 235 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 236 | See the License for the specific language governing permissions and 237 | limitations under the License. 238 | -------------------------------------------------------------------------------- /Project.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "SublimeLinter": 3 | { 4 | "linters": 5 | { 6 | "rust": 7 | { 8 | "use-cargo": true 9 | } 10 | } 11 | }, 12 | "folders": 13 | [ 14 | { 15 | "follow_symlinks": true, 16 | "path": "." 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A random assortment of *things* that don't really demand their own package. 2 | 3 | ## License 4 | 5 | Licensed under either of 6 | 7 | * MIT license (see [LICENSE](LICENSE) or ) 8 | * Apache License, Version 2.0 (see [LICENSE](LICENSE) or ) 9 | 10 | at your option. 11 | 12 | ### Contribution 13 | 14 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions. 15 | -------------------------------------------------------------------------------- /grabbag_macros/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /grabbag_macros/CHANGES.md: -------------------------------------------------------------------------------- 1 | # 0.1.2 2 | 3 | * **Breaking change**: Removed `sequence!` and `recurrence!` for Rust 1.9.0+. Due to language changes, these macros **cannot** be fixed in a backward-compatible fashion. Existing working code should be unaffected. 4 | 5 | * Fixed `recurrence!` macro for Rust < 1.9.0. 6 | 7 | # 0.1.1 8 | 9 | * Updated to 1.7.0. 10 | 11 | # 0.1.0 12 | 13 | * Fixed double-eval in `collect!` macro. 14 | 15 | # 0.0.3 16 | 17 | * Update to recent rust nightly. 18 | 19 | # 0.0.2 20 | 21 | * Changed `collect!` to *not* allocate temporary storage. 22 | 23 | # 0.0.1 24 | 25 | Initial package release. 26 | 27 | * `collect!` 28 | * `count_exprs!` 29 | * `sequence!` 30 | * `recurrence!` 31 | -------------------------------------------------------------------------------- /grabbag_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "grabbag_macros" 4 | version = "0.1.3" 5 | authors = ["Daniel Keep "] 6 | 7 | description = "A random grab-bag of functionality. (macro package)" 8 | repository = "https://github.com/DanielKeep/rust-grabbag" 9 | documentation = "https://danielkeep.github.io/rust-grabbag/doc/grabbag_macros/index.html" 10 | license = "MIT/Apache-2.0" 11 | 12 | build = "build.rs" 13 | 14 | [lib] 15 | 16 | name = "grabbag_macros" 17 | plugin = true 18 | 19 | [build-dependencies] 20 | rustc_version = "0.1.4" 21 | -------------------------------------------------------------------------------- /grabbag_macros/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright ⓒ 2015, 2016 grabbag contributors. 2 | 3 | Licensed under either of: 4 | 5 | * MIT license, or 6 | * Apache License, Version 2.0 7 | 8 | at your option. 9 | 10 | Unless you explicitly state otherwise, any contribution intentionally 11 | submitted for inclusion in the work by you shall be dual licensed as 12 | above, without any additional terms or conditions. 13 | 14 | # MIT License 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining 17 | a copy of this software and associated documentation files (the 18 | "Software"), to deal in the Software without restriction, including 19 | without limitation the rights to use, copy, modify, merge, publish, 20 | distribute, sublicense, and/or sell copies of the Software, and to 21 | permit persons to whom the Software is furnished to do so, subject 22 | to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included 25 | in all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 28 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 30 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 31 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 32 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 33 | OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | # Apache License, Version 2.0 36 | 37 | Apache License 38 | Version 2.0, January 2004 39 | http://www.apache.org/licenses/ 40 | 41 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 42 | 43 | 1. Definitions. 44 | 45 | "License" shall mean the terms and conditions for use, reproduction, 46 | and distribution as defined by Sections 1 through 9 of this document. 47 | 48 | "Licensor" shall mean the copyright owner or entity authorized by 49 | the copyright owner that is granting the License. 50 | 51 | "Legal Entity" shall mean the union of the acting entity and all 52 | other entities that control, are controlled by, or are under common 53 | control with that entity. For the purposes of this definition, 54 | "control" means (i) the power, direct or indirect, to cause the 55 | direction or management of such entity, whether by contract or 56 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 57 | outstanding shares, or (iii) beneficial ownership of such entity. 58 | 59 | "You" (or "Your") shall mean an individual or Legal Entity 60 | exercising permissions granted by this License. 61 | 62 | "Source" form shall mean the preferred form for making modifications, 63 | including but not limited to software source code, documentation 64 | source, and configuration files. 65 | 66 | "Object" form shall mean any form resulting from mechanical 67 | transformation or translation of a Source form, including but 68 | not limited to compiled object code, generated documentation, 69 | and conversions to other media types. 70 | 71 | "Work" shall mean the work of authorship, whether in Source or 72 | Object form, made available under the License, as indicated by a 73 | copyright notice that is included in or attached to the work 74 | (an example is provided in the Appendix below). 75 | 76 | "Derivative Works" shall mean any work, whether in Source or Object 77 | form, that is based on (or derived from) the Work and for which the 78 | editorial revisions, annotations, elaborations, or other modifications 79 | represent, as a whole, an original work of authorship. For the purposes 80 | of this License, Derivative Works shall not include works that remain 81 | separable from, or merely link (or bind by name) to the interfaces of, 82 | the Work and Derivative Works thereof. 83 | 84 | "Contribution" shall mean any work of authorship, including 85 | the original version of the Work and any modifications or additions 86 | to that Work or Derivative Works thereof, that is intentionally 87 | submitted to Licensor for inclusion in the Work by the copyright owner 88 | or by an individual or Legal Entity authorized to submit on behalf of 89 | the copyright owner. For the purposes of this definition, "submitted" 90 | means any form of electronic, verbal, or written communication sent 91 | to the Licensor or its representatives, including but not limited to 92 | communication on electronic mailing lists, source code control systems, 93 | and issue tracking systems that are managed by, or on behalf of, the 94 | Licensor for the purpose of discussing and improving the Work, but 95 | excluding communication that is conspicuously marked or otherwise 96 | designated in writing by the copyright owner as "Not a Contribution." 97 | 98 | "Contributor" shall mean Licensor and any individual or Legal Entity 99 | on behalf of whom a Contribution has been received by Licensor and 100 | subsequently incorporated within the Work. 101 | 102 | 2. Grant of Copyright License. Subject to the terms and conditions of 103 | this License, each Contributor hereby grants to You a perpetual, 104 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 105 | copyright license to reproduce, prepare Derivative Works of, 106 | publicly display, publicly perform, sublicense, and distribute the 107 | Work and such Derivative Works in Source or Object form. 108 | 109 | 3. Grant of Patent License. Subject to the terms and conditions of 110 | this License, each Contributor hereby grants to You a perpetual, 111 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 112 | (except as stated in this section) patent license to make, have made, 113 | use, offer to sell, sell, import, and otherwise transfer the Work, 114 | where such license applies only to those patent claims licensable 115 | by such Contributor that are necessarily infringed by their 116 | Contribution(s) alone or by combination of their Contribution(s) 117 | with the Work to which such Contribution(s) was submitted. If You 118 | institute patent litigation against any entity (including a 119 | cross-claim or counterclaim in a lawsuit) alleging that the Work 120 | or a Contribution incorporated within the Work constitutes direct 121 | or contributory patent infringement, then any patent licenses 122 | granted to You under this License for that Work shall terminate 123 | as of the date such litigation is filed. 124 | 125 | 4. Redistribution. You may reproduce and distribute copies of the 126 | Work or Derivative Works thereof in any medium, with or without 127 | modifications, and in Source or Object form, provided that You 128 | meet the following conditions: 129 | 130 | (a) You must give any other recipients of the Work or 131 | Derivative Works a copy of this License; and 132 | 133 | (b) You must cause any modified files to carry prominent notices 134 | stating that You changed the files; and 135 | 136 | (c) You must retain, in the Source form of any Derivative Works 137 | that You distribute, all copyright, patent, trademark, and 138 | attribution notices from the Source form of the Work, 139 | excluding those notices that do not pertain to any part of 140 | the Derivative Works; and 141 | 142 | (d) If the Work includes a "NOTICE" text file as part of its 143 | distribution, then any Derivative Works that You distribute must 144 | include a readable copy of the attribution notices contained 145 | within such NOTICE file, excluding those notices that do not 146 | pertain to any part of the Derivative Works, in at least one 147 | of the following places: within a NOTICE text file distributed 148 | as part of the Derivative Works; within the Source form or 149 | documentation, if provided along with the Derivative Works; or, 150 | within a display generated by the Derivative Works, if and 151 | wherever such third-party notices normally appear. The contents 152 | of the NOTICE file are for informational purposes only and 153 | do not modify the License. You may add Your own attribution 154 | notices within Derivative Works that You distribute, alongside 155 | or as an addendum to the NOTICE text from the Work, provided 156 | that such additional attribution notices cannot be construed 157 | as modifying the License. 158 | 159 | You may add Your own copyright statement to Your modifications and 160 | may provide additional or different license terms and conditions 161 | for use, reproduction, or distribution of Your modifications, or 162 | for any such Derivative Works as a whole, provided Your use, 163 | reproduction, and distribution of the Work otherwise complies with 164 | the conditions stated in this License. 165 | 166 | 5. Submission of Contributions. Unless You explicitly state otherwise, 167 | any Contribution intentionally submitted for inclusion in the Work 168 | by You to the Licensor shall be under the terms and conditions of 169 | this License, without any additional terms or conditions. 170 | Notwithstanding the above, nothing herein shall supersede or modify 171 | the terms of any separate license agreement you may have executed 172 | with Licensor regarding such Contributions. 173 | 174 | 6. Trademarks. This License does not grant permission to use the trade 175 | names, trademarks, service marks, or product names of the Licensor, 176 | except as required for reasonable and customary use in describing the 177 | origin of the Work and reproducing the content of the NOTICE file. 178 | 179 | 7. Disclaimer of Warranty. Unless required by applicable law or 180 | agreed to in writing, Licensor provides the Work (and each 181 | Contributor provides its Contributions) on an "AS IS" BASIS, 182 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 183 | implied, including, without limitation, any warranties or conditions 184 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 185 | PARTICULAR PURPOSE. You are solely responsible for determining the 186 | appropriateness of using or redistributing the Work and assume any 187 | risks associated with Your exercise of permissions under this License. 188 | 189 | 8. Limitation of Liability. In no event and under no legal theory, 190 | whether in tort (including negligence), contract, or otherwise, 191 | unless required by applicable law (such as deliberate and grossly 192 | negligent acts) or agreed to in writing, shall any Contributor be 193 | liable to You for damages, including any direct, indirect, special, 194 | incidental, or consequential damages of any character arising as a 195 | result of this License or out of the use or inability to use the 196 | Work (including but not limited to damages for loss of goodwill, 197 | work stoppage, computer failure or malfunction, or any and all 198 | other commercial damages or losses), even if such Contributor 199 | has been advised of the possibility of such damages. 200 | 201 | 9. Accepting Warranty or Additional Liability. While redistributing 202 | the Work or Derivative Works thereof, You may choose to offer, 203 | and charge a fee for, acceptance of support, warranty, indemnity, 204 | or other liability obligations and/or rights consistent with this 205 | License. However, in accepting such obligations, You may act only 206 | on Your own behalf and on Your sole responsibility, not on behalf 207 | of any other Contributor, and only if You agree to indemnify, 208 | defend, and hold each Contributor harmless for any liability 209 | incurred by, or claims asserted against, such Contributor by reason 210 | of your accepting any such warranty or additional liability. 211 | 212 | END OF TERMS AND CONDITIONS 213 | 214 | APPENDIX: How to apply the Apache License to your work. 215 | 216 | To apply the Apache License to your work, attach the following 217 | boilerplate notice, with the fields enclosed by brackets "[]" 218 | replaced with your own identifying information. (Don't include 219 | the brackets!) The text should be enclosed in the appropriate 220 | comment syntax for the file format. We also recommend that a 221 | file or class name and description of purpose be included on the 222 | same "printed page" as the copyright notice for easier 223 | identification within third-party archives. 224 | 225 | Copyright [yyyy] [name of copyright owner] 226 | 227 | Licensed under the Apache License, Version 2.0 (the "License"); 228 | you may not use this file except in compliance with the License. 229 | You may obtain a copy of the License at 230 | 231 | http://www.apache.org/licenses/LICENSE-2.0 232 | 233 | Unless required by applicable law or agreed to in writing, software 234 | distributed under the License is distributed on an "AS IS" BASIS, 235 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 236 | See the License for the specific language governing permissions and 237 | limitations under the License. 238 | -------------------------------------------------------------------------------- /grabbag_macros/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2016 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | extern crate rustc_version; 11 | use rustc_version::{version_matches}; 12 | 13 | fn main() { 14 | println!("cargo:rerun-if-changed=build.rs"); 15 | 16 | if version_matches("1.9.0") { 17 | println!("cargo:rustc-cfg=cannot_use_dotdotdot"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /grabbag_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2016 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /*! 11 | This is a random grab-bag of macros that didn't really seem to deserve a separate home. 12 | 13 | If you see something you think you have a better place for, let me know! 14 | */ 15 | 16 | /** 17 | Counts the number of comma-delimited expressions passed to it. The result is a compile-time evaluable expression, suitable for use as a static array size, or the value of a `const`. 18 | 19 | Example: 20 | 21 | ``` 22 | # #[macro_use] extern crate grabbag_macros; 23 | # fn main() { 24 | const COUNT: usize = count_exprs!(a, 5+1, "hi there!".into_string()); 25 | assert_eq!(COUNT, 3); 26 | # } 27 | ``` 28 | */ 29 | #[macro_export] 30 | macro_rules! count_exprs { 31 | () => { 0 }; 32 | ($e:expr) => { 1 }; 33 | ($e:expr, $($es:expr),+) => { 1 + count_exprs!($($es),*) }; 34 | } 35 | 36 | /** 37 | This macro provides a way to initialise any container for which there is a FromIterator implementation. It allows for both sequence and map syntax to be used, as well as inline type ascription for the result. 38 | 39 | For example: 40 | 41 | ``` 42 | # #[macro_use] extern crate grabbag_macros; 43 | # use std::collections::{HashMap, VecMap}; 44 | # fn main() { 45 | // Initialise an empty collection. 46 | let a: Vec = collect![]; 47 | let b: HashMap = collect![]; 48 | 49 | // Initialise a sequence. 50 | let c: String = collect!['a', 'b', 'c']; 51 | 52 | // Initialise a sequence with a type constraint. 53 | let d = collect![into Vec<_>: 0, 1, 2]; 54 | 55 | // Initialise a map collection. 56 | let e: VecMap<&str> = collect![1 => "one", 2 => "two", 3 => "many", 4 => "lots"]; 57 | 58 | // Initialise a map with a type constraint. 59 | let f: HashMap<_, u8> = collect![into HashMap: 42 => 0, -11 => 2]; 60 | # } 61 | ``` 62 | */ 63 | #[macro_export] 64 | macro_rules! collect { 65 | // Short-hands for initialising an empty collection. 66 | [] => { collect![into _] }; 67 | [into $col_ty:ty] => { collect![into $col_ty:] }; 68 | [into $col_ty:ty:] => { 69 | { 70 | let col: $col_ty = ::std::default::Default::default(); 71 | col 72 | } 73 | }; 74 | 75 | // Initialise a sequence with a constrained container type. 76 | [into $col_ty:ty: $v0:expr] => { collect![into $col_ty: $v0,] }; 77 | 78 | [into $col_ty:ty: $v0:expr, $($vs:expr),* $(,)*] => { 79 | { 80 | use std::marker::PhantomData; 81 | 82 | const NUM_ELEMS: usize = count_exprs!($v0 $(, $vs)*); 83 | 84 | // This trick is stolen from std::iter, and *should* serve to give the container enough information to pre-allocate sufficient storage for all the elements. 85 | struct SizeHint(PhantomData); 86 | 87 | impl SizeHint { 88 | // This method is needed to help the compiler work out which `Extend` impl to use in cases where there is more than one (e.g. `String`). 89 | #[inline(always)] 90 | fn type_hint(_: &E) -> SizeHint { SizeHint(PhantomData) } 91 | } 92 | 93 | impl Iterator for SizeHint { 94 | type Item = E; 95 | 96 | #[inline(always)] 97 | fn next(&mut self) -> Option { 98 | None 99 | } 100 | 101 | #[inline(always)] 102 | fn size_hint(&self) -> (usize, Option) { 103 | (NUM_ELEMS, Some(NUM_ELEMS)) 104 | } 105 | } 106 | 107 | let mut col: $col_ty = ::std::default::Default::default(); 108 | let v0 = $v0; 109 | 110 | Extend::extend(&mut col, SizeHint::type_hint(&v0)); 111 | 112 | Extend::extend(&mut col, Some(v0).into_iter()); 113 | $(Extend::extend(&mut col, Some($vs).into_iter());)* 114 | 115 | col 116 | } 117 | }; 118 | 119 | // Initialise a sequence with a fully inferred contained type. 120 | [$($vs:expr),+] => { collect![into _: $($vs),+] }; 121 | 122 | // Initialise a map with a constrained container type. 123 | [into $col_ty:ty: $($ks:expr => $vs:expr),+] => { 124 | // Maps implement FromIterator by taking tuples, so we just need to rewrite each `a:b` as `(a,b)`. 125 | collect![into $col_ty: $(($ks, $vs)),+] 126 | }; 127 | 128 | // Initialise a map with a fully inferred contained type. 129 | [$($ks:expr => $vs:expr),+] => { collect![into _: $($ks => $vs),+] }; 130 | } 131 | 132 | /** 133 | Expands to an expression implementing the `Iterator` trait, which yields successive 134 | elements of the given closed-form sequence. 135 | 136 | For example, you can define the sequence of positive odd integers like so: 137 | 138 | ``` 139 | # #[macro_use] extern crate grabbag_macros; 140 | # fn main() { 141 | # let _ = 142 | sequence![ n: u64 = 2*(n as u64) + 1 ] 143 | # ; 144 | # } 145 | ``` 146 | 147 | You can also specify one or more initial members of the sequence that are also used in the closed form expression like so: 148 | 149 | ``` 150 | # #[macro_use] extern crate grabbag_macros; 151 | # fn main() { 152 | # let _ = 153 | sequence![ a[n]: u64 = 1, 2... a[0]*(n as u64) + a[1] ] 154 | # ; 155 | # } 156 | ``` 157 | */ 158 | #[macro_export] 159 | #[cfg(not(cannot_use_dotdotdot))] 160 | macro_rules! sequence { 161 | ( $ind:ident: $sty:ty = $closed_form:expr ) => { 162 | { 163 | struct Sequence { 164 | pos: usize, 165 | } 166 | 167 | impl Iterator for Sequence { 168 | type Item = $sty; 169 | 170 | #[inline] 171 | fn next(&mut self) -> Option<$sty> { 172 | if self.pos == ::std::usize::MAX { 173 | return None 174 | } 175 | 176 | let next_val: $sty = { 177 | let $ind = self.pos; 178 | $closed_form 179 | }; 180 | 181 | self.pos += 1; 182 | Some(next_val) 183 | } 184 | } 185 | 186 | Sequence { pos: 0 } 187 | } 188 | }; 189 | ( $seq:ident [ $ind:ident ]: $sty:ty = $($inits:expr),+ ... $closed_form:expr ) => { 190 | { 191 | const INITS: usize = count_exprs!($($inits),+); 192 | 193 | struct Sequence { 194 | inits: [$sty; INITS], 195 | pos: usize, 196 | } 197 | 198 | impl Iterator for Sequence { 199 | type Item = $sty; 200 | 201 | #[inline] 202 | fn next(&mut self) -> Option<$sty> { 203 | if self.pos == ::std::usize::MAX { 204 | return None 205 | } 206 | 207 | if self.pos < INITS { 208 | let next_val = self.inits[self.pos]; 209 | self.pos += 1; 210 | Some(next_val) 211 | } else { 212 | let next_val: $sty = { 213 | let $ind = self.pos; 214 | let $seq = &self.inits; 215 | $closed_form 216 | }; 217 | 218 | self.pos += 1; 219 | Some(next_val) 220 | } 221 | } 222 | } 223 | 224 | Sequence { inits: [$($inits),+], pos: 0 } 225 | } 226 | }; 227 | } 228 | 229 | /** 230 | Expands to an expression implementing the `Iterator` trait, which yields successive 231 | elements of the given recurrence relationship. 232 | 233 | For example, you can define a Fibonnaci sequence iterator like so: 234 | 235 | ``` 236 | # #[macro_use] extern crate grabbag_macros; 237 | # fn main() { 238 | # let _ = 239 | recurrence![ fib[n]: f64 = 0.0, 1.0 ... fib[n-1] + fib[n-2] ] 240 | # ; 241 | # } 242 | ``` 243 | */ 244 | #[macro_export] 245 | #[cfg(not(cannot_use_dotdotdot))] 246 | macro_rules! recurrence { 247 | ( $seq:ident [ $ind:ident ]: $sty:ty = $($inits:expr),+ ... $recur:expr ) => { 248 | { 249 | use std::ops::Index; 250 | 251 | const MEMORY: usize = count_exprs!($($inits),+); 252 | 253 | struct Recurrence { 254 | mem: [$sty; MEMORY], 255 | pos: usize, 256 | } 257 | 258 | struct IndexOffset<'a> { 259 | slice: &'a [$sty; MEMORY], 260 | offset: usize, 261 | } 262 | 263 | impl<'a> Index for IndexOffset<'a> { 264 | type Output = $sty; 265 | 266 | #[inline(always)] 267 | fn index<'b>(&'b self, index: usize) -> &'b $sty { 268 | let real_index = index + MEMORY - self.offset; 269 | &self.slice[real_index] 270 | } 271 | } 272 | 273 | impl Iterator for Recurrence { 274 | type Item = $sty; 275 | 276 | #[inline] 277 | fn next(&mut self) -> Option<$sty> { 278 | if self.pos == ::std::usize::MAX { 279 | return None 280 | } 281 | 282 | if self.pos < MEMORY { 283 | let next_val = self.mem[self.pos]; 284 | self.pos += 1; 285 | Some(next_val) 286 | } else { 287 | let next_val: $sty = { 288 | let $ind = self.pos; 289 | let $seq = IndexOffset { slice: &self.mem, offset: $ind }; 290 | $recur 291 | }; 292 | 293 | { 294 | use std::mem::swap; 295 | 296 | let mut swap_tmp = next_val; 297 | for i in (0..MEMORY).rev() { 298 | swap(&mut swap_tmp, &mut self.mem[i]); 299 | } 300 | } 301 | 302 | self.pos += 1; 303 | Some(next_val) 304 | } 305 | } 306 | } 307 | 308 | Recurrence { mem: [$($inits),+], pos: 0 } 309 | } 310 | }; 311 | } 312 | -------------------------------------------------------------------------------- /grabbag_macros/tests/collect.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | #[macro_use] extern crate grabbag_macros; 11 | 12 | use std::collections::{HashMap, BTreeSet}; 13 | 14 | macro_rules! assert_eq_iter { 15 | (== $it:expr,) => { 16 | { 17 | assert_eq!($it.next(), None); 18 | } 19 | }; 20 | (== $it:expr, $e:expr) => { 21 | { 22 | assert_eq!($it.next(), Some($e)); 23 | } 24 | }; 25 | (== $it:expr, $e:expr, $($es:expr),*) => { 26 | { 27 | assert_eq!($it.next(), Some($e)); 28 | assert_eq_iter!($it, $($es),*); 29 | } 30 | }; 31 | ($it:expr, $($es:expr),*) => { 32 | { 33 | let mut it = $it; 34 | assert_eq_iter!(== it, $($es),*); 35 | } 36 | }; 37 | } 38 | 39 | macro_rules! assert_eq_iter_sort { 40 | ($it:expr, $($es:expr),*) => { 41 | { 42 | let mut it = $it.collect::>(); 43 | it.sort(); 44 | let mut it = it.into_iter(); 45 | assert_eq_iter!(== it, $($es),*); 46 | } 47 | }; 48 | } 49 | 50 | #[test] 51 | fn test_collect_empty_full_inference() { 52 | let c: Vec = collect![]; 53 | assert_eq!(c.len(), 0); 54 | 55 | let c: String = collect![]; 56 | assert_eq!(c.len(), 0); 57 | 58 | let c: HashMap> = collect![]; 59 | assert_eq!(c.len(), 0); 60 | 61 | let c: BTreeSet = collect![]; 62 | assert_eq!(c.len(), 0); 63 | } 64 | 65 | #[test] 66 | fn test_collect_empty_constrained() { 67 | let c = collect![into Vec]; 68 | assert_eq!(c.len(), 0); 69 | 70 | let c = collect![into String]; 71 | assert_eq!(c.len(), 0); 72 | 73 | let c = collect![into HashMap>]; 74 | assert_eq!(c.len(), 0); 75 | 76 | let c = collect![into BTreeSet]; 77 | assert_eq!(c.len(), 0); 78 | } 79 | 80 | #[test] 81 | fn test_collect_sequence_full_inference() { 82 | let c: Vec = collect![1, 2, 3]; 83 | assert_eq_iter!(c.into_iter(), 1, 2, 3); 84 | 85 | let c: String = collect!['a', 'b', 'c', '刀']; 86 | assert_eq_iter!(c.chars(), 'a', 'b', 'c', '刀'); 87 | 88 | let c: BTreeSet = collect![2, 1, 3]; 89 | assert_eq_iter!(c.iter().map(deref), 1, 2, 3); 90 | } 91 | 92 | #[test] 93 | fn test_collect_sequence_constrained() { 94 | let c = collect![into Vec<_>: 1, 2, 3]; 95 | assert_eq_iter!(c.into_iter(), 1, 2, 3); 96 | 97 | let c = collect![into String: 'a', 'b', 'c', '刀']; 98 | assert_eq_iter!(c.chars(), 'a', 'b', 'c', '刀'); 99 | 100 | let c = collect![into BTreeSet<_>: 2, 1, 3]; 101 | assert_eq_iter!(c.iter().map(deref), 1, 2, 3); 102 | } 103 | 104 | #[test] 105 | fn test_collect_map_full_inference() { 106 | let c: HashMap<&str, i32> = collect!["a" => 0, "b" => 2, "c" => 42]; 107 | assert_eq_iter_sort!(c.into_iter(), ("a", 0), ("b", 2), ("c", 42)); 108 | } 109 | 110 | #[test] 111 | fn test_collect_map_constrained() { 112 | let c = collect![into HashMap<&str, i32>: "a" => 0, "b" => 2, "c" => 42]; 113 | assert_eq_iter_sort!(c.into_iter(), ("a", 0), ("b", 2), ("c", 42)); 114 | 115 | let c = collect![into HashMap<_, _>: "a" => 0, "b" => 2, "c" => 42]; 116 | assert_eq_iter_sort!(c.into_iter(), ("a", 0), ("b", 2), ("c", 42)); 117 | } 118 | 119 | #[test] 120 | fn test_collect_eval_once() { 121 | let mut n = 0; 122 | 123 | macro_rules! npp { 124 | () => ({n += 1; n}); 125 | } 126 | 127 | let _: Vec<_> = collect![npp!()]; 128 | assert_eq!(n, 1); 129 | 130 | n = 0; 131 | let _: Vec<_> = collect![npp!(), npp!()]; 132 | assert_eq!(n, 2); 133 | 134 | n = 0; 135 | let _: Vec<_> = collect![npp!(), npp!(), npp!()]; 136 | assert_eq!(n, 3); 137 | 138 | n = 0; 139 | let _: HashMap<_, _> = collect![npp!() => npp!()]; 140 | assert_eq!(n, 2); 141 | 142 | n = 0; 143 | let _: HashMap<_, _> = collect![npp!() => npp!(), npp!() => npp!()]; 144 | assert_eq!(n, 4); 145 | 146 | n = 0; 147 | let _: HashMap<_, _> = collect![npp!() => npp!(), npp!() => npp!(), npp!() => npp!()]; 148 | assert_eq!(n, 6); 149 | } 150 | 151 | fn deref(r: &T) -> T where T: Copy { 152 | *r 153 | } 154 | -------------------------------------------------------------------------------- /grabbag_macros/tests/count_exprs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | #[macro_use] extern crate grabbag_macros; 11 | 12 | #[test] 13 | fn test_count_exprs() { 14 | assert_eq!(count_exprs!(), 0); 15 | assert_eq!(count_exprs!(0), 1); 16 | assert_eq!(count_exprs!(x), 1); 17 | assert_eq!(count_exprs!(a*x.pow(2) + b*x + c == 0), 1); 18 | assert_eq!(count_exprs!(0, 1, 2), 3); 19 | } 20 | -------------------------------------------------------------------------------- /grabbag_macros/tests/sequence_recurrence.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2016 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | #![cfg(not(cannot_use_dotdotdot))] 11 | #[macro_use] extern crate grabbag_macros; 12 | 13 | macro_rules! iter_assert_eq { 14 | ($it:expr, [$($exs:expr),* $(,)*] ...) => { 15 | { 16 | let mut it = ::std::iter::IntoIterator::into_iter($it); 17 | let mut _i = 0; 18 | $( 19 | match (it.next(), $exs) { 20 | (Some(e), ex) => { 21 | if !(e == ex) { 22 | panic!("assertion failed: `(left == right)` \ 23 | (left: `{:?}`, \ 24 | right: `{:?}`, \ 25 | element: {})", e, ex, _i); 26 | } 27 | }, 28 | (None, ex) => { 29 | panic!("assertion failed: `(left == right)` \ 30 | (left: None, \ 31 | right: `{:?}`, \ 32 | element: {})", ex, _i); 33 | } 34 | } 35 | _i += 1; 36 | )* 37 | } 38 | }; 39 | } 40 | 41 | #[test] 42 | fn test_sequence() { 43 | iter_assert_eq!(sequence![ n: u64 = 2*(n as u64) + 1 ], [1, 3, 5, 7]...); 44 | iter_assert_eq!(sequence![ a[n]: u64 = 1, 2... a[0]*(n as u64) + a[1] ], [1, 2, 4, 5, 6]...); 45 | } 46 | 47 | #[test] 48 | fn test_recurrence() { 49 | iter_assert_eq!(recurrence![ fib[n]: f64 = 0.0, 1.0 ... fib[n-1] + fib[n-2] ], 50 | [0.0, 1.0, 1.0, 2.0, 3.0, 5.0, 8.0]...); 51 | } 52 | -------------------------------------------------------------------------------- /scripts/test-matrix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | # Copyright ⓒ 2016 Daniel Keep. 5 | # 6 | # Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 8 | # ), at your option. All 9 | # files in the project carrying such notice may not be copied, modified, 10 | # or distributed except according to those terms. 11 | 12 | import os.path 13 | import re 14 | import subprocess 15 | import sys 16 | import yaml 17 | from itertools import chain 18 | 19 | LOG_DIR = os.path.join('local', 'tests') 20 | 21 | TRACE = os.environ.get('TRACE_TEST_MATRIX', '') != '' 22 | USE_ANSI = True if sys.platform != 'win32' else os.environ.get('FORCE_ANSI', '') != '' or os.environ.get('ConEmuANSI', 'OFF') == 'ON' 23 | 24 | def main(): 25 | travis = yaml.load(open('.travis.yml')) 26 | script = translate_script(travis['script']) 27 | default_rust_vers = travis['rust'] 28 | # env = {e[0].strip(): e[1].strip() for e in ( 29 | # e.split('=', maxsplit=1) for e in travis['env'])} 30 | 31 | matrix_includes = travis.get('matrix', {}).get('include', []) 32 | 33 | vers = set(default_rust_vers) 34 | include_vers = [] 35 | exclude_vers = set() 36 | 37 | if not os.path.exists(LOG_DIR): 38 | os.makedirs(LOG_DIR) 39 | 40 | for arg in sys.argv[1:]: 41 | if arg in vers and arg not in include_vers: 42 | include_vers.append(arg) 43 | elif arg.startswith('-') and arg[1:] in vers: 44 | exclude_vers.add(arg[1:]) 45 | else: 46 | msg("Don't know how to deal with argument `%s`." % arg) 47 | sys.exit(1) 48 | 49 | if include_vers == []: 50 | include_vers = default_rust_vers[:] 51 | 52 | rust_vers = [v for v in include_vers if v not in exclude_vers] 53 | msg('Tests will be run for: %s' % ', '.join(rust_vers)) 54 | 55 | results = [] 56 | for rust_ver in rust_vers: 57 | seq_id = 0 58 | for env_var_str in travis.get('env', [""]): 59 | env_vars = parse_env_vars(env_var_str) 60 | for row in chain([{}], matrix_includes): 61 | if row.get('rust', None) not in (None, rust_ver): 62 | continue 63 | 64 | row_env_vars = parse_env_vars(row.get('env', "")) 65 | 66 | cmd_env = {} 67 | cmd_env.update(env_vars) 68 | cmd_env.update(row_env_vars) 69 | 70 | success = run_script(script, rust_ver, seq_id, cmd_env) 71 | results.append((rust_ver, seq_id, success)) 72 | seq_id += 1 73 | 74 | print("") 75 | 76 | msg('Results:') 77 | for rust_ver, seq_id, success in results: 78 | msg('%s #%d: %s' % (rust_ver, seq_id, 'OK' if success else 'Failed!')) 79 | 80 | def msg(*args): 81 | if USE_ANSI: sys.stdout.write('\x1b[1;34m') 82 | sys.stdout.write('> ') 83 | if USE_ANSI: sys.stdout.write('\x1b[1;32m') 84 | for arg in args: 85 | sys.stdout.write(str(arg)) 86 | if USE_ANSI: sys.stdout.write('\x1b[0m') 87 | sys.stdout.write('\n') 88 | sys.stdout.flush() 89 | 90 | def msg_trace(*args): 91 | if TRACE: 92 | if USE_ANSI: sys.stderr.write('\x1b[1;31m') 93 | sys.stderr.write('$ ') 94 | if USE_ANSI: sys.stderr.write('\x1b[0m') 95 | for arg in args: 96 | sys.stderr.write(str(arg)) 97 | sys.stderr.write('\n') 98 | sys.stderr.flush() 99 | 100 | def parse_env_vars(s): 101 | env_vars = {} 102 | for m in re.finditer(r"""([A-Za-z0-9_]+)=(?:"([^"]+)"|(\S*))""", s.strip()): 103 | k = m.group(1) 104 | v = m.group(2) or m.group(3) 105 | env_vars[k] = v 106 | return env_vars 107 | 108 | def run_script(script, rust_ver, seq_id, env): 109 | target_dir = os.path.join('target', '%s-%d' % (rust_ver, seq_id)) 110 | log_path = os.path.join(LOG_DIR, '%s-%d.log' % (rust_ver, seq_id)) 111 | log_file = open(log_path, 'wt') 112 | msg('Running tests for %s #%d...' % (rust_ver, seq_id)) 113 | success = True 114 | 115 | def sub_env(m): 116 | name = m.group(1) or m.group(2) 117 | return cmd_env[name] 118 | 119 | log_file.write('# %s #%d\n' % (rust_ver, seq_id)) 120 | for k, v in env.items(): 121 | log_file.write('# %s=%r\n' % (k, v)) 122 | 123 | cmd_env = os.environ.copy() 124 | cmd_env['CARGO_TARGET_DIR'] = target_dir 125 | cmd_env.update(env) 126 | 127 | for cmd in script: 128 | cmd = re.sub(r"\$(?:([A-Za-z0-9_]+)|{([A-Za-z0-9_]+)})\b", sub_env, cmd) 129 | cmd_str = '> multirust run %s %s' % (rust_ver, cmd) 130 | log_file.write(cmd_str) 131 | log_file.write("\n") 132 | log_file.flush() 133 | success = sh( 134 | 'multirust run %s %s' % (rust_ver, cmd), 135 | checked=False, 136 | stdout=log_file, stderr=log_file, 137 | env=cmd_env, 138 | ) 139 | if not success: 140 | log_file.write('Command failed.\n') 141 | log_file.flush() 142 | break 143 | msg('... ', 'OK' if success else 'Failed!') 144 | log_file.close() 145 | return success 146 | 147 | def sh(cmd, env=None, stdout=None, stderr=None, checked=True): 148 | msg_trace('sh(%r, env=%r)' % (cmd, env)) 149 | try: 150 | subprocess.check_call(cmd, env=env, stdout=stdout, stderr=stderr, shell=True) 151 | except: 152 | msg_trace('FAILED!') 153 | if checked: 154 | raise 155 | else: 156 | return False 157 | if not checked: 158 | return True 159 | 160 | def translate_script(script): 161 | parts = script.split("&&") 162 | return [p.strip() for p in parts] 163 | 164 | if __name__ == '__main__': 165 | main() 166 | -------------------------------------------------------------------------------- /src/iter/accumulate.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | use std::mem::replace; 11 | 12 | /** 13 | ( 14 | a0, 15 | a1, 16 | a2, 17 | ... 18 | ), 19 | ⊗ 20 |  →  21 | ( 22 | a0, 23 | (a0a1), 24 | ((a0a1) ⊗ a2), 25 | ... 26 | ) 27 | 28 | */ 29 | pub trait AccumulateIterator: Iterator + Sized { 30 | /** 31 | Creates an iterator that scans from left to right over the input sequence, returning the accumulated result of calling the provided function on the entire sequence up to that point. 32 | 33 | # Example 34 | 35 | ``` 36 | # extern crate grabbag; 37 | # use grabbag::iter::AccumulateIterator; 38 | # fn main() { 39 | let v = vec![0usize, 1, 2, 3, 4]; 40 | 41 | // `r` is the sequence of partial sums of `v`. 42 | let r: Vec<_> = v.into_iter().accumulate(|a,b| a+b).collect(); 43 | assert_eq!(r, vec![0, 1, 3, 6, 10]); 44 | # } 45 | ``` 46 | */ 47 | fn accumulate E>(self, f: F) -> Accumulate { 48 | Accumulate { 49 | iter: self, 50 | f: f, 51 | accum: None, 52 | } 53 | } 54 | } 55 | 56 | impl AccumulateIterator for It where It: Iterator {} 57 | 58 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 59 | pub struct Accumulate where It: Iterator { 60 | iter: It, 61 | f: F, 62 | accum: Option, 63 | } 64 | 65 | impl Accumulate where It: Iterator { 66 | /** 67 | Unwraps the iterator, returning the underlying iterator. 68 | */ 69 | pub fn unwrap(self) -> It { 70 | let Accumulate { iter, .. } = self; 71 | iter 72 | } 73 | } 74 | 75 | impl Iterator for Accumulate where It: Iterator, F: FnMut(E, E) -> E, E: Clone { 76 | type Item = E; 77 | 78 | fn next(&mut self) -> Option { 79 | match replace(&mut self.accum, None) { 80 | None => match self.iter.next() { 81 | None => None, 82 | e @ _ => { 83 | self.accum = e; 84 | self.accum.clone() 85 | } 86 | }, 87 | Some(accum) => match self.iter.next() { 88 | None => { 89 | self.accum = None; 90 | None 91 | }, 92 | Some(rhs) => { 93 | self.accum = Some((self.f)(accum, rhs)); 94 | self.accum.clone() 95 | } 96 | } 97 | } 98 | } 99 | } 100 | 101 | #[test] 102 | fn test_accumulate() { 103 | let v = vec![0usize, 1, 2, 3, 4]; 104 | let r: Vec<_> = v.into_iter().accumulate(|a,b| a+b).collect(); 105 | assert_eq!(r, vec![0, 1, 3, 6, 10]); 106 | } 107 | 108 | #[test] 109 | fn test_accumulate_unwrap() { 110 | let v = vec![0usize, 1, 2, 3, 4]; 111 | let mut i = v.into_iter().accumulate(|a,b| a+b); 112 | assert_eq!(i.next(), Some(0)); 113 | assert_eq!(i.next(), Some(1)); 114 | assert_eq!(i.next(), Some(3)); 115 | let r: Vec<_> = i.unwrap().collect(); 116 | assert_eq!(r, vec![3, 4]); 117 | } 118 | -------------------------------------------------------------------------------- /src/iter/cartesian_product.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /** 11 | ( 12 | a0, 13 | a1, 14 | ... 15 | ), 16 | ( 17 | b0, 18 | b1, 19 | ... 20 | ) 21 |  →  22 | ( 23 | (a0, b0), 24 | (a0, b1), 25 | ..., 26 | (a1, b0), 27 | (a1, b1), 28 | ... 29 | ) 30 | 31 | */ 32 | pub trait CartesianProductIterator: Iterator + Sized { 33 | /** 34 | Creates an iterator that yields the cartesian product of two input iterators. 35 | 36 | The element type of the first input iterator must implement Clone, as must the second iterator type. 37 | */ 38 | fn cartesian_product(self, right: RightIt) -> CartesianProduct where RightIt: Clone + Iterator { 39 | CartesianProduct { 40 | left: self, 41 | right: right.clone(), 42 | right_checkpoint: right, 43 | left_value: None, 44 | } 45 | } 46 | } 47 | 48 | impl CartesianProductIterator for LeftIt where LeftIt: Iterator, LeftItem: Clone {} 49 | 50 | #[derive(Clone, Debug)] 51 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 52 | pub struct CartesianProduct where LeftIt: Iterator, RightIt: Iterator { 53 | left: LeftIt, 54 | right: RightIt, 55 | right_checkpoint: RightIt, 56 | left_value: Option, 57 | } 58 | 59 | impl Iterator for CartesianProduct where LeftIt: Iterator, RightIt: Clone + Iterator, LeftItem: Clone { 60 | type Item = (LeftItem, RightItem); 61 | 62 | fn next(&mut self) -> Option<(LeftItem, RightItem)> { 63 | loop { 64 | if let Some(e0) = self.left_value.clone() { 65 | match self.right.next() { 66 | Some(e1) => return Some((e0, e1)), 67 | None => { 68 | self.left_value = None; 69 | self.right = self.right_checkpoint.clone(); 70 | } 71 | } 72 | } 73 | 74 | match self.left.next() { 75 | Some(e0) => { 76 | self.left_value = Some(e0); 77 | } 78 | None => return None 79 | } 80 | } 81 | } 82 | 83 | fn size_hint(&self) -> (usize, Option) { 84 | let (l0, mu0) = self.left.size_hint(); 85 | let (l1, mu1) = self.right.size_hint(); 86 | let (lc, muc) = self.right_checkpoint.size_hint(); 87 | 88 | let combine_bounds: fn(usize, usize, usize) -> usize; 89 | 90 | if self.left_value.is_some() { 91 | combine_bounds = combine_bounds_with_partial; 92 | } else { 93 | combine_bounds = combine_bounds_without_partial; 94 | } 95 | 96 | let l = combine_bounds(l1, l0, lc); 97 | let mu = match (mu0, mu1, muc) { 98 | (None, _, _) | (_, None, _) | (_, _, None) => None, 99 | (Some(u0), Some(u1), Some(uc)) => Some(combine_bounds(u1, u0, uc)) 100 | }; 101 | 102 | (l, mu) 103 | } 104 | } 105 | 106 | fn combine_bounds_with_partial(b0: usize, b1: usize, bc: usize) -> usize { 107 | b1 + b0*bc 108 | } 109 | 110 | fn combine_bounds_without_partial(b0: usize, _: usize, bc: usize) -> usize { 111 | b0*bc 112 | } 113 | 114 | #[test] 115 | fn test_cartesian_product() { 116 | use super::CloneEachIterator; 117 | 118 | let a = vec![0usize, 1, 2]; 119 | let b = vec![3usize, 4]; 120 | let r: Vec<_> = a.into_iter().cartesian_product(b.iter().clone_each()).collect(); 121 | assert_eq!(r, vec![(0,3),(0,4),(1,3),(1,4),(2,3),(2,4)]); 122 | } 123 | -------------------------------------------------------------------------------- /src/iter/clone_each.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /** 11 | ( 12 | a0, 13 | a1, 14 | ... 15 | ) 16 |  →  17 | ( 18 | a0`.clone()`, 19 | a1`.clone()`, 20 | ... 21 | ) 22 | 23 | */ 24 | pub trait CloneEachIterator<'a, E>: Iterator + Sized where E: 'a + Clone { 25 | /** 26 | Creates an iterator which will clone each element of the input iterator. 27 | */ 28 | fn clone_each(self) -> CloneEach { 29 | CloneEach { 30 | iter: self, 31 | } 32 | } 33 | } 34 | 35 | impl<'a, It, E> CloneEachIterator<'a, E> for It where It: Iterator, E: 'a + Clone {} 36 | 37 | #[derive(Clone, Debug)] 38 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 39 | pub struct CloneEach { 40 | iter: It, 41 | } 42 | 43 | impl CloneEach { 44 | /** 45 | Unwraps the iterator, returning the underlying iterator. 46 | */ 47 | pub fn unwrap(self) -> It { 48 | self.iter 49 | } 50 | } 51 | 52 | impl<'a, It, E> Iterator for CloneEach where It: Iterator, E: 'a + Clone { 53 | type Item = E; 54 | 55 | fn next(&mut self) -> Option { 56 | match self.iter.next() { 57 | None => None, 58 | Some(e) => Some(e.clone()) 59 | } 60 | } 61 | 62 | fn size_hint(&self) -> (usize, Option) { 63 | self.iter.size_hint() 64 | } 65 | } 66 | 67 | impl<'a, E, It> DoubleEndedIterator for CloneEach where It: DoubleEndedIterator, E: 'a + Clone { 68 | fn next_back(&mut self) -> Option { 69 | self.iter.next_back().map(|e| e.clone()) 70 | } 71 | } 72 | 73 | #[test] 74 | fn test_clone_each() { 75 | let it: Vec = vec![1, 2, 3]; 76 | let mut it = it.iter().clone_each(); 77 | assert_eq!(it.next(), Some(1)); 78 | assert_eq!(it.next(), Some(2)); 79 | assert_eq!(it.next(), Some(3)); 80 | assert_eq!(it.next(), None); 81 | } 82 | -------------------------------------------------------------------------------- /src/iter/fold.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /** 11 | ( 12 | a0, 13 | a1, 14 | a2, 15 | ... 16 | ), 17 |  →  18 |  →  19 | ((a0) ⊗ a1) ⊗ a2) ⊗ ... 20 | 21 | */ 22 | pub trait FoldlIterator: Iterator + Sized { 23 | /** 24 | Folds the elements of the iterator together, from left to right, using `f`. 25 | 26 | Returns `None` if the iterator is empty. 27 | */ 28 | fn foldl E>(mut self, f: F) -> Option { 29 | let first = match self.next() { 30 | None => return None, 31 | Some(e) => e 32 | }; 33 | 34 | Some(self.fold(first, f)) 35 | } 36 | 37 | /** 38 | Folds the elements of the iterator together, from left to right, using `f. 39 | 40 | In addition, the first element is transformed using `map` before folding begins. 41 | 42 | Returns `None` if the iterator is empty. 43 | */ 44 | fn foldl_map E1, MapFn: FnOnce(E) -> E1>(mut self, map: MapFn, f: F) -> Option { 45 | let first = match self.next() { 46 | None => return None, 47 | Some(e) => map(e) 48 | }; 49 | 50 | Some(self.fold(first, f)) 51 | } 52 | } 53 | 54 | impl FoldlIterator for It where It: Iterator {} 55 | 56 | #[test] 57 | fn test_foldl() { 58 | use std::borrow::ToOwned; 59 | 60 | let vs = vec!["a", "b", "c"]; 61 | let vs = vs.into_iter().map(|e| e.to_owned()); 62 | assert_eq!(Some("((a, b), c)".to_owned()), vs.foldl(|a,b| format!("({}, {})", a, b))); 63 | } 64 | 65 | #[test] 66 | fn test_foldl_map() { 67 | use std::borrow::ToOwned; 68 | 69 | let v = vec!["a", "b", "c"]; 70 | let r = v.into_iter().foldl_map(|e| e.to_owned(), |e,f| (e+", ")+f); 71 | assert_eq!(r, Some("a, b, c".to_owned())); 72 | } 73 | 74 | /** 75 | ( 76 | ..., 77 | an-2, 78 | an-1, 79 | an, 80 | ), 81 | ⊗ 82 | → 83 | ... ⊗ (an-2 ⊗ (an-1 ⊗ (an))) 84 | 85 | */ 86 | pub trait FoldrIterator: DoubleEndedIterator + Iterator + Sized { 87 | /** 88 | Folds the elements of the iterator together, from right to left, using `f`. 89 | 90 | Returns `None` if the iterator is empty. 91 | */ 92 | fn foldr E>(mut self, mut f: F) -> Option { 93 | let mut last = match self.next_back() { 94 | None => return None, 95 | Some(e) => e 96 | }; 97 | 98 | loop { 99 | match self.next_back() { 100 | None => break, 101 | Some(e) => last = f(e, last) 102 | } 103 | } 104 | 105 | Some(last) 106 | } 107 | } 108 | 109 | impl FoldrIterator for It where It: DoubleEndedIterator + Iterator {} 110 | 111 | #[test] 112 | fn test_foldr() { 113 | use std::borrow::ToOwned; 114 | 115 | let vs = vec!["a", "b", "c"]; 116 | let vs = vs.into_iter().map(|e| e.to_owned()); 117 | assert_eq!(Some("(a, (b, c))".to_owned()), vs.foldr(|a,b| format!("({}, {})", a, b))); 118 | } 119 | -------------------------------------------------------------------------------- /src/iter/group_by.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | use std::cell::RefCell; 11 | use std::cmp::min; 12 | use std::mem::replace; 13 | use std::rc::Rc; 14 | 15 | /** 16 | Sequence of iterators containing successive elements of the subject which have the same group according to a group function. 17 | */ 18 | pub trait GroupByIterator: Iterator + Sized { 19 | /** 20 | Creates an iterator that yields a succession of `(group, sub_iterator)` pairs. Each `sub_iterator` yields successive elements of the input iterator that have the same `group`. An element's `group` is computed using the `f` closure. 21 | 22 | For example: 23 | 24 | ``` 25 | # extern crate grabbag; 26 | # use grabbag::iter::GroupByIterator; 27 | # fn main () { 28 | let v = vec![7usize, 5, 6, 2, 4, 7, 6, 1, 6, 4, 4, 6, 0, 0, 8, 8, 6, 1, 8, 7]; 29 | let is_even = |n: &usize| if *n & 1 == 0 { true } else { false }; 30 | for (even, mut ns) in v.into_iter().group_by(is_even) { 31 | println!("{}...", if even { "Evens" } else { "Odds" }); 32 | for n in ns { 33 | println!(" - {}", n); 34 | } 35 | } 36 | # } 37 | ``` 38 | */ 39 | fn group_by G, G>(self, group: GroupFn) -> GroupBy { 40 | GroupBy { 41 | state: Rc::new(RefCell::new(GroupByShared { 42 | iter: self, 43 | group: group, 44 | last_group: None, 45 | push_back: None, 46 | })), 47 | } 48 | } 49 | } 50 | 51 | impl GroupByIterator for It where It: Iterator {} 52 | 53 | // **NOTE**: Although `Clone` *can* be implemented for this, you *should not* do so, since you cannot clone the underlying `GroupByItemsShared` value. 54 | pub struct GroupBy { 55 | state: Rc>>, 56 | } 57 | 58 | pub struct GroupByShared { 59 | iter: It, 60 | group: GroupFn, 61 | last_group: Option, 62 | push_back: Option<(G, E)>, 63 | 64 | } 65 | 66 | impl Iterator for GroupBy where GroupFn: FnMut(&E) -> G, It: Iterator, G: Clone + Eq { 67 | type Item = (G, Group); 68 | 69 | fn next(&mut self) -> Option<(G, Group)> { 70 | // First, get a mutable borrow to the underlying state. 71 | let mut state = self.state.borrow_mut(); 72 | let state = &mut *state; 73 | 74 | // If we have a push-back element, immediately construct a sub iterator. 75 | if let Some((g, e)) = replace(&mut state.push_back, None) { 76 | return Some(( 77 | g.clone(), 78 | Group { 79 | state: self.state.clone(), 80 | group_value: g, 81 | first_value: Some(e), 82 | } 83 | )); 84 | } 85 | 86 | // Otherwise, try to pull the next element from the input iterator. 87 | // Complication: a sub iterator *might* stop *before* the group is exhausted. We need to account for this (so that users can easily skip groups). 88 | let (e, g) = match replace(&mut state.last_group, None) { 89 | None => { 90 | // We don't *have* a previous group, just grab the next element. 91 | let e = match state.iter.next() { 92 | Some(e) => e, 93 | None => return None 94 | }; 95 | let g = (state.group)(&e); 96 | (e, g) 97 | }, 98 | Some(last_g) => { 99 | // We have to keep pulling elements until the group changes. 100 | let mut e; 101 | let mut g; 102 | loop { 103 | e = match state.iter.next() { 104 | Some(e) => e, 105 | None => return None 106 | }; 107 | g = (state.group)(&e); 108 | if g != last_g { break; } 109 | } 110 | (e, g) 111 | } 112 | }; 113 | 114 | // Remember this group. 115 | state.last_group = Some(g.clone()); 116 | 117 | // Construct the sub-iterator and yield it. 118 | Some(( 119 | g.clone(), 120 | Group { 121 | state: self.state.clone(), 122 | group_value: g, 123 | first_value: Some(e), 124 | } 125 | )) 126 | } 127 | 128 | fn size_hint(&self) -> (usize, Option) { 129 | let (lb, mub) = self.state.borrow().iter.size_hint(); 130 | let lb = min(lb, 1); 131 | (lb, mub) 132 | } 133 | } 134 | 135 | // **NOTE**: Although `Clone` *can* be implemented for this, you *should not* do so, since you cannot clone the underlying `GroupByShared` value. 136 | pub struct Group { 137 | state: Rc>>, 138 | group_value: G, 139 | first_value: Option, 140 | } 141 | 142 | impl Iterator for Group where GroupFn: FnMut(&E) -> G, It: Iterator, G: Eq { 143 | type Item = E; 144 | 145 | fn next(&mut self) -> Option { 146 | // If we have a first_value, consume and yield that. 147 | if let Some(e) = replace(&mut self.first_value, None) { 148 | return Some(e) 149 | } 150 | 151 | // Get a mutable borrow to the shared state. 152 | let mut state = self.state.borrow_mut(); 153 | let state = &mut *state; 154 | 155 | let e = match state.iter.next() { 156 | Some(e) => e, 157 | None => return None 158 | }; 159 | 160 | let g = (state.group)(&e); 161 | 162 | match g == self.group_value { 163 | true => { 164 | // Still in the same group. 165 | Some(e) 166 | }, 167 | false => { 168 | // Different group! We need to push (g, e) back into the master iterator. 169 | state.push_back = Some((g, e)); 170 | None 171 | } 172 | } 173 | } 174 | 175 | fn size_hint(&self) -> (usize, Option) { 176 | let state = self.state.borrow(); 177 | 178 | let lb = if self.first_value.is_some() { 1 } else { 0 }; 179 | let (_, mub) = state.iter.size_hint(); 180 | (lb, mub) 181 | } 182 | } 183 | 184 | #[test] 185 | fn test_group_by() { 186 | { 187 | let v = vec![0usize, 1, 2, 3, 5, 4, 6, 8, 7]; 188 | let mut oi = v.into_iter().group_by(|&e| e & 1); 189 | 190 | let (g, mut ii) = oi.next().unwrap(); 191 | assert_eq!(g, 0); 192 | assert_eq!(ii.next(), Some(0)); 193 | assert_eq!(ii.next(), None); 194 | 195 | let (g, mut ii) = oi.next().unwrap(); 196 | assert_eq!(g, 1); 197 | assert_eq!(ii.next(), Some(1)); 198 | assert_eq!(ii.next(), None); 199 | 200 | let (g, mut ii) = oi.next().unwrap(); 201 | assert_eq!(g, 0); 202 | assert_eq!(ii.next(), Some(2)); 203 | assert_eq!(ii.next(), None); 204 | 205 | let (g, mut ii) = oi.next().unwrap(); 206 | assert_eq!(g, 1); 207 | assert_eq!(ii.next(), Some(3)); 208 | assert_eq!(ii.next(), Some(5)); 209 | assert_eq!(ii.next(), None); 210 | 211 | let (g, mut ii) = oi.next().unwrap(); 212 | assert_eq!(g, 0); 213 | assert_eq!(ii.next(), Some(4)); 214 | assert_eq!(ii.next(), Some(6)); 215 | assert_eq!(ii.next(), Some(8)); 216 | assert_eq!(ii.next(), None); 217 | 218 | let (g, mut ii) = oi.next().unwrap(); 219 | assert_eq!(g, 1); 220 | assert_eq!(ii.next(), Some(7)); 221 | assert_eq!(ii.next(), None); 222 | 223 | assert!(oi.next().is_none()); 224 | } 225 | { 226 | let v = vec![0usize, 1, 2, 3, 5, 4, 6, 8, 7]; 227 | let mut oi = v.into_iter().group_by(|&e| e & 1); 228 | 229 | let (g, _) = oi.next().unwrap(); 230 | assert_eq!(g, 0); 231 | 232 | let (g, _) = oi.next().unwrap(); 233 | assert_eq!(g, 1); 234 | 235 | let (g, _) = oi.next().unwrap(); 236 | assert_eq!(g, 0); 237 | 238 | let (g, _) = oi.next().unwrap(); 239 | assert_eq!(g, 1); 240 | 241 | let (g, mut ii) = oi.next().unwrap(); 242 | assert_eq!(g, 0); 243 | assert_eq!(ii.next(), Some(4)); 244 | assert_eq!(ii.next(), Some(6)); 245 | 246 | let (g, _) = oi.next().unwrap(); 247 | assert_eq!(g, 1); 248 | 249 | assert!(oi.next().is_none()); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/iter/intersperse.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | use std::mem::replace; 11 | 12 | /** 13 | (a0, a1, ..., an), i 14 |  →  15 | (a0, i, a1, i, ..., i, an) 16 | 17 | */ 18 | pub trait IntersperseIterator: Iterator + Sized { 19 | /** 20 | Creates an iterator that yields `inject` between each element of the input iterator. `inject` will not appear as the first or last element of the resulting iterator. 21 | */ 22 | fn intersperse(self, inject: E) -> Intersperse; 23 | } 24 | 25 | impl IntersperseIterator for It where It: Iterator { 26 | fn intersperse(mut self, inject: E) -> Intersperse { 27 | let look_ahead = self.next(); 28 | Intersperse { 29 | iter: self, 30 | look_ahead: look_ahead, 31 | inject: inject, 32 | next_is_inject: false, 33 | } 34 | } 35 | } 36 | 37 | #[derive(Clone, Debug)] 38 | pub struct Intersperse { 39 | iter: It, 40 | look_ahead: Option, 41 | inject: E, 42 | next_is_inject: bool, 43 | } 44 | 45 | impl Iterator for Intersperse where It: Iterator, E: Clone { 46 | type Item = E; 47 | 48 | fn next(&mut self) -> Option { 49 | match self.next_is_inject { 50 | false => match replace(&mut self.look_ahead, None) { 51 | None => None, 52 | Some(e) => { 53 | self.look_ahead = self.iter.next(); 54 | self.next_is_inject = true; 55 | Some(e) 56 | } 57 | }, 58 | true => match self.look_ahead.is_some() { 59 | true => { 60 | self.next_is_inject = false; 61 | Some(self.inject.clone()) 62 | }, 63 | false => { 64 | None 65 | } 66 | } 67 | } 68 | } 69 | 70 | fn size_hint(&self) -> (usize, Option) { 71 | let (li, mui) = self.iter.size_hint(); 72 | let l = match li { 73 | 0 => 0, 74 | n => n + n - 1 75 | }; 76 | let mu = match mui { 77 | Some(0) => Some(0), 78 | Some(n) => Some(n + n - 1), 79 | None => None 80 | }; 81 | (l, mu) 82 | } 83 | } 84 | 85 | #[test] 86 | fn test_intersperse() { 87 | let v: Vec<&str> = vec![]; 88 | let r: Vec<_> = v.into_iter().intersperse(",").collect(); 89 | assert_eq!(r, Vec::<&str>::new()); 90 | 91 | let v = vec!["a"]; 92 | let r: Vec<_> = v.into_iter().intersperse(",").collect(); 93 | assert_eq!(r, vec!["a"]); 94 | 95 | let v = vec!["a", "b"]; 96 | let r: Vec<_> = v.into_iter().intersperse(",").collect(); 97 | assert_eq!(r, vec!["a", ",", "b"]); 98 | 99 | let v = vec!["a", "b", "c"]; 100 | let r: Vec<_> = v.into_iter().intersperse(",").collect(); 101 | assert_eq!(r, vec!["a", ",", "b", ",", "c"]); 102 | } 103 | -------------------------------------------------------------------------------- /src/iter/keep_some.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /** 11 | a  →  12 | (e | e  →  a : Some(e)) 13 | 14 | */ 15 | pub trait KeepSomeIterator: Sized { 16 | /** 17 | Creates an iterator that, given a sequence of `Option` values, unwraps all `Some(E)`s, and discards all `None`s. 18 | */ 19 | fn keep_some(self) -> KeepSome; 20 | } 21 | 22 | impl KeepSomeIterator for It where It: Iterator { 23 | fn keep_some(self) -> KeepSome { 24 | KeepSome { 25 | iter: self, 26 | } 27 | } 28 | } 29 | 30 | #[derive(Clone, Debug)] 31 | pub struct KeepSome { 32 | iter: It, 33 | } 34 | 35 | impl KeepSome { 36 | /** 37 | Unwraps the iterator, returning the underlying iterator. 38 | */ 39 | pub fn unwrap(self) -> It { 40 | self.iter 41 | } 42 | } 43 | 44 | impl Iterator for KeepSome where It: Iterator> { 45 | type Item = E; 46 | 47 | fn next(&mut self) -> Option { 48 | loop { 49 | match self.iter.next() { 50 | Some(v @ Some(_)) => return v, 51 | Some(None) => { /* do nothing */ }, 52 | None => return None, 53 | } 54 | } 55 | } 56 | 57 | fn size_hint(&self) -> (usize, Option) { 58 | let (_, mu) = self.iter.size_hint(); 59 | (0, mu) 60 | } 61 | } 62 | 63 | impl DoubleEndedIterator for KeepSome where It: DoubleEndedIterator + Iterator> { 64 | fn next_back(&mut self) -> Option { 65 | loop { 66 | match self.iter.next_back() { 67 | Some(v @ Some(_)) => return v, 68 | Some(None) => (), 69 | None => return None, 70 | } 71 | } 72 | } 73 | } 74 | 75 | #[test] 76 | fn test_keep_some() { 77 | let v = vec![None, Some(0usize), Some(1), None, Some(2), None, None, Some(3), None]; 78 | let r: Vec<_> = v.into_iter().keep_some().collect(); 79 | assert_eq!(r, vec![0, 1, 2, 3]); 80 | } 81 | -------------------------------------------------------------------------------- /src/iter/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /*! 11 | This module contains a set of iterator extensions. Rather than being named for the type of iterator they are implemented on, they are named for the method (or group of associated methods) being implemented. 12 | 13 | If you can't be bothered to import them individually, you can just pull in `iter::prelude::*` instead. 14 | 15 | # Standard Features 16 | 17 | The iterator extensions, where possible, should implement the following features: 18 | 19 | - `Clone`, which produces an *independent* version of the iterator. 20 | - `DoubleEndedIterator`. 21 | - `ExactSizeIterator`. 22 | - `Show`. 23 | - Accurate `size_hint` (depending on the transform being performed, and the accuracy of the underlying iterator). 24 | - An `unwrap` method, which returns any owned values passed into the iterator; typically, this is the original iterator. 25 | */ 26 | 27 | #[doc(inline)] 28 | pub use self::prelude::{ 29 | AccumulateIterator, 30 | CartesianProductIterator, 31 | CloneEachIterator, 32 | GroupByIterator, 33 | FoldlIterator, FoldrIterator, 34 | IntersperseIterator, 35 | KeepSomeIterator, 36 | PadTailToIterator, 37 | RoundRobinIterator, 38 | SkipExactlyIterator, 39 | SortedIterator, 40 | StrideIterator, 41 | TakeExactlyIterator, 42 | TeeIterator, 43 | ZipLongestIterator, 44 | }; 45 | 46 | pub mod accumulate; 47 | pub mod cartesian_product; 48 | pub mod clone_each; 49 | pub mod group_by; 50 | pub mod fold; 51 | pub mod intersperse; 52 | pub mod keep_some; 53 | pub mod pad_tail_to; 54 | pub mod round_robin; 55 | pub mod skip_exactly; 56 | pub mod sorted; 57 | pub mod stride; 58 | pub mod take_exactly; 59 | pub mod tee; 60 | pub mod zip_longest; 61 | 62 | /** 63 | This module just centralises all the iterator extension traits in one place, suitable for glob importing. 64 | */ 65 | pub mod prelude { 66 | pub use super::accumulate::AccumulateIterator; 67 | pub use super::cartesian_product::CartesianProductIterator; 68 | pub use super::clone_each::CloneEachIterator; 69 | pub use super::group_by::GroupByIterator; 70 | pub use super::fold::{FoldlIterator, FoldrIterator}; 71 | pub use super::intersperse::IntersperseIterator; 72 | pub use super::keep_some::KeepSomeIterator; 73 | pub use super::pad_tail_to::PadTailToIterator; 74 | pub use super::round_robin::RoundRobinIterator; 75 | pub use super::skip_exactly::SkipExactlyIterator; 76 | pub use super::sorted::SortedIterator; 77 | pub use super::stride::StrideIterator; 78 | pub use super::take_exactly::TakeExactlyIterator; 79 | pub use super::tee::TeeIterator; 80 | pub use super::zip_longest::ZipLongestIterator; 81 | } 82 | -------------------------------------------------------------------------------- /src/iter/pad_tail_to.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /** 11 | Pads a sequence to a minimum length. 12 | */ 13 | pub trait PadTailToIterator: Iterator + Sized { 14 | /** 15 | Creates an iterator that ensures there are at least `n` elements in a sequence. If the input iterator is too short, the difference is made up with a filler value. 16 | */ 17 | fn pad_tail_to E>(self, n: usize, filler: F) -> PadTailTo { 18 | PadTailTo { 19 | iter: self, 20 | min: n, 21 | pos: 0, 22 | filler: filler, 23 | } 24 | } 25 | } 26 | 27 | impl PadTailToIterator for It where It: Iterator {} 28 | 29 | pub struct PadTailTo { 30 | iter: It, 31 | min: usize, 32 | pos: usize, 33 | filler: F, 34 | } 35 | 36 | impl PadTailTo { 37 | /** 38 | Unwraps the iterator, returning the underlying iterator and filler closure. 39 | */ 40 | pub fn unwrap(self) -> (It, F) { 41 | let PadTailTo { iter, filler, .. } = self; 42 | (iter, filler) 43 | } 44 | } 45 | 46 | impl Iterator for PadTailTo where It: Iterator, F: FnMut(usize) -> E { 47 | type Item = E; 48 | 49 | fn next(&mut self) -> Option { 50 | match self.iter.next() { 51 | None => { 52 | if self.pos < self.min { 53 | let e = Some((self.filler)(self.pos)); 54 | self.pos += 1; 55 | e 56 | } else { 57 | None 58 | } 59 | }, 60 | e @ _ => { 61 | self.pos += 1; 62 | e 63 | } 64 | } 65 | } 66 | } 67 | 68 | impl DoubleEndedIterator for PadTailTo where It: DoubleEndedIterator + ExactSizeIterator + Iterator, F: FnMut(usize) -> E { 69 | fn next_back(&mut self) -> Option { 70 | if self.min == 0 { 71 | self.next_back() 72 | } else if self.iter.len() >= self.min { 73 | self.min -= 1; 74 | self.next_back() 75 | } else { 76 | self.min -= 1; 77 | Some((self.filler)(self.pos)) 78 | } 79 | } 80 | } 81 | 82 | #[test] 83 | fn test_pad_tail_to() { 84 | let v: Vec = vec![0, 1, 2]; 85 | let r: Vec<_> = v.into_iter().pad_tail_to(5, |n| n).collect(); 86 | assert_eq!(r, vec![0, 1, 2, 3, 4]); 87 | 88 | let v: Vec = vec![0, 1, 2]; 89 | let r: Vec<_> = v.into_iter().pad_tail_to(1, |_| panic!()).collect(); 90 | assert_eq!(r, vec![0, 1, 2]); 91 | } 92 | -------------------------------------------------------------------------------- /src/iter/round_robin.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | use std::cmp::min; 11 | 12 | /** 13 | (a0, a1, ...), 14 | (b0, b1, ...) 15 |  →  16 | (a0, b0, a1, b1, ...) 17 | 18 | */ 19 | pub trait RoundRobinIterator: Iterator + Sized { 20 | /** 21 | Creates an iterator that alternates between yielding elements of the two input iterators. It stops as soon as either iterator is exhausted. 22 | */ 23 | fn round_robin::Item>>(self, other_it: OtherIt) -> RoundRobin { 24 | RoundRobin { 25 | it0: self, 26 | it1: other_it, 27 | phase: 0, 28 | } 29 | } 30 | 31 | /** 32 | Creates an iterator that alternates between yielding elements of the two input iterators. If one iterator stops before the other, it is simply skipped. 33 | */ 34 | fn round_robin_longest::Item>>(self, other_it: OtherIt) -> RoundRobinLongest { 35 | RoundRobinLongest { 36 | it0: self, 37 | it1: other_it, 38 | phase: 0, 39 | fused: false, 40 | } 41 | } 42 | } 43 | 44 | impl RoundRobinIterator for It where It: Iterator {} 45 | 46 | #[derive(Clone, Debug)] 47 | pub struct RoundRobin { 48 | it0: It0, 49 | it1: It1, 50 | phase: u8, 51 | } 52 | 53 | impl RoundRobin { 54 | /** 55 | Unwraps the iterator, returning the underlying iterators. 56 | */ 57 | pub fn unwrap(self) -> (It0, It1) { 58 | let RoundRobin { it0, it1, .. } = self; 59 | (it0, it1) 60 | } 61 | } 62 | 63 | impl Iterator for RoundRobin where It0: Iterator, It1: Iterator { 64 | type Item = E; 65 | 66 | fn next(&mut self) -> Option { 67 | match self.phase { 68 | 0 => match self.it0.next() { 69 | None => None, 70 | e @ _ => { 71 | self.phase = 1; 72 | e 73 | } 74 | }, 75 | _ => match self.it1.next() { 76 | None => None, 77 | e @ _ => { 78 | self.phase = 0; 79 | e 80 | } 81 | }, 82 | } 83 | } 84 | 85 | fn size_hint(&self) -> (usize, Option) { 86 | match (self.it0.size_hint(), self.it1.size_hint()) { 87 | ((l0, None), (l1, _)) | ((l0, _), (l1, None)) => (l0+l1, None), 88 | ((l0, Some(u0)), (l1, Some(u1))) => (l0+l1, Some(2*min(u0, u1))) 89 | } 90 | } 91 | } 92 | 93 | #[derive(Clone, Debug)] 94 | pub struct RoundRobinLongest { 95 | it0: It0, 96 | it1: It1, 97 | phase: u8, 98 | fused: bool, 99 | } 100 | 101 | impl RoundRobinLongest { 102 | /** 103 | Unwraps the iterator, returning the underlying iterators. 104 | */ 105 | pub fn unwrap(self) -> (It0, It1) { 106 | let RoundRobinLongest { it0, it1, .. } = self; 107 | (it0, it1) 108 | } 109 | } 110 | 111 | impl Iterator for RoundRobinLongest where It0: Iterator, It1: Iterator { 112 | type Item = E; 113 | 114 | fn next(&mut self) -> Option { 115 | match (self.phase, self.fused) { 116 | (0, true) => match self.it0.next() { 117 | None => None, 118 | e @ _ => e, 119 | }, 120 | (_, true) => match self.it1.next() { 121 | None => None, 122 | e @ _ => e, 123 | }, 124 | (0, false) => match self.it0.next() { 125 | None => { 126 | self.phase = 1; 127 | self.fused = true; 128 | self.next() 129 | }, 130 | e @ _ => { 131 | self.phase = 1; 132 | e 133 | }, 134 | }, 135 | (_, false) => match self.it1.next() { 136 | None => { 137 | self.phase = 0; 138 | self.fused = true; 139 | self.next() 140 | }, 141 | e @ _ => { 142 | self.phase = 0; 143 | e 144 | }, 145 | }, 146 | } 147 | } 148 | 149 | fn size_hint(&self) -> (usize, Option) { 150 | match (self.it0.size_hint(), self.it1.size_hint()) { 151 | ((l0, None), (l1, _)) | ((l0, _), (l1, None)) => (l0+l1, None), 152 | ((l0, Some(u0)), (l1, Some(u1))) => (l0+l1, Some(u0+u1)) 153 | } 154 | } 155 | } 156 | 157 | #[test] 158 | fn test_round_robin() { 159 | let v0 = vec![0usize, 2, 4]; 160 | let v1 = vec![1usize, 3, 5, 7]; 161 | let mut it = v0.into_iter().round_robin(v1.into_iter()); 162 | assert_eq!(it.next(), Some(0)); 163 | assert_eq!(it.next(), Some(1)); 164 | assert_eq!(it.next(), Some(2)); 165 | assert_eq!(it.next(), Some(3)); 166 | assert_eq!(it.next(), Some(4)); 167 | assert_eq!(it.next(), Some(5)); 168 | assert_eq!(it.next(), None); 169 | } 170 | 171 | #[test] 172 | fn test_round_robin_longest() { 173 | let v0 = vec![0usize, 2, 4]; 174 | let v1 = vec![1usize, 3, 5, 7]; 175 | let mut it = v0.into_iter().round_robin_longest(v1.into_iter()); 176 | assert_eq!(it.next(), Some(0)); 177 | assert_eq!(it.next(), Some(1)); 178 | assert_eq!(it.next(), Some(2)); 179 | assert_eq!(it.next(), Some(3)); 180 | assert_eq!(it.next(), Some(4)); 181 | assert_eq!(it.next(), Some(5)); 182 | assert_eq!(it.next(), Some(7)); 183 | assert_eq!(it.next(), None); 184 | } 185 | -------------------------------------------------------------------------------- /src/iter/skip_exactly.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | #[test] 11 | fn test_skip() { 12 | let v = vec![0isize, 1, 2, 3]; 13 | let r: Vec<_> = v.into_iter().skip(3).collect(); 14 | assert_eq!(r, vec![3]); 15 | 16 | let v = vec![0isize, 1]; 17 | let r: Vec<_> = v.into_iter().skip(3).collect(); 18 | assert_eq!(r, vec![]); 19 | } 20 | 21 | /** 22 | (..., ai-1, ai, ai+1, ...), i 23 |  →  24 | (ai, ai+1, ...) 25 | 26 | */ 27 | pub trait SkipExactlyIterator: Iterator + Sized { 28 | /** 29 | Skips *exactly* `n` elements from the iterator. 30 | 31 | # Failure 32 | 33 | This method will panic if there are less than `n` elements in the iterator. 34 | */ 35 | fn skip_exactly(mut self, n: usize) -> Self { 36 | for i in 0..n { 37 | match self.next() { 38 | None => panic!("skip_exactly asked to skip {} elements, but only got {}", n, i), 39 | _ => () 40 | } 41 | } 42 | self 43 | } 44 | } 45 | 46 | impl SkipExactlyIterator for It where It: Iterator {} 47 | 48 | #[test] 49 | fn test_skip_exactly() { 50 | use std::thread; 51 | 52 | let v = vec![0isize, 1, 2, 3]; 53 | let r: Vec<_> = v.into_iter().skip_exactly(3).collect(); 54 | assert_eq!(r, vec![3]); 55 | 56 | let v = vec![0isize, 1]; 57 | let r = thread::spawn(move || { v.into_iter().skip_exactly(3).collect::>(); }).join(); 58 | assert!(r.is_err()); 59 | } 60 | -------------------------------------------------------------------------------- /src/iter/sorted.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | use std::cmp::Ordering; 11 | 12 | /** 13 | Shorthand for collecting and sorting an iterator. 14 | */ 15 | pub trait SortedIterator: Iterator + Sized where E: Ord { 16 | /** 17 | Returns a `Vec` with the elements of the input iterator in sorted order. 18 | */ 19 | fn sorted(self) -> Vec; 20 | 21 | /** 22 | Returns a `Vec` with the elements of the input iterator in sorted order, as specified by a comparison function. 23 | */ 24 | fn sorted_by Ordering>(self, compare: F) -> Vec; 25 | } 26 | 27 | impl SortedIterator for It where It: Iterator, E: Ord { 28 | fn sorted(self) -> Vec { 29 | let mut v = self.collect::>(); 30 | v.sort(); 31 | v 32 | } 33 | 34 | fn sorted_by Ordering>(self, compare: F) -> Vec { 35 | let mut v = self.collect::>(); 36 | v.sort_by(compare); 37 | v 38 | } 39 | } 40 | 41 | #[test] 42 | fn test_sorted() { 43 | let v = vec![1usize, 3, 2, 0, 4]; 44 | let s = v.into_iter().sorted(); 45 | assert_eq!(s, vec![0usize, 1, 2, 3, 4]); 46 | } 47 | 48 | #[test] 49 | fn test_sorted_by() { 50 | let v = vec![1usize, 3, 2, 0, 4]; 51 | let s = v.into_iter().sorted_by(|a,b| (!*a).cmp(&!*b)); 52 | assert_eq!(s, vec![4, 3, 2, 1, 0usize]); 53 | } 54 | -------------------------------------------------------------------------------- /src/iter/stride.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /** 11 | a, n 12 |  →  13 | (ai | ai  →  a : imod n ) 14 | 15 | */ 16 | pub trait StrideIterator: Iterator + Sized { 17 | /** 18 | Creates an iterator which yields every `n`th element of the input iterator, including the first. 19 | */ 20 | fn stride(self, n: usize) -> Stride { 21 | Stride { 22 | iter: self, 23 | stride: n 24 | } 25 | } 26 | } 27 | 28 | impl StrideIterator for It where It: Iterator {} 29 | 30 | #[derive(Clone, Debug)] 31 | pub struct Stride { 32 | iter: It, 33 | stride: usize, 34 | } 35 | 36 | impl Stride { 37 | /** 38 | Unwraps the iterator, returning the underlying iterator. 39 | */ 40 | pub fn unwrap(self) -> It { 41 | self.iter 42 | } 43 | } 44 | 45 | impl Iterator for Stride where It: Iterator { 46 | type Item = E; 47 | 48 | fn next(&mut self) -> Option { 49 | let v = match self.iter.next() { 50 | Some(v) => v, 51 | None => return None 52 | }; 53 | 54 | for _ in 0..(self.stride - 1) { 55 | match self.iter.next() { 56 | None => break, 57 | _ => () 58 | } 59 | } 60 | 61 | Some(v) 62 | } 63 | 64 | fn size_hint(&self) -> (usize, Option) { 65 | match self.iter.size_hint() { 66 | (lb, Some(ub)) => ((lb + self.stride - 1) / self.stride, 67 | Some((ub + self.stride - 1) / self.stride)), 68 | (lb, None) => (lb, None) 69 | } 70 | } 71 | } 72 | 73 | #[test] 74 | fn test_stride() { 75 | use super::CloneEachIterator; 76 | 77 | let v = vec![0isize, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 78 | let mut it = v.iter().clone_each().stride(2); 79 | assert_eq!(it.size_hint(), (5, Some(5))); 80 | assert_eq!(it.next(), Some(0)); 81 | assert_eq!(it.next(), Some(2)); 82 | assert_eq!(it.next(), Some(4)); 83 | assert_eq!(it.next(), Some(6)); 84 | assert_eq!(it.next(), Some(8)); 85 | assert_eq!(it.next(), None); 86 | 87 | let v = vec![0isize, 1, 2, 3, 4, 5]; 88 | let it = v.iter().clone_each().stride(3); 89 | assert_eq!(it.size_hint(), (2, Some(2))); 90 | 91 | let v = vec![0isize, 1, 2, 3, 4, 5, 6]; 92 | let it = v.iter().clone_each().stride(3); 93 | assert_eq!(it.size_hint(), (3, Some(3))); 94 | 95 | let v = vec![0isize, 1, 2, 3, 4, 5, 6, 7]; 96 | let it = v.iter().clone_each().stride(3); 97 | assert_eq!(it.size_hint(), (3, Some(3))); 98 | 99 | let v = vec![0isize, 1, 2, 3, 4, 5, 6, 7, 8]; 100 | let it = v.iter().clone_each().stride(3); 101 | assert_eq!(it.size_hint(), (3, Some(3))); 102 | 103 | let v = vec![0isize, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 104 | let it = v.iter().clone_each().stride(3); 105 | assert_eq!(it.size_hint(), (4, Some(4))); 106 | } 107 | -------------------------------------------------------------------------------- /src/iter/take_exactly.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | #[test] 11 | fn test_take() { 12 | let v = vec![0isize, 1, 2, 3]; 13 | let r: Vec<_> = v.into_iter().take(3).collect(); 14 | assert_eq!(r, vec![0, 1, 2]); 15 | 16 | let v = vec![0isize, 1]; 17 | let r: Vec<_> = v.into_iter().take(3).collect(); 18 | assert_eq!(r, vec![0, 1]); 19 | } 20 | 21 | /** 22 | (a0, ..., ai-1, ai, ai+1, ...), i 23 |  →  24 | (a0, ..., ai-1) 25 | 26 | */ 27 | pub trait TakeExactlyIterator: Iterator + Sized { 28 | /** 29 | Creates an iterator that yields *exactly* `n` elements from the subject iterator. 30 | 31 | # Failure 32 | 33 | The iterator will panic if there are less than `n` elements in the subject iterator. 34 | */ 35 | fn take_exactly(self, n: usize) -> TakeExactly { 36 | TakeExactly { 37 | iter: self, 38 | left: n, 39 | } 40 | } 41 | } 42 | 43 | impl TakeExactlyIterator for It where It: Iterator {} 44 | 45 | #[derive(Clone, Debug)] 46 | pub struct TakeExactly { 47 | iter: It, 48 | left: usize, 49 | } 50 | 51 | impl TakeExactly { 52 | /** 53 | Unwraps the iterator, returning the underlying iterator. 54 | */ 55 | pub fn unwrap(self) -> It { 56 | self.iter 57 | } 58 | } 59 | 60 | impl Iterator for TakeExactly where It: Iterator { 61 | type Item = E; 62 | 63 | fn next(&mut self) -> Option { 64 | match self.left { 65 | 0 => None, 66 | _ => match self.iter.next() { 67 | None => panic!("take_exactly expected {} more elements from iterator, but ran out", self.left), 68 | e @ _ => { 69 | self.left -= 1; 70 | e 71 | } 72 | } 73 | } 74 | } 75 | 76 | fn size_hint(&self) -> (usize, Option) { 77 | (self.left, Some(self.left)) 78 | } 79 | } 80 | 81 | #[test] 82 | fn test_take_exactly() { 83 | use std::thread; 84 | 85 | let v = vec![0isize, 1, 2, 3]; 86 | let r: Vec<_> = v.into_iter().take_exactly(3).collect(); 87 | assert_eq!(r, vec![0, 1, 2]); 88 | 89 | let v = vec![0isize, 1]; 90 | let r = thread::spawn(move || { v.into_iter().take_exactly(3).collect::>(); }).join(); 91 | assert!(r.is_err()); 92 | } 93 | -------------------------------------------------------------------------------- /src/iter/tee.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | use std::cell::RefCell; 11 | use std::collections::VecDeque; 12 | use std::rc::Rc; 13 | 14 | /** 15 | a  →  a, a 16 | */ 17 | pub trait TeeIterator: Iterator + Sized { 18 | /** 19 | Creates a pair of iterators that will yield the same sequence of values. 20 | 21 | The element type must implement Clone. 22 | */ 23 | fn tee(self) -> (Tee, Tee) { 24 | let tee0 = Tee { 25 | state: Rc::new(RefCell::new(TeeState { 26 | iter: self, 27 | iter_next: 0, 28 | buffer: VecDeque::new(), 29 | })), 30 | this_next: 0, 31 | }; 32 | let tee1 = Tee { 33 | state: tee0.state.clone(), 34 | this_next: 0, 35 | }; 36 | (tee0, tee1) 37 | } 38 | } 39 | 40 | impl TeeIterator for It where It: Iterator {} 41 | 42 | // **NOTE**: Although `Clone` *can* be implemented for this, you *should not* do so, since you cannot clone the underlying `TeeState` value. 43 | pub struct Tee { 44 | /* 45 | /!\ Important /!\ 46 | 47 | In order for this to *not* panic, you need to ensure that nothing is re-entrant whilst it holds a mutable reference to the `RefCell`. This should still be safe from the user's perspective. 48 | */ 49 | state: Rc>>, 50 | this_next: usize, 51 | } 52 | 53 | pub struct TeeState { 54 | iter: It, 55 | iter_next: usize, 56 | buffer: VecDeque, 57 | } 58 | 59 | impl Iterator for Tee where It: Iterator, E: Clone { 60 | type Item = E; 61 | 62 | fn next(&mut self) -> Option { 63 | let mut state = self.state.borrow_mut(); 64 | let state = &mut *state; 65 | 66 | match (self.this_next, state.iter_next, state.buffer.len()) { 67 | // Tee is even with iter. 68 | (i, j, _) if i == j => { 69 | match state.iter.next() { 70 | None => None, 71 | Some(e) => { 72 | self.this_next += 1; 73 | state.iter_next += 1; 74 | state.buffer.push_back(e.clone()); 75 | Some(e) 76 | } 77 | } 78 | }, 79 | // Error: Tee is behind iter, nothing in the buffer. 80 | (i, j, 0) if i < j => { 81 | panic!("tee fell behind iterator, but buffer is empty"); 82 | }, 83 | // Tee is behind iter, elements in buffer. 84 | (i, j, _) if i < j => { 85 | let e = state.buffer.pop_front().unwrap(); 86 | self.this_next += 1; 87 | Some(e) 88 | }, 89 | // Error: Tee is ahead of iter. 90 | _ /*(i, j, _) if i > j*/ => { 91 | panic!("tee got ahead of iterator"); 92 | } 93 | } 94 | } 95 | 96 | fn size_hint(&self) -> (usize, Option) { 97 | let state = self.state.borrow_mut(); 98 | let state = &*state; 99 | 100 | match (self.this_next, state.iter_next, state.buffer.len()) { 101 | // Tee is even with iter. 102 | (i, j, _) if i == j => { 103 | state.iter.size_hint() 104 | }, 105 | // Error: Tee is behind iter, nothing in the buffer. 106 | (i, j, 0) if i < j => { 107 | panic!("tee fell behind iterator, but buffer is empty"); 108 | }, 109 | // Tee is behind iter, elements in buffer. 110 | (i, j, n) if i < j => { 111 | match state.iter.size_hint() { 112 | (lb, None) => (lb+n, None), 113 | (lb, Some(ub)) => (lb+n, Some(ub+n)) 114 | } 115 | }, 116 | // Error: Tee is ahead of iter. 117 | _ /*(i, j, _) if i > j*/ => { 118 | panic!("tee got ahead of iterator"); 119 | } 120 | } 121 | } 122 | } 123 | 124 | #[test] 125 | fn test_tee() { 126 | let v = vec![0usize, 1, 2, 3]; 127 | let (a, b) = v.into_iter().tee(); 128 | assert_eq!(a.collect::>(), vec![0, 1, 2, 3]); 129 | assert_eq!(b.collect::>(), vec![0, 1, 2, 3]); 130 | 131 | let v = vec![0usize, 1, 2, 3]; 132 | let (a, b) = v.into_iter().tee(); 133 | assert_eq!(b.collect::>(), vec![0, 1, 2, 3]); 134 | assert_eq!(a.collect::>(), vec![0, 1, 2, 3]); 135 | 136 | let v = vec![0usize, 1, 2, 3]; 137 | let (mut a, mut b) = v.into_iter().tee(); 138 | assert_eq!(a.next(), Some(0)); 139 | assert_eq!(a.next(), Some(1)); 140 | assert_eq!(b.next(), Some(0)); 141 | assert_eq!(b.next(), Some(1)); 142 | assert_eq!(b.next(), Some(2)); 143 | assert_eq!(a.next(), Some(2)); 144 | assert_eq!(b.next(), Some(3)); 145 | assert_eq!(a.next(), Some(3)); 146 | assert_eq!(a.next(), None); 147 | assert_eq!(b.next(), None); 148 | } 149 | -------------------------------------------------------------------------------- /src/iter/zip_longest.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | use std::cmp::max; 11 | 12 | /** 13 | (..., am), 14 | (..., bm, bm+1, ..., bn) 15 |  →  16 | ( 17 | ..., 18 | (Some(am), Some(bm)), 19 | (None, Some(bm+1)), 20 | ..., 21 | (None, Some(bn)) 22 | ) 23 | 24 | */ 25 | pub trait ZipLongestIterator: Iterator + Sized { 26 | /** 27 | Creates an iterator which yields elements from both input iterators in lockstep. If one iterator ends before the other, the elements from that iterator will be replaced with `None`. 28 | */ 29 | fn zip_longest(self, right: RightIt) -> ZipLongest { 30 | ZipLongest { 31 | left: self, 32 | right: right, 33 | } 34 | } 35 | } 36 | 37 | impl ZipLongestIterator for LeftIt where LeftIt: Iterator {} 38 | 39 | #[derive(Clone, Debug)] 40 | pub struct ZipLongest { 41 | left: LeftIt, 42 | right: RightIt, 43 | } 44 | 45 | impl ZipLongest { 46 | /** 47 | Unwraps the iterator, returning the underlying iterators. 48 | */ 49 | pub fn unwrap(self) -> (LeftIt, RightIt) { 50 | let ZipLongest { left, right } = self; 51 | (left, right) 52 | } 53 | } 54 | 55 | impl Iterator for ZipLongest where LeftIt: Iterator, RightIt: Iterator { 56 | type Item = (Option, Option); 57 | 58 | fn next(&mut self) -> Option<(Option, Option)> { 59 | match (self.left.next(), self.right.next()) { 60 | (None, None) => None, 61 | e @ _ => Some(e) 62 | } 63 | } 64 | 65 | fn size_hint(&self) -> (usize, Option) { 66 | let (l0, mu0) = self.left.size_hint(); 67 | let (l1, mu1) = self.right.size_hint(); 68 | let l = max(l0, l1); 69 | let mu = match (mu0, mu1) { 70 | (None, _) | (_, None) => None, 71 | (Some(u0), Some(u1)) => Some(max(u0, u1)) 72 | }; 73 | (l, mu) 74 | } 75 | } 76 | 77 | #[test] 78 | fn test_zip_longest() { 79 | let a = vec![0usize, 1, 2, 3]; 80 | let b = vec!["a", "b", "c"]; 81 | let r: Vec<_> = a.into_iter().zip_longest(b.into_iter()).collect(); 82 | assert_eq!(r, vec![ 83 | (Some(0), Some("a")), 84 | (Some(1), Some("b")), 85 | (Some(2), Some("c")), 86 | (Some(3), None), 87 | ]); 88 | 89 | let a = vec![0usize, 1, 2]; 90 | let b = vec!["a", "b", "c"]; 91 | let r: Vec<_> = a.into_iter().zip_longest(b.into_iter()).collect(); 92 | assert_eq!(r, vec![ 93 | (Some(0), Some("a")), 94 | (Some(1), Some("b")), 95 | (Some(2), Some("c")), 96 | ]); 97 | 98 | let a = vec![0usize, 1, 2]; 99 | let b = vec!["a", "b", "c", "d"]; 100 | let r: Vec<_> = a.into_iter().zip_longest(b.into_iter()).collect(); 101 | assert_eq!(r, vec![ 102 | (Some(0), Some("a")), 103 | (Some(1), Some("b")), 104 | (Some(2), Some("c")), 105 | (None, Some("d")), 106 | ]); 107 | } 108 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ⓒ 2015 grabbag contributors. 3 | 4 | Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 6 | ), at your option. All 7 | files in the project carrying such notice may not be copied, modified, 8 | or distributed except according to those terms. 9 | */ 10 | /*! 11 | This is a random grab-bag of functionality that didn't really seem to deserve a separate home. 12 | 13 | If you see something you think you have a better place for, let me know! 14 | */ 15 | 16 | pub mod iter; 17 | -------------------------------------------------------------------------------- /update-docs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # coding: utf-8 3 | 4 | # Copyright ⓒ 2015 Daniel Keep. 5 | # 6 | # Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of 8 | # ), at your option. All 9 | # files in the project carrying such notice may not be copied, modified, 10 | # or distributed except according to those terms. 11 | 12 | import distutils.dir_util 13 | import os 14 | import shutil 15 | import subprocess 16 | import sys 17 | import tempfile 18 | import time 19 | 20 | DOC_ARGS = '' 21 | DOC_TARGET_BRANCH = 'gh-pages' 22 | TEMP_CHECKOUT_PREFIX = 'gh-pages-checkout-' 23 | TEMP_OUTPUT_PREFIX = 'gh-pages-generated-' 24 | 25 | USE_ANSI = True if sys.platform != 'win32' else os.environ.get('FORCE_ANSI', '') != '' 26 | TRACE_UPDATE_DOCS = os.environ.get('TRACE_UPDATE_DOCS', '') != '' 27 | 28 | def sh(cmd): 29 | msg_trace('sh(%r)' % cmd) 30 | try: 31 | subprocess.check_call(cmd, shell=True) 32 | except: 33 | msg_trace('FAILED!') 34 | raise 35 | 36 | def sh_eval(cmd, codec='utf-8', dont_strip=False): 37 | msg_trace('sh_eval(%r)' % cmd) 38 | result = None 39 | try: 40 | result = subprocess.check_output(cmd, shell=True).decode(codec) 41 | if not dont_strip: 42 | result = result.strip() 43 | except: 44 | msg_trace('FAILED!') 45 | raise 46 | return result 47 | 48 | def msg(*args): 49 | if USE_ANSI: sys.stdout.write('\x1b[1;34m') 50 | sys.stdout.write('> ') 51 | if USE_ANSI: sys.stdout.write('\x1b[1;32m') 52 | for arg in args: 53 | sys.stdout.write(str(arg)) 54 | if USE_ANSI: sys.stdout.write('\x1b[0m') 55 | sys.stdout.write('\n') 56 | sys.stdout.flush() 57 | 58 | def msg_trace(*args): 59 | if TRACE_UPDATE_DOCS: 60 | if USE_ANSI: sys.stderr.write('\x1b[1;31m') 61 | sys.stderr.write('$ ') 62 | if USE_ANSI: sys.stderr.write('\x1b[0m') 63 | for arg in args: 64 | sys.stderr.write(str(arg)) 65 | sys.stderr.write('\n') 66 | sys.stderr.flush() 67 | 68 | def copytree(src, dst): 69 | msg_trace('copytree(%r, %r)' % (src, dst)) 70 | distutils.dir_util.copy_tree(src=src, dst=dst) 71 | 72 | def really_rmtree(path): 73 | msg_trace('really_rmtree(%r)' % path) 74 | 75 | WAIT_TIME_SECS = 1.0 76 | MAX_TRIES = 10 77 | 78 | def on_error(func, path, exc_info): 79 | """ 80 | Error handler for ``shutil.rmtree``. 81 | 82 | If the error is due to an access error (read only file) 83 | it attempts to add write permission and then retries. 84 | 85 | If the error is for another reason it re-raises the error. 86 | 87 | Usage: ``shutil.rmtree(path, onerror=on_error)`` 88 | 89 | From _. 90 | """ 91 | import stat 92 | if not os.access(path, os.W_OK): 93 | # Is the error an access error ? 94 | os.chmod(path, stat.S_IWUSR) 95 | func(path) 96 | else: 97 | raise 98 | 99 | for _ in range(MAX_TRIES): 100 | failed = True 101 | try: 102 | msg_trace('shutil.rmtree(%r)' % path) 103 | shutil.rmtree(path, onerror=on_error) 104 | failed = False 105 | except WindowsError: 106 | time.sleep(WAIT_TIME_SECS) 107 | if not failed: return 108 | 109 | msg('Warning: failed to remove directory %r' % path) 110 | 111 | def init_doc_branch(): 112 | msg("Initialising %s branch" % DOC_TARGET_BRANCH) 113 | 114 | dir = os.getcwdu() 115 | msg_trace('dir = %r' % dir) 116 | 117 | tmp = tempfile.mkdtemp(prefix=TEMP_CHECKOUT_PREFIX) 118 | msg_trace('tmp = %r' % tmp) 119 | 120 | try: 121 | msg("Cloning into a temporary directory...") 122 | sh('git init -q "%s"' % tmp) 123 | msg_trace('os.chdir(%r)' % tmp) 124 | os.chdir(tmp) 125 | sh('git checkout -q --orphan "%s"' % DOC_TARGET_BRANCH) 126 | sh('git commit -qm "Initial commit." --allow-empty') 127 | sh('git remote add origin "%s"' % dir) 128 | sh('git push -q origin gh-pages') 129 | 130 | finally: 131 | msg('Cleaning up...') 132 | msg_trace('os.chdir(%r)' % dir) 133 | os.chdir(dir) 134 | msg_trace('shutil.rmtree(%r)' % tmp) 135 | really_rmtree(tmp) 136 | 137 | msg('%s is ready. Continuing.' % DOC_TARGET_BRANCH) 138 | 139 | def main(): 140 | if sh_eval('git symbolic-ref --short HEAD') != u'master': 141 | msg('Not on master; doing nothing.') 142 | return 0 143 | 144 | # Sanity check: does the doc branch exist at all? 145 | branches = {b[2:].strip() for b in sh_eval('git branch', dont_strip=True).splitlines()} 146 | msg_trace('branches = %r' % branches) 147 | if DOC_TARGET_BRANCH not in branches: 148 | init_doc_branch() 149 | 150 | last_rev = sh_eval('git rev-parse HEAD') 151 | last_msg = sh_eval('git log -1 --pretty=%B') 152 | msg_trace('last_rev = %r' % last_rev) 153 | msg_trace('last_msg = %r' % last_msg) 154 | 155 | dir = os.getcwdu() 156 | msg_trace('dir = %r' % dir) 157 | 158 | tmp1 = tempfile.mkdtemp(prefix=TEMP_CHECKOUT_PREFIX) 159 | tmp2 = tempfile.mkdtemp(prefix=TEMP_OUTPUT_PREFIX) 160 | msg_trace('tmp1 = %r' % tmp1) 161 | msg_trace('tmp2 = %r' % tmp2) 162 | 163 | try: 164 | msg("Cloning into a temporary directory...") 165 | sh('git clone -qb "%s" "%s" "%s"' % (DOC_TARGET_BRANCH, dir, tmp1)) 166 | msg_trace('os.chdir(%r)' % tmp1) 167 | os.chdir(tmp1) 168 | sh('git checkout -q master') 169 | 170 | msg("Generating documentation...") 171 | sh('cargo doc %s' % DOC_ARGS) 172 | tmp1_target_doc = '%s/target/doc' % tmp1 173 | msg_trace('shutil.move(%r, %r)' % (tmp1_target_doc, tmp2)) 174 | shutil.move(tmp1_target_doc, tmp2) 175 | 176 | msg('Updating %s...' % DOC_TARGET_BRANCH) 177 | sh('git checkout -q "%s"' % DOC_TARGET_BRANCH) 178 | sh('git clean -dfq') 179 | tmp2_doc = '%s/doc' % tmp2 180 | 181 | msg_trace('copytree(%r, %r)' % (tmp2_doc, './doc')) 182 | copytree(tmp2_doc, './doc') 183 | 184 | msg('Committing changes...') 185 | sh('git add .') 186 | sh('git commit --amend -m "Update docs for %s" -m "%s"' % (last_rev[:7], last_msg)) 187 | 188 | sh('git push -fqu origin "%s"' % DOC_TARGET_BRANCH) 189 | 190 | finally: 191 | msg('Cleaning up...') 192 | msg_trace('os.chdir(%r)' % dir) 193 | os.chdir(dir) 194 | msg_trace('shutil.rmtree(%r)' % tmp2) 195 | really_rmtree(tmp2) 196 | msg_trace('shutil.rmtree(%r)' % tmp1) 197 | really_rmtree(tmp1) 198 | 199 | msg('Done. Use `git push origin %s` to update live documentation.' % DOC_TARGET_BRANCH) 200 | 201 | 202 | if __name__ == '__main__': 203 | sys.exit(main()) 204 | --------------------------------------------------------------------------------