From 81de013103a2fa9bbe9f4ae65cf26dbfb9f8ac7d Mon Sep 17 00:00:00 2001 From: John Doty Date: Tue, 20 Dec 2022 01:02:23 +0000 Subject: [PATCH] Fix up vendor for... linux? --- vendor/cassowary/CHANGELOG.md | 36 +- vendor/cassowary/LICENSE-APACHE | 402 +- vendor/cassowary/LICENSE-MIT | 42 +- vendor/cassowary/README.md | 108 +- vendor/cassowary/src/lib.rs | 1246 +++--- vendor/cassowary/src/operators.rs | 1122 ++--- vendor/cassowary/src/solver_impl.rs | 1568 +++---- vendor/cassowary/tests/common/mod.rs | 68 +- vendor/cassowary/tests/quadrilateral.rs | 212 +- vendor/cassowary/tests/removal.rs | 60 +- vendor/crossterm/CHANGELOG.md | 1384 +++--- vendor/crossterm/LICENSE | 42 +- vendor/crossterm/README.md | 398 +- vendor/crossterm/docs/CONTRIBUTING.md | 130 +- vendor/crossterm/docs/crossterm_full.svg | 206 +- vendor/crossterm/docs/know-problems.md | 28 +- vendor/crossterm/examples/README.md | 80 +- .../examples/event-match-modifiers.rs | 136 +- vendor/crossterm/examples/event-poll-read.rs | 124 +- .../examples/event-read-char-line.rs | 90 +- vendor/crossterm/examples/event-read.rs | 194 +- .../examples/event-stream-async-std.rs | 136 +- .../crossterm/examples/event-stream-tokio.rs | 138 +- vendor/crossterm/examples/is_tty.rs | 36 +- vendor/crossterm/examples/stderr.rs | 192 +- vendor/crossterm/src/ansi_support.rs | 96 +- vendor/crossterm/src/command.rs | 460 +- vendor/crossterm/src/cursor.rs | 998 ++--- vendor/crossterm/src/cursor/sys.rs | 34 +- vendor/crossterm/src/cursor/sys/unix.rs | 114 +- vendor/crossterm/src/cursor/sys/windows.rs | 634 +-- vendor/crossterm/src/error.rs | 16 +- vendor/crossterm/src/event.rs | 1946 ++++---- vendor/crossterm/src/event/filter.rs | 136 +- vendor/crossterm/src/event/read.rs | 890 ++-- vendor/crossterm/src/event/source.rs | 54 +- vendor/crossterm/src/event/source/unix.rs | 482 +- vendor/crossterm/src/event/source/windows.rs | 192 +- vendor/crossterm/src/event/stream.rs | 294 +- vendor/crossterm/src/event/sys.rs | 18 +- vendor/crossterm/src/event/sys/unix.rs | 10 +- .../src/event/sys/unix/file_descriptor.rs | 164 +- vendor/crossterm/src/event/sys/unix/parse.rs | 2734 ++++++------ vendor/crossterm/src/event/sys/unix/waker.rs | 72 +- vendor/crossterm/src/event/sys/windows.rs | 102 +- .../crossterm/src/event/sys/windows/parse.rs | 732 +-- .../crossterm/src/event/sys/windows/poll.rs | 178 +- .../crossterm/src/event/sys/windows/waker.rs | 84 +- vendor/crossterm/src/event/timeout.rs | 184 +- vendor/crossterm/src/lib.rs | 520 +-- vendor/crossterm/src/macros.rs | 744 ++-- vendor/crossterm/src/style.rs | 1002 ++--- vendor/crossterm/src/style/attributes.rs | 240 +- vendor/crossterm/src/style/content_style.rs | 86 +- vendor/crossterm/src/style/styled_content.rs | 154 +- vendor/crossterm/src/style/stylize.rs | 400 +- vendor/crossterm/src/style/sys.rs | 4 +- vendor/crossterm/src/style/sys/windows.rs | 412 +- vendor/crossterm/src/style/types.rs | 12 +- vendor/crossterm/src/style/types/attribute.rs | 366 +- vendor/crossterm/src/style/types/color.rs | 952 ++-- vendor/crossterm/src/style/types/colored.rs | 540 +-- vendor/crossterm/src/style/types/colors.rs | 468 +- vendor/crossterm/src/terminal.rs | 884 ++-- vendor/crossterm/src/terminal/sys.rs | 30 +- vendor/crossterm/src/terminal/sys/unix.rs | 288 +- vendor/crossterm/src/terminal/sys/windows.rs | 792 ++-- vendor/crossterm/src/tty.rs | 92 +- vendor/crossterm_winapi/CHANGELOG.md | 90 +- vendor/crossterm_winapi/LICENSE | 42 +- vendor/crossterm_winapi/README.md | 144 +- vendor/crossterm_winapi/docs/CONTRIBUTING.md | 8 +- .../crossterm_winapi/docs/known-problems.md | 20 +- .../examples/coloring_example.rs | 128 +- vendor/crossterm_winapi/examples/console.rs | 52 +- vendor/crossterm_winapi/examples/handle.rs | 60 +- .../examples/screen_buffer.rs | 82 +- vendor/crossterm_winapi/src/console.rs | 472 +- vendor/crossterm_winapi/src/console_mode.rs | 150 +- vendor/crossterm_winapi/src/csbi.rs | 164 +- vendor/crossterm_winapi/src/handle.rs | 438 +- vendor/crossterm_winapi/src/lib.rs | 142 +- vendor/crossterm_winapi/src/screen_buffer.rs | 234 +- vendor/crossterm_winapi/src/semaphore.rs | 82 +- vendor/crossterm_winapi/src/structs.rs | 22 +- vendor/crossterm_winapi/src/structs/coord.rs | 84 +- vendor/crossterm_winapi/src/structs/input.rs | 654 +-- vendor/crossterm_winapi/src/structs/size.rs | 62 +- .../src/structs/window_coords.rs | 92 +- .../tools/win_python_wrapper.bat | 44 +- .../buck/prelude/tools/find_and_replace.bat | 50 +- .../linux_like/android/b64/aarch64/int128.rs | 14 +- .../linux/gnu/b64/aarch64/int128.rs | 14 +- .../linux/musl/b64/aarch64/int128.rs | 14 +- vendor/log/CHANGELOG.md | 482 +- vendor/log/LICENSE-APACHE | 402 +- vendor/log/LICENSE-MIT | 50 +- vendor/log/README.md | 234 +- vendor/log/benches/value.rs | 60 +- vendor/log/build.rs | 92 +- vendor/log/src/kv/error.rs | 180 +- vendor/log/src/kv/key.rs | 344 +- vendor/log/src/kv/mod.rs | 52 +- vendor/log/src/kv/source.rs | 1508 +++---- vendor/log/src/kv/value.rs | 2068 ++++----- vendor/log/src/lib.rs | 3942 ++++++++--------- vendor/log/src/macros.rs | 560 +-- vendor/log/src/serde.rs | 800 ++-- vendor/log/triagebot.toml | 2 +- vendor/remove_dir_all/LICENCE-APACHE | 382 +- vendor/remove_dir_all/LICENCE-MIT | 52 +- vendor/remove_dir_all/README.md | 40 +- vendor/remove_dir_all/src/fs.rs | 556 +-- vendor/remove_dir_all/src/lib.rs | 52 +- 114 files changed, 21002 insertions(+), 21002 deletions(-) diff --git a/vendor/cassowary/CHANGELOG.md b/vendor/cassowary/CHANGELOG.md index 106dbf4..f1385db 100644 --- a/vendor/cassowary/CHANGELOG.md +++ b/vendor/cassowary/CHANGELOG.md @@ -1,18 +1,18 @@ -# 0.3 - -* Various fixes (PR #4) from @christolliday. - Main breaking change is that variables no longer silently initialise to zero and will report - their initial value in the first call to `fetch_changes`, also `has_edit_variable` now takes `&self` - instead of `&mut self`. - -## 0.2.1 - -* Fixed crash under certain use cases. See PR #1 (Thanks @christolliday!). - -# 0.2.0 - -* Changed API to only report changes to the values of variables. This allows for more efficient use of the library in typical applications. - -# 0.1 - -Initial release +# 0.3 + +* Various fixes (PR #4) from @christolliday. + Main breaking change is that variables no longer silently initialise to zero and will report + their initial value in the first call to `fetch_changes`, also `has_edit_variable` now takes `&self` + instead of `&mut self`. + +## 0.2.1 + +* Fixed crash under certain use cases. See PR #1 (Thanks @christolliday!). + +# 0.2.0 + +* Changed API to only report changes to the values of variables. This allows for more efficient use of the library in typical applications. + +# 0.1 + +Initial release diff --git a/vendor/cassowary/LICENSE-APACHE b/vendor/cassowary/LICENSE-APACHE index 6f75635..462333d 100644 --- a/vendor/cassowary/LICENSE-APACHE +++ b/vendor/cassowary/LICENSE-APACHE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/cassowary/LICENSE-MIT b/vendor/cassowary/LICENSE-MIT index 93cbd53..e8d11a4 100644 --- a/vendor/cassowary/LICENSE-MIT +++ b/vendor/cassowary/LICENSE-MIT @@ -1,21 +1,21 @@ -The MIT License (MIT) - -Copyright (c) 2016 Dylan Ede - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +The MIT License (MIT) + +Copyright (c) 2016 Dylan Ede + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/cassowary/README.md b/vendor/cassowary/README.md index faf8044..ba38947 100644 --- a/vendor/cassowary/README.md +++ b/vendor/cassowary/README.md @@ -1,54 +1,54 @@ -# cassowary-rs - -[![Build Status](https://travis-ci.org/dylanede/cassowary-rs.svg?branch=master)](https://travis-ci.org/dylanede/cassowary-rs) - -This is a Rust implementation of the Cassowary constraint solving algorithm -([Badros et. al 2001](https://constraints.cs.washington.edu/solvers/cassowary-tochi.pdf)). -It is based heavily on the implementation for C++ at -[nucleic/kiwi](https://github.com/nucleic/kiwi). The implementation does -however differ in some details. - -Cassowary is designed for solving constraints to lay out user interfaces. -Constraints typically take the form "this button must line up with this -text box", or "this box should try to be 3 times the size of this other box". -Its most popular incarnation by far is in Apple's Autolayout -system for Mac OS X and iOS user interfaces. UI libraries using the Cassowary -algorithm manage to achieve a much more natural approach to specifying UI -layouts than traditional approaches like those found in HTML. - -This library is a low level interface to the solving algorithm, though it -tries to be as convenient as possible. As a result it does not have any -intrinsic knowledge of common user interface conventions like rectangular -regions or even two dimensions. These abstractions belong in a higher level -crate. - -For more information, please read -**[the documentation](https://dylanede.github.io/cassowary-rs)**. - -## Getting Started - -Add the following to your Cargo.toml: - -```toml -[dependencies] -cassowary = "^0.3.0" -``` - -Please read the documentation (linked above) for how to best use this crate. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or - http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +# cassowary-rs + +[![Build Status](https://travis-ci.org/dylanede/cassowary-rs.svg?branch=master)](https://travis-ci.org/dylanede/cassowary-rs) + +This is a Rust implementation of the Cassowary constraint solving algorithm +([Badros et. al 2001](https://constraints.cs.washington.edu/solvers/cassowary-tochi.pdf)). +It is based heavily on the implementation for C++ at +[nucleic/kiwi](https://github.com/nucleic/kiwi). The implementation does +however differ in some details. + +Cassowary is designed for solving constraints to lay out user interfaces. +Constraints typically take the form "this button must line up with this +text box", or "this box should try to be 3 times the size of this other box". +Its most popular incarnation by far is in Apple's Autolayout +system for Mac OS X and iOS user interfaces. UI libraries using the Cassowary +algorithm manage to achieve a much more natural approach to specifying UI +layouts than traditional approaches like those found in HTML. + +This library is a low level interface to the solving algorithm, though it +tries to be as convenient as possible. As a result it does not have any +intrinsic knowledge of common user interface conventions like rectangular +regions or even two dimensions. These abstractions belong in a higher level +crate. + +For more information, please read +**[the documentation](https://dylanede.github.io/cassowary-rs)**. + +## Getting Started + +Add the following to your Cargo.toml: + +```toml +[dependencies] +cassowary = "^0.3.0" +``` + +Please read the documentation (linked above) for how to best use this crate. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/vendor/cassowary/src/lib.rs b/vendor/cassowary/src/lib.rs index b883aa7..b1ab8fc 100644 --- a/vendor/cassowary/src/lib.rs +++ b/vendor/cassowary/src/lib.rs @@ -1,623 +1,623 @@ -//! This crate contains an implementation of the Cassowary constraint solving algorithm, based upon the work by -//! G.J. Badros et al. in 2001. This algorithm is designed primarily for use constraining elements in user interfaces. -//! Constraints are linear combinations of the problem variables. The notable features of Cassowary that make it -//! ideal for user interfaces are that it is incremental (i.e. you can add and remove constraints at runtime -//! and it will perform the minimum work to update the result) and that the constraints can be violated if -//! necessary, -//! with the order in which they are violated specified by setting a "strength" for each constraint. -//! This allows the solution to gracefully degrade, which is useful for when a -//! user interface needs to compromise on its constraints in order to still be able to display something. -//! -//! ## Constraint syntax -//! -//! This crate aims to provide syntax for describing linear constraints as naturally as possible, within -//! the limitations of Rust's type system. Generally you can write constraints as you would naturally, however -//! the operator symbol (for greater-than, less-than, equals) is replaced with an instance of the -//! `WeightedRelation` enum wrapped in "pipe brackets". -//! -//! For example, for the constraint -//! `(a + b) * 2 + c >= d + 1` with strength `s`, the code to use is -//! -//! ```ignore -//! (a + b) * 2.0 + c |GE(s)| d + 1.0 -//! ``` -//! -//! # A simple example -//! -//! Imagine a layout consisting of two elements laid out horizontally. For small window widths the elements -//! should compress to fit, but if there is enough space they should display at their preferred widths. The -//! first element will align to the left, and the second to the right. For this example we will ignore -//! vertical layout. -//! -//! First we need to include the relevant parts of `cassowary`: -//! -//! ``` -//! use cassowary::{ Solver, Variable }; -//! use cassowary::WeightedRelation::*; -//! use cassowary::strength::{ WEAK, MEDIUM, STRONG, REQUIRED }; -//! ``` -//! -//! And we'll construct some conveniences for pretty printing (which should hopefully be self-explanatory): -//! -//! ```ignore -//! use std::collections::HashMap; -//! let mut names = HashMap::new(); -//! fn print_changes(names: &HashMap, changes: &[(Variable, f64)]) { -//! println!("Changes:"); -//! for &(ref var, ref val) in changes { -//! println!("{}: {}", names[var], val); -//! } -//! } -//! ``` -//! -//! Let's define the variables required - the left and right edges of the elements, and the width of the window. -//! -//! ```ignore -//! let window_width = Variable::new(); -//! names.insert(window_width, "window_width"); -//! -//! struct Element { -//! left: Variable, -//! right: Variable -//! } -//! let box1 = Element { -//! left: Variable::new(), -//! right: Variable::new() -//! }; -//! names.insert(box1.left, "box1.left"); -//! names.insert(box1.right, "box1.right"); -//! -//! let box2 = Element { -//! left: Variable::new(), -//! right: Variable::new() -//! }; -//! names.insert(box2.left, "box2.left"); -//! names.insert(box2.right, "box2.right"); -//! ``` -//! -//! Now to set up the solver and constraints. -//! -//! ```ignore -//! let mut solver = Solver::new(); -//! solver.add_constraints(&[window_width |GE(REQUIRED)| 0.0, // positive window width -//! box1.left |EQ(REQUIRED)| 0.0, // left align -//! box2.right |EQ(REQUIRED)| window_width, // right align -//! box2.left |GE(REQUIRED)| box1.right, // no overlap -//! // positive widths -//! box1.left |LE(REQUIRED)| box1.right, -//! box2.left |LE(REQUIRED)| box2.right, -//! // preferred widths: -//! box1.right - box1.left |EQ(WEAK)| 50.0, -//! box2.right - box2.left |EQ(WEAK)| 100.0]).unwrap(); -//! ``` -//! -//! The window width is currently free to take any positive value. Let's constrain it to a particular value. -//! Since for this example we will repeatedly change the window width, it is most efficient to use an -//! "edit variable", instead of repeatedly removing and adding constraints (note that for efficiency -//! reasons we cannot edit a normal constraint that has been added to the solver). -//! -//! ```ignore -//! solver.add_edit_variable(window_width, STRONG).unwrap(); -//! solver.suggest_value(window_width, 300.0).unwrap(); -//! ``` -//! -//! This value of 300 is enough to fit both boxes in with room to spare, so let's check that this is the case. -//! We can fetch a list of changes to the values of variables in the solver. Using the pretty printer defined -//! earlier we can see what values our variables now hold. -//! -//! ```ignore -//! print_changes(&names, solver.fetch_changes()); -//! ``` -//! -//! This should print (in a possibly different order): -//! -//! ```ignore -//! Changes: -//! window_width: 300 -//! box1.right: 50 -//! box2.left: 200 -//! box2.right: 300 -//! ``` -//! -//! Note that the value of `box1.left` is not mentioned. This is because `solver.fetch_changes` only lists -//! *changes* to variables, and since each variable starts in the solver with a value of zero, any values that -//! have not changed from zero will not be reported. -//! -//! Now let's try compressing the window so that the boxes can't take up their preferred widths. -//! -//! ```ignore -//! solver.suggest_value(window_width, 75.0); -//! print_changes(&names, solver.fetch_changes); -//! ``` -//! -//! Now the solver can't satisfy all of the constraints. It will pick at least one of the weakest constraints to -//! violate. In this case it will be one or both of the preferred widths. For efficiency reasons this is picked -//! nondeterministically, so there are two possible results. This could be -//! -//! ```ignore -//! Changes: -//! window_width: 75 -//! box1.right: 0 -//! box2.left: 0 -//! box2.right: 75 -//! ``` -//! -//! or -//! -//! ```ignore -//! Changes: -//! window_width: 75 -//! box2.left: 50 -//! box2.right: 75 -//! ``` -//! -//! Due to the nature of the algorithm, "in-between" solutions, although just as valid, are not picked. -//! -//! In a user interface this is not likely a result we would prefer. The solution is to add another constraint -//! to control the behaviour when the preferred widths cannot both be satisfied. In this example we are going -//! to constrain the boxes to try to maintain a ratio between their widths. -//! -//! ``` -//! # use cassowary::{ Solver, Variable }; -//! # use cassowary::WeightedRelation::*; -//! # use cassowary::strength::{ WEAK, MEDIUM, STRONG, REQUIRED }; -//! # -//! # use std::collections::HashMap; -//! # let mut names = HashMap::new(); -//! # fn print_changes(names: &HashMap, changes: &[(Variable, f64)]) { -//! # println!("Changes:"); -//! # for &(ref var, ref val) in changes { -//! # println!("{}: {}", names[var], val); -//! # } -//! # } -//! # -//! # let window_width = Variable::new(); -//! # names.insert(window_width, "window_width"); -//! # struct Element { -//! # left: Variable, -//! # right: Variable -//! # } -//! # let box1 = Element { -//! # left: Variable::new(), -//! # right: Variable::new() -//! # }; -//! # names.insert(box1.left, "box1.left"); -//! # names.insert(box1.right, "box1.right"); -//! # let box2 = Element { -//! # left: Variable::new(), -//! # right: Variable::new() -//! # }; -//! # names.insert(box2.left, "box2.left"); -//! # names.insert(box2.right, "box2.right"); -//! # let mut solver = Solver::new(); -//! # solver.add_constraints(&[window_width |GE(REQUIRED)| 0.0, // positive window width -//! # box1.left |EQ(REQUIRED)| 0.0, // left align -//! # box2.right |EQ(REQUIRED)| window_width, // right align -//! # box2.left |GE(REQUIRED)| box1.right, // no overlap -//! # // positive widths -//! # box1.left |LE(REQUIRED)| box1.right, -//! # box2.left |LE(REQUIRED)| box2.right, -//! # // preferred widths: -//! # box1.right - box1.left |EQ(WEAK)| 50.0, -//! # box2.right - box2.left |EQ(WEAK)| 100.0]).unwrap(); -//! # solver.add_edit_variable(window_width, STRONG).unwrap(); -//! # solver.suggest_value(window_width, 300.0).unwrap(); -//! # print_changes(&names, solver.fetch_changes()); -//! # solver.suggest_value(window_width, 75.0); -//! # print_changes(&names, solver.fetch_changes()); -//! solver.add_constraint( -//! (box1.right - box1.left) / 50.0 |EQ(MEDIUM)| (box2.right - box2.left) / 100.0 -//! ).unwrap(); -//! print_changes(&names, solver.fetch_changes()); -//! ``` -//! -//! Now the result gives values that maintain the ratio between the sizes of the two boxes: -//! -//! ```ignore -//! Changes: -//! box1.right: 25 -//! box2.left: 25 -//! ``` -//! -//! This example may have appeared somewhat contrived, but hopefully it shows the power of the cassowary -//! algorithm for laying out user interfaces. -//! -//! One thing that this example exposes is that this crate is a rather low level library. It does not have -//! any inherent knowledge of user interfaces, directions or boxes. Thus for use in a user interface this -//! crate should ideally be wrapped by a higher level API, which is outside the scope of this crate. -use std::sync::Arc; -use std::collections::HashMap; -use std::collections::hash_map::{Entry}; - -mod solver_impl; -mod operators; - -static VARIABLE_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT; - -/// Identifies a variable for the constraint solver. -/// Each new variable is unique in the view of the solver, but copying or cloning the variable produces -/// a copy of the same variable. -#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct Variable(usize); - -impl Variable { - /// Produces a new unique variable for use in constraint solving. - pub fn new() -> Variable { - Variable(VARIABLE_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed)) - } -} - -/// A variable and a coefficient to multiply that variable by. This is a sub-expression in -/// a constraint equation. -#[derive(Copy, Clone, Debug)] -pub struct Term { - pub variable: Variable, - pub coefficient: f64 -} - -impl Term { - /// Construct a new Term from a variable and a coefficient. - fn new(variable: Variable, coefficient: f64) -> Term { - Term { - variable: variable, - coefficient: coefficient - } - } -} - -/// An expression that can be the left hand or right hand side of a constraint equation. -/// It is a linear combination of variables, i.e. a sum of variables weighted by coefficients, plus an optional constant. -#[derive(Clone, Debug)] -pub struct Expression { - pub terms: Vec, - pub constant: f64 -} - -impl Expression { - /// Constructs an expression of the form _n_, where n is a constant real number, not a variable. - pub fn from_constant(v: f64) -> Expression { - Expression { - terms: Vec::new(), - constant: v - } - } - /// Constructs an expression from a single term. Forms an expression of the form _n x_ - /// where n is the coefficient, and x is the variable. - pub fn from_term(term: Term) -> Expression { - Expression { - terms: vec![term], - constant: 0.0 - } - } - /// General constructor. Each `Term` in `terms` is part of the sum forming the expression, as well as `constant`. - pub fn new(terms: Vec, constant: f64) -> Expression { - Expression { - terms: terms, - constant: constant - } - } - /// Mutates this expression by multiplying it by minus one. - pub fn negate(&mut self) { - self.constant = -self.constant; - for t in &mut self.terms { - *t = -*t; - } - } -} - -impl From for Expression { - fn from(v: f64) -> Expression { - Expression::from_constant(v) - } -} - -impl From for Expression { - fn from(v: Variable) -> Expression { - Expression::from_term(Term::new(v, 1.0)) - } -} - -impl From for Expression { - fn from(t: Term) -> Expression { - Expression::from_term(t) - } -} - -/// Contains useful constants and functions for producing strengths for use in the constraint solver. -/// Each constraint added to the solver has an associated strength specifying the precedence the solver should -/// impose when choosing which constraints to enforce. It will try to enforce all constraints, but if that -/// is impossible the lowest strength constraints are the first to be violated. -/// -/// Strengths are simply real numbers. The strongest legal strength is 1,001,001,000.0. The weakest is 0.0. -/// For convenience constants are declared for commonly used strengths. These are `REQUIRED`, `STRONG`, -/// `MEDIUM` and `WEAK`. Feel free to multiply these by other values to get intermediate strengths. -/// Note that the solver will clip given strengths to the legal range. -/// -/// `REQUIRED` signifies a constraint that cannot be violated under any circumstance. Use this special strength -/// sparingly, as the solver will fail completely if it find that not all of the `REQUIRED` constraints -/// can be satisfied. The other strengths represent fallible constraints. These should be the most -/// commonly used strenghts for use cases where violating a constraint is acceptable or even desired. -/// -/// The solver will try to get as close to satisfying the constraints it violates as possible, strongest first. -/// This behaviour can be used (for example) to provide a "default" value for a variable should no other -/// stronger constraints be put upon it. -pub mod strength { - /// Create a constraint as a linear combination of STRONG, MEDIUM and WEAK strengths, corresponding to `a` - /// `b` and `c` respectively. The result is further multiplied by `w`. - pub fn create(a: f64, b: f64, c: f64, w: f64) -> f64 { - (a * w).max(0.0).min(1000.0) * 1_000_000.0 + - (b * w).max(0.0).min(1000.0) * 1000.0 + - (c * w).max(0.0).min(1000.0) - } - pub const REQUIRED: f64 = 1_001_001_000.0; - pub const STRONG: f64 = 1_000_000.0; - pub const MEDIUM: f64 = 1_000.0; - pub const WEAK: f64 = 1.0; - - /// Clips a strength value to the legal range - pub fn clip(s: f64) -> f64 { - s.min(REQUIRED).max(0.0) - } -} - -/// The possible relations that a constraint can specify. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] -pub enum RelationalOperator { - /// `<=` - LessOrEqual, - /// `==` - Equal, - /// `>=` - GreaterOrEqual -} - -impl std::fmt::Display for RelationalOperator { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - match *self { - RelationalOperator::LessOrEqual => write!(fmt, "<=") ?, - RelationalOperator::Equal => write!(fmt, "==") ?, - RelationalOperator::GreaterOrEqual => write!(fmt, ">=") ?, - }; - Ok(()) - } -} - -#[derive(Debug)] -struct ConstraintData { - expression: Expression, - strength: f64, - op: RelationalOperator -} - -/// A constraint, consisting of an equation governed by an expression and a relational operator, -/// and an associated strength. -#[derive(Clone, Debug)] -pub struct Constraint(Arc); - -impl Constraint { - /// Construct a new constraint from an expression, a relational operator and a strength. - /// This corresponds to the equation `e op 0.0`, e.g. `x + y >= 0.0`. For equations with a non-zero - /// right hand side, subtract it from the equation to give a zero right hand side. - pub fn new(e: Expression, op: RelationalOperator, strength: f64) -> Constraint { - Constraint(Arc::new(ConstraintData { - expression: e, - op: op, - strength: strength - })) - } - /// The expression of the left hand side of the constraint equation. - pub fn expr(&self) -> &Expression { - &self.0.expression - } - /// The relational operator governing the constraint. - pub fn op(&self) -> RelationalOperator { - self.0.op - } - /// The strength of the constraint that the solver will use. - pub fn strength(&self) -> f64 { - self.0.strength - } -} - -impl ::std::hash::Hash for Constraint { - fn hash(&self, hasher: &mut H) { - use ::std::ops::Deref; - hasher.write_usize(self.0.deref() as *const _ as usize); - } -} - -impl PartialEq for Constraint { - fn eq(&self, other: &Constraint) -> bool { - use ::std::ops::Deref; - self.0.deref() as *const _ == other.0.deref() as *const _ - } -} - -impl Eq for Constraint {} - -/// This is part of the syntactic sugar used for specifying constraints. This enum should be used as part of a -/// constraint expression. See the module documentation for more information. -pub enum WeightedRelation { - /// `==` - EQ(f64), - /// `<=` - LE(f64), - /// `>=` - GE(f64) -} -impl From for (RelationalOperator, f64) { - fn from(r: WeightedRelation) -> (RelationalOperator, f64) { - use WeightedRelation::*; - match r { - EQ(s) => (RelationalOperator::Equal, s), - LE(s) => (RelationalOperator::LessOrEqual, s), - GE(s) => (RelationalOperator::GreaterOrEqual, s), - } - } -} - -/// This is an intermediate type used in the syntactic sugar for specifying constraints. You should not use it -/// directly. -pub struct PartialConstraint(Expression, WeightedRelation); - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum SymbolType { - Invalid, - External, - Slack, - Error, - Dummy -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct Symbol(usize, SymbolType); - -impl Symbol { - fn invalid() -> Symbol { Symbol(0, SymbolType::Invalid) } - fn type_(&self) -> SymbolType { self.1 } -} - -#[derive(Clone)] -struct Row { - cells: HashMap, - constant: f64 -} - -fn near_zero(value: f64) -> bool { - const EPS: f64 = 1E-8; - if value < 0.0 { - -value < EPS - } else { - value < EPS - } -} - -impl Row { - fn new(constant: f64) -> Row { - Row { - cells: HashMap::new(), - constant: constant - } - } - fn add(&mut self, v: f64) -> f64 { - self.constant += v; - self.constant - } - fn insert_symbol(&mut self, s: Symbol, coefficient: f64) { - match self.cells.entry(s) { - Entry::Vacant(entry) => if !near_zero(coefficient) { - entry.insert(coefficient); - }, - Entry::Occupied(mut entry) => { - *entry.get_mut() += coefficient; - if near_zero(*entry.get_mut()) { - entry.remove(); - } - } - } - } - - fn insert_row(&mut self, other: &Row, coefficient: f64) -> bool { - let constant_diff = other.constant * coefficient; - self.constant += constant_diff; - for (s, v) in &other.cells { - self.insert_symbol(*s, v * coefficient); - } - constant_diff != 0.0 - } - - fn remove(&mut self, s: Symbol) { - self.cells.remove(&s); - } - - fn reverse_sign(&mut self) { - self.constant = -self.constant; - for (_, v) in &mut self.cells { - *v = -*v; - } - } - - fn solve_for_symbol(&mut self, s: Symbol) { - let coeff = -1.0 / match self.cells.entry(s) { - Entry::Occupied(entry) => entry.remove(), - Entry::Vacant(_) => unreachable!() - }; - self.constant *= coeff; - for (_, v) in &mut self.cells { - *v *= coeff; - } - } - - fn solve_for_symbols(&mut self, lhs: Symbol, rhs: Symbol) { - self.insert_symbol(lhs, -1.0); - self.solve_for_symbol(rhs); - } - - fn coefficient_for(&self, s: Symbol) -> f64 { - self.cells.get(&s).cloned().unwrap_or(0.0) - } - - fn substitute(&mut self, s: Symbol, row: &Row) -> bool { - if let Some(coeff) = self.cells.remove(&s) { - self.insert_row(row, coeff) - } else { - false - } - } -} - -/// The possible error conditions that `Solver::add_constraint` can fail with. -#[derive(Debug, Copy, Clone)] -pub enum AddConstraintError { - /// The constraint specified has already been added to the solver. - DuplicateConstraint, - /// The constraint is required, but it is unsatisfiable in conjunction with the existing constraints. - UnsatisfiableConstraint, - /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies - /// additional details as a string. - InternalSolverError(&'static str) -} - -/// The possible error conditions that `Solver::remove_constraint` can fail with. -#[derive(Debug, Copy, Clone)] -pub enum RemoveConstraintError { - /// The constraint specified was not already in the solver, so cannot be removed. - UnknownConstraint, - /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies - /// additional details as a string. - InternalSolverError(&'static str) -} - -/// The possible error conditions that `Solver::add_edit_variable` can fail with. -#[derive(Debug, Copy, Clone)] -pub enum AddEditVariableError { - /// The specified variable is already marked as an edit variable in the solver. - DuplicateEditVariable, - /// The specified strength was `REQUIRED`. This is illegal for edit variable strengths. - BadRequiredStrength -} - -/// The possible error conditions that `Solver::remove_edit_variable` can fail with. -#[derive(Debug, Copy, Clone)] -pub enum RemoveEditVariableError { - /// The specified variable was not an edit variable in the solver, so cannot be removed. - UnknownEditVariable, - /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies - /// additional details as a string. - InternalSolverError(&'static str) -} - -/// The possible error conditions that `Solver::suggest_value` can fail with. -#[derive(Debug, Copy, Clone)] -pub enum SuggestValueError { - /// The specified variable was not an edit variable in the solver, so cannot have its value suggested. - UnknownEditVariable, - /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies - /// additional details as a string. - InternalSolverError(&'static str) -} - -#[derive(Debug, Copy, Clone)] -struct InternalSolverError(&'static str); - -pub use solver_impl::Solver; +//! This crate contains an implementation of the Cassowary constraint solving algorithm, based upon the work by +//! G.J. Badros et al. in 2001. This algorithm is designed primarily for use constraining elements in user interfaces. +//! Constraints are linear combinations of the problem variables. The notable features of Cassowary that make it +//! ideal for user interfaces are that it is incremental (i.e. you can add and remove constraints at runtime +//! and it will perform the minimum work to update the result) and that the constraints can be violated if +//! necessary, +//! with the order in which they are violated specified by setting a "strength" for each constraint. +//! This allows the solution to gracefully degrade, which is useful for when a +//! user interface needs to compromise on its constraints in order to still be able to display something. +//! +//! ## Constraint syntax +//! +//! This crate aims to provide syntax for describing linear constraints as naturally as possible, within +//! the limitations of Rust's type system. Generally you can write constraints as you would naturally, however +//! the operator symbol (for greater-than, less-than, equals) is replaced with an instance of the +//! `WeightedRelation` enum wrapped in "pipe brackets". +//! +//! For example, for the constraint +//! `(a + b) * 2 + c >= d + 1` with strength `s`, the code to use is +//! +//! ```ignore +//! (a + b) * 2.0 + c |GE(s)| d + 1.0 +//! ``` +//! +//! # A simple example +//! +//! Imagine a layout consisting of two elements laid out horizontally. For small window widths the elements +//! should compress to fit, but if there is enough space they should display at their preferred widths. The +//! first element will align to the left, and the second to the right. For this example we will ignore +//! vertical layout. +//! +//! First we need to include the relevant parts of `cassowary`: +//! +//! ``` +//! use cassowary::{ Solver, Variable }; +//! use cassowary::WeightedRelation::*; +//! use cassowary::strength::{ WEAK, MEDIUM, STRONG, REQUIRED }; +//! ``` +//! +//! And we'll construct some conveniences for pretty printing (which should hopefully be self-explanatory): +//! +//! ```ignore +//! use std::collections::HashMap; +//! let mut names = HashMap::new(); +//! fn print_changes(names: &HashMap, changes: &[(Variable, f64)]) { +//! println!("Changes:"); +//! for &(ref var, ref val) in changes { +//! println!("{}: {}", names[var], val); +//! } +//! } +//! ``` +//! +//! Let's define the variables required - the left and right edges of the elements, and the width of the window. +//! +//! ```ignore +//! let window_width = Variable::new(); +//! names.insert(window_width, "window_width"); +//! +//! struct Element { +//! left: Variable, +//! right: Variable +//! } +//! let box1 = Element { +//! left: Variable::new(), +//! right: Variable::new() +//! }; +//! names.insert(box1.left, "box1.left"); +//! names.insert(box1.right, "box1.right"); +//! +//! let box2 = Element { +//! left: Variable::new(), +//! right: Variable::new() +//! }; +//! names.insert(box2.left, "box2.left"); +//! names.insert(box2.right, "box2.right"); +//! ``` +//! +//! Now to set up the solver and constraints. +//! +//! ```ignore +//! let mut solver = Solver::new(); +//! solver.add_constraints(&[window_width |GE(REQUIRED)| 0.0, // positive window width +//! box1.left |EQ(REQUIRED)| 0.0, // left align +//! box2.right |EQ(REQUIRED)| window_width, // right align +//! box2.left |GE(REQUIRED)| box1.right, // no overlap +//! // positive widths +//! box1.left |LE(REQUIRED)| box1.right, +//! box2.left |LE(REQUIRED)| box2.right, +//! // preferred widths: +//! box1.right - box1.left |EQ(WEAK)| 50.0, +//! box2.right - box2.left |EQ(WEAK)| 100.0]).unwrap(); +//! ``` +//! +//! The window width is currently free to take any positive value. Let's constrain it to a particular value. +//! Since for this example we will repeatedly change the window width, it is most efficient to use an +//! "edit variable", instead of repeatedly removing and adding constraints (note that for efficiency +//! reasons we cannot edit a normal constraint that has been added to the solver). +//! +//! ```ignore +//! solver.add_edit_variable(window_width, STRONG).unwrap(); +//! solver.suggest_value(window_width, 300.0).unwrap(); +//! ``` +//! +//! This value of 300 is enough to fit both boxes in with room to spare, so let's check that this is the case. +//! We can fetch a list of changes to the values of variables in the solver. Using the pretty printer defined +//! earlier we can see what values our variables now hold. +//! +//! ```ignore +//! print_changes(&names, solver.fetch_changes()); +//! ``` +//! +//! This should print (in a possibly different order): +//! +//! ```ignore +//! Changes: +//! window_width: 300 +//! box1.right: 50 +//! box2.left: 200 +//! box2.right: 300 +//! ``` +//! +//! Note that the value of `box1.left` is not mentioned. This is because `solver.fetch_changes` only lists +//! *changes* to variables, and since each variable starts in the solver with a value of zero, any values that +//! have not changed from zero will not be reported. +//! +//! Now let's try compressing the window so that the boxes can't take up their preferred widths. +//! +//! ```ignore +//! solver.suggest_value(window_width, 75.0); +//! print_changes(&names, solver.fetch_changes); +//! ``` +//! +//! Now the solver can't satisfy all of the constraints. It will pick at least one of the weakest constraints to +//! violate. In this case it will be one or both of the preferred widths. For efficiency reasons this is picked +//! nondeterministically, so there are two possible results. This could be +//! +//! ```ignore +//! Changes: +//! window_width: 75 +//! box1.right: 0 +//! box2.left: 0 +//! box2.right: 75 +//! ``` +//! +//! or +//! +//! ```ignore +//! Changes: +//! window_width: 75 +//! box2.left: 50 +//! box2.right: 75 +//! ``` +//! +//! Due to the nature of the algorithm, "in-between" solutions, although just as valid, are not picked. +//! +//! In a user interface this is not likely a result we would prefer. The solution is to add another constraint +//! to control the behaviour when the preferred widths cannot both be satisfied. In this example we are going +//! to constrain the boxes to try to maintain a ratio between their widths. +//! +//! ``` +//! # use cassowary::{ Solver, Variable }; +//! # use cassowary::WeightedRelation::*; +//! # use cassowary::strength::{ WEAK, MEDIUM, STRONG, REQUIRED }; +//! # +//! # use std::collections::HashMap; +//! # let mut names = HashMap::new(); +//! # fn print_changes(names: &HashMap, changes: &[(Variable, f64)]) { +//! # println!("Changes:"); +//! # for &(ref var, ref val) in changes { +//! # println!("{}: {}", names[var], val); +//! # } +//! # } +//! # +//! # let window_width = Variable::new(); +//! # names.insert(window_width, "window_width"); +//! # struct Element { +//! # left: Variable, +//! # right: Variable +//! # } +//! # let box1 = Element { +//! # left: Variable::new(), +//! # right: Variable::new() +//! # }; +//! # names.insert(box1.left, "box1.left"); +//! # names.insert(box1.right, "box1.right"); +//! # let box2 = Element { +//! # left: Variable::new(), +//! # right: Variable::new() +//! # }; +//! # names.insert(box2.left, "box2.left"); +//! # names.insert(box2.right, "box2.right"); +//! # let mut solver = Solver::new(); +//! # solver.add_constraints(&[window_width |GE(REQUIRED)| 0.0, // positive window width +//! # box1.left |EQ(REQUIRED)| 0.0, // left align +//! # box2.right |EQ(REQUIRED)| window_width, // right align +//! # box2.left |GE(REQUIRED)| box1.right, // no overlap +//! # // positive widths +//! # box1.left |LE(REQUIRED)| box1.right, +//! # box2.left |LE(REQUIRED)| box2.right, +//! # // preferred widths: +//! # box1.right - box1.left |EQ(WEAK)| 50.0, +//! # box2.right - box2.left |EQ(WEAK)| 100.0]).unwrap(); +//! # solver.add_edit_variable(window_width, STRONG).unwrap(); +//! # solver.suggest_value(window_width, 300.0).unwrap(); +//! # print_changes(&names, solver.fetch_changes()); +//! # solver.suggest_value(window_width, 75.0); +//! # print_changes(&names, solver.fetch_changes()); +//! solver.add_constraint( +//! (box1.right - box1.left) / 50.0 |EQ(MEDIUM)| (box2.right - box2.left) / 100.0 +//! ).unwrap(); +//! print_changes(&names, solver.fetch_changes()); +//! ``` +//! +//! Now the result gives values that maintain the ratio between the sizes of the two boxes: +//! +//! ```ignore +//! Changes: +//! box1.right: 25 +//! box2.left: 25 +//! ``` +//! +//! This example may have appeared somewhat contrived, but hopefully it shows the power of the cassowary +//! algorithm for laying out user interfaces. +//! +//! One thing that this example exposes is that this crate is a rather low level library. It does not have +//! any inherent knowledge of user interfaces, directions or boxes. Thus for use in a user interface this +//! crate should ideally be wrapped by a higher level API, which is outside the scope of this crate. +use std::sync::Arc; +use std::collections::HashMap; +use std::collections::hash_map::{Entry}; + +mod solver_impl; +mod operators; + +static VARIABLE_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT; + +/// Identifies a variable for the constraint solver. +/// Each new variable is unique in the view of the solver, but copying or cloning the variable produces +/// a copy of the same variable. +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct Variable(usize); + +impl Variable { + /// Produces a new unique variable for use in constraint solving. + pub fn new() -> Variable { + Variable(VARIABLE_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed)) + } +} + +/// A variable and a coefficient to multiply that variable by. This is a sub-expression in +/// a constraint equation. +#[derive(Copy, Clone, Debug)] +pub struct Term { + pub variable: Variable, + pub coefficient: f64 +} + +impl Term { + /// Construct a new Term from a variable and a coefficient. + fn new(variable: Variable, coefficient: f64) -> Term { + Term { + variable: variable, + coefficient: coefficient + } + } +} + +/// An expression that can be the left hand or right hand side of a constraint equation. +/// It is a linear combination of variables, i.e. a sum of variables weighted by coefficients, plus an optional constant. +#[derive(Clone, Debug)] +pub struct Expression { + pub terms: Vec, + pub constant: f64 +} + +impl Expression { + /// Constructs an expression of the form _n_, where n is a constant real number, not a variable. + pub fn from_constant(v: f64) -> Expression { + Expression { + terms: Vec::new(), + constant: v + } + } + /// Constructs an expression from a single term. Forms an expression of the form _n x_ + /// where n is the coefficient, and x is the variable. + pub fn from_term(term: Term) -> Expression { + Expression { + terms: vec![term], + constant: 0.0 + } + } + /// General constructor. Each `Term` in `terms` is part of the sum forming the expression, as well as `constant`. + pub fn new(terms: Vec, constant: f64) -> Expression { + Expression { + terms: terms, + constant: constant + } + } + /// Mutates this expression by multiplying it by minus one. + pub fn negate(&mut self) { + self.constant = -self.constant; + for t in &mut self.terms { + *t = -*t; + } + } +} + +impl From for Expression { + fn from(v: f64) -> Expression { + Expression::from_constant(v) + } +} + +impl From for Expression { + fn from(v: Variable) -> Expression { + Expression::from_term(Term::new(v, 1.0)) + } +} + +impl From for Expression { + fn from(t: Term) -> Expression { + Expression::from_term(t) + } +} + +/// Contains useful constants and functions for producing strengths for use in the constraint solver. +/// Each constraint added to the solver has an associated strength specifying the precedence the solver should +/// impose when choosing which constraints to enforce. It will try to enforce all constraints, but if that +/// is impossible the lowest strength constraints are the first to be violated. +/// +/// Strengths are simply real numbers. The strongest legal strength is 1,001,001,000.0. The weakest is 0.0. +/// For convenience constants are declared for commonly used strengths. These are `REQUIRED`, `STRONG`, +/// `MEDIUM` and `WEAK`. Feel free to multiply these by other values to get intermediate strengths. +/// Note that the solver will clip given strengths to the legal range. +/// +/// `REQUIRED` signifies a constraint that cannot be violated under any circumstance. Use this special strength +/// sparingly, as the solver will fail completely if it find that not all of the `REQUIRED` constraints +/// can be satisfied. The other strengths represent fallible constraints. These should be the most +/// commonly used strenghts for use cases where violating a constraint is acceptable or even desired. +/// +/// The solver will try to get as close to satisfying the constraints it violates as possible, strongest first. +/// This behaviour can be used (for example) to provide a "default" value for a variable should no other +/// stronger constraints be put upon it. +pub mod strength { + /// Create a constraint as a linear combination of STRONG, MEDIUM and WEAK strengths, corresponding to `a` + /// `b` and `c` respectively. The result is further multiplied by `w`. + pub fn create(a: f64, b: f64, c: f64, w: f64) -> f64 { + (a * w).max(0.0).min(1000.0) * 1_000_000.0 + + (b * w).max(0.0).min(1000.0) * 1000.0 + + (c * w).max(0.0).min(1000.0) + } + pub const REQUIRED: f64 = 1_001_001_000.0; + pub const STRONG: f64 = 1_000_000.0; + pub const MEDIUM: f64 = 1_000.0; + pub const WEAK: f64 = 1.0; + + /// Clips a strength value to the legal range + pub fn clip(s: f64) -> f64 { + s.min(REQUIRED).max(0.0) + } +} + +/// The possible relations that a constraint can specify. +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +pub enum RelationalOperator { + /// `<=` + LessOrEqual, + /// `==` + Equal, + /// `>=` + GreaterOrEqual +} + +impl std::fmt::Display for RelationalOperator { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match *self { + RelationalOperator::LessOrEqual => write!(fmt, "<=") ?, + RelationalOperator::Equal => write!(fmt, "==") ?, + RelationalOperator::GreaterOrEqual => write!(fmt, ">=") ?, + }; + Ok(()) + } +} + +#[derive(Debug)] +struct ConstraintData { + expression: Expression, + strength: f64, + op: RelationalOperator +} + +/// A constraint, consisting of an equation governed by an expression and a relational operator, +/// and an associated strength. +#[derive(Clone, Debug)] +pub struct Constraint(Arc); + +impl Constraint { + /// Construct a new constraint from an expression, a relational operator and a strength. + /// This corresponds to the equation `e op 0.0`, e.g. `x + y >= 0.0`. For equations with a non-zero + /// right hand side, subtract it from the equation to give a zero right hand side. + pub fn new(e: Expression, op: RelationalOperator, strength: f64) -> Constraint { + Constraint(Arc::new(ConstraintData { + expression: e, + op: op, + strength: strength + })) + } + /// The expression of the left hand side of the constraint equation. + pub fn expr(&self) -> &Expression { + &self.0.expression + } + /// The relational operator governing the constraint. + pub fn op(&self) -> RelationalOperator { + self.0.op + } + /// The strength of the constraint that the solver will use. + pub fn strength(&self) -> f64 { + self.0.strength + } +} + +impl ::std::hash::Hash for Constraint { + fn hash(&self, hasher: &mut H) { + use ::std::ops::Deref; + hasher.write_usize(self.0.deref() as *const _ as usize); + } +} + +impl PartialEq for Constraint { + fn eq(&self, other: &Constraint) -> bool { + use ::std::ops::Deref; + self.0.deref() as *const _ == other.0.deref() as *const _ + } +} + +impl Eq for Constraint {} + +/// This is part of the syntactic sugar used for specifying constraints. This enum should be used as part of a +/// constraint expression. See the module documentation for more information. +pub enum WeightedRelation { + /// `==` + EQ(f64), + /// `<=` + LE(f64), + /// `>=` + GE(f64) +} +impl From for (RelationalOperator, f64) { + fn from(r: WeightedRelation) -> (RelationalOperator, f64) { + use WeightedRelation::*; + match r { + EQ(s) => (RelationalOperator::Equal, s), + LE(s) => (RelationalOperator::LessOrEqual, s), + GE(s) => (RelationalOperator::GreaterOrEqual, s), + } + } +} + +/// This is an intermediate type used in the syntactic sugar for specifying constraints. You should not use it +/// directly. +pub struct PartialConstraint(Expression, WeightedRelation); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum SymbolType { + Invalid, + External, + Slack, + Error, + Dummy +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Symbol(usize, SymbolType); + +impl Symbol { + fn invalid() -> Symbol { Symbol(0, SymbolType::Invalid) } + fn type_(&self) -> SymbolType { self.1 } +} + +#[derive(Clone)] +struct Row { + cells: HashMap, + constant: f64 +} + +fn near_zero(value: f64) -> bool { + const EPS: f64 = 1E-8; + if value < 0.0 { + -value < EPS + } else { + value < EPS + } +} + +impl Row { + fn new(constant: f64) -> Row { + Row { + cells: HashMap::new(), + constant: constant + } + } + fn add(&mut self, v: f64) -> f64 { + self.constant += v; + self.constant + } + fn insert_symbol(&mut self, s: Symbol, coefficient: f64) { + match self.cells.entry(s) { + Entry::Vacant(entry) => if !near_zero(coefficient) { + entry.insert(coefficient); + }, + Entry::Occupied(mut entry) => { + *entry.get_mut() += coefficient; + if near_zero(*entry.get_mut()) { + entry.remove(); + } + } + } + } + + fn insert_row(&mut self, other: &Row, coefficient: f64) -> bool { + let constant_diff = other.constant * coefficient; + self.constant += constant_diff; + for (s, v) in &other.cells { + self.insert_symbol(*s, v * coefficient); + } + constant_diff != 0.0 + } + + fn remove(&mut self, s: Symbol) { + self.cells.remove(&s); + } + + fn reverse_sign(&mut self) { + self.constant = -self.constant; + for (_, v) in &mut self.cells { + *v = -*v; + } + } + + fn solve_for_symbol(&mut self, s: Symbol) { + let coeff = -1.0 / match self.cells.entry(s) { + Entry::Occupied(entry) => entry.remove(), + Entry::Vacant(_) => unreachable!() + }; + self.constant *= coeff; + for (_, v) in &mut self.cells { + *v *= coeff; + } + } + + fn solve_for_symbols(&mut self, lhs: Symbol, rhs: Symbol) { + self.insert_symbol(lhs, -1.0); + self.solve_for_symbol(rhs); + } + + fn coefficient_for(&self, s: Symbol) -> f64 { + self.cells.get(&s).cloned().unwrap_or(0.0) + } + + fn substitute(&mut self, s: Symbol, row: &Row) -> bool { + if let Some(coeff) = self.cells.remove(&s) { + self.insert_row(row, coeff) + } else { + false + } + } +} + +/// The possible error conditions that `Solver::add_constraint` can fail with. +#[derive(Debug, Copy, Clone)] +pub enum AddConstraintError { + /// The constraint specified has already been added to the solver. + DuplicateConstraint, + /// The constraint is required, but it is unsatisfiable in conjunction with the existing constraints. + UnsatisfiableConstraint, + /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies + /// additional details as a string. + InternalSolverError(&'static str) +} + +/// The possible error conditions that `Solver::remove_constraint` can fail with. +#[derive(Debug, Copy, Clone)] +pub enum RemoveConstraintError { + /// The constraint specified was not already in the solver, so cannot be removed. + UnknownConstraint, + /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies + /// additional details as a string. + InternalSolverError(&'static str) +} + +/// The possible error conditions that `Solver::add_edit_variable` can fail with. +#[derive(Debug, Copy, Clone)] +pub enum AddEditVariableError { + /// The specified variable is already marked as an edit variable in the solver. + DuplicateEditVariable, + /// The specified strength was `REQUIRED`. This is illegal for edit variable strengths. + BadRequiredStrength +} + +/// The possible error conditions that `Solver::remove_edit_variable` can fail with. +#[derive(Debug, Copy, Clone)] +pub enum RemoveEditVariableError { + /// The specified variable was not an edit variable in the solver, so cannot be removed. + UnknownEditVariable, + /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies + /// additional details as a string. + InternalSolverError(&'static str) +} + +/// The possible error conditions that `Solver::suggest_value` can fail with. +#[derive(Debug, Copy, Clone)] +pub enum SuggestValueError { + /// The specified variable was not an edit variable in the solver, so cannot have its value suggested. + UnknownEditVariable, + /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies + /// additional details as a string. + InternalSolverError(&'static str) +} + +#[derive(Debug, Copy, Clone)] +struct InternalSolverError(&'static str); + +pub use solver_impl::Solver; diff --git a/vendor/cassowary/src/operators.rs b/vendor/cassowary/src/operators.rs index 178ac1b..229128d 100644 --- a/vendor/cassowary/src/operators.rs +++ b/vendor/cassowary/src/operators.rs @@ -1,561 +1,561 @@ -use ::std::ops; -use { - Term, - Variable, - Expression, - WeightedRelation, - PartialConstraint, - Constraint -}; - -// Relation - -impl ops::BitOr for f64 { - type Output = PartialConstraint; - fn bitor(self, r: WeightedRelation) -> PartialConstraint { - PartialConstraint(self.into(), r) - } -} -impl ops::BitOr for f32 { - type Output = PartialConstraint; - fn bitor(self, r: WeightedRelation) -> PartialConstraint { - (self as f64).bitor(r) - } -} -impl ops::BitOr for Variable { - type Output = PartialConstraint; - fn bitor(self, r: WeightedRelation) -> PartialConstraint { - PartialConstraint(self.into(), r) - } -} -impl ops::BitOr for Term { - type Output = PartialConstraint; - fn bitor(self, r: WeightedRelation) -> PartialConstraint { - PartialConstraint(self.into(), r) - } -} -impl ops::BitOr for Expression { - type Output = PartialConstraint; - fn bitor(self, r: WeightedRelation) -> PartialConstraint { - PartialConstraint(self.into(), r) - } -} - -impl ops::BitOr for PartialConstraint { - type Output = Constraint; - fn bitor(self, rhs: f64) -> Constraint { - let (op, s) = self.1.into(); - Constraint::new(self.0 - rhs, op, s) - } -} -impl ops::BitOr for PartialConstraint { - type Output = Constraint; - fn bitor(self, rhs: f32) -> Constraint { - self.bitor(rhs as f64) - } -} -impl ops::BitOr for PartialConstraint { - type Output = Constraint; - fn bitor(self, rhs: Variable) -> Constraint { - let (op, s) = self.1.into(); - Constraint::new(self.0 - rhs, op, s) - } -} -impl ops::BitOr for PartialConstraint { - type Output = Constraint; - fn bitor(self, rhs: Term) -> Constraint { - let (op, s) = self.1.into(); - Constraint::new(self.0 - rhs, op, s) - } -} -impl ops::BitOr for PartialConstraint { - type Output = Constraint; - fn bitor(self, rhs: Expression) -> Constraint { - let (op, s) = self.1.into(); - Constraint::new(self.0 - rhs, op, s) - } -} - -// Variable - -impl ops::Add for Variable { - type Output = Expression; - fn add(self, v: f64) -> Expression { - Expression::new(vec![Term::new(self, 1.0)], v) - } -} - -impl ops::Add for Variable { - type Output = Expression; - fn add(self, v: f32) -> Expression { - self.add(v as f64) - } -} - -impl ops::Add for f64 { - type Output = Expression; - fn add(self, v: Variable) -> Expression { - Expression::new(vec![Term::new(v, 1.0)], self) - } -} - -impl ops::Add for f32 { - type Output = Expression; - fn add(self, v: Variable) -> Expression { - (self as f64).add(v) - } -} - -impl ops::Add for Variable { - type Output = Expression; - fn add(self, v: Variable) -> Expression { - Expression::new(vec![Term::new(self, 1.0), Term::new(v, 1.0)], 0.0) - } -} - -impl ops::Add for Variable { - type Output = Expression; - fn add(self, t: Term) -> Expression { - Expression::new(vec![Term::new(self, 1.0), t], 0.0) - } -} - -impl ops::Add for Term { - type Output = Expression; - fn add(self, v: Variable) -> Expression { - Expression::new(vec![self, Term::new(v, 1.0)], 0.0) - } -} - -impl ops::Add for Variable { - type Output = Expression; - fn add(self, mut e: Expression) -> Expression { - e.terms.push(Term::new(self, 1.0)); - e - } -} - -impl ops::Add for Expression { - type Output = Expression; - fn add(mut self, v: Variable) -> Expression { - self.terms.push(Term::new(v, 1.0)); - self - } -} - -impl ops::Neg for Variable { - type Output = Term; - fn neg(self) -> Term { - Term::new(self, -1.0) - } -} - -impl ops::Sub for Variable { - type Output = Expression; - fn sub(self, v: f64) -> Expression { - Expression::new(vec![Term::new(self, 1.0)], -v) - } -} - -impl ops::Sub for Variable { - type Output = Expression; - fn sub(self, v: f32) -> Expression { - self.sub(v as f64) - } -} - -impl ops::Sub for f64 { - type Output = Expression; - fn sub(self, v: Variable) -> Expression { - Expression::new(vec![Term::new(v, -1.0)], self) - } -} - -impl ops::Sub for f32 { - type Output = Expression; - fn sub(self, v: Variable) -> Expression { - (self as f64).sub(v) - } -} - -impl ops::Sub for Variable { - type Output = Expression; - fn sub(self, v: Variable) -> Expression { - Expression::new(vec![Term::new(self, 1.0), Term::new(v, -1.0)], 0.0) - } -} - -impl ops::Sub for Variable { - type Output = Expression; - fn sub(self, t: Term) -> Expression { - Expression::new(vec![Term::new(self, 1.0), -t], 0.0) - } -} - -impl ops::Sub for Term { - type Output = Expression; - fn sub(self, v: Variable) -> Expression { - Expression::new(vec![self, Term::new(v, -1.0)], 0.0) - } -} - -impl ops::Sub for Variable { - type Output = Expression; - fn sub(self, mut e: Expression) -> Expression { - e.negate(); - e.terms.push(Term::new(self, 1.0)); - e - } -} - -impl ops::Sub for Expression { - type Output = Expression; - fn sub(mut self, v: Variable) -> Expression { - self.terms.push(Term::new(v, -1.0)); - self - } -} - -impl ops::Mul for Variable { - type Output = Term; - fn mul(self, v: f64) -> Term { - Term::new(self, v) - } -} - -impl ops::Mul for Variable { - type Output = Term; - fn mul(self, v: f32) -> Term { - self.mul(v as f64) - } -} - -impl ops::Mul for f64 { - type Output = Term; - fn mul(self, v: Variable) -> Term { - Term::new(v, self) - } -} - -impl ops::Mul for f32 { - type Output = Term; - fn mul(self, v: Variable) -> Term { - (self as f64).mul(v) - } -} - -impl ops::Div for Variable { - type Output = Term; - fn div(self, v: f64) -> Term { - Term::new(self, 1.0 / v) - } -} - -impl ops::Div for Variable { - type Output = Term; - fn div(self, v: f32) -> Term { - self.div(v as f64) - } -} - -// Term - -impl ops::Mul for Term { - type Output = Term; - fn mul(mut self, v: f64) -> Term { - self.coefficient *= v; - self - } -} - -impl ops::Mul for Term { - type Output = Term; - fn mul(self, v: f32) -> Term { - self.mul(v as f64) - } -} - -impl ops::Mul for f64 { - type Output = Term; - fn mul(self, mut t: Term) -> Term { - t.coefficient *= self; - t - } -} - -impl ops::Mul for f32 { - type Output = Term; - fn mul(self, t: Term) -> Term { - (self as f64).mul(t) - } -} - -impl ops::Div for Term { - type Output = Term; - fn div(mut self, v: f64) -> Term { - self.coefficient /= v; - self - } -} - -impl ops::Div for Term { - type Output = Term; - fn div(self, v: f32) -> Term { - self.div(v as f64) - } -} - -impl ops::Add for Term { - type Output = Expression; - fn add(self, v: f64) -> Expression { - Expression::new(vec![self], v) - } -} - -impl ops::Add for Term { - type Output = Expression; - fn add(self, v: f32) -> Expression { - self.add(v as f64) - } -} - -impl ops::Add for f64 { - type Output = Expression; - fn add(self, t: Term) -> Expression { - Expression::new(vec![t], self) - } -} - -impl ops::Add for f32 { - type Output = Expression; - fn add(self, t: Term) -> Expression { - (self as f64).add(t) - } -} - -impl ops::Add for Term { - type Output = Expression; - fn add(self, t: Term) -> Expression { - Expression::new(vec![self, t], 0.0) - } -} - -impl ops::Add for Term { - type Output = Expression; - fn add(self, mut e: Expression) -> Expression { - e.terms.push(self); - e - } -} - -impl ops::Add for Expression { - type Output = Expression; - fn add(mut self, t: Term) -> Expression { - self.terms.push(t); - self - } -} - -impl ops::Neg for Term { - type Output = Term; - fn neg(mut self) -> Term { - self.coefficient = -self.coefficient; - self - } -} - -impl ops::Sub for Term { - type Output = Expression; - fn sub(self, v: f64) -> Expression { - Expression::new(vec![self], -v) - } -} - -impl ops::Sub for Term { - type Output = Expression; - fn sub(self, v: f32) -> Expression { - self.sub(v as f64) - } -} - -impl ops::Sub for f64 { - type Output = Expression; - fn sub(self, t: Term) -> Expression { - Expression::new(vec![-t], self) - } -} - -impl ops::Sub for f32 { - type Output = Expression; - fn sub(self, t: Term) -> Expression { - (self as f64).sub(t) - } -} - -impl ops::Sub for Term { - type Output = Expression; - fn sub(self, t: Term) -> Expression { - Expression::new(vec![self, -t], 0.0) - } -} - -impl ops::Sub for Term { - type Output = Expression; - fn sub(self, mut e: Expression) -> Expression { - e.negate(); - e.terms.push(self); - e - } -} - -impl ops::Sub for Expression { - type Output = Expression; - fn sub(mut self, t: Term) -> Expression { - self.terms.push(-t); - self - } -} - -// Expression - -impl ops::Mul for Expression { - type Output = Expression; - fn mul(mut self, v: f64) -> Expression { - self.constant *= v; - for t in &mut self.terms { - *t = *t * v; - } - self - } -} - -impl ops::Mul for Expression { - type Output = Expression; - fn mul(self, v: f32) -> Expression { - self.mul(v as f64) - } -} - -impl ops::Mul for f64 { - type Output = Expression; - fn mul(self, mut e: Expression) -> Expression { - e.constant *= self; - for t in &mut e.terms { - *t = *t * self; - } - e - } -} - -impl ops::Mul for f32 { - type Output = Expression; - fn mul(self, e: Expression) -> Expression { - (self as f64).mul(e) - } -} - -impl ops::Div for Expression { - type Output = Expression; - fn div(mut self, v: f64) -> Expression { - self.constant /= v; - for t in &mut self.terms { - *t = *t / v; - } - self - } -} - -impl ops::Div for Expression { - type Output = Expression; - fn div(self, v: f32) -> Expression { - self.div(v as f64) - } -} - -impl ops::Add for Expression { - type Output = Expression; - fn add(mut self, v: f64) -> Expression { - self.constant += v; - self - } -} - -impl ops::Add for Expression { - type Output = Expression; - fn add(self, v: f32) -> Expression { - self.add(v as f64) - } -} - -impl ops::Add for f64 { - type Output = Expression; - fn add(self, mut e: Expression) -> Expression { - e.constant += self; - e - } -} - -impl ops::Add for f32 { - type Output = Expression; - fn add(self, e: Expression) -> Expression { - (self as f64).add(e) - } -} - -impl ops::Add for Expression { - type Output = Expression; - fn add(mut self, mut e: Expression) -> Expression { - self.terms.append(&mut e.terms); - self.constant += e.constant; - self - } -} - -impl ops::Neg for Expression { - type Output = Expression; - fn neg(mut self) -> Expression { - self.negate(); - self - } -} - -impl ops::Sub for Expression { - type Output = Expression; - fn sub(mut self, v: f64) -> Expression { - self.constant -= v; - self - } -} - -impl ops::Sub for Expression { - type Output = Expression; - fn sub(self, v: f32) -> Expression { - self.sub(v as f64) - } -} - -impl ops::Sub for f64 { - type Output = Expression; - fn sub(self, mut e: Expression) -> Expression { - e.negate(); - e.constant += self; - e - } -} - -impl ops::Sub for f32 { - type Output = Expression; - fn sub(self, e: Expression) -> Expression { - (self as f64).sub(e) - } -} - -impl ops::Sub for Expression { - type Output = Expression; - fn sub(mut self, mut e: Expression) -> Expression { - e.negate(); - self.terms.append(&mut e.terms); - self.constant += e.constant; - self - } -} +use ::std::ops; +use { + Term, + Variable, + Expression, + WeightedRelation, + PartialConstraint, + Constraint +}; + +// Relation + +impl ops::BitOr for f64 { + type Output = PartialConstraint; + fn bitor(self, r: WeightedRelation) -> PartialConstraint { + PartialConstraint(self.into(), r) + } +} +impl ops::BitOr for f32 { + type Output = PartialConstraint; + fn bitor(self, r: WeightedRelation) -> PartialConstraint { + (self as f64).bitor(r) + } +} +impl ops::BitOr for Variable { + type Output = PartialConstraint; + fn bitor(self, r: WeightedRelation) -> PartialConstraint { + PartialConstraint(self.into(), r) + } +} +impl ops::BitOr for Term { + type Output = PartialConstraint; + fn bitor(self, r: WeightedRelation) -> PartialConstraint { + PartialConstraint(self.into(), r) + } +} +impl ops::BitOr for Expression { + type Output = PartialConstraint; + fn bitor(self, r: WeightedRelation) -> PartialConstraint { + PartialConstraint(self.into(), r) + } +} + +impl ops::BitOr for PartialConstraint { + type Output = Constraint; + fn bitor(self, rhs: f64) -> Constraint { + let (op, s) = self.1.into(); + Constraint::new(self.0 - rhs, op, s) + } +} +impl ops::BitOr for PartialConstraint { + type Output = Constraint; + fn bitor(self, rhs: f32) -> Constraint { + self.bitor(rhs as f64) + } +} +impl ops::BitOr for PartialConstraint { + type Output = Constraint; + fn bitor(self, rhs: Variable) -> Constraint { + let (op, s) = self.1.into(); + Constraint::new(self.0 - rhs, op, s) + } +} +impl ops::BitOr for PartialConstraint { + type Output = Constraint; + fn bitor(self, rhs: Term) -> Constraint { + let (op, s) = self.1.into(); + Constraint::new(self.0 - rhs, op, s) + } +} +impl ops::BitOr for PartialConstraint { + type Output = Constraint; + fn bitor(self, rhs: Expression) -> Constraint { + let (op, s) = self.1.into(); + Constraint::new(self.0 - rhs, op, s) + } +} + +// Variable + +impl ops::Add for Variable { + type Output = Expression; + fn add(self, v: f64) -> Expression { + Expression::new(vec![Term::new(self, 1.0)], v) + } +} + +impl ops::Add for Variable { + type Output = Expression; + fn add(self, v: f32) -> Expression { + self.add(v as f64) + } +} + +impl ops::Add for f64 { + type Output = Expression; + fn add(self, v: Variable) -> Expression { + Expression::new(vec![Term::new(v, 1.0)], self) + } +} + +impl ops::Add for f32 { + type Output = Expression; + fn add(self, v: Variable) -> Expression { + (self as f64).add(v) + } +} + +impl ops::Add for Variable { + type Output = Expression; + fn add(self, v: Variable) -> Expression { + Expression::new(vec![Term::new(self, 1.0), Term::new(v, 1.0)], 0.0) + } +} + +impl ops::Add for Variable { + type Output = Expression; + fn add(self, t: Term) -> Expression { + Expression::new(vec![Term::new(self, 1.0), t], 0.0) + } +} + +impl ops::Add for Term { + type Output = Expression; + fn add(self, v: Variable) -> Expression { + Expression::new(vec![self, Term::new(v, 1.0)], 0.0) + } +} + +impl ops::Add for Variable { + type Output = Expression; + fn add(self, mut e: Expression) -> Expression { + e.terms.push(Term::new(self, 1.0)); + e + } +} + +impl ops::Add for Expression { + type Output = Expression; + fn add(mut self, v: Variable) -> Expression { + self.terms.push(Term::new(v, 1.0)); + self + } +} + +impl ops::Neg for Variable { + type Output = Term; + fn neg(self) -> Term { + Term::new(self, -1.0) + } +} + +impl ops::Sub for Variable { + type Output = Expression; + fn sub(self, v: f64) -> Expression { + Expression::new(vec![Term::new(self, 1.0)], -v) + } +} + +impl ops::Sub for Variable { + type Output = Expression; + fn sub(self, v: f32) -> Expression { + self.sub(v as f64) + } +} + +impl ops::Sub for f64 { + type Output = Expression; + fn sub(self, v: Variable) -> Expression { + Expression::new(vec![Term::new(v, -1.0)], self) + } +} + +impl ops::Sub for f32 { + type Output = Expression; + fn sub(self, v: Variable) -> Expression { + (self as f64).sub(v) + } +} + +impl ops::Sub for Variable { + type Output = Expression; + fn sub(self, v: Variable) -> Expression { + Expression::new(vec![Term::new(self, 1.0), Term::new(v, -1.0)], 0.0) + } +} + +impl ops::Sub for Variable { + type Output = Expression; + fn sub(self, t: Term) -> Expression { + Expression::new(vec![Term::new(self, 1.0), -t], 0.0) + } +} + +impl ops::Sub for Term { + type Output = Expression; + fn sub(self, v: Variable) -> Expression { + Expression::new(vec![self, Term::new(v, -1.0)], 0.0) + } +} + +impl ops::Sub for Variable { + type Output = Expression; + fn sub(self, mut e: Expression) -> Expression { + e.negate(); + e.terms.push(Term::new(self, 1.0)); + e + } +} + +impl ops::Sub for Expression { + type Output = Expression; + fn sub(mut self, v: Variable) -> Expression { + self.terms.push(Term::new(v, -1.0)); + self + } +} + +impl ops::Mul for Variable { + type Output = Term; + fn mul(self, v: f64) -> Term { + Term::new(self, v) + } +} + +impl ops::Mul for Variable { + type Output = Term; + fn mul(self, v: f32) -> Term { + self.mul(v as f64) + } +} + +impl ops::Mul for f64 { + type Output = Term; + fn mul(self, v: Variable) -> Term { + Term::new(v, self) + } +} + +impl ops::Mul for f32 { + type Output = Term; + fn mul(self, v: Variable) -> Term { + (self as f64).mul(v) + } +} + +impl ops::Div for Variable { + type Output = Term; + fn div(self, v: f64) -> Term { + Term::new(self, 1.0 / v) + } +} + +impl ops::Div for Variable { + type Output = Term; + fn div(self, v: f32) -> Term { + self.div(v as f64) + } +} + +// Term + +impl ops::Mul for Term { + type Output = Term; + fn mul(mut self, v: f64) -> Term { + self.coefficient *= v; + self + } +} + +impl ops::Mul for Term { + type Output = Term; + fn mul(self, v: f32) -> Term { + self.mul(v as f64) + } +} + +impl ops::Mul for f64 { + type Output = Term; + fn mul(self, mut t: Term) -> Term { + t.coefficient *= self; + t + } +} + +impl ops::Mul for f32 { + type Output = Term; + fn mul(self, t: Term) -> Term { + (self as f64).mul(t) + } +} + +impl ops::Div for Term { + type Output = Term; + fn div(mut self, v: f64) -> Term { + self.coefficient /= v; + self + } +} + +impl ops::Div for Term { + type Output = Term; + fn div(self, v: f32) -> Term { + self.div(v as f64) + } +} + +impl ops::Add for Term { + type Output = Expression; + fn add(self, v: f64) -> Expression { + Expression::new(vec![self], v) + } +} + +impl ops::Add for Term { + type Output = Expression; + fn add(self, v: f32) -> Expression { + self.add(v as f64) + } +} + +impl ops::Add for f64 { + type Output = Expression; + fn add(self, t: Term) -> Expression { + Expression::new(vec![t], self) + } +} + +impl ops::Add for f32 { + type Output = Expression; + fn add(self, t: Term) -> Expression { + (self as f64).add(t) + } +} + +impl ops::Add for Term { + type Output = Expression; + fn add(self, t: Term) -> Expression { + Expression::new(vec![self, t], 0.0) + } +} + +impl ops::Add for Term { + type Output = Expression; + fn add(self, mut e: Expression) -> Expression { + e.terms.push(self); + e + } +} + +impl ops::Add for Expression { + type Output = Expression; + fn add(mut self, t: Term) -> Expression { + self.terms.push(t); + self + } +} + +impl ops::Neg for Term { + type Output = Term; + fn neg(mut self) -> Term { + self.coefficient = -self.coefficient; + self + } +} + +impl ops::Sub for Term { + type Output = Expression; + fn sub(self, v: f64) -> Expression { + Expression::new(vec![self], -v) + } +} + +impl ops::Sub for Term { + type Output = Expression; + fn sub(self, v: f32) -> Expression { + self.sub(v as f64) + } +} + +impl ops::Sub for f64 { + type Output = Expression; + fn sub(self, t: Term) -> Expression { + Expression::new(vec![-t], self) + } +} + +impl ops::Sub for f32 { + type Output = Expression; + fn sub(self, t: Term) -> Expression { + (self as f64).sub(t) + } +} + +impl ops::Sub for Term { + type Output = Expression; + fn sub(self, t: Term) -> Expression { + Expression::new(vec![self, -t], 0.0) + } +} + +impl ops::Sub for Term { + type Output = Expression; + fn sub(self, mut e: Expression) -> Expression { + e.negate(); + e.terms.push(self); + e + } +} + +impl ops::Sub for Expression { + type Output = Expression; + fn sub(mut self, t: Term) -> Expression { + self.terms.push(-t); + self + } +} + +// Expression + +impl ops::Mul for Expression { + type Output = Expression; + fn mul(mut self, v: f64) -> Expression { + self.constant *= v; + for t in &mut self.terms { + *t = *t * v; + } + self + } +} + +impl ops::Mul for Expression { + type Output = Expression; + fn mul(self, v: f32) -> Expression { + self.mul(v as f64) + } +} + +impl ops::Mul for f64 { + type Output = Expression; + fn mul(self, mut e: Expression) -> Expression { + e.constant *= self; + for t in &mut e.terms { + *t = *t * self; + } + e + } +} + +impl ops::Mul for f32 { + type Output = Expression; + fn mul(self, e: Expression) -> Expression { + (self as f64).mul(e) + } +} + +impl ops::Div for Expression { + type Output = Expression; + fn div(mut self, v: f64) -> Expression { + self.constant /= v; + for t in &mut self.terms { + *t = *t / v; + } + self + } +} + +impl ops::Div for Expression { + type Output = Expression; + fn div(self, v: f32) -> Expression { + self.div(v as f64) + } +} + +impl ops::Add for Expression { + type Output = Expression; + fn add(mut self, v: f64) -> Expression { + self.constant += v; + self + } +} + +impl ops::Add for Expression { + type Output = Expression; + fn add(self, v: f32) -> Expression { + self.add(v as f64) + } +} + +impl ops::Add for f64 { + type Output = Expression; + fn add(self, mut e: Expression) -> Expression { + e.constant += self; + e + } +} + +impl ops::Add for f32 { + type Output = Expression; + fn add(self, e: Expression) -> Expression { + (self as f64).add(e) + } +} + +impl ops::Add for Expression { + type Output = Expression; + fn add(mut self, mut e: Expression) -> Expression { + self.terms.append(&mut e.terms); + self.constant += e.constant; + self + } +} + +impl ops::Neg for Expression { + type Output = Expression; + fn neg(mut self) -> Expression { + self.negate(); + self + } +} + +impl ops::Sub for Expression { + type Output = Expression; + fn sub(mut self, v: f64) -> Expression { + self.constant -= v; + self + } +} + +impl ops::Sub for Expression { + type Output = Expression; + fn sub(self, v: f32) -> Expression { + self.sub(v as f64) + } +} + +impl ops::Sub for f64 { + type Output = Expression; + fn sub(self, mut e: Expression) -> Expression { + e.negate(); + e.constant += self; + e + } +} + +impl ops::Sub for f32 { + type Output = Expression; + fn sub(self, e: Expression) -> Expression { + (self as f64).sub(e) + } +} + +impl ops::Sub for Expression { + type Output = Expression; + fn sub(mut self, mut e: Expression) -> Expression { + e.negate(); + self.terms.append(&mut e.terms); + self.constant += e.constant; + self + } +} diff --git a/vendor/cassowary/src/solver_impl.rs b/vendor/cassowary/src/solver_impl.rs index 7c5948e..518d46a 100644 --- a/vendor/cassowary/src/solver_impl.rs +++ b/vendor/cassowary/src/solver_impl.rs @@ -1,784 +1,784 @@ -use { - Symbol, - SymbolType, - Constraint, - Variable, - Expression, - Term, - Row, - AddConstraintError, - RemoveConstraintError, - InternalSolverError, - SuggestValueError, - AddEditVariableError, - RemoveEditVariableError, - RelationalOperator, - near_zero -}; - -use ::std::rc::Rc; -use ::std::cell::RefCell; -use ::std::collections::{ HashMap, HashSet }; -use ::std::collections::hash_map::Entry; - -#[derive(Copy, Clone)] -struct Tag { - marker: Symbol, - other: Symbol -} - -#[derive(Clone)] -struct EditInfo { - tag: Tag, - constraint: Constraint, - constant: f64 -} - -/// A constraint solver using the Cassowary algorithm. For proper usage please see the top level crate documentation. -pub struct Solver { - cns: HashMap, - var_data: HashMap, - var_for_symbol: HashMap, - public_changes: Vec<(Variable, f64)>, - changed: HashSet, - should_clear_changes: bool, - rows: HashMap>, - edits: HashMap, - infeasible_rows: Vec, // never contains external symbols - objective: Rc>, - artificial: Option>>, - id_tick: usize -} - -impl Solver { - /// Construct a new solver. - pub fn new() -> Solver { - Solver { - cns: HashMap::new(), - var_data: HashMap::new(), - var_for_symbol: HashMap::new(), - public_changes: Vec::new(), - changed: HashSet::new(), - should_clear_changes: false, - rows: HashMap::new(), - edits: HashMap::new(), - infeasible_rows: Vec::new(), - objective: Rc::new(RefCell::new(Row::new(0.0))), - artificial: None, - id_tick: 1 - } - } - - pub fn add_constraints<'a, I: IntoIterator>( - &mut self, - constraints: I) -> Result<(), AddConstraintError> - { - for constraint in constraints { - try!(self.add_constraint(constraint.clone())); - } - Ok(()) - } - - /// Add a constraint to the solver. - pub fn add_constraint(&mut self, constraint: Constraint) -> Result<(), AddConstraintError> { - if self.cns.contains_key(&constraint) { - return Err(AddConstraintError::DuplicateConstraint); - } - - // Creating a row causes symbols to reserved for the variables - // in the constraint. If this method exits with an exception, - // then its possible those variables will linger in the var map. - // Since its likely that those variables will be used in other - // constraints and since exceptional conditions are uncommon, - // i'm not too worried about aggressive cleanup of the var map. - let (mut row, tag) = self.create_row(&constraint); - let mut subject = Solver::choose_subject(&row, &tag); - - // If chooseSubject could find a valid entering symbol, one - // last option is available if the entire row is composed of - // dummy variables. If the constant of the row is zero, then - // this represents redundant constraints and the new dummy - // marker can enter the basis. If the constant is non-zero, - // then it represents an unsatisfiable constraint. - if subject.type_() == SymbolType::Invalid && Solver::all_dummies(&row) { - if !near_zero(row.constant) { - return Err(AddConstraintError::UnsatisfiableConstraint); - } else { - subject = tag.marker; - } - } - - // If an entering symbol still isn't found, then the row must - // be added using an artificial variable. If that fails, then - // the row represents an unsatisfiable constraint. - if subject.type_() == SymbolType::Invalid { - if !try!(self.add_with_artificial_variable(&row) - .map_err(|e| AddConstraintError::InternalSolverError(e.0))) { - return Err(AddConstraintError::UnsatisfiableConstraint); - } - } else { - row.solve_for_symbol(subject); - self.substitute(subject, &row); - if subject.type_() == SymbolType::External && row.constant != 0.0 { - let v = self.var_for_symbol[&subject]; - self.var_changed(v); - } - self.rows.insert(subject, row); - } - - self.cns.insert(constraint, tag); - - // Optimizing after each constraint is added performs less - // aggregate work due to a smaller average system size. It - // also ensures the solver remains in a consistent state. - let objective = self.objective.clone(); - try!(self.optimise(&objective).map_err(|e| AddConstraintError::InternalSolverError(e.0))); - Ok(()) - } - - /// Remove a constraint from the solver. - pub fn remove_constraint(&mut self, constraint: &Constraint) -> Result<(), RemoveConstraintError> { - let tag = try!(self.cns.remove(constraint).ok_or(RemoveConstraintError::UnknownConstraint)); - - // Remove the error effects from the objective function - // *before* pivoting, or substitutions into the objective - // will lead to incorrect solver results. - self.remove_constraint_effects(constraint, &tag); - - // If the marker is basic, simply drop the row. Otherwise, - // pivot the marker into the basis and then drop the row. - if let None = self.rows.remove(&tag.marker) { - let (leaving, mut row) = try!(self.get_marker_leaving_row(tag.marker) - .ok_or( - RemoveConstraintError::InternalSolverError( - "Failed to find leaving row."))); - row.solve_for_symbols(leaving, tag.marker); - self.substitute(tag.marker, &row); - } - - // Optimizing after each constraint is removed ensures that the - // solver remains consistent. It makes the solver api easier to - // use at a small tradeoff for speed. - let objective = self.objective.clone(); - try!(self.optimise(&objective).map_err(|e| RemoveConstraintError::InternalSolverError(e.0))); - - // Check for and decrease the reference count for variables referenced by the constraint - // If the reference count is zero remove the variable from the variable map - for term in &constraint.expr().terms { - if !near_zero(term.coefficient) { - let mut should_remove = false; - if let Some(&mut (_, _, ref mut count)) = self.var_data.get_mut(&term.variable) { - *count -= 1; - should_remove = *count == 0; - } - if should_remove { - self.var_for_symbol.remove(&self.var_data[&term.variable].1); - self.var_data.remove(&term.variable); - } - } - } - Ok(()) - } - - /// Test whether a constraint has been added to the solver. - pub fn has_constraint(&self, constraint: &Constraint) -> bool { - self.cns.contains_key(constraint) - } - - /// Add an edit variable to the solver. - /// - /// This method should be called before the `suggest_value` method is - /// used to supply a suggested value for the given edit variable. - pub fn add_edit_variable(&mut self, v: Variable, strength: f64) -> Result<(), AddEditVariableError> { - if self.edits.contains_key(&v) { - return Err(AddEditVariableError::DuplicateEditVariable); - } - let strength = ::strength::clip(strength); - if strength == ::strength::REQUIRED { - return Err(AddEditVariableError::BadRequiredStrength); - } - let cn = Constraint::new(Expression::from_term(Term::new(v.clone(), 1.0)), - RelationalOperator::Equal, - strength); - self.add_constraint(cn.clone()).unwrap(); - self.edits.insert(v.clone(), EditInfo { - tag: self.cns[&cn].clone(), - constraint: cn, - constant: 0.0 - }); - Ok(()) - } - - /// Remove an edit variable from the solver. - pub fn remove_edit_variable(&mut self, v: Variable) -> Result<(), RemoveEditVariableError> { - if let Some(constraint) = self.edits.remove(&v).map(|e| e.constraint) { - try!(self.remove_constraint(&constraint) - .map_err(|e| match e { - RemoveConstraintError::UnknownConstraint => - RemoveEditVariableError::InternalSolverError("Edit constraint not in system"), - RemoveConstraintError::InternalSolverError(s) => - RemoveEditVariableError::InternalSolverError(s) - })); - Ok(()) - } else { - Err(RemoveEditVariableError::UnknownEditVariable) - } - } - - /// Test whether an edit variable has been added to the solver. - pub fn has_edit_variable(&self, v: &Variable) -> bool { - self.edits.contains_key(v) - } - - /// Suggest a value for the given edit variable. - /// - /// This method should be used after an edit variable has been added to - /// the solver in order to suggest the value for that variable. - pub fn suggest_value(&mut self, variable: Variable, value: f64) -> Result<(), SuggestValueError> { - let (info_tag_marker, info_tag_other, delta) = { - let info = try!(self.edits.get_mut(&variable).ok_or(SuggestValueError::UnknownEditVariable)); - let delta = value - info.constant; - info.constant = value; - (info.tag.marker, info.tag.other, delta) - }; - // tag.marker and tag.other are never external symbols - - // The nice version of the following code runs into non-lexical borrow issues. - // Ideally the `if row...` code would be in the body of the if. Pretend that it is. - { - let infeasible_rows = &mut self.infeasible_rows; - if self.rows.get_mut(&info_tag_marker) - .map(|row| - if row.add(-delta) < 0.0 { - infeasible_rows.push(info_tag_marker); - }).is_some() - { - - } else if self.rows.get_mut(&info_tag_other) - .map(|row| - if row.add(delta) < 0.0 { - infeasible_rows.push(info_tag_other); - }).is_some() - { - - } else { - for (symbol, row) in &mut self.rows { - let coeff = row.coefficient_for(info_tag_marker); - let diff = delta * coeff; - if diff != 0.0 && symbol.type_() == SymbolType::External { - let v = self.var_for_symbol[symbol]; - // inline var_changed - borrow checker workaround - if self.should_clear_changes { - self.changed.clear(); - self.should_clear_changes = false; - } - self.changed.insert(v); - } - if coeff != 0.0 && - row.add(diff) < 0.0 && - symbol.type_() != SymbolType::External - { - infeasible_rows.push(*symbol); - } - } - } - } - try!(self.dual_optimise().map_err(|e| SuggestValueError::InternalSolverError(e.0))); - return Ok(()); - } - - fn var_changed(&mut self, v: Variable) { - if self.should_clear_changes { - self.changed.clear(); - self.should_clear_changes = false; - } - self.changed.insert(v); - } - - /// Fetches all changes to the values of variables since the last call to this function. - /// - /// The list of changes returned is not in a specific order. Each change comprises the variable changed and - /// the new value of that variable. - pub fn fetch_changes(&mut self) -> &[(Variable, f64)] { - if self.should_clear_changes { - self.changed.clear(); - self.should_clear_changes = false; - } else { - self.should_clear_changes = true; - } - self.public_changes.clear(); - for &v in &self.changed { - if let Some(var_data) = self.var_data.get_mut(&v) { - let new_value = self.rows.get(&var_data.1).map(|r| r.constant).unwrap_or(0.0); - let old_value = var_data.0; - if old_value != new_value { - self.public_changes.push((v, new_value)); - var_data.0 = new_value; - } - } - } - &self.public_changes - } - - /// Reset the solver to the empty starting condition. - /// - /// This method resets the internal solver state to the empty starting - /// condition, as if no constraints or edit variables have been added. - /// This can be faster than deleting the solver and creating a new one - /// when the entire system must change, since it can avoid unnecessary - /// heap (de)allocations. - pub fn reset(&mut self) { - self.rows.clear(); - self.cns.clear(); - self.var_data.clear(); - self.var_for_symbol.clear(); - self.changed.clear(); - self.should_clear_changes = false; - self.edits.clear(); - self.infeasible_rows.clear(); - *self.objective.borrow_mut() = Row::new(0.0); - self.artificial = None; - self.id_tick = 1; - } - - /// Get the symbol for the given variable. - /// - /// If a symbol does not exist for the variable, one will be created. - fn get_var_symbol(&mut self, v: Variable) -> Symbol { - let id_tick = &mut self.id_tick; - let var_for_symbol = &mut self.var_for_symbol; - let value = self.var_data.entry(v).or_insert_with(|| { - let s = Symbol(*id_tick, SymbolType::External); - var_for_symbol.insert(s, v); - *id_tick += 1; - (::std::f64::NAN, s, 0) - }); - value.2 += 1; - value.1 - } - - /// Create a new Row object for the given constraint. - /// - /// The terms in the constraint will be converted to cells in the row. - /// Any term in the constraint with a coefficient of zero is ignored. - /// This method uses the `getVarSymbol` method to get the symbol for - /// the variables added to the row. If the symbol for a given cell - /// variable is basic, the cell variable will be substituted with the - /// basic row. - /// - /// The necessary slack and error variables will be added to the row. - /// If the constant for the row is negative, the sign for the row - /// will be inverted so the constant becomes positive. - /// - /// The tag will be updated with the marker and error symbols to use - /// for tracking the movement of the constraint in the tableau. - fn create_row(&mut self, constraint: &Constraint) -> (Box, Tag) { - let expr = constraint.expr(); - let mut row = Row::new(expr.constant); - // Substitute the current basic variables into the row. - for term in &expr.terms { - if !near_zero(term.coefficient) { - let symbol = self.get_var_symbol(term.variable); - if let Some(other_row) = self.rows.get(&symbol) { - row.insert_row(other_row, term.coefficient); - } else { - row.insert_symbol(symbol, term.coefficient); - } - } - } - - let mut objective = self.objective.borrow_mut(); - - // Add the necessary slack, error, and dummy variables. - let tag = match constraint.op() { - RelationalOperator::GreaterOrEqual | - RelationalOperator::LessOrEqual => { - let coeff = if constraint.op() == RelationalOperator::LessOrEqual { - 1.0 - } else { - -1.0 - }; - let slack = Symbol(self.id_tick, SymbolType::Slack); - self.id_tick += 1; - row.insert_symbol(slack, coeff); - if constraint.strength() < ::strength::REQUIRED { - let error = Symbol(self.id_tick, SymbolType::Error); - self.id_tick += 1; - row.insert_symbol(error, -coeff); - objective.insert_symbol(error, constraint.strength()); - Tag { - marker: slack, - other: error - } - } else { - Tag { - marker: slack, - other: Symbol::invalid() - } - } - } - RelationalOperator::Equal => { - if constraint.strength() < ::strength::REQUIRED { - let errplus = Symbol(self.id_tick, SymbolType::Error); - self.id_tick += 1; - let errminus = Symbol(self.id_tick, SymbolType::Error); - self.id_tick += 1; - row.insert_symbol(errplus, -1.0); // v = eplus - eminus - row.insert_symbol(errminus, 1.0); // v - eplus + eminus = 0 - objective.insert_symbol(errplus, constraint.strength()); - objective.insert_symbol(errminus, constraint.strength()); - Tag { - marker: errplus, - other: errminus - } - } else { - let dummy = Symbol(self.id_tick, SymbolType::Dummy); - self.id_tick += 1; - row.insert_symbol(dummy, 1.0); - Tag { - marker: dummy, - other: Symbol::invalid() - } - } - } - }; - - // Ensure the row has a positive constant. - if row.constant < 0.0 { - row.reverse_sign(); - } - (Box::new(row), tag) - } - - /// Choose the subject for solving for the row. - /// - /// This method will choose the best subject for using as the solve - /// target for the row. An invalid symbol will be returned if there - /// is no valid target. - /// - /// The symbols are chosen according to the following precedence: - /// - /// 1) The first symbol representing an external variable. - /// 2) A negative slack or error tag variable. - /// - /// If a subject cannot be found, an invalid symbol will be returned. - fn choose_subject(row: &Row, tag: &Tag) -> Symbol { - for s in row.cells.keys() { - if s.type_() == SymbolType::External { - return *s - } - } - if tag.marker.type_() == SymbolType::Slack || tag.marker.type_() == SymbolType::Error { - if row.coefficient_for(tag.marker) < 0.0 { - return tag.marker; - } - } - if tag.other.type_() == SymbolType::Slack || tag.other.type_() == SymbolType::Error { - if row.coefficient_for(tag.other) < 0.0 { - return tag.other; - } - } - Symbol::invalid() - } - - /// Add the row to the tableau using an artificial variable. - /// - /// This will return false if the constraint cannot be satisfied. - fn add_with_artificial_variable(&mut self, row: &Row) -> Result { - // Create and add the artificial variable to the tableau - let art = Symbol(self.id_tick, SymbolType::Slack); - self.id_tick += 1; - self.rows.insert(art, Box::new(row.clone())); - self.artificial = Some(Rc::new(RefCell::new(row.clone()))); - - // Optimize the artificial objective. This is successful - // only if the artificial objective is optimized to zero. - let artificial = self.artificial.as_ref().unwrap().clone(); - try!(self.optimise(&artificial)); - let success = near_zero(artificial.borrow().constant); - self.artificial = None; - - // If the artificial variable is basic, pivot the row so that - // it becomes basic. If the row is constant, exit early. - if let Some(mut row) = self.rows.remove(&art) { - if row.cells.is_empty() { - return Ok(success); - } - let entering = Solver::any_pivotable_symbol(&row); // never External - if entering.type_() == SymbolType::Invalid { - return Ok(false); // unsatisfiable (will this ever happen?) - } - row.solve_for_symbols(art, entering); - self.substitute(entering, &row); - self.rows.insert(entering, row); - } - - // Remove the artificial row from the tableau - for (_, row) in &mut self.rows { - row.remove(art); - } - self.objective.borrow_mut().remove(art); - Ok(success) - } - - /// Substitute the parametric symbol with the given row. - /// - /// This method will substitute all instances of the parametric symbol - /// in the tableau and the objective function with the given row. - fn substitute(&mut self, symbol: Symbol, row: &Row) { - for (&other_symbol, other_row) in &mut self.rows { - let constant_changed = other_row.substitute(symbol, row); - if other_symbol.type_() == SymbolType::External && constant_changed { - let v = self.var_for_symbol[&other_symbol]; - // inline var_changed - if self.should_clear_changes { - self.changed.clear(); - self.should_clear_changes = false; - } - self.changed.insert(v); - } - if other_symbol.type_() != SymbolType::External && other_row.constant < 0.0 { - self.infeasible_rows.push(other_symbol); - } - } - self.objective.borrow_mut().substitute(symbol, row); - if let Some(artificial) = self.artificial.as_ref() { - artificial.borrow_mut().substitute(symbol, row); - } - } - - /// Optimize the system for the given objective function. - /// - /// This method performs iterations of Phase 2 of the simplex method - /// until the objective function reaches a minimum. - fn optimise(&mut self, objective: &RefCell) -> Result<(), InternalSolverError> { - loop { - let entering = Solver::get_entering_symbol(&objective.borrow()); - if entering.type_() == SymbolType::Invalid { - return Ok(()); - } - let (leaving, mut row) = try!(self.get_leaving_row(entering) - .ok_or(InternalSolverError("The objective is unbounded"))); - // pivot the entering symbol into the basis - row.solve_for_symbols(leaving, entering); - self.substitute(entering, &row); - if entering.type_() == SymbolType::External && row.constant != 0.0 { - let v = self.var_for_symbol[&entering]; - self.var_changed(v); - } - self.rows.insert(entering, row); - } - } - - /// Optimize the system using the dual of the simplex method. - /// - /// The current state of the system should be such that the objective - /// function is optimal, but not feasible. This method will perform - /// an iteration of the dual simplex method to make the solution both - /// optimal and feasible. - fn dual_optimise(&mut self) -> Result<(), InternalSolverError> { - while !self.infeasible_rows.is_empty() { - let leaving = self.infeasible_rows.pop().unwrap(); - - let row = if let Entry::Occupied(entry) = self.rows.entry(leaving) { - if entry.get().constant < 0.0 { - Some(entry.remove()) - } else { - None - } - } else { - None - }; - if let Some(mut row) = row { - let entering = self.get_dual_entering_symbol(&row); - if entering.type_() == SymbolType::Invalid { - return Err(InternalSolverError("Dual optimise failed.")); - } - // pivot the entering symbol into the basis - row.solve_for_symbols(leaving, entering); - self.substitute(entering, &row); - if entering.type_() == SymbolType::External && row.constant != 0.0 { - let v = self.var_for_symbol[&entering]; - self.var_changed(v); - } - self.rows.insert(entering, row); - } - } - Ok(()) - } - - /// Compute the entering variable for a pivot operation. - /// - /// This method will return first symbol in the objective function which - /// is non-dummy and has a coefficient less than zero. If no symbol meets - /// the criteria, it means the objective function is at a minimum, and an - /// invalid symbol is returned. - /// Could return an External symbol - fn get_entering_symbol(objective: &Row) -> Symbol { - for (symbol, value) in &objective.cells { - if symbol.type_() != SymbolType::Dummy && *value < 0.0 { - return *symbol; - } - } - Symbol::invalid() - } - - /// Compute the entering symbol for the dual optimize operation. - /// - /// This method will return the symbol in the row which has a positive - /// coefficient and yields the minimum ratio for its respective symbol - /// in the objective function. The provided row *must* be infeasible. - /// If no symbol is found which meats the criteria, an invalid symbol - /// is returned. - /// Could return an External symbol - fn get_dual_entering_symbol(&self, row: &Row) -> Symbol { - let mut entering = Symbol::invalid(); - let mut ratio = ::std::f64::INFINITY; - let objective = self.objective.borrow(); - for (symbol, value) in &row.cells { - if *value > 0.0 && symbol.type_() != SymbolType::Dummy { - let coeff = objective.coefficient_for(*symbol); - let r = coeff / *value; - if r < ratio { - ratio = r; - entering = *symbol; - } - } - } - entering - } - - /// Get the first Slack or Error symbol in the row. - /// - /// If no such symbol is present, and Invalid symbol will be returned. - /// Never returns an External symbol - fn any_pivotable_symbol(row: &Row) -> Symbol { - for symbol in row.cells.keys() { - if symbol.type_() == SymbolType::Slack || symbol.type_() == SymbolType::Error { - return *symbol; - } - } - Symbol::invalid() - } - - /// Compute the row which holds the exit symbol for a pivot. - /// - /// This method will return an iterator to the row in the row map - /// which holds the exit symbol. If no appropriate exit symbol is - /// found, the end() iterator will be returned. This indicates that - /// the objective function is unbounded. - /// Never returns a row for an External symbol - fn get_leaving_row(&mut self, entering: Symbol) -> Option<(Symbol, Box)> { - let mut ratio = ::std::f64::INFINITY; - let mut found = None; - for (symbol, row) in &self.rows { - if symbol.type_() != SymbolType::External { - let temp = row.coefficient_for(entering); - if temp < 0.0 { - let temp_ratio = -row.constant / temp; - if temp_ratio < ratio { - ratio = temp_ratio; - found = Some(*symbol); - } - } - } - } - found.map(|s| (s, self.rows.remove(&s).unwrap())) - } - - /// Compute the leaving row for a marker variable. - /// - /// This method will return an iterator to the row in the row map - /// which holds the given marker variable. The row will be chosen - /// according to the following precedence: - /// - /// 1) The row with a restricted basic varible and a negative coefficient - /// for the marker with the smallest ratio of -constant / coefficient. - /// - /// 2) The row with a restricted basic variable and the smallest ratio - /// of constant / coefficient. - /// - /// 3) The last unrestricted row which contains the marker. - /// - /// If the marker does not exist in any row, the row map end() iterator - /// will be returned. This indicates an internal solver error since - /// the marker *should* exist somewhere in the tableau. - fn get_marker_leaving_row(&mut self, marker: Symbol) -> Option<(Symbol, Box)> { - let mut r1 = ::std::f64::INFINITY; - let mut r2 = r1; - let mut first = None; - let mut second = None; - let mut third = None; - for (symbol, row) in &self.rows { - let c = row.coefficient_for(marker); - if c == 0.0 { - continue; - } - if symbol.type_() == SymbolType::External { - third = Some(*symbol); - } else if c < 0.0 { - let r = -row.constant / c; - if r < r1 { - r1 = r; - first = Some(*symbol); - } - } else { - let r = row.constant / c; - if r < r2 { - r2 = r; - second = Some(*symbol); - } - } - } - first - .or(second) - .or(third) - .and_then(|s| { - if s.type_() == SymbolType::External && self.rows[&s].constant != 0.0 { - let v = self.var_for_symbol[&s]; - self.var_changed(v); - } - self.rows - .remove(&s) - .map(|r| (s, r)) - }) - } - - /// Remove the effects of a constraint on the objective function. - fn remove_constraint_effects(&mut self, cn: &Constraint, tag: &Tag) { - if tag.marker.type_() == SymbolType::Error { - self.remove_marker_effects(tag.marker, cn.strength()); - } else if tag.other.type_() == SymbolType::Error { - self.remove_marker_effects(tag.other, cn.strength()); - } - } - - /// Remove the effects of an error marker on the objective function. - fn remove_marker_effects(&mut self, marker: Symbol, strength: f64) { - if let Some(row) = self.rows.get(&marker) { - self.objective.borrow_mut().insert_row(row, -strength); - } else { - self.objective.borrow_mut().insert_symbol(marker, -strength); - } - } - - /// Test whether a row is composed of all dummy variables. - fn all_dummies(row: &Row) -> bool { - for symbol in row.cells.keys() { - if symbol.type_() != SymbolType::Dummy { - return false; - } - } - true - } - - /// Get the stored value for a variable. - /// - /// Normally values should be retrieved and updated using `fetch_changes`, but - /// this method can be used for debugging or testing. - pub fn get_value(&self, v: Variable) -> f64 { - self.var_data.get(&v).and_then(|s| { - self.rows.get(&s.1).map(|r| r.constant) - }).unwrap_or(0.0) - } -} +use { + Symbol, + SymbolType, + Constraint, + Variable, + Expression, + Term, + Row, + AddConstraintError, + RemoveConstraintError, + InternalSolverError, + SuggestValueError, + AddEditVariableError, + RemoveEditVariableError, + RelationalOperator, + near_zero +}; + +use ::std::rc::Rc; +use ::std::cell::RefCell; +use ::std::collections::{ HashMap, HashSet }; +use ::std::collections::hash_map::Entry; + +#[derive(Copy, Clone)] +struct Tag { + marker: Symbol, + other: Symbol +} + +#[derive(Clone)] +struct EditInfo { + tag: Tag, + constraint: Constraint, + constant: f64 +} + +/// A constraint solver using the Cassowary algorithm. For proper usage please see the top level crate documentation. +pub struct Solver { + cns: HashMap, + var_data: HashMap, + var_for_symbol: HashMap, + public_changes: Vec<(Variable, f64)>, + changed: HashSet, + should_clear_changes: bool, + rows: HashMap>, + edits: HashMap, + infeasible_rows: Vec, // never contains external symbols + objective: Rc>, + artificial: Option>>, + id_tick: usize +} + +impl Solver { + /// Construct a new solver. + pub fn new() -> Solver { + Solver { + cns: HashMap::new(), + var_data: HashMap::new(), + var_for_symbol: HashMap::new(), + public_changes: Vec::new(), + changed: HashSet::new(), + should_clear_changes: false, + rows: HashMap::new(), + edits: HashMap::new(), + infeasible_rows: Vec::new(), + objective: Rc::new(RefCell::new(Row::new(0.0))), + artificial: None, + id_tick: 1 + } + } + + pub fn add_constraints<'a, I: IntoIterator>( + &mut self, + constraints: I) -> Result<(), AddConstraintError> + { + for constraint in constraints { + try!(self.add_constraint(constraint.clone())); + } + Ok(()) + } + + /// Add a constraint to the solver. + pub fn add_constraint(&mut self, constraint: Constraint) -> Result<(), AddConstraintError> { + if self.cns.contains_key(&constraint) { + return Err(AddConstraintError::DuplicateConstraint); + } + + // Creating a row causes symbols to reserved for the variables + // in the constraint. If this method exits with an exception, + // then its possible those variables will linger in the var map. + // Since its likely that those variables will be used in other + // constraints and since exceptional conditions are uncommon, + // i'm not too worried about aggressive cleanup of the var map. + let (mut row, tag) = self.create_row(&constraint); + let mut subject = Solver::choose_subject(&row, &tag); + + // If chooseSubject could find a valid entering symbol, one + // last option is available if the entire row is composed of + // dummy variables. If the constant of the row is zero, then + // this represents redundant constraints and the new dummy + // marker can enter the basis. If the constant is non-zero, + // then it represents an unsatisfiable constraint. + if subject.type_() == SymbolType::Invalid && Solver::all_dummies(&row) { + if !near_zero(row.constant) { + return Err(AddConstraintError::UnsatisfiableConstraint); + } else { + subject = tag.marker; + } + } + + // If an entering symbol still isn't found, then the row must + // be added using an artificial variable. If that fails, then + // the row represents an unsatisfiable constraint. + if subject.type_() == SymbolType::Invalid { + if !try!(self.add_with_artificial_variable(&row) + .map_err(|e| AddConstraintError::InternalSolverError(e.0))) { + return Err(AddConstraintError::UnsatisfiableConstraint); + } + } else { + row.solve_for_symbol(subject); + self.substitute(subject, &row); + if subject.type_() == SymbolType::External && row.constant != 0.0 { + let v = self.var_for_symbol[&subject]; + self.var_changed(v); + } + self.rows.insert(subject, row); + } + + self.cns.insert(constraint, tag); + + // Optimizing after each constraint is added performs less + // aggregate work due to a smaller average system size. It + // also ensures the solver remains in a consistent state. + let objective = self.objective.clone(); + try!(self.optimise(&objective).map_err(|e| AddConstraintError::InternalSolverError(e.0))); + Ok(()) + } + + /// Remove a constraint from the solver. + pub fn remove_constraint(&mut self, constraint: &Constraint) -> Result<(), RemoveConstraintError> { + let tag = try!(self.cns.remove(constraint).ok_or(RemoveConstraintError::UnknownConstraint)); + + // Remove the error effects from the objective function + // *before* pivoting, or substitutions into the objective + // will lead to incorrect solver results. + self.remove_constraint_effects(constraint, &tag); + + // If the marker is basic, simply drop the row. Otherwise, + // pivot the marker into the basis and then drop the row. + if let None = self.rows.remove(&tag.marker) { + let (leaving, mut row) = try!(self.get_marker_leaving_row(tag.marker) + .ok_or( + RemoveConstraintError::InternalSolverError( + "Failed to find leaving row."))); + row.solve_for_symbols(leaving, tag.marker); + self.substitute(tag.marker, &row); + } + + // Optimizing after each constraint is removed ensures that the + // solver remains consistent. It makes the solver api easier to + // use at a small tradeoff for speed. + let objective = self.objective.clone(); + try!(self.optimise(&objective).map_err(|e| RemoveConstraintError::InternalSolverError(e.0))); + + // Check for and decrease the reference count for variables referenced by the constraint + // If the reference count is zero remove the variable from the variable map + for term in &constraint.expr().terms { + if !near_zero(term.coefficient) { + let mut should_remove = false; + if let Some(&mut (_, _, ref mut count)) = self.var_data.get_mut(&term.variable) { + *count -= 1; + should_remove = *count == 0; + } + if should_remove { + self.var_for_symbol.remove(&self.var_data[&term.variable].1); + self.var_data.remove(&term.variable); + } + } + } + Ok(()) + } + + /// Test whether a constraint has been added to the solver. + pub fn has_constraint(&self, constraint: &Constraint) -> bool { + self.cns.contains_key(constraint) + } + + /// Add an edit variable to the solver. + /// + /// This method should be called before the `suggest_value` method is + /// used to supply a suggested value for the given edit variable. + pub fn add_edit_variable(&mut self, v: Variable, strength: f64) -> Result<(), AddEditVariableError> { + if self.edits.contains_key(&v) { + return Err(AddEditVariableError::DuplicateEditVariable); + } + let strength = ::strength::clip(strength); + if strength == ::strength::REQUIRED { + return Err(AddEditVariableError::BadRequiredStrength); + } + let cn = Constraint::new(Expression::from_term(Term::new(v.clone(), 1.0)), + RelationalOperator::Equal, + strength); + self.add_constraint(cn.clone()).unwrap(); + self.edits.insert(v.clone(), EditInfo { + tag: self.cns[&cn].clone(), + constraint: cn, + constant: 0.0 + }); + Ok(()) + } + + /// Remove an edit variable from the solver. + pub fn remove_edit_variable(&mut self, v: Variable) -> Result<(), RemoveEditVariableError> { + if let Some(constraint) = self.edits.remove(&v).map(|e| e.constraint) { + try!(self.remove_constraint(&constraint) + .map_err(|e| match e { + RemoveConstraintError::UnknownConstraint => + RemoveEditVariableError::InternalSolverError("Edit constraint not in system"), + RemoveConstraintError::InternalSolverError(s) => + RemoveEditVariableError::InternalSolverError(s) + })); + Ok(()) + } else { + Err(RemoveEditVariableError::UnknownEditVariable) + } + } + + /// Test whether an edit variable has been added to the solver. + pub fn has_edit_variable(&self, v: &Variable) -> bool { + self.edits.contains_key(v) + } + + /// Suggest a value for the given edit variable. + /// + /// This method should be used after an edit variable has been added to + /// the solver in order to suggest the value for that variable. + pub fn suggest_value(&mut self, variable: Variable, value: f64) -> Result<(), SuggestValueError> { + let (info_tag_marker, info_tag_other, delta) = { + let info = try!(self.edits.get_mut(&variable).ok_or(SuggestValueError::UnknownEditVariable)); + let delta = value - info.constant; + info.constant = value; + (info.tag.marker, info.tag.other, delta) + }; + // tag.marker and tag.other are never external symbols + + // The nice version of the following code runs into non-lexical borrow issues. + // Ideally the `if row...` code would be in the body of the if. Pretend that it is. + { + let infeasible_rows = &mut self.infeasible_rows; + if self.rows.get_mut(&info_tag_marker) + .map(|row| + if row.add(-delta) < 0.0 { + infeasible_rows.push(info_tag_marker); + }).is_some() + { + + } else if self.rows.get_mut(&info_tag_other) + .map(|row| + if row.add(delta) < 0.0 { + infeasible_rows.push(info_tag_other); + }).is_some() + { + + } else { + for (symbol, row) in &mut self.rows { + let coeff = row.coefficient_for(info_tag_marker); + let diff = delta * coeff; + if diff != 0.0 && symbol.type_() == SymbolType::External { + let v = self.var_for_symbol[symbol]; + // inline var_changed - borrow checker workaround + if self.should_clear_changes { + self.changed.clear(); + self.should_clear_changes = false; + } + self.changed.insert(v); + } + if coeff != 0.0 && + row.add(diff) < 0.0 && + symbol.type_() != SymbolType::External + { + infeasible_rows.push(*symbol); + } + } + } + } + try!(self.dual_optimise().map_err(|e| SuggestValueError::InternalSolverError(e.0))); + return Ok(()); + } + + fn var_changed(&mut self, v: Variable) { + if self.should_clear_changes { + self.changed.clear(); + self.should_clear_changes = false; + } + self.changed.insert(v); + } + + /// Fetches all changes to the values of variables since the last call to this function. + /// + /// The list of changes returned is not in a specific order. Each change comprises the variable changed and + /// the new value of that variable. + pub fn fetch_changes(&mut self) -> &[(Variable, f64)] { + if self.should_clear_changes { + self.changed.clear(); + self.should_clear_changes = false; + } else { + self.should_clear_changes = true; + } + self.public_changes.clear(); + for &v in &self.changed { + if let Some(var_data) = self.var_data.get_mut(&v) { + let new_value = self.rows.get(&var_data.1).map(|r| r.constant).unwrap_or(0.0); + let old_value = var_data.0; + if old_value != new_value { + self.public_changes.push((v, new_value)); + var_data.0 = new_value; + } + } + } + &self.public_changes + } + + /// Reset the solver to the empty starting condition. + /// + /// This method resets the internal solver state to the empty starting + /// condition, as if no constraints or edit variables have been added. + /// This can be faster than deleting the solver and creating a new one + /// when the entire system must change, since it can avoid unnecessary + /// heap (de)allocations. + pub fn reset(&mut self) { + self.rows.clear(); + self.cns.clear(); + self.var_data.clear(); + self.var_for_symbol.clear(); + self.changed.clear(); + self.should_clear_changes = false; + self.edits.clear(); + self.infeasible_rows.clear(); + *self.objective.borrow_mut() = Row::new(0.0); + self.artificial = None; + self.id_tick = 1; + } + + /// Get the symbol for the given variable. + /// + /// If a symbol does not exist for the variable, one will be created. + fn get_var_symbol(&mut self, v: Variable) -> Symbol { + let id_tick = &mut self.id_tick; + let var_for_symbol = &mut self.var_for_symbol; + let value = self.var_data.entry(v).or_insert_with(|| { + let s = Symbol(*id_tick, SymbolType::External); + var_for_symbol.insert(s, v); + *id_tick += 1; + (::std::f64::NAN, s, 0) + }); + value.2 += 1; + value.1 + } + + /// Create a new Row object for the given constraint. + /// + /// The terms in the constraint will be converted to cells in the row. + /// Any term in the constraint with a coefficient of zero is ignored. + /// This method uses the `getVarSymbol` method to get the symbol for + /// the variables added to the row. If the symbol for a given cell + /// variable is basic, the cell variable will be substituted with the + /// basic row. + /// + /// The necessary slack and error variables will be added to the row. + /// If the constant for the row is negative, the sign for the row + /// will be inverted so the constant becomes positive. + /// + /// The tag will be updated with the marker and error symbols to use + /// for tracking the movement of the constraint in the tableau. + fn create_row(&mut self, constraint: &Constraint) -> (Box, Tag) { + let expr = constraint.expr(); + let mut row = Row::new(expr.constant); + // Substitute the current basic variables into the row. + for term in &expr.terms { + if !near_zero(term.coefficient) { + let symbol = self.get_var_symbol(term.variable); + if let Some(other_row) = self.rows.get(&symbol) { + row.insert_row(other_row, term.coefficient); + } else { + row.insert_symbol(symbol, term.coefficient); + } + } + } + + let mut objective = self.objective.borrow_mut(); + + // Add the necessary slack, error, and dummy variables. + let tag = match constraint.op() { + RelationalOperator::GreaterOrEqual | + RelationalOperator::LessOrEqual => { + let coeff = if constraint.op() == RelationalOperator::LessOrEqual { + 1.0 + } else { + -1.0 + }; + let slack = Symbol(self.id_tick, SymbolType::Slack); + self.id_tick += 1; + row.insert_symbol(slack, coeff); + if constraint.strength() < ::strength::REQUIRED { + let error = Symbol(self.id_tick, SymbolType::Error); + self.id_tick += 1; + row.insert_symbol(error, -coeff); + objective.insert_symbol(error, constraint.strength()); + Tag { + marker: slack, + other: error + } + } else { + Tag { + marker: slack, + other: Symbol::invalid() + } + } + } + RelationalOperator::Equal => { + if constraint.strength() < ::strength::REQUIRED { + let errplus = Symbol(self.id_tick, SymbolType::Error); + self.id_tick += 1; + let errminus = Symbol(self.id_tick, SymbolType::Error); + self.id_tick += 1; + row.insert_symbol(errplus, -1.0); // v = eplus - eminus + row.insert_symbol(errminus, 1.0); // v - eplus + eminus = 0 + objective.insert_symbol(errplus, constraint.strength()); + objective.insert_symbol(errminus, constraint.strength()); + Tag { + marker: errplus, + other: errminus + } + } else { + let dummy = Symbol(self.id_tick, SymbolType::Dummy); + self.id_tick += 1; + row.insert_symbol(dummy, 1.0); + Tag { + marker: dummy, + other: Symbol::invalid() + } + } + } + }; + + // Ensure the row has a positive constant. + if row.constant < 0.0 { + row.reverse_sign(); + } + (Box::new(row), tag) + } + + /// Choose the subject for solving for the row. + /// + /// This method will choose the best subject for using as the solve + /// target for the row. An invalid symbol will be returned if there + /// is no valid target. + /// + /// The symbols are chosen according to the following precedence: + /// + /// 1) The first symbol representing an external variable. + /// 2) A negative slack or error tag variable. + /// + /// If a subject cannot be found, an invalid symbol will be returned. + fn choose_subject(row: &Row, tag: &Tag) -> Symbol { + for s in row.cells.keys() { + if s.type_() == SymbolType::External { + return *s + } + } + if tag.marker.type_() == SymbolType::Slack || tag.marker.type_() == SymbolType::Error { + if row.coefficient_for(tag.marker) < 0.0 { + return tag.marker; + } + } + if tag.other.type_() == SymbolType::Slack || tag.other.type_() == SymbolType::Error { + if row.coefficient_for(tag.other) < 0.0 { + return tag.other; + } + } + Symbol::invalid() + } + + /// Add the row to the tableau using an artificial variable. + /// + /// This will return false if the constraint cannot be satisfied. + fn add_with_artificial_variable(&mut self, row: &Row) -> Result { + // Create and add the artificial variable to the tableau + let art = Symbol(self.id_tick, SymbolType::Slack); + self.id_tick += 1; + self.rows.insert(art, Box::new(row.clone())); + self.artificial = Some(Rc::new(RefCell::new(row.clone()))); + + // Optimize the artificial objective. This is successful + // only if the artificial objective is optimized to zero. + let artificial = self.artificial.as_ref().unwrap().clone(); + try!(self.optimise(&artificial)); + let success = near_zero(artificial.borrow().constant); + self.artificial = None; + + // If the artificial variable is basic, pivot the row so that + // it becomes basic. If the row is constant, exit early. + if let Some(mut row) = self.rows.remove(&art) { + if row.cells.is_empty() { + return Ok(success); + } + let entering = Solver::any_pivotable_symbol(&row); // never External + if entering.type_() == SymbolType::Invalid { + return Ok(false); // unsatisfiable (will this ever happen?) + } + row.solve_for_symbols(art, entering); + self.substitute(entering, &row); + self.rows.insert(entering, row); + } + + // Remove the artificial row from the tableau + for (_, row) in &mut self.rows { + row.remove(art); + } + self.objective.borrow_mut().remove(art); + Ok(success) + } + + /// Substitute the parametric symbol with the given row. + /// + /// This method will substitute all instances of the parametric symbol + /// in the tableau and the objective function with the given row. + fn substitute(&mut self, symbol: Symbol, row: &Row) { + for (&other_symbol, other_row) in &mut self.rows { + let constant_changed = other_row.substitute(symbol, row); + if other_symbol.type_() == SymbolType::External && constant_changed { + let v = self.var_for_symbol[&other_symbol]; + // inline var_changed + if self.should_clear_changes { + self.changed.clear(); + self.should_clear_changes = false; + } + self.changed.insert(v); + } + if other_symbol.type_() != SymbolType::External && other_row.constant < 0.0 { + self.infeasible_rows.push(other_symbol); + } + } + self.objective.borrow_mut().substitute(symbol, row); + if let Some(artificial) = self.artificial.as_ref() { + artificial.borrow_mut().substitute(symbol, row); + } + } + + /// Optimize the system for the given objective function. + /// + /// This method performs iterations of Phase 2 of the simplex method + /// until the objective function reaches a minimum. + fn optimise(&mut self, objective: &RefCell) -> Result<(), InternalSolverError> { + loop { + let entering = Solver::get_entering_symbol(&objective.borrow()); + if entering.type_() == SymbolType::Invalid { + return Ok(()); + } + let (leaving, mut row) = try!(self.get_leaving_row(entering) + .ok_or(InternalSolverError("The objective is unbounded"))); + // pivot the entering symbol into the basis + row.solve_for_symbols(leaving, entering); + self.substitute(entering, &row); + if entering.type_() == SymbolType::External && row.constant != 0.0 { + let v = self.var_for_symbol[&entering]; + self.var_changed(v); + } + self.rows.insert(entering, row); + } + } + + /// Optimize the system using the dual of the simplex method. + /// + /// The current state of the system should be such that the objective + /// function is optimal, but not feasible. This method will perform + /// an iteration of the dual simplex method to make the solution both + /// optimal and feasible. + fn dual_optimise(&mut self) -> Result<(), InternalSolverError> { + while !self.infeasible_rows.is_empty() { + let leaving = self.infeasible_rows.pop().unwrap(); + + let row = if let Entry::Occupied(entry) = self.rows.entry(leaving) { + if entry.get().constant < 0.0 { + Some(entry.remove()) + } else { + None + } + } else { + None + }; + if let Some(mut row) = row { + let entering = self.get_dual_entering_symbol(&row); + if entering.type_() == SymbolType::Invalid { + return Err(InternalSolverError("Dual optimise failed.")); + } + // pivot the entering symbol into the basis + row.solve_for_symbols(leaving, entering); + self.substitute(entering, &row); + if entering.type_() == SymbolType::External && row.constant != 0.0 { + let v = self.var_for_symbol[&entering]; + self.var_changed(v); + } + self.rows.insert(entering, row); + } + } + Ok(()) + } + + /// Compute the entering variable for a pivot operation. + /// + /// This method will return first symbol in the objective function which + /// is non-dummy and has a coefficient less than zero. If no symbol meets + /// the criteria, it means the objective function is at a minimum, and an + /// invalid symbol is returned. + /// Could return an External symbol + fn get_entering_symbol(objective: &Row) -> Symbol { + for (symbol, value) in &objective.cells { + if symbol.type_() != SymbolType::Dummy && *value < 0.0 { + return *symbol; + } + } + Symbol::invalid() + } + + /// Compute the entering symbol for the dual optimize operation. + /// + /// This method will return the symbol in the row which has a positive + /// coefficient and yields the minimum ratio for its respective symbol + /// in the objective function. The provided row *must* be infeasible. + /// If no symbol is found which meats the criteria, an invalid symbol + /// is returned. + /// Could return an External symbol + fn get_dual_entering_symbol(&self, row: &Row) -> Symbol { + let mut entering = Symbol::invalid(); + let mut ratio = ::std::f64::INFINITY; + let objective = self.objective.borrow(); + for (symbol, value) in &row.cells { + if *value > 0.0 && symbol.type_() != SymbolType::Dummy { + let coeff = objective.coefficient_for(*symbol); + let r = coeff / *value; + if r < ratio { + ratio = r; + entering = *symbol; + } + } + } + entering + } + + /// Get the first Slack or Error symbol in the row. + /// + /// If no such symbol is present, and Invalid symbol will be returned. + /// Never returns an External symbol + fn any_pivotable_symbol(row: &Row) -> Symbol { + for symbol in row.cells.keys() { + if symbol.type_() == SymbolType::Slack || symbol.type_() == SymbolType::Error { + return *symbol; + } + } + Symbol::invalid() + } + + /// Compute the row which holds the exit symbol for a pivot. + /// + /// This method will return an iterator to the row in the row map + /// which holds the exit symbol. If no appropriate exit symbol is + /// found, the end() iterator will be returned. This indicates that + /// the objective function is unbounded. + /// Never returns a row for an External symbol + fn get_leaving_row(&mut self, entering: Symbol) -> Option<(Symbol, Box)> { + let mut ratio = ::std::f64::INFINITY; + let mut found = None; + for (symbol, row) in &self.rows { + if symbol.type_() != SymbolType::External { + let temp = row.coefficient_for(entering); + if temp < 0.0 { + let temp_ratio = -row.constant / temp; + if temp_ratio < ratio { + ratio = temp_ratio; + found = Some(*symbol); + } + } + } + } + found.map(|s| (s, self.rows.remove(&s).unwrap())) + } + + /// Compute the leaving row for a marker variable. + /// + /// This method will return an iterator to the row in the row map + /// which holds the given marker variable. The row will be chosen + /// according to the following precedence: + /// + /// 1) The row with a restricted basic varible and a negative coefficient + /// for the marker with the smallest ratio of -constant / coefficient. + /// + /// 2) The row with a restricted basic variable and the smallest ratio + /// of constant / coefficient. + /// + /// 3) The last unrestricted row which contains the marker. + /// + /// If the marker does not exist in any row, the row map end() iterator + /// will be returned. This indicates an internal solver error since + /// the marker *should* exist somewhere in the tableau. + fn get_marker_leaving_row(&mut self, marker: Symbol) -> Option<(Symbol, Box)> { + let mut r1 = ::std::f64::INFINITY; + let mut r2 = r1; + let mut first = None; + let mut second = None; + let mut third = None; + for (symbol, row) in &self.rows { + let c = row.coefficient_for(marker); + if c == 0.0 { + continue; + } + if symbol.type_() == SymbolType::External { + third = Some(*symbol); + } else if c < 0.0 { + let r = -row.constant / c; + if r < r1 { + r1 = r; + first = Some(*symbol); + } + } else { + let r = row.constant / c; + if r < r2 { + r2 = r; + second = Some(*symbol); + } + } + } + first + .or(second) + .or(third) + .and_then(|s| { + if s.type_() == SymbolType::External && self.rows[&s].constant != 0.0 { + let v = self.var_for_symbol[&s]; + self.var_changed(v); + } + self.rows + .remove(&s) + .map(|r| (s, r)) + }) + } + + /// Remove the effects of a constraint on the objective function. + fn remove_constraint_effects(&mut self, cn: &Constraint, tag: &Tag) { + if tag.marker.type_() == SymbolType::Error { + self.remove_marker_effects(tag.marker, cn.strength()); + } else if tag.other.type_() == SymbolType::Error { + self.remove_marker_effects(tag.other, cn.strength()); + } + } + + /// Remove the effects of an error marker on the objective function. + fn remove_marker_effects(&mut self, marker: Symbol, strength: f64) { + if let Some(row) = self.rows.get(&marker) { + self.objective.borrow_mut().insert_row(row, -strength); + } else { + self.objective.borrow_mut().insert_symbol(marker, -strength); + } + } + + /// Test whether a row is composed of all dummy variables. + fn all_dummies(row: &Row) -> bool { + for symbol in row.cells.keys() { + if symbol.type_() != SymbolType::Dummy { + return false; + } + } + true + } + + /// Get the stored value for a variable. + /// + /// Normally values should be retrieved and updated using `fetch_changes`, but + /// this method can be used for debugging or testing. + pub fn get_value(&self, v: Variable) -> f64 { + self.var_data.get(&v).and_then(|s| { + self.rows.get(&s.1).map(|r| r.constant) + }).unwrap_or(0.0) + } +} diff --git a/vendor/cassowary/tests/common/mod.rs b/vendor/cassowary/tests/common/mod.rs index c03016a..6c3db0a 100644 --- a/vendor/cassowary/tests/common/mod.rs +++ b/vendor/cassowary/tests/common/mod.rs @@ -1,35 +1,35 @@ -use std::collections::HashMap; -use std::cell::RefCell; -use std::rc::Rc; - -use cassowary::Variable; - -#[derive(Clone, Default)] -struct Values(Rc>>); - -impl Values { - fn value_of(&self, var: Variable) -> f64 { - *self.0.borrow().get(&var).unwrap_or(&0.0) - } - fn update_values(&self, changes: &[(Variable, f64)]) { - for &(ref var, ref value) in changes { - println!("{:?} changed to {:?}", var, value); - self.0.borrow_mut().insert(*var, *value); - } - } -} - -pub fn new_values() -> (Box f64>, Box) { - let values = Values(Rc::new(RefCell::new(HashMap::new()))); - let value_of = { - let values = values.clone(); - move |v| values.value_of(v) - }; - let update_values = { - let values = values.clone(); - move |changes: &[_]| { - values.update_values(changes); - } - }; - (Box::new(value_of), Box::new(update_values)) +use std::collections::HashMap; +use std::cell::RefCell; +use std::rc::Rc; + +use cassowary::Variable; + +#[derive(Clone, Default)] +struct Values(Rc>>); + +impl Values { + fn value_of(&self, var: Variable) -> f64 { + *self.0.borrow().get(&var).unwrap_or(&0.0) + } + fn update_values(&self, changes: &[(Variable, f64)]) { + for &(ref var, ref value) in changes { + println!("{:?} changed to {:?}", var, value); + self.0.borrow_mut().insert(*var, *value); + } + } +} + +pub fn new_values() -> (Box f64>, Box) { + let values = Values(Rc::new(RefCell::new(HashMap::new()))); + let value_of = { + let values = values.clone(); + move |v| values.value_of(v) + }; + let update_values = { + let values = values.clone(); + move |changes: &[_]| { + values.update_values(changes); + } + }; + (Box::new(value_of), Box::new(update_values)) } \ No newline at end of file diff --git a/vendor/cassowary/tests/quadrilateral.rs b/vendor/cassowary/tests/quadrilateral.rs index 5df91a4..db18de2 100644 --- a/vendor/cassowary/tests/quadrilateral.rs +++ b/vendor/cassowary/tests/quadrilateral.rs @@ -1,106 +1,106 @@ -extern crate cassowary; -use cassowary::{ Solver, Variable }; -use cassowary::WeightedRelation::*; - -mod common; -use common::new_values; - -#[test] -fn test_quadrilateral() { - use cassowary::strength::{WEAK, STRONG, REQUIRED}; - struct Point { - x: Variable, - y: Variable - } - impl Point { - fn new() -> Point { - Point { - x: Variable::new(), - y: Variable::new() - } - } - } - let (value_of, update_values) = new_values(); - - let points = [Point::new(), - Point::new(), - Point::new(), - Point::new()]; - let point_starts = [(10.0, 10.0), (10.0, 200.0), (200.0, 200.0), (200.0, 10.0)]; - let midpoints = [Point::new(), - Point::new(), - Point::new(), - Point::new()]; - let mut solver = Solver::new(); - let mut weight = 1.0; - let multiplier = 2.0; - for i in 0..4 { - solver.add_constraints(&[points[i].x |EQ(WEAK * weight)| point_starts[i].0, - points[i].y |EQ(WEAK * weight)| point_starts[i].1]) - .unwrap(); - weight *= multiplier; - } - - for (start, end) in vec![(0, 1), (1, 2), (2, 3), (3, 0)] { - solver.add_constraints(&[midpoints[start].x |EQ(REQUIRED)| (points[start].x + points[end].x) / 2.0, - midpoints[start].y |EQ(REQUIRED)| (points[start].y + points[end].y) / 2.0]) - .unwrap(); - } - - solver.add_constraints(&[points[0].x + 20.0 |LE(STRONG)| points[2].x, - points[0].x + 20.0 |LE(STRONG)| points[3].x, - - points[1].x + 20.0 |LE(STRONG)| points[2].x, - points[1].x + 20.0 |LE(STRONG)| points[3].x, - - points[0].y + 20.0 |LE(STRONG)| points[1].y, - points[0].y + 20.0 |LE(STRONG)| points[2].y, - - points[3].y + 20.0 |LE(STRONG)| points[1].y, - points[3].y + 20.0 |LE(STRONG)| points[2].y]) - .unwrap(); - - for point in &points { - solver.add_constraints(&[point.x |GE(REQUIRED)| 0.0, - point.y |GE(REQUIRED)| 0.0, - - point.x |LE(REQUIRED)| 500.0, - point.y |LE(REQUIRED)| 500.0]).unwrap() - } - - update_values(solver.fetch_changes()); - - assert_eq!([(value_of(midpoints[0].x), value_of(midpoints[0].y)), - (value_of(midpoints[1].x), value_of(midpoints[1].y)), - (value_of(midpoints[2].x), value_of(midpoints[2].y)), - (value_of(midpoints[3].x), value_of(midpoints[3].y))], - [(10.0, 105.0), - (105.0, 200.0), - (200.0, 105.0), - (105.0, 10.0)]); - - solver.add_edit_variable(points[2].x, STRONG).unwrap(); - solver.add_edit_variable(points[2].y, STRONG).unwrap(); - solver.suggest_value(points[2].x, 300.0).unwrap(); - solver.suggest_value(points[2].y, 400.0).unwrap(); - - update_values(solver.fetch_changes()); - - assert_eq!([(value_of(points[0].x), value_of(points[0].y)), - (value_of(points[1].x), value_of(points[1].y)), - (value_of(points[2].x), value_of(points[2].y)), - (value_of(points[3].x), value_of(points[3].y))], - [(10.0, 10.0), - (10.0, 200.0), - (300.0, 400.0), - (200.0, 10.0)]); - - assert_eq!([(value_of(midpoints[0].x), value_of(midpoints[0].y)), - (value_of(midpoints[1].x), value_of(midpoints[1].y)), - (value_of(midpoints[2].x), value_of(midpoints[2].y)), - (value_of(midpoints[3].x), value_of(midpoints[3].y))], - [(10.0, 105.0), - (155.0, 300.0), - (250.0, 205.0), - (105.0, 10.0)]); -} +extern crate cassowary; +use cassowary::{ Solver, Variable }; +use cassowary::WeightedRelation::*; + +mod common; +use common::new_values; + +#[test] +fn test_quadrilateral() { + use cassowary::strength::{WEAK, STRONG, REQUIRED}; + struct Point { + x: Variable, + y: Variable + } + impl Point { + fn new() -> Point { + Point { + x: Variable::new(), + y: Variable::new() + } + } + } + let (value_of, update_values) = new_values(); + + let points = [Point::new(), + Point::new(), + Point::new(), + Point::new()]; + let point_starts = [(10.0, 10.0), (10.0, 200.0), (200.0, 200.0), (200.0, 10.0)]; + let midpoints = [Point::new(), + Point::new(), + Point::new(), + Point::new()]; + let mut solver = Solver::new(); + let mut weight = 1.0; + let multiplier = 2.0; + for i in 0..4 { + solver.add_constraints(&[points[i].x |EQ(WEAK * weight)| point_starts[i].0, + points[i].y |EQ(WEAK * weight)| point_starts[i].1]) + .unwrap(); + weight *= multiplier; + } + + for (start, end) in vec![(0, 1), (1, 2), (2, 3), (3, 0)] { + solver.add_constraints(&[midpoints[start].x |EQ(REQUIRED)| (points[start].x + points[end].x) / 2.0, + midpoints[start].y |EQ(REQUIRED)| (points[start].y + points[end].y) / 2.0]) + .unwrap(); + } + + solver.add_constraints(&[points[0].x + 20.0 |LE(STRONG)| points[2].x, + points[0].x + 20.0 |LE(STRONG)| points[3].x, + + points[1].x + 20.0 |LE(STRONG)| points[2].x, + points[1].x + 20.0 |LE(STRONG)| points[3].x, + + points[0].y + 20.0 |LE(STRONG)| points[1].y, + points[0].y + 20.0 |LE(STRONG)| points[2].y, + + points[3].y + 20.0 |LE(STRONG)| points[1].y, + points[3].y + 20.0 |LE(STRONG)| points[2].y]) + .unwrap(); + + for point in &points { + solver.add_constraints(&[point.x |GE(REQUIRED)| 0.0, + point.y |GE(REQUIRED)| 0.0, + + point.x |LE(REQUIRED)| 500.0, + point.y |LE(REQUIRED)| 500.0]).unwrap() + } + + update_values(solver.fetch_changes()); + + assert_eq!([(value_of(midpoints[0].x), value_of(midpoints[0].y)), + (value_of(midpoints[1].x), value_of(midpoints[1].y)), + (value_of(midpoints[2].x), value_of(midpoints[2].y)), + (value_of(midpoints[3].x), value_of(midpoints[3].y))], + [(10.0, 105.0), + (105.0, 200.0), + (200.0, 105.0), + (105.0, 10.0)]); + + solver.add_edit_variable(points[2].x, STRONG).unwrap(); + solver.add_edit_variable(points[2].y, STRONG).unwrap(); + solver.suggest_value(points[2].x, 300.0).unwrap(); + solver.suggest_value(points[2].y, 400.0).unwrap(); + + update_values(solver.fetch_changes()); + + assert_eq!([(value_of(points[0].x), value_of(points[0].y)), + (value_of(points[1].x), value_of(points[1].y)), + (value_of(points[2].x), value_of(points[2].y)), + (value_of(points[3].x), value_of(points[3].y))], + [(10.0, 10.0), + (10.0, 200.0), + (300.0, 400.0), + (200.0, 10.0)]); + + assert_eq!([(value_of(midpoints[0].x), value_of(midpoints[0].y)), + (value_of(midpoints[1].x), value_of(midpoints[1].y)), + (value_of(midpoints[2].x), value_of(midpoints[2].y)), + (value_of(midpoints[3].x), value_of(midpoints[3].y))], + [(10.0, 105.0), + (155.0, 300.0), + (250.0, 205.0), + (105.0, 10.0)]); +} diff --git a/vendor/cassowary/tests/removal.rs b/vendor/cassowary/tests/removal.rs index d9387b2..13873df 100644 --- a/vendor/cassowary/tests/removal.rs +++ b/vendor/cassowary/tests/removal.rs @@ -1,30 +1,30 @@ -extern crate cassowary; - -use cassowary::{Variable, Solver, Constraint}; -use cassowary::WeightedRelation::*; -use cassowary::strength::*; - -mod common; - -use common::new_values; - -#[test] -fn remove_constraint() { - let (value_of, update_values) = new_values(); - - let mut solver = Solver::new(); - - let val = Variable::new(); - - let constraint: Constraint = val | EQ(REQUIRED) | 100.0; - solver.add_constraint(constraint.clone()).unwrap(); - update_values(solver.fetch_changes()); - - assert_eq!(value_of(val), 100.0); - - solver.remove_constraint(&constraint).unwrap(); - solver.add_constraint(val | EQ(REQUIRED) | 0.0).unwrap(); - update_values(solver.fetch_changes()); - - assert_eq!(value_of(val), 0.0); -} +extern crate cassowary; + +use cassowary::{Variable, Solver, Constraint}; +use cassowary::WeightedRelation::*; +use cassowary::strength::*; + +mod common; + +use common::new_values; + +#[test] +fn remove_constraint() { + let (value_of, update_values) = new_values(); + + let mut solver = Solver::new(); + + let val = Variable::new(); + + let constraint: Constraint = val | EQ(REQUIRED) | 100.0; + solver.add_constraint(constraint.clone()).unwrap(); + update_values(solver.fetch_changes()); + + assert_eq!(value_of(val), 100.0); + + solver.remove_constraint(&constraint).unwrap(); + solver.add_constraint(val | EQ(REQUIRED) | 0.0).unwrap(); + update_values(solver.fetch_changes()); + + assert_eq!(value_of(val), 0.0); +} diff --git a/vendor/crossterm/CHANGELOG.md b/vendor/crossterm/CHANGELOG.md index e89d583..b2aa881 100644 --- a/vendor/crossterm/CHANGELOG.md +++ b/vendor/crossterm/CHANGELOG.md @@ -1,692 +1,692 @@ - -# Version 0.25.0 -BREAKING: `Copy` trait is removed from `Event`, you can keep it by removing the "bracked-paste" feature flag. However this flag might be standardized in the future. -We removed the `Copy` from `Event` because the new `Paste` event, which contains a pasted string into the terminal, which is a non-copy string. - -- Add ability to paste a string in into the terminal and fetch the pasted string via events (see `Event::Paste` and `EnableBracketedPaste `). -- Add support for functional key codes from kitty keyboard protocol. Try out by `PushKeyboardEnhancementFlags`. This protocol allows for: - - See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#modifiers - - Press, Repeat, Release event kinds. - - SUPER, HYPER, META modifiers. - - Media keycodes - - Right/left SHIFT, Control, Alt, Super, Hyper, Meta - - IsoLevel3Shift, IsoLevel5Shift - - Capslock, scroll lock, numlock - - Printscreen, pauze, menue, keyboard begin. -- Create `SetStyle` command to allow setting various styling in one command. -- Terminal Focus events (see `Event::FocusGained` and `Event::FocusLost`) - -# Version 0.24.0 -- Add DoubleUnderlined, Undercurled, Underdots the text, Underdotted, Underdashes, Underdashed attributes and allow coloring their foreground / background color. -- Fix windows unicode character parsing, this fixed various key combinations and support typing unicode characters. -- Consistency and better documentation on mouse cursor operations (BREAKING CHANGE). - - MoveTo, MoveToColumn, MoveToRow are 0-based. (left top most cell is 0,0). Moving like this is absolute - - MoveToNextLine, MoveToPreviousLine, MoveUp, MoveDown, MoveRight, MoveLeft are 1-based,. Moving like this is relative. Moving 1 left means moving 1 left. Moving 0 to the left is not possible, wikipedia states that most terminals will just default to 1. -- terminal::size returns error when previously it returned (0,0). -- Remove println from serialisation code. -- Fix mouse up for middle and right buttons. -- Fix escape codes on Git-Bash + Windows Terminal / Alacritty / WezTerm. -- Add support for cursor keys in application mode. -# Version 0.23.2 -- Update signal-hook and mio to version 0.8. - -# Version 0.23.1 -- Fix control key parsing problem. - -# Version 0.23 -- Update dependencies. -- Add 0 check for all cursor functions to prevent undefined behaviour. -- Add CSIu key parsing for unix. -- Improve control character window key parsing supporting (e.g. CTRL [ and ]) -- Update library to 2021 edition. - -# Version 0.22.1 -- Update yanked version crossterm-winapi and move to crossterm-winapi 0.9.0. -- Changed panic to error when calling disable-mouse capture without setting it first. -- Update bitflags dependency. - -# Version 0.22 -- Fix serde Color serialisation/deserialization inconsistency. -- Update crossterm-winapi 0.8.1 to fix panic for certain mouse events - -# Version 0.21 -- Expose `is_raw` function. -- Add 'purge' option on unix system, this clears the entire screen buffer. -- Improve serialisation for color enum values. - -# Version 0.20 -- Update from signal-hook with 'mio-feature flag' to signal-hook-mio 0.2.1. -- Manually implements Eq, PartialEq and Hash for KeyEvent improving equality checks and hash calculation. -- `crossterm::ErrorKind` to `io::Error`. -- Added Cursor Shape Support. -- Add support for function keys F13...F20. -- Support taking any Display in `SetTitle` command. -- Remove lazy_static dependency. -- Remove extra Clone bounds in the style module. - - Add `MoveToRow` command. - - Remove writer parameter from execute_winapi - -# Version 0.19 -- Use single thread for async event reader. -- Patch timeout handling for event polling this was not working correctly. -- Add unix support for more key combinations mainly complex ones with ALT/SHIFT/CTRL. -- Derive `PartialEq` and `Eq` for ContentStyle -- Fix windows resize event size, this used to be the buffer size but is screen size now. -- Change `Command::ansi_code` to `Command::write_ansi`, this way the ansi code will be written to given formatter. - -# Version 0.18.2 -- Fix panic when only setting bold and redirecting stdout. -- Use `tty_fd` for set/get terminal attributes - -# Version 0.18.1 -- Fix enabling ANSI support when stdout is redirected -- Update crossterm-winapi to 0.6.2 - -# Version 0.18.0 -- Fix get position bug -- Fix windows 8 or lower write to user-given stdout instead of stdout. -- Make MoveCursor(Left/Right/Up/Dow) command with input 0 not move. -- Switch to futures-core to reduce dependencies. -- Command API restricts to only accept `std::io::Write` -- Make `supports_ansi` public -- Implement ALT + numbers windows systems. - -# Version 0.17.7 -- Fix cursor position retrieval bug linux. - -# Version 0.17.6 -- Add functionality to retrieve color based on passed ansi code. -- Switch from 'futures' to 'futures-util' crate to reduce dependency count -- Mio 0.7 update -- signal-hook update -- Make windows raw_mode act on CONIN$ -- Added From<(u8, u8, u8)> Trait to Color::Rgb Enum -- Implement Color::try_from() -- Implement styler traits for `&'a str` - -# Version 0.17.5 -- Improved support of keymodifier for linux, arrow keys, function keys, home keys etc. -- Add `SetTitle` command to change the terminal title. -- Mio 0.7 update - -# Version 0.17.4 -- Add macros for `Colorize` and `Styler` impls, add an impl for `String` -- Add shift modifier to uppercase char events on unix - -# Version 0.17.3 -- Fix get terminal size mac os, this did not report the correct size. - -# Version 0.17.2 -- Windows unicode support - -# Version 0.17.1 -- Reverted bug in 0.17.0: "Make terminal size function fallback to `STDOUT_FILENO` if `/dev/tty` is missing.". -- Support for querying whether the current instance is a TTY. - -# Version 0.17 -- Impl Display for MoveToColumn, MoveToNextLine, MoveToPreviousLine -- Make unix event reader always use `/dev/tty`. -- Direct write command ansi_codes into formatter instead of double allocation. -- Add NONE flag to KeyModifiers -- Add support for converting chars to StylizedContent -- Make terminal size function fallback to `STDOUT_FILENO` if `/dev/tty` is missing. - -# Version 0.16.0 -- Change attribute vector in `ContentStyle` to bitmask. -- Add `SetAttributes` command. -- Add `Attributes` type, which is a bitfield of enabled attributes. -- Remove `exit()`, was useless. - -# Version 0.15.0 -- Fix CTRL + J key combination. This used to return an ENTER event. -- Add a generic implementation `Command` for `&T: Command`. This allows commands to be queued by reference, as well as by value. -- Remove unnecessary `Clone` trait bounds from `StyledContent`. -- Add `StyledContent::style_mut`. -- Handle error correctly for `execute!` and `queue!`. -- Fix minor syntax bug in `execute!` and `queue!`. -- Change `ContentStyle::apply` to take self by value instead of reference, to prevent an unnecessary extra clone. -- Added basic trait implementations (`Debug`, `Clone`, `Copy`, etc) to all of the command structs -- `ResetColor` uses `&'static str` instead of `String` - -# Version 0.14.2 -- Fix TIOCGWINSZ for FreeBSD - -# Version 0.14.1 -- Made windows cursor position relative to the window instead absolute to the screen buffer windows. -- Fix windows bug with `queue` macro were it consumed a type and required an type to be `Copy`. - -# Version 0.14 - -- Replace the `input` module with brand new `event` module - - Terminal Resize Events - - Advanced modifier (SHIFT | ALT | CTRL) support for both mouse and key events and - - futures Stream (feature 'event-stream') - - Poll/read API - - It's **highly recommended** to read the - [Upgrade from 0.13 to 0.14](https://github.com/crossterm-rs/crossterm/wiki/Upgrade-from-0.13-to-0.14) - documentation -- Replace `docs/UPGRADE.md` with the [Upgrade Paths](https://github.com/crossterm-rs/crossterm/wiki#upgrade-paths) - documentation -- Add `MoveToColumn`, `MoveToPreviousLine`, `MoveToNextLine` commands -- Merge `screen` module into `terminal` - - Remove `screen::AlternateScreen` - - Remove `screen::Rawscreen` - * Move and rename `Rawscreen::into_raw_mode` and `Rawscreen::disable_raw_mode` to `terminal::enable_raw_mode` and `terminal::disable_raw_mode` - - Move `screen::EnterAlternateScreen` and `screen::LeaveAlternateScreen` to `terminal::EnterAlternateScreen` and `terminal::LeaveAlternateScreen` - - Replace `utils::Output` command with `style::Print` command -- Fix enable/disable mouse capture commands on Windows -- Allow trailing comma `queue!` & `execute!` macros - -# Version 0.13.3 - -- Remove thread from AsyncReader on Windows. -- Improve HANDLE management windows. - -# Version 0.13.2 - -- New `input::stop_reading_thread()` function - - Temporary workaround for the UNIX platform to stop the background - reading thread and close the file descriptor - - This function will be removed in the next version - -# Version 0.13.1 - -- Async Reader fix, join background thread and avoid looping forever on windows. - -# Version 0.13.0 - -**Major API-change, removed old-api** - -- Remove `Crossterm` type -- Remove `TerminalCursor`, `TerminalColor`, `Terminal` -- Remove `cursor()`, `color()` , `terminal()` -- Remove re-exports at root, accessible via `module::types` (`cursor::MoveTo`) -- `input` module - - Derive 'Copy' for 'KeyEvent' - - Add the `EnableMouseCapture` and `EnableMouseCapture` commands -- `cursor` module - - Introduce static function `crossterm::cursor::position` in place of `TerminalCursor::pos` - - Rename `Goto` to `MoveTo` - - Rename `Up` to `MoveLeft` - - Rename `Right` to `MoveRight` - - Rename `Down` to `MoveDown` - - Rename `BlinkOn` to `EnableBlinking` - - Rename `BlinkOff` to `DisableBlinking` - - Rename `ResetPos` to `ResetPosition` - - Rename `SavePos` to `SavePosition` -- `terminal` - - Introduce static function `crossterm::terminal::size` in place of `Terminal::size` - - Introduce static function `crossterm::terminal::exit` in place of `Terminal::exit` -- `style module` - - Rename `ObjectStyle` to `ContentStyle`. Now full names are used for methods - - Rename `StyledObject` to `StyledContent` and made members private - - Rename `PrintStyledFont` to `PrintStyledContent` - - Rename `attr` method to `attribute`. - - Rename `Attribute::NoInverse` to `NoReverse` - - Update documentation - - Made `Colored` private, user should use commands instead - - Rename `SetFg` -> `SetForegroundColor` - - Rename `SetBg` -> `SetBackgroundColor` - - Rename `SetAttr` -> `SetAttribute` - - Rename `ContentStyle::fg_color` -> `ContentStyle::foreground_color` - - Rename `ContentStyle::bg_color` -> `ContentStyle::background_color` - - Rename `ContentStyle::attrs` -> `ContentStyle::attributes` -- Improve documentation -- Unix terminal size calculation with TPUT - -# Version 0.12.1 - -- Move all the `crossterm_` crates code was moved to the `crossterm` crate - - `crossterm_cursor` is in the `cursor` module, etc. - - All these modules are public -- No public API breaking changes - -# Version 0.12.0 - -- Following crates are deprecated and no longer maintained - - `crossterm_cursor` - - `crossterm_input` - - `crossterm_screen` - - `crossterm_style` - - `crossterm_terminal` - - `crossterm_utils` - -## `crossterm_cursor` 0.4.0 - -- Fix examples link ([PR #6](https://github.com/crossterm-rs/crossterm-cursor/pull/6)) -- Sync documentation style ([PR #7](https://github.com/crossterm-rs/crossterm-cursor/pull/7)) -- Remove all references to the crossterm book ([PR #8](https://github.com/crossterm-rs/crossterm-cursor/pull/8)) -- Replace `RAW_MODE_ENABLED` with `is_raw_mode_enabled` ([PR #9](https://github.com/crossterm-rs/crossterm-cursor/pull/9)) -- Use `SyncReader` & `InputEvent::CursorPosition` for `pos_raw()` ([PR #10](https://github.com/crossterm-rs/crossterm-cursor/pull/10)) - -## `crossterm_input` 0.5.0 - -- Sync documentation style ([PR #4](https://github.com/crossterm-rs/crossterm-input/pull/4)) -- Sync `SyncReader::next()` Windows and UNIX behavior ([PR #5](https://github.com/crossterm-rs/crossterm-input/pull/5)) -- Remove all references to the crossterm book ([PR #6](https://github.com/crossterm-rs/crossterm-input/pull/6)) -- Mouse coordinates synchronized with the cursor ([PR #7](https://github.com/crossterm-rs/crossterm-input/pull/7)) - - Upper/left reported as `(0, 0)` -- Fix bug that read sync didn't block (Windows) ([PR #8](https://github.com/crossterm-rs/crossterm-input/pull/8)) -- Refactor UNIX readers ([PR #9](https://github.com/crossterm-rs/crossterm-input/pull/9)) - - AsyncReader produces mouse events - - One reading thread per application, not per `AsyncReader` - - Cursor position no longer consumed by another `AsyncReader` - - Implement sync reader for read_char (requires raw mode) - - Fix `SIGTTIN` when executed under the LLDB - - Add mio for reading from FD and more efficient polling (UNIX only) -- Sync UNIX and Windows vertical mouse position ([PR #11](https://github.com/crossterm-rs/crossterm-input/pull/11)) - - Top is always reported as `0` - -## `crossterm_screen` 0.3.2 - -- `to_alternate` switch back to main screen if it fails to switch into raw mode ([PR #4](https://github.com/crossterm-rs/crossterm-screen/pull/4)) -- Improve the documentation ([PR #5](https://github.com/crossterm-rs/crossterm-screen/pull/5)) - - Public API - - Include the book content in the documentation -- Remove all references to the crossterm book ([PR #6](https://github.com/crossterm-rs/crossterm-screen/pull/6)) -- New commands introduced ([PR #7](https://github.com/crossterm-rs/crossterm-screen/pull/7)) - - `EnterAlternateScreen` - - `LeaveAlternateScreen` -- Sync Windows and UNIX raw mode behavior ([PR #8](https://github.com/crossterm-rs/crossterm-screen/pull/8)) - -## `crossterm_style` 0.5.2 - -- Refactor ([PR #2](https://github.com/crossterm-rs/crossterm-style/pull/2)) - - Added unit tests - - Improved documentation and added book page to `lib.rs` - - Fixed bug with `SetBg` command, WinApi logic - - Fixed bug with `StyledObject`, used stdout for resetting terminal color - - Introduced `ResetColor` command -- Sync documentation style ([PR #3](https://github.com/crossterm-rs/crossterm-style/pull/3)) -- Remove all references to the crossterm book ([PR #4](https://github.com/crossterm-rs/crossterm-style/pull/4)) -- Windows 7 grey/white foreground/intensity swapped ([PR #5](https://github.com/crossterm-rs/crossterm-style/pull/5)) - -## `crossterm_terminal` 0.3.2 - -- Removed `crossterm_cursor::sys` dependency ([PR #2](https://github.com/crossterm-rs/crossterm-terminal/pull/2)) -- Internal refactoring & documentation ([PR #3](https://github.com/crossterm-rs/crossterm-terminal/pull/3)) -- Removed all references to the crossterm book ([PR #4](https://github.com/crossterm-rs/crossterm-terminal/pull/4)) - -## `crossterm_utils` 0.4.0 - -- Add deprecation note ([PR #3](https://github.com/crossterm-rs/crossterm-utils/pull/3)) -- Remove all references to the crossterm book ([PR #4](https://github.com/crossterm-rs/crossterm-utils/pull/4)) -- Remove unsafe static mut ([PR #5](https://github.com/crossterm-rs/crossterm-utils/pull/5)) - - `sys::unix::RAW_MODE_ENABLED` replaced with `sys::unix::is_raw_mode_enabled()` (breaking) - - New `lazy_static` dependency - -## `crossterm_winapi` 0.3.0 - -- Make read sync block for windows systems ([PR #2](https://github.com/crossterm-rs/crossterm-winapi/pull/2)) - -# Version 0.11.1 - -- Maintenance release -- All sub-crates were moved to their own repositories in the `crossterm-rs` organization - -# Version 0.11.0 - -As a preparation for crossterm 0.1.0 we have moved crossterm to an organisation called 'crossterm-rs'. - -### Code Quality - -- Code Cleanup: [warning-cleanup], [crossterm_style-cleanup], [crossterm_screen-cleanup], [crossterm_terminal-cleanup], [crossterm_utils-cleanup], [2018-cleanup], [api-cleanup-1], [api-cleanup-2], [api-cleanup-3] -- Examples: [example-cleanup_1], [example-cleanup_2], [example-fix], [commandbar-fix], [snake-game-improved] -- Fixed all broken tests and added tests - -### Important Changes - -- Return written bytes: [return-written-bytes] -- Added derives: `Debug` for `ObjectStyle` [debug-derive], Serialize/Deserialize for key events [serde] -- Improved error handling: - - Return `crossterm::Result` from all api's: [return_crossterm_result] - * `TerminalCursor::pos()` returns `Result<(u16, u16)>` - * `Terminal::size()` returns `Result<(u16, u16)>` - * `TerminalCursor::move_*` returns `crossterm::Result` - * `ExecutableCommand::queue` returns `crossterm::Result` - * `QueueableCommand::queue` returns `crossterm::Result` - * `get_available_color_count` returns no result - * `RawScreen::into_raw_mode` returns `crossterm::Result` instead of `io::Result` - * `RawScreen::disable_raw_mode` returns `crossterm::Result` instead of `io::Result` - * `AlternateScreen::to_alternate` returns `crossterm::Result` instead of `io::Result` - * `TerminalInput::read_line` returns `crossterm::Result` instead of `io::Result` - * `TerminalInput::read_char` returns `crossterm::Result` instead of `io::Result` - * Maybe I forgot something, a lot of functions have changed - - Removed all unwraps/expects from library -- Add KeyEvent::Enter and KeyEvent::Tab: [added-key-event-enter], [added-key-event-tab] -- Sync set/get terminal size behaviour: [fixed-get-set-terminal-size] -- Method renames: - * `AsyncReader::stop_reading()` to `stop()` - * `RawScreen::disable_raw_mode_on_drop` to `keep_raw_mode_on_drop` - * `TerminalCursor::reset_position()` to `restore_position()` - * `Command::get_anis_code()` to `ansi_code()` - * `available_color_count` to `available_color_count()` - * `Terminal::terminal_size` to `Terminal::size` - * `Console::get_handle` to `Console::handle` -- All `i16` values for indexing: set size, set cursor pos, scrolling synced to `u16` values -- Command API takes mutable self instead of self - -[serde]: https://github.com/crossterm-rs/crossterm/pull/190 - -[debug-derive]: https://github.com/crossterm-rs/crossterm/pull/192 -[example-fix]: https://github.com/crossterm-rs/crossterm/pull/193 -[commandbar-fix]: https://github.com/crossterm-rs/crossterm/pull/204 - -[warning-cleanup]: https://github.com/crossterm-rs/crossterm/pull/198 -[example-cleanup_1]: https://github.com/crossterm-rs/crossterm/pull/196 -[example-cleanup_2]: https://github.com/crossterm-rs/crossterm/pull/225 -[snake-game-improved]: https://github.com/crossterm-rs/crossterm/pull/231 -[crossterm_style-cleanup]: https://github.com/crossterm-rs/crossterm/pull/208 -[crossterm_screen-cleanup]: https://github.com/crossterm-rs/crossterm/pull/209 -[crossterm_terminal-cleanup]: https://github.com/crossterm-rs/crossterm/pull/210 -[crossterm_utils-cleanup]: https://github.com/crossterm-rs/crossterm/pull/211 -[2018-cleanup]: https://github.com/crossterm-rs/crossterm/pull/222 -[wild-card-cleanup]: https://github.com/crossterm-rs/crossterm/pull/224 - -[api-cleanup-1]: https://github.com/crossterm-rs/crossterm/pull/235 -[api-cleanup-2]: https://github.com/crossterm-rs/crossterm/pull/238 -[api-cleanup-3]: https://github.com/crossterm-rs/crossterm/pull/240 - -[return-written-bytes]: https://github.com/crossterm-rs/crossterm/pull/212 - -[return_crossterm_result]: https://github.com/crossterm-rs/crossterm/pull/232 -[added-key-event-tab]: https://github.com/crossterm-rs/crossterm/pull/239 -[added-key-event-enter]: https://github.com/crossterm-rs/crossterm/pull/236 -[fixed-get-set-terminal-size]: https://github.com/crossterm-rs/crossterm/pull/242 - -# Version 0.10.1 - -# Version 0.10.0 ~ yanked -- Implement command API, to have better performance and more control over how and when commands are executed. [PR](https://github.com/crossterm-rs/crossterm/commit/1a60924abd462ab169b6706aab68f4cca31d7bc2), [issue](https://github.com/crossterm-rs/crossterm/issues/171) -- Fix showing, hiding cursor windows implementation -- Remove some of the parsing logic from windows keys to ansi codes to key events [PR](https://github.com/crossterm-rs/crossterm/commit/762c3a9b8e3d1fba87acde237f8ed09e74cd9ecd) -- Made terminal size 1-based [PR](https://github.com/crossterm-rs/crossterm/commit/d689d7e8ed46a335474b8262bd76f21feaaf0c50) -- Add some derives - -# Version 0.9.6 - -- Copy for KeyEvent -- CTRL + Left, Down, Up, Right key support -- SHIFT + Left, Down, Up, Right key support -- Fixed UNIX cursor position bug [issue](https://github.com/crossterm-rs/crossterm/issues/140), [PR](https://github.com/crossterm-rs/crossterm/pull/152) - -# Version 0.9.5 - -- Prefetch buffer size for more efficient windows input reads. [PR](https://github.com/crossterm-rs/crossterm/pull/144) - -# Version 0.9.4 - -- Reset foreground and background color individually. [PR](https://github.com/crossterm-rs/crossterm/pull/138) -- Backtap input support. [PR](https://github.com/crossterm-rs/crossterm/pull/129) -- Corrected white/grey and added dark grey. -- Fixed getting cursor position with raw screen enabled. [PR](https://github.com/crossterm-rs/crossterm/pull/134) -- Removed one redundant stdout lock - -# Version 0.9.3 - -- Removed println from `SyncReader` - -## Version 0.9.2 - -- Terminal size linux was not 0-based -- Windows mouse input event position was 0-based and should be 1-based -- Result, ErrorKind are made re-exported -- Fixed some special key combination detections for UNIX systems -- Made FreeBSD compile - -## Version 0.9.1 - -- Fixed libc compile error - -## Version 0.9.0 (yanked) - -This release is all about moving to a stabilized API for 1.0. - -- Major refactor and cleanup. -- Improved performance; - - No locking when writing to stdout. - - UNIX doesn't have any dynamic dispatch anymore. - - Windows has improved the way to check if ANSI modes are enabled. - - Removed lot's of complex API calls: `from_screen`, `from_output` - - Removed `Arc` from all internal Api's. -- Removed termios dependency for UNIX systems. -- Upgraded deps. -- Removed about 1000 lines of code - - `TerminalOutput` - - `Screen` - - unsafe code - - Some duplicated code introduced by a previous refactor. -- Raw modes UNIX systems improved -- Added `NoItalic` attribute - -## Version 0.8.2 - -- Bug fix for sync reader UNIX. - -## Version 0.8.1 - -- Added public re-exports for input. - -# Version 0.8.0 - -- Introduced KeyEvents -- Introduced MouseEvents -- Upgraded crossterm_winapi 0.2 - -# Version 0.7.0 - -- Introduced more `Attributes` -- Introduced easier ways to style text [issue 87](https://github.com/crossterm-rs/crossterm/issues/87). -- Removed `ColorType` since it was unnecessary. - -# Version 0.6.0 - -- Introduced feature flags; input, cursor, style, terminal, screen. -- All modules are moved to their own crate. -- Introduced crossterm workspace -- Less dependencies. -- Improved namespaces. - -[PR 84](https://github.com/crossterm-rs/crossterm/pull/84) - -# Version 0.5.5 - -- Error module is made public [PR 78](https://github.com/crossterm-rs/crossterm/pull/78). - -# Version 0.5.4 - -- WinApi rewrite and correctly error handled [PR 67](https://github.com/crossterm-rs/crossterm/pull/67) -- Windows attribute support [PR 62](https://github.com/crossterm-rs/crossterm/pull/62) -- Readline bug fix windows systems [PR 62](https://github.com/crossterm-rs/crossterm/pull/62) -- Error handling improvement. -- General refactoring, all warnings removed. -- Documentation improvement. - -# Version 0.5.1 - -- Documentation refactor. -- Fixed broken API documentation [PR 53](https://github.com/crossterm-rs/crossterm/pull/53). - -# Version 0.5.0 - -- Added ability to pause the terminal [issue](https://github.com/crossterm-rs/crossterm/issues/39) -- RGB support for Windows 10 systems -- ANSI color value (255) color support -- More convenient API, no need to care about `Screen` unless working with when working with alternate or raw screen [PR](https://github.com/crossterm-rs/crossterm/pull/44) -- Implemented Display for styled object - -# Version 0.4.3 - -- Fixed bug [issue 41](https://github.com/crossterm-rs/crossterm/issues/41) - -# Version 0.4.2 - -- Added functionality to make a styled object writable to screen [issue 33](https://github.com/crossterm-rs/crossterm/issues/33) -- Added unit tests. -- Bugfix with getting terminal size unix. -- Bugfix with returning written bytes [pull request 31](https://github.com/crossterm-rs/crossterm/pull/31) -- removed methods calls: `as_any()` and `as_any_mut()` from `TerminalOutput` - -# Version 0.4.1 - -- Fixed resizing of ansi terminal with and height where in the wrong order. - -# Version 0.4.0 - -- Input support (read_line, read_char, read_async, read_until_async) -- Styling module improved -- Everything is multithreaded (`Send`, `Sync`) -- Performance enhancements: removed mutexes, removed state manager, removed context type removed unnecessarily RC types. -- Bug fix resetting console color. -- Bug fix whit undoing raw modes. -- More correct error handling. -- Overall command improvement. -- Overall refactor of code. - -# Version 0.3.0 - -This version has some braking changes check [upgrade manual](UPGRADE%20Manual.md) for more information about what is changed. -I think you should not switch to version `0.3.0` if you aren't going to use the AlternateScreen feature. -Because you will have some work to get to the new version of crossterm depending on your situation. - -Some Features crossterm 0.3.0 -- Alternate Screen for windows and unix systems. -- Raw screen for unix and windows systems [Issue 5](https://github.com/crossterm-rs/crossterm/issues/5).. -- Hiding an showing the cursor. -- Control over blinking of the terminal cursor (only some terminals are supporting this). -- The terminal state will be set to its original state when process ends [issue7](https://github.com/crossterm-rs/crossterm/issues/7). -- exit the current process. - -## Alternate screen - -This create supports alternate screen for both windows and unix systems. You can use - -*Nix style applications often utilize an alternate screen buffer, so that they can modify the entire contents of the buffer, without affecting the application that started them. -The alternate buffer is exactly the dimensions of the window, without any scrollback region. -For an example of this behavior, consider when vim is launched from bash. -Vim uses the entirety of the screen to edit the file, then returning to bash leaves the original buffer unchanged. - -I Highly recommend you to check the `examples/program_examples/first_depth_search` for seeing this in action. - -## Raw screen - -This crate now supports raw screen for both windows and unix systems. -What exactly is raw state: -- No line buffering. - Normally the terminals uses line buffering. This means that the input will be send to the terminal line by line. - With raw mode the input will be send one byte at a time. -- Input - All input has to be written manually by the programmer. -- Characters - The characters are not processed by the terminal driver, but are sent straight through. - Special character have no meaning, like backspace will not be interpret as backspace but instead will be directly send to the terminal. -With these modes you can easier design the terminal screen. - -## Some functionalities added - -- Hiding and showing terminal cursor -- Enable or disabling blinking of the cursor for unix systems (this is not widely supported) -- Restoring the terminal to original modes. -- Added a [wrapper](https://github.com/crossterm-rs/crossterm/blob/master/src/shared/crossterm.rs) for managing all the functionalities of crossterm `Crossterm`. -- Exit the current running process - -## Examples -Added [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) for each version of the crossterm version. -Also added a folder with some [real life examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/program_examples). - -## Context - -What is the `Context` all about? This `Context` has several reasons why it is introduced into `crossterm version 0.3.0`. -These points are related to the features like `Alternatescreen` and managing the terminal state. - -- At first `Terminal state`: - - Because this is a terminal manipulating library there will be made changes to terminal when running an process. - If you stop the process you want the terminal back in its original state. - Therefore, I need to track the changes made to the terminal. - -- At second `Handle to the console` - - In Rust we can use `stdout()` to get an handle to the current default console handle. - For example when in unix systems you want to print something to the main screen you can use the following code: - - write!(std::io::stdout(), "{}", "some text"). - - But things change when we are in alternate screen modes. - We can not simply use `stdout()` to get a handle to the alternate screen, since this call returns the current default console handle (handle to mainscreen). - - Because of that we need to store an handle to the current screen. - This handle could be used to put into alternate screen modes and back into main screen modes. - Through this stored handle Crossterm can execute its command and write on and to the current screen whether it be alternate screen or main screen. - - For unix systems we store the handle gotten from `stdout()` for windows systems that are not supporting ANSI escape codes we store WinApi `HANDLE` struct witch will provide access to the current screen. - -So to recap this `Context` struct is a wrapper for a type that manges terminal state changes. -When this `Context` goes out of scope all changes made will be undone. -Also is this `Context` is a wrapper for access to the current console screen. - -Because Crossterm needs access to the above to types quite often I have chosen to add those two in one struct called `Context` so that this type could be shared throughout library. -Check this link for more info: [cleanup of rust code](https://stackoverflow.com/questions/48732387/how-can-i-run-clean-up-code-in-a-rust-library). -More info over writing to alternate screen buffer on windows and unix see this [link](https://github.com/crossterm-rs/crossterm/issues/17) - -__Now the user has to pass an context type to the modules of Crossterm like this:__ - - let context = Context::new(); - - let cursor = cursor(&context); - let terminal = terminal(&context); - let color = color(&context); - -Because this looks a little odd I will provide a type widths will manage the `Context` for you. You can call the different modules like the following: - - let crossterm = Crossterm::new(); - let color = crossterm.color(); - let cursor = crossterm.cursor(); - let terminal = crossterm.terminal(); - - -### Alternate screen -When you want to switch to alternate screen there are a couple of things to keep in mind for it to work correctly. -First off some code of how to switch to Alternate screen, for more info check the [alternate screen example](https://github.com/crossterm-rs/crossterm/blob/master/examples/alternate_screen.rs). - -_Create alternate screen from `Context`_ - - // create context. - let context = crossterm::Context::new(); - // create instance of Alternatescreen by the given context, this will also switch to it. - let mut screen = crossterm::AlternateScreen::from(context.clone()); - // write to the alternate screen. - write!(screen, "test"); - -_Create alternate screen from `Crossterm`:_ - - // create context. - let crossterm = ::crossterm::Crossterm::new(); - // create instance of Alternatescreen by the given reference to crossterm, this will also switch to it. - let mut screen = crossterm::AlternateScreen::from(&crossterm); - // write to the alternate screen. - write!(screen, "test"); - -like demonstrated above, to get the functionalities of `cursor(), color(), terminal()` also working on alternate screen. -You need to pass it the same `Context` as you have passed to the previous three called functions, -If you don't use the same `Context` in `cursor(), color(), terminal()` than these modules will be using the main screen and you will not see anything at the alternate screen. If you use the [Crossterm](https://github.com/crossterm-rs/crossterm/blob/master/src/shared/crossterm.rs) type you can get the `Context` from it by calling the crossterm.get_context() whereafter you can create the AlternateScreen from it. - -# Version 0.2.2 - -- Bug see [issue 15](https://github.com/crossterm-rs/crossterm/issues/15) - -# Version 0.2.1 - -- Default ANSI escape codes for windows machines, if windows does not support ANSI switch back to WinApi. -- method grammar mistake fixed [Issue 3](https://github.com/crossterm-rs/crossterm/issues/3) -- Some Refactorings in method names see [issue 4](https://github.com/crossterm-rs/crossterm/issues/4) -- Removed bin reference from crate [Issue 6](https://github.com/crossterm-rs/crossterm/issues/6) -- Get position unix fixed [issue 8](https://github.com/crossterm-rs/crossterm/issues/8) - -# Version 0.2 - -- 256 color support. -- Text Attributes like: bold, italic, underscore and crossed word etc. -- Custom ANSI color code input to set fore- and background color for unix. -- Storing the current cursor position and resetting to that stored cursor position later. -- Resizing the terminal. + +# Version 0.25.0 +BREAKING: `Copy` trait is removed from `Event`, you can keep it by removing the "bracked-paste" feature flag. However this flag might be standardized in the future. +We removed the `Copy` from `Event` because the new `Paste` event, which contains a pasted string into the terminal, which is a non-copy string. + +- Add ability to paste a string in into the terminal and fetch the pasted string via events (see `Event::Paste` and `EnableBracketedPaste `). +- Add support for functional key codes from kitty keyboard protocol. Try out by `PushKeyboardEnhancementFlags`. This protocol allows for: + - See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#modifiers + - Press, Repeat, Release event kinds. + - SUPER, HYPER, META modifiers. + - Media keycodes + - Right/left SHIFT, Control, Alt, Super, Hyper, Meta + - IsoLevel3Shift, IsoLevel5Shift + - Capslock, scroll lock, numlock + - Printscreen, pauze, menue, keyboard begin. +- Create `SetStyle` command to allow setting various styling in one command. +- Terminal Focus events (see `Event::FocusGained` and `Event::FocusLost`) + +# Version 0.24.0 +- Add DoubleUnderlined, Undercurled, Underdots the text, Underdotted, Underdashes, Underdashed attributes and allow coloring their foreground / background color. +- Fix windows unicode character parsing, this fixed various key combinations and support typing unicode characters. +- Consistency and better documentation on mouse cursor operations (BREAKING CHANGE). + - MoveTo, MoveToColumn, MoveToRow are 0-based. (left top most cell is 0,0). Moving like this is absolute + - MoveToNextLine, MoveToPreviousLine, MoveUp, MoveDown, MoveRight, MoveLeft are 1-based,. Moving like this is relative. Moving 1 left means moving 1 left. Moving 0 to the left is not possible, wikipedia states that most terminals will just default to 1. +- terminal::size returns error when previously it returned (0,0). +- Remove println from serialisation code. +- Fix mouse up for middle and right buttons. +- Fix escape codes on Git-Bash + Windows Terminal / Alacritty / WezTerm. +- Add support for cursor keys in application mode. +# Version 0.23.2 +- Update signal-hook and mio to version 0.8. + +# Version 0.23.1 +- Fix control key parsing problem. + +# Version 0.23 +- Update dependencies. +- Add 0 check for all cursor functions to prevent undefined behaviour. +- Add CSIu key parsing for unix. +- Improve control character window key parsing supporting (e.g. CTRL [ and ]) +- Update library to 2021 edition. + +# Version 0.22.1 +- Update yanked version crossterm-winapi and move to crossterm-winapi 0.9.0. +- Changed panic to error when calling disable-mouse capture without setting it first. +- Update bitflags dependency. + +# Version 0.22 +- Fix serde Color serialisation/deserialization inconsistency. +- Update crossterm-winapi 0.8.1 to fix panic for certain mouse events + +# Version 0.21 +- Expose `is_raw` function. +- Add 'purge' option on unix system, this clears the entire screen buffer. +- Improve serialisation for color enum values. + +# Version 0.20 +- Update from signal-hook with 'mio-feature flag' to signal-hook-mio 0.2.1. +- Manually implements Eq, PartialEq and Hash for KeyEvent improving equality checks and hash calculation. +- `crossterm::ErrorKind` to `io::Error`. +- Added Cursor Shape Support. +- Add support for function keys F13...F20. +- Support taking any Display in `SetTitle` command. +- Remove lazy_static dependency. +- Remove extra Clone bounds in the style module. + - Add `MoveToRow` command. + - Remove writer parameter from execute_winapi + +# Version 0.19 +- Use single thread for async event reader. +- Patch timeout handling for event polling this was not working correctly. +- Add unix support for more key combinations mainly complex ones with ALT/SHIFT/CTRL. +- Derive `PartialEq` and `Eq` for ContentStyle +- Fix windows resize event size, this used to be the buffer size but is screen size now. +- Change `Command::ansi_code` to `Command::write_ansi`, this way the ansi code will be written to given formatter. + +# Version 0.18.2 +- Fix panic when only setting bold and redirecting stdout. +- Use `tty_fd` for set/get terminal attributes + +# Version 0.18.1 +- Fix enabling ANSI support when stdout is redirected +- Update crossterm-winapi to 0.6.2 + +# Version 0.18.0 +- Fix get position bug +- Fix windows 8 or lower write to user-given stdout instead of stdout. +- Make MoveCursor(Left/Right/Up/Dow) command with input 0 not move. +- Switch to futures-core to reduce dependencies. +- Command API restricts to only accept `std::io::Write` +- Make `supports_ansi` public +- Implement ALT + numbers windows systems. + +# Version 0.17.7 +- Fix cursor position retrieval bug linux. + +# Version 0.17.6 +- Add functionality to retrieve color based on passed ansi code. +- Switch from 'futures' to 'futures-util' crate to reduce dependency count +- Mio 0.7 update +- signal-hook update +- Make windows raw_mode act on CONIN$ +- Added From<(u8, u8, u8)> Trait to Color::Rgb Enum +- Implement Color::try_from() +- Implement styler traits for `&'a str` + +# Version 0.17.5 +- Improved support of keymodifier for linux, arrow keys, function keys, home keys etc. +- Add `SetTitle` command to change the terminal title. +- Mio 0.7 update + +# Version 0.17.4 +- Add macros for `Colorize` and `Styler` impls, add an impl for `String` +- Add shift modifier to uppercase char events on unix + +# Version 0.17.3 +- Fix get terminal size mac os, this did not report the correct size. + +# Version 0.17.2 +- Windows unicode support + +# Version 0.17.1 +- Reverted bug in 0.17.0: "Make terminal size function fallback to `STDOUT_FILENO` if `/dev/tty` is missing.". +- Support for querying whether the current instance is a TTY. + +# Version 0.17 +- Impl Display for MoveToColumn, MoveToNextLine, MoveToPreviousLine +- Make unix event reader always use `/dev/tty`. +- Direct write command ansi_codes into formatter instead of double allocation. +- Add NONE flag to KeyModifiers +- Add support for converting chars to StylizedContent +- Make terminal size function fallback to `STDOUT_FILENO` if `/dev/tty` is missing. + +# Version 0.16.0 +- Change attribute vector in `ContentStyle` to bitmask. +- Add `SetAttributes` command. +- Add `Attributes` type, which is a bitfield of enabled attributes. +- Remove `exit()`, was useless. + +# Version 0.15.0 +- Fix CTRL + J key combination. This used to return an ENTER event. +- Add a generic implementation `Command` for `&T: Command`. This allows commands to be queued by reference, as well as by value. +- Remove unnecessary `Clone` trait bounds from `StyledContent`. +- Add `StyledContent::style_mut`. +- Handle error correctly for `execute!` and `queue!`. +- Fix minor syntax bug in `execute!` and `queue!`. +- Change `ContentStyle::apply` to take self by value instead of reference, to prevent an unnecessary extra clone. +- Added basic trait implementations (`Debug`, `Clone`, `Copy`, etc) to all of the command structs +- `ResetColor` uses `&'static str` instead of `String` + +# Version 0.14.2 +- Fix TIOCGWINSZ for FreeBSD + +# Version 0.14.1 +- Made windows cursor position relative to the window instead absolute to the screen buffer windows. +- Fix windows bug with `queue` macro were it consumed a type and required an type to be `Copy`. + +# Version 0.14 + +- Replace the `input` module with brand new `event` module + - Terminal Resize Events + - Advanced modifier (SHIFT | ALT | CTRL) support for both mouse and key events and + - futures Stream (feature 'event-stream') + - Poll/read API + - It's **highly recommended** to read the + [Upgrade from 0.13 to 0.14](https://github.com/crossterm-rs/crossterm/wiki/Upgrade-from-0.13-to-0.14) + documentation +- Replace `docs/UPGRADE.md` with the [Upgrade Paths](https://github.com/crossterm-rs/crossterm/wiki#upgrade-paths) + documentation +- Add `MoveToColumn`, `MoveToPreviousLine`, `MoveToNextLine` commands +- Merge `screen` module into `terminal` + - Remove `screen::AlternateScreen` + - Remove `screen::Rawscreen` + * Move and rename `Rawscreen::into_raw_mode` and `Rawscreen::disable_raw_mode` to `terminal::enable_raw_mode` and `terminal::disable_raw_mode` + - Move `screen::EnterAlternateScreen` and `screen::LeaveAlternateScreen` to `terminal::EnterAlternateScreen` and `terminal::LeaveAlternateScreen` + - Replace `utils::Output` command with `style::Print` command +- Fix enable/disable mouse capture commands on Windows +- Allow trailing comma `queue!` & `execute!` macros + +# Version 0.13.3 + +- Remove thread from AsyncReader on Windows. +- Improve HANDLE management windows. + +# Version 0.13.2 + +- New `input::stop_reading_thread()` function + - Temporary workaround for the UNIX platform to stop the background + reading thread and close the file descriptor + - This function will be removed in the next version + +# Version 0.13.1 + +- Async Reader fix, join background thread and avoid looping forever on windows. + +# Version 0.13.0 + +**Major API-change, removed old-api** + +- Remove `Crossterm` type +- Remove `TerminalCursor`, `TerminalColor`, `Terminal` +- Remove `cursor()`, `color()` , `terminal()` +- Remove re-exports at root, accessible via `module::types` (`cursor::MoveTo`) +- `input` module + - Derive 'Copy' for 'KeyEvent' + - Add the `EnableMouseCapture` and `EnableMouseCapture` commands +- `cursor` module + - Introduce static function `crossterm::cursor::position` in place of `TerminalCursor::pos` + - Rename `Goto` to `MoveTo` + - Rename `Up` to `MoveLeft` + - Rename `Right` to `MoveRight` + - Rename `Down` to `MoveDown` + - Rename `BlinkOn` to `EnableBlinking` + - Rename `BlinkOff` to `DisableBlinking` + - Rename `ResetPos` to `ResetPosition` + - Rename `SavePos` to `SavePosition` +- `terminal` + - Introduce static function `crossterm::terminal::size` in place of `Terminal::size` + - Introduce static function `crossterm::terminal::exit` in place of `Terminal::exit` +- `style module` + - Rename `ObjectStyle` to `ContentStyle`. Now full names are used for methods + - Rename `StyledObject` to `StyledContent` and made members private + - Rename `PrintStyledFont` to `PrintStyledContent` + - Rename `attr` method to `attribute`. + - Rename `Attribute::NoInverse` to `NoReverse` + - Update documentation + - Made `Colored` private, user should use commands instead + - Rename `SetFg` -> `SetForegroundColor` + - Rename `SetBg` -> `SetBackgroundColor` + - Rename `SetAttr` -> `SetAttribute` + - Rename `ContentStyle::fg_color` -> `ContentStyle::foreground_color` + - Rename `ContentStyle::bg_color` -> `ContentStyle::background_color` + - Rename `ContentStyle::attrs` -> `ContentStyle::attributes` +- Improve documentation +- Unix terminal size calculation with TPUT + +# Version 0.12.1 + +- Move all the `crossterm_` crates code was moved to the `crossterm` crate + - `crossterm_cursor` is in the `cursor` module, etc. + - All these modules are public +- No public API breaking changes + +# Version 0.12.0 + +- Following crates are deprecated and no longer maintained + - `crossterm_cursor` + - `crossterm_input` + - `crossterm_screen` + - `crossterm_style` + - `crossterm_terminal` + - `crossterm_utils` + +## `crossterm_cursor` 0.4.0 + +- Fix examples link ([PR #6](https://github.com/crossterm-rs/crossterm-cursor/pull/6)) +- Sync documentation style ([PR #7](https://github.com/crossterm-rs/crossterm-cursor/pull/7)) +- Remove all references to the crossterm book ([PR #8](https://github.com/crossterm-rs/crossterm-cursor/pull/8)) +- Replace `RAW_MODE_ENABLED` with `is_raw_mode_enabled` ([PR #9](https://github.com/crossterm-rs/crossterm-cursor/pull/9)) +- Use `SyncReader` & `InputEvent::CursorPosition` for `pos_raw()` ([PR #10](https://github.com/crossterm-rs/crossterm-cursor/pull/10)) + +## `crossterm_input` 0.5.0 + +- Sync documentation style ([PR #4](https://github.com/crossterm-rs/crossterm-input/pull/4)) +- Sync `SyncReader::next()` Windows and UNIX behavior ([PR #5](https://github.com/crossterm-rs/crossterm-input/pull/5)) +- Remove all references to the crossterm book ([PR #6](https://github.com/crossterm-rs/crossterm-input/pull/6)) +- Mouse coordinates synchronized with the cursor ([PR #7](https://github.com/crossterm-rs/crossterm-input/pull/7)) + - Upper/left reported as `(0, 0)` +- Fix bug that read sync didn't block (Windows) ([PR #8](https://github.com/crossterm-rs/crossterm-input/pull/8)) +- Refactor UNIX readers ([PR #9](https://github.com/crossterm-rs/crossterm-input/pull/9)) + - AsyncReader produces mouse events + - One reading thread per application, not per `AsyncReader` + - Cursor position no longer consumed by another `AsyncReader` + - Implement sync reader for read_char (requires raw mode) + - Fix `SIGTTIN` when executed under the LLDB + - Add mio for reading from FD and more efficient polling (UNIX only) +- Sync UNIX and Windows vertical mouse position ([PR #11](https://github.com/crossterm-rs/crossterm-input/pull/11)) + - Top is always reported as `0` + +## `crossterm_screen` 0.3.2 + +- `to_alternate` switch back to main screen if it fails to switch into raw mode ([PR #4](https://github.com/crossterm-rs/crossterm-screen/pull/4)) +- Improve the documentation ([PR #5](https://github.com/crossterm-rs/crossterm-screen/pull/5)) + - Public API + - Include the book content in the documentation +- Remove all references to the crossterm book ([PR #6](https://github.com/crossterm-rs/crossterm-screen/pull/6)) +- New commands introduced ([PR #7](https://github.com/crossterm-rs/crossterm-screen/pull/7)) + - `EnterAlternateScreen` + - `LeaveAlternateScreen` +- Sync Windows and UNIX raw mode behavior ([PR #8](https://github.com/crossterm-rs/crossterm-screen/pull/8)) + +## `crossterm_style` 0.5.2 + +- Refactor ([PR #2](https://github.com/crossterm-rs/crossterm-style/pull/2)) + - Added unit tests + - Improved documentation and added book page to `lib.rs` + - Fixed bug with `SetBg` command, WinApi logic + - Fixed bug with `StyledObject`, used stdout for resetting terminal color + - Introduced `ResetColor` command +- Sync documentation style ([PR #3](https://github.com/crossterm-rs/crossterm-style/pull/3)) +- Remove all references to the crossterm book ([PR #4](https://github.com/crossterm-rs/crossterm-style/pull/4)) +- Windows 7 grey/white foreground/intensity swapped ([PR #5](https://github.com/crossterm-rs/crossterm-style/pull/5)) + +## `crossterm_terminal` 0.3.2 + +- Removed `crossterm_cursor::sys` dependency ([PR #2](https://github.com/crossterm-rs/crossterm-terminal/pull/2)) +- Internal refactoring & documentation ([PR #3](https://github.com/crossterm-rs/crossterm-terminal/pull/3)) +- Removed all references to the crossterm book ([PR #4](https://github.com/crossterm-rs/crossterm-terminal/pull/4)) + +## `crossterm_utils` 0.4.0 + +- Add deprecation note ([PR #3](https://github.com/crossterm-rs/crossterm-utils/pull/3)) +- Remove all references to the crossterm book ([PR #4](https://github.com/crossterm-rs/crossterm-utils/pull/4)) +- Remove unsafe static mut ([PR #5](https://github.com/crossterm-rs/crossterm-utils/pull/5)) + - `sys::unix::RAW_MODE_ENABLED` replaced with `sys::unix::is_raw_mode_enabled()` (breaking) + - New `lazy_static` dependency + +## `crossterm_winapi` 0.3.0 + +- Make read sync block for windows systems ([PR #2](https://github.com/crossterm-rs/crossterm-winapi/pull/2)) + +# Version 0.11.1 + +- Maintenance release +- All sub-crates were moved to their own repositories in the `crossterm-rs` organization + +# Version 0.11.0 + +As a preparation for crossterm 0.1.0 we have moved crossterm to an organisation called 'crossterm-rs'. + +### Code Quality + +- Code Cleanup: [warning-cleanup], [crossterm_style-cleanup], [crossterm_screen-cleanup], [crossterm_terminal-cleanup], [crossterm_utils-cleanup], [2018-cleanup], [api-cleanup-1], [api-cleanup-2], [api-cleanup-3] +- Examples: [example-cleanup_1], [example-cleanup_2], [example-fix], [commandbar-fix], [snake-game-improved] +- Fixed all broken tests and added tests + +### Important Changes + +- Return written bytes: [return-written-bytes] +- Added derives: `Debug` for `ObjectStyle` [debug-derive], Serialize/Deserialize for key events [serde] +- Improved error handling: + - Return `crossterm::Result` from all api's: [return_crossterm_result] + * `TerminalCursor::pos()` returns `Result<(u16, u16)>` + * `Terminal::size()` returns `Result<(u16, u16)>` + * `TerminalCursor::move_*` returns `crossterm::Result` + * `ExecutableCommand::queue` returns `crossterm::Result` + * `QueueableCommand::queue` returns `crossterm::Result` + * `get_available_color_count` returns no result + * `RawScreen::into_raw_mode` returns `crossterm::Result` instead of `io::Result` + * `RawScreen::disable_raw_mode` returns `crossterm::Result` instead of `io::Result` + * `AlternateScreen::to_alternate` returns `crossterm::Result` instead of `io::Result` + * `TerminalInput::read_line` returns `crossterm::Result` instead of `io::Result` + * `TerminalInput::read_char` returns `crossterm::Result` instead of `io::Result` + * Maybe I forgot something, a lot of functions have changed + - Removed all unwraps/expects from library +- Add KeyEvent::Enter and KeyEvent::Tab: [added-key-event-enter], [added-key-event-tab] +- Sync set/get terminal size behaviour: [fixed-get-set-terminal-size] +- Method renames: + * `AsyncReader::stop_reading()` to `stop()` + * `RawScreen::disable_raw_mode_on_drop` to `keep_raw_mode_on_drop` + * `TerminalCursor::reset_position()` to `restore_position()` + * `Command::get_anis_code()` to `ansi_code()` + * `available_color_count` to `available_color_count()` + * `Terminal::terminal_size` to `Terminal::size` + * `Console::get_handle` to `Console::handle` +- All `i16` values for indexing: set size, set cursor pos, scrolling synced to `u16` values +- Command API takes mutable self instead of self + +[serde]: https://github.com/crossterm-rs/crossterm/pull/190 + +[debug-derive]: https://github.com/crossterm-rs/crossterm/pull/192 +[example-fix]: https://github.com/crossterm-rs/crossterm/pull/193 +[commandbar-fix]: https://github.com/crossterm-rs/crossterm/pull/204 + +[warning-cleanup]: https://github.com/crossterm-rs/crossterm/pull/198 +[example-cleanup_1]: https://github.com/crossterm-rs/crossterm/pull/196 +[example-cleanup_2]: https://github.com/crossterm-rs/crossterm/pull/225 +[snake-game-improved]: https://github.com/crossterm-rs/crossterm/pull/231 +[crossterm_style-cleanup]: https://github.com/crossterm-rs/crossterm/pull/208 +[crossterm_screen-cleanup]: https://github.com/crossterm-rs/crossterm/pull/209 +[crossterm_terminal-cleanup]: https://github.com/crossterm-rs/crossterm/pull/210 +[crossterm_utils-cleanup]: https://github.com/crossterm-rs/crossterm/pull/211 +[2018-cleanup]: https://github.com/crossterm-rs/crossterm/pull/222 +[wild-card-cleanup]: https://github.com/crossterm-rs/crossterm/pull/224 + +[api-cleanup-1]: https://github.com/crossterm-rs/crossterm/pull/235 +[api-cleanup-2]: https://github.com/crossterm-rs/crossterm/pull/238 +[api-cleanup-3]: https://github.com/crossterm-rs/crossterm/pull/240 + +[return-written-bytes]: https://github.com/crossterm-rs/crossterm/pull/212 + +[return_crossterm_result]: https://github.com/crossterm-rs/crossterm/pull/232 +[added-key-event-tab]: https://github.com/crossterm-rs/crossterm/pull/239 +[added-key-event-enter]: https://github.com/crossterm-rs/crossterm/pull/236 +[fixed-get-set-terminal-size]: https://github.com/crossterm-rs/crossterm/pull/242 + +# Version 0.10.1 + +# Version 0.10.0 ~ yanked +- Implement command API, to have better performance and more control over how and when commands are executed. [PR](https://github.com/crossterm-rs/crossterm/commit/1a60924abd462ab169b6706aab68f4cca31d7bc2), [issue](https://github.com/crossterm-rs/crossterm/issues/171) +- Fix showing, hiding cursor windows implementation +- Remove some of the parsing logic from windows keys to ansi codes to key events [PR](https://github.com/crossterm-rs/crossterm/commit/762c3a9b8e3d1fba87acde237f8ed09e74cd9ecd) +- Made terminal size 1-based [PR](https://github.com/crossterm-rs/crossterm/commit/d689d7e8ed46a335474b8262bd76f21feaaf0c50) +- Add some derives + +# Version 0.9.6 + +- Copy for KeyEvent +- CTRL + Left, Down, Up, Right key support +- SHIFT + Left, Down, Up, Right key support +- Fixed UNIX cursor position bug [issue](https://github.com/crossterm-rs/crossterm/issues/140), [PR](https://github.com/crossterm-rs/crossterm/pull/152) + +# Version 0.9.5 + +- Prefetch buffer size for more efficient windows input reads. [PR](https://github.com/crossterm-rs/crossterm/pull/144) + +# Version 0.9.4 + +- Reset foreground and background color individually. [PR](https://github.com/crossterm-rs/crossterm/pull/138) +- Backtap input support. [PR](https://github.com/crossterm-rs/crossterm/pull/129) +- Corrected white/grey and added dark grey. +- Fixed getting cursor position with raw screen enabled. [PR](https://github.com/crossterm-rs/crossterm/pull/134) +- Removed one redundant stdout lock + +# Version 0.9.3 + +- Removed println from `SyncReader` + +## Version 0.9.2 + +- Terminal size linux was not 0-based +- Windows mouse input event position was 0-based and should be 1-based +- Result, ErrorKind are made re-exported +- Fixed some special key combination detections for UNIX systems +- Made FreeBSD compile + +## Version 0.9.1 + +- Fixed libc compile error + +## Version 0.9.0 (yanked) + +This release is all about moving to a stabilized API for 1.0. + +- Major refactor and cleanup. +- Improved performance; + - No locking when writing to stdout. + - UNIX doesn't have any dynamic dispatch anymore. + - Windows has improved the way to check if ANSI modes are enabled. + - Removed lot's of complex API calls: `from_screen`, `from_output` + - Removed `Arc` from all internal Api's. +- Removed termios dependency for UNIX systems. +- Upgraded deps. +- Removed about 1000 lines of code + - `TerminalOutput` + - `Screen` + - unsafe code + - Some duplicated code introduced by a previous refactor. +- Raw modes UNIX systems improved +- Added `NoItalic` attribute + +## Version 0.8.2 + +- Bug fix for sync reader UNIX. + +## Version 0.8.1 + +- Added public re-exports for input. + +# Version 0.8.0 + +- Introduced KeyEvents +- Introduced MouseEvents +- Upgraded crossterm_winapi 0.2 + +# Version 0.7.0 + +- Introduced more `Attributes` +- Introduced easier ways to style text [issue 87](https://github.com/crossterm-rs/crossterm/issues/87). +- Removed `ColorType` since it was unnecessary. + +# Version 0.6.0 + +- Introduced feature flags; input, cursor, style, terminal, screen. +- All modules are moved to their own crate. +- Introduced crossterm workspace +- Less dependencies. +- Improved namespaces. + +[PR 84](https://github.com/crossterm-rs/crossterm/pull/84) + +# Version 0.5.5 + +- Error module is made public [PR 78](https://github.com/crossterm-rs/crossterm/pull/78). + +# Version 0.5.4 + +- WinApi rewrite and correctly error handled [PR 67](https://github.com/crossterm-rs/crossterm/pull/67) +- Windows attribute support [PR 62](https://github.com/crossterm-rs/crossterm/pull/62) +- Readline bug fix windows systems [PR 62](https://github.com/crossterm-rs/crossterm/pull/62) +- Error handling improvement. +- General refactoring, all warnings removed. +- Documentation improvement. + +# Version 0.5.1 + +- Documentation refactor. +- Fixed broken API documentation [PR 53](https://github.com/crossterm-rs/crossterm/pull/53). + +# Version 0.5.0 + +- Added ability to pause the terminal [issue](https://github.com/crossterm-rs/crossterm/issues/39) +- RGB support for Windows 10 systems +- ANSI color value (255) color support +- More convenient API, no need to care about `Screen` unless working with when working with alternate or raw screen [PR](https://github.com/crossterm-rs/crossterm/pull/44) +- Implemented Display for styled object + +# Version 0.4.3 + +- Fixed bug [issue 41](https://github.com/crossterm-rs/crossterm/issues/41) + +# Version 0.4.2 + +- Added functionality to make a styled object writable to screen [issue 33](https://github.com/crossterm-rs/crossterm/issues/33) +- Added unit tests. +- Bugfix with getting terminal size unix. +- Bugfix with returning written bytes [pull request 31](https://github.com/crossterm-rs/crossterm/pull/31) +- removed methods calls: `as_any()` and `as_any_mut()` from `TerminalOutput` + +# Version 0.4.1 + +- Fixed resizing of ansi terminal with and height where in the wrong order. + +# Version 0.4.0 + +- Input support (read_line, read_char, read_async, read_until_async) +- Styling module improved +- Everything is multithreaded (`Send`, `Sync`) +- Performance enhancements: removed mutexes, removed state manager, removed context type removed unnecessarily RC types. +- Bug fix resetting console color. +- Bug fix whit undoing raw modes. +- More correct error handling. +- Overall command improvement. +- Overall refactor of code. + +# Version 0.3.0 + +This version has some braking changes check [upgrade manual](UPGRADE%20Manual.md) for more information about what is changed. +I think you should not switch to version `0.3.0` if you aren't going to use the AlternateScreen feature. +Because you will have some work to get to the new version of crossterm depending on your situation. + +Some Features crossterm 0.3.0 +- Alternate Screen for windows and unix systems. +- Raw screen for unix and windows systems [Issue 5](https://github.com/crossterm-rs/crossterm/issues/5).. +- Hiding an showing the cursor. +- Control over blinking of the terminal cursor (only some terminals are supporting this). +- The terminal state will be set to its original state when process ends [issue7](https://github.com/crossterm-rs/crossterm/issues/7). +- exit the current process. + +## Alternate screen + +This create supports alternate screen for both windows and unix systems. You can use + +*Nix style applications often utilize an alternate screen buffer, so that they can modify the entire contents of the buffer, without affecting the application that started them. +The alternate buffer is exactly the dimensions of the window, without any scrollback region. +For an example of this behavior, consider when vim is launched from bash. +Vim uses the entirety of the screen to edit the file, then returning to bash leaves the original buffer unchanged. + +I Highly recommend you to check the `examples/program_examples/first_depth_search` for seeing this in action. + +## Raw screen + +This crate now supports raw screen for both windows and unix systems. +What exactly is raw state: +- No line buffering. + Normally the terminals uses line buffering. This means that the input will be send to the terminal line by line. + With raw mode the input will be send one byte at a time. +- Input + All input has to be written manually by the programmer. +- Characters + The characters are not processed by the terminal driver, but are sent straight through. + Special character have no meaning, like backspace will not be interpret as backspace but instead will be directly send to the terminal. +With these modes you can easier design the terminal screen. + +## Some functionalities added + +- Hiding and showing terminal cursor +- Enable or disabling blinking of the cursor for unix systems (this is not widely supported) +- Restoring the terminal to original modes. +- Added a [wrapper](https://github.com/crossterm-rs/crossterm/blob/master/src/shared/crossterm.rs) for managing all the functionalities of crossterm `Crossterm`. +- Exit the current running process + +## Examples +Added [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) for each version of the crossterm version. +Also added a folder with some [real life examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/program_examples). + +## Context + +What is the `Context` all about? This `Context` has several reasons why it is introduced into `crossterm version 0.3.0`. +These points are related to the features like `Alternatescreen` and managing the terminal state. + +- At first `Terminal state`: + + Because this is a terminal manipulating library there will be made changes to terminal when running an process. + If you stop the process you want the terminal back in its original state. + Therefore, I need to track the changes made to the terminal. + +- At second `Handle to the console` + + In Rust we can use `stdout()` to get an handle to the current default console handle. + For example when in unix systems you want to print something to the main screen you can use the following code: + + write!(std::io::stdout(), "{}", "some text"). + + But things change when we are in alternate screen modes. + We can not simply use `stdout()` to get a handle to the alternate screen, since this call returns the current default console handle (handle to mainscreen). + + Because of that we need to store an handle to the current screen. + This handle could be used to put into alternate screen modes and back into main screen modes. + Through this stored handle Crossterm can execute its command and write on and to the current screen whether it be alternate screen or main screen. + + For unix systems we store the handle gotten from `stdout()` for windows systems that are not supporting ANSI escape codes we store WinApi `HANDLE` struct witch will provide access to the current screen. + +So to recap this `Context` struct is a wrapper for a type that manges terminal state changes. +When this `Context` goes out of scope all changes made will be undone. +Also is this `Context` is a wrapper for access to the current console screen. + +Because Crossterm needs access to the above to types quite often I have chosen to add those two in one struct called `Context` so that this type could be shared throughout library. +Check this link for more info: [cleanup of rust code](https://stackoverflow.com/questions/48732387/how-can-i-run-clean-up-code-in-a-rust-library). +More info over writing to alternate screen buffer on windows and unix see this [link](https://github.com/crossterm-rs/crossterm/issues/17) + +__Now the user has to pass an context type to the modules of Crossterm like this:__ + + let context = Context::new(); + + let cursor = cursor(&context); + let terminal = terminal(&context); + let color = color(&context); + +Because this looks a little odd I will provide a type widths will manage the `Context` for you. You can call the different modules like the following: + + let crossterm = Crossterm::new(); + let color = crossterm.color(); + let cursor = crossterm.cursor(); + let terminal = crossterm.terminal(); + + +### Alternate screen +When you want to switch to alternate screen there are a couple of things to keep in mind for it to work correctly. +First off some code of how to switch to Alternate screen, for more info check the [alternate screen example](https://github.com/crossterm-rs/crossterm/blob/master/examples/alternate_screen.rs). + +_Create alternate screen from `Context`_ + + // create context. + let context = crossterm::Context::new(); + // create instance of Alternatescreen by the given context, this will also switch to it. + let mut screen = crossterm::AlternateScreen::from(context.clone()); + // write to the alternate screen. + write!(screen, "test"); + +_Create alternate screen from `Crossterm`:_ + + // create context. + let crossterm = ::crossterm::Crossterm::new(); + // create instance of Alternatescreen by the given reference to crossterm, this will also switch to it. + let mut screen = crossterm::AlternateScreen::from(&crossterm); + // write to the alternate screen. + write!(screen, "test"); + +like demonstrated above, to get the functionalities of `cursor(), color(), terminal()` also working on alternate screen. +You need to pass it the same `Context` as you have passed to the previous three called functions, +If you don't use the same `Context` in `cursor(), color(), terminal()` than these modules will be using the main screen and you will not see anything at the alternate screen. If you use the [Crossterm](https://github.com/crossterm-rs/crossterm/blob/master/src/shared/crossterm.rs) type you can get the `Context` from it by calling the crossterm.get_context() whereafter you can create the AlternateScreen from it. + +# Version 0.2.2 + +- Bug see [issue 15](https://github.com/crossterm-rs/crossterm/issues/15) + +# Version 0.2.1 + +- Default ANSI escape codes for windows machines, if windows does not support ANSI switch back to WinApi. +- method grammar mistake fixed [Issue 3](https://github.com/crossterm-rs/crossterm/issues/3) +- Some Refactorings in method names see [issue 4](https://github.com/crossterm-rs/crossterm/issues/4) +- Removed bin reference from crate [Issue 6](https://github.com/crossterm-rs/crossterm/issues/6) +- Get position unix fixed [issue 8](https://github.com/crossterm-rs/crossterm/issues/8) + +# Version 0.2 + +- 256 color support. +- Text Attributes like: bold, italic, underscore and crossed word etc. +- Custom ANSI color code input to set fore- and background color for unix. +- Storing the current cursor position and resetting to that stored cursor position later. +- Resizing the terminal. diff --git a/vendor/crossterm/LICENSE b/vendor/crossterm/LICENSE index 8b02a7f..5618e2a 100644 --- a/vendor/crossterm/LICENSE +++ b/vendor/crossterm/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2019 Timon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2019 Timon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/crossterm/README.md b/vendor/crossterm/README.md index 4a891c4..324ad18 100644 --- a/vendor/crossterm/README.md +++ b/vendor/crossterm/README.md @@ -1,199 +1,199 @@ -

- -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z8QK6XU749JB2) ![Travis][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6] [![Join us on Discord][s5]][l5] - -# Cross-platform Terminal Manipulation Library - -Crossterm is a pure-rust, terminal manipulation library that makes it possible to write cross-platform text-based interfaces (see [features](#features)). It supports all UNIX and Windows terminals down to Windows 7 (not all terminals are tested, -see [Tested Terminals](#tested-terminals) for more info). - -## Table of Contents - -* [Features](#features) - * [Tested Terminals](#tested-terminals) -* [Getting Started](#getting-started) - * [Feature Flags](#feature-flags) -* [Other Resources](#other-resources) -* [Used By](#used-by) -* [Contributing](#contributing) - -## Features - -- Cross-platform -- Multi-threaded (send, sync) -- Detailed documentation -- Few dependencies -- Full control over writing and flushing output buffer -- Is tty -- Cursor - - Move the cursor N times (up, down, left, right) - - Move to previous / next line - - Move to column - - Set/get the cursor position - - Store the cursor position and restore to it later - - Hide/show the cursor - - Enable/disable cursor blinking (not all terminals do support this feature) -- Styled output - - Foreground color (16 base colors) - - Background color (16 base colors) - - 256 (ANSI) color support (Windows 10 and UNIX only) - - RGB color support (Windows 10 and UNIX only) - - Text attributes like bold, italic, underscore, crossed, etc -- Terminal - - Clear (all lines, current line, from cursor down and up, until new line) - - Scroll up, down - - Set/get the terminal size - - Exit current process - - Alternate screen - - Raw screen - - Set terminal title - - Enable/disable line wrapping -- Event - - Input Events - - Mouse Events (press, release, position, button, drag) - - Terminal Resize Events - - Advanced modifier (SHIFT | ALT | CTRL) support for both mouse and key events and - - futures Stream (feature 'event-stream') - - Poll/read API - - - -### Tested Terminals - -- Console Host - - Windows 10 (Pro) - - Windows 8.1 (N) -- Ubuntu Desktop Terminal - - Ubuntu 17.10 - - Pop!_OS ( Ubuntu ) 20.04 -- (Arch, Manjaro) KDE Konsole -- (Arch) Kitty -- Linux Mint -- (OpenSuse) Alacritty - -This crate supports all UNIX terminals and Windows terminals down to Windows 7; however, not all of the -terminals have been tested. If you have used this library for a terminal other than the above list without -issues, then feel free to add it to the above list - I really would appreciate it! - -## Getting Started -_see the [examples directory](examples/) and [documentation](https://docs.rs/crossterm/) for more advanced examples._ - -
- -Click to show Cargo.toml. - - -```toml -[dependencies] -crossterm = "0.23" -``` - -
-

- -```rust -use std::io::{stdout, Write}; - -use crossterm::{ - execute, - style::{Color, Print, ResetColor, SetBackgroundColor, SetForegroundColor}, - ExecutableCommand, Result, - event, -}; - -fn main() -> Result<()> { - // using the macro - execute!( - stdout(), - SetForegroundColor(Color::Blue), - SetBackgroundColor(Color::Red), - Print("Styled text here."), - ResetColor - )?; - - // or using functions - stdout() - .execute(SetForegroundColor(Color::Blue))? - .execute(SetBackgroundColor(Color::Red))? - .execute(Print("Styled text here."))? - .execute(ResetColor)?; - - Ok(()) -} -``` - -Checkout this [list](https://docs.rs/crossterm/0.14.0/crossterm/index.html#supported-commands) with all possible commands. - -### Feature Flags - -```toml -[dependencies.crossterm] -version = "0.23" -features = ["event-stream"] -``` - -| Feature | Description | -|:---------------|:---------------------------------------------| -| `event-stream` | `futures::Stream` producing `Result`. | -| `serde` | Se/dese/rializing of events. | - -### Dependency Justification - -| Dependency | Used for | Included | -|:---------------|:---------------------------------------------------------------------------------|:--------------------------------------| -| `bitflags` | `KeyModifiers`, those are differ based on input. | always | -| `parking_lot` | locking `RwLock`s with a timeout, const mutexes. | always | -| `libc` | UNIX terminal_size/raw modes/set_title and several other lowlevel functionality. | UNIX only | -| `Mio` | event readiness polling, waking up poller | UNIX only | -| `signal-hook` | signalhook is used to handle terminal resize SIGNAL with Mio. | UNIX only | -| `winapi` | Used for low-level windows system calls which ANSI codes can't replace | windows only | -| `futures-core` | For async stream of events | only with `event-stream` feature flag | -| `serde` | ***ser***ializing and ***de***serializing of events | only with `serde` feature flag | - -### Other Resources - -- [API documentation](https://docs.rs/crossterm/) -- [Deprecated examples repository](https://github.com/crossterm-rs/examples) - -## Used By - -- [Broot](https://dystroy.org/broot/) -- [Cursive](https://github.com/gyscos/Cursive) -- [TUI](https://github.com/fdehau/tui-rs) -- [Rust-sloth](https://github.com/ecumene/rust-sloth) -- [Rusty-rain](https://github.com/cowboy8625/rusty-rain) - -## Contributing - -We highly appreciate when anyone contributes to this crate. Before you do, please, -read the [Contributing](docs/CONTRIBUTING.md) guidelines. - -## Authors - -* **Timon Post** - *Project Owner & creator* - -## License - -This project, `crossterm` and all its sub-crates: `crossterm_screen`, `crossterm_cursor`, `crossterm_style`, -`crossterm_input`, `crossterm_terminal`, `crossterm_winapi`, `crossterm_utils` are licensed under the MIT -License - see the [LICENSE](https://github.com/crossterm-rs/crossterm/blob/master/LICENSE) file for details. - -[s1]: https://img.shields.io/crates/v/crossterm.svg -[l1]: https://crates.io/crates/crossterm - -[s2]: https://img.shields.io/badge/license-MIT-blue.svg -[l2]: ./LICENSE - -[s3]: https://docs.rs/crossterm/badge.svg -[l3]: https://docs.rs/crossterm/ - -[s3]: https://docs.rs/crossterm/badge.svg -[l3]: https://docs.rs/crossterm/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s6]: https://tokei.rs/b1/github/crossterm-rs/crossterm?category=code -[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master +

+ +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z8QK6XU749JB2) ![Travis][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6] [![Join us on Discord][s5]][l5] + +# Cross-platform Terminal Manipulation Library + +Crossterm is a pure-rust, terminal manipulation library that makes it possible to write cross-platform text-based interfaces (see [features](#features)). It supports all UNIX and Windows terminals down to Windows 7 (not all terminals are tested, +see [Tested Terminals](#tested-terminals) for more info). + +## Table of Contents + +* [Features](#features) + * [Tested Terminals](#tested-terminals) +* [Getting Started](#getting-started) + * [Feature Flags](#feature-flags) +* [Other Resources](#other-resources) +* [Used By](#used-by) +* [Contributing](#contributing) + +## Features + +- Cross-platform +- Multi-threaded (send, sync) +- Detailed documentation +- Few dependencies +- Full control over writing and flushing output buffer +- Is tty +- Cursor + - Move the cursor N times (up, down, left, right) + - Move to previous / next line + - Move to column + - Set/get the cursor position + - Store the cursor position and restore to it later + - Hide/show the cursor + - Enable/disable cursor blinking (not all terminals do support this feature) +- Styled output + - Foreground color (16 base colors) + - Background color (16 base colors) + - 256 (ANSI) color support (Windows 10 and UNIX only) + - RGB color support (Windows 10 and UNIX only) + - Text attributes like bold, italic, underscore, crossed, etc +- Terminal + - Clear (all lines, current line, from cursor down and up, until new line) + - Scroll up, down + - Set/get the terminal size + - Exit current process + - Alternate screen + - Raw screen + - Set terminal title + - Enable/disable line wrapping +- Event + - Input Events + - Mouse Events (press, release, position, button, drag) + - Terminal Resize Events + - Advanced modifier (SHIFT | ALT | CTRL) support for both mouse and key events and + - futures Stream (feature 'event-stream') + - Poll/read API + + + +### Tested Terminals + +- Console Host + - Windows 10 (Pro) + - Windows 8.1 (N) +- Ubuntu Desktop Terminal + - Ubuntu 17.10 + - Pop!_OS ( Ubuntu ) 20.04 +- (Arch, Manjaro) KDE Konsole +- (Arch) Kitty +- Linux Mint +- (OpenSuse) Alacritty + +This crate supports all UNIX terminals and Windows terminals down to Windows 7; however, not all of the +terminals have been tested. If you have used this library for a terminal other than the above list without +issues, then feel free to add it to the above list - I really would appreciate it! + +## Getting Started +_see the [examples directory](examples/) and [documentation](https://docs.rs/crossterm/) for more advanced examples._ + +
+ +Click to show Cargo.toml. + + +```toml +[dependencies] +crossterm = "0.23" +``` + +
+

+ +```rust +use std::io::{stdout, Write}; + +use crossterm::{ + execute, + style::{Color, Print, ResetColor, SetBackgroundColor, SetForegroundColor}, + ExecutableCommand, Result, + event, +}; + +fn main() -> Result<()> { + // using the macro + execute!( + stdout(), + SetForegroundColor(Color::Blue), + SetBackgroundColor(Color::Red), + Print("Styled text here."), + ResetColor + )?; + + // or using functions + stdout() + .execute(SetForegroundColor(Color::Blue))? + .execute(SetBackgroundColor(Color::Red))? + .execute(Print("Styled text here."))? + .execute(ResetColor)?; + + Ok(()) +} +``` + +Checkout this [list](https://docs.rs/crossterm/0.14.0/crossterm/index.html#supported-commands) with all possible commands. + +### Feature Flags + +```toml +[dependencies.crossterm] +version = "0.23" +features = ["event-stream"] +``` + +| Feature | Description | +|:---------------|:---------------------------------------------| +| `event-stream` | `futures::Stream` producing `Result`. | +| `serde` | Se/dese/rializing of events. | + +### Dependency Justification + +| Dependency | Used for | Included | +|:---------------|:---------------------------------------------------------------------------------|:--------------------------------------| +| `bitflags` | `KeyModifiers`, those are differ based on input. | always | +| `parking_lot` | locking `RwLock`s with a timeout, const mutexes. | always | +| `libc` | UNIX terminal_size/raw modes/set_title and several other lowlevel functionality. | UNIX only | +| `Mio` | event readiness polling, waking up poller | UNIX only | +| `signal-hook` | signalhook is used to handle terminal resize SIGNAL with Mio. | UNIX only | +| `winapi` | Used for low-level windows system calls which ANSI codes can't replace | windows only | +| `futures-core` | For async stream of events | only with `event-stream` feature flag | +| `serde` | ***ser***ializing and ***de***serializing of events | only with `serde` feature flag | + +### Other Resources + +- [API documentation](https://docs.rs/crossterm/) +- [Deprecated examples repository](https://github.com/crossterm-rs/examples) + +## Used By + +- [Broot](https://dystroy.org/broot/) +- [Cursive](https://github.com/gyscos/Cursive) +- [TUI](https://github.com/fdehau/tui-rs) +- [Rust-sloth](https://github.com/ecumene/rust-sloth) +- [Rusty-rain](https://github.com/cowboy8625/rusty-rain) + +## Contributing + +We highly appreciate when anyone contributes to this crate. Before you do, please, +read the [Contributing](docs/CONTRIBUTING.md) guidelines. + +## Authors + +* **Timon Post** - *Project Owner & creator* + +## License + +This project, `crossterm` and all its sub-crates: `crossterm_screen`, `crossterm_cursor`, `crossterm_style`, +`crossterm_input`, `crossterm_terminal`, `crossterm_winapi`, `crossterm_utils` are licensed under the MIT +License - see the [LICENSE](https://github.com/crossterm-rs/crossterm/blob/master/LICENSE) file for details. + +[s1]: https://img.shields.io/crates/v/crossterm.svg +[l1]: https://crates.io/crates/crossterm + +[s2]: https://img.shields.io/badge/license-MIT-blue.svg +[l2]: ./LICENSE + +[s3]: https://docs.rs/crossterm/badge.svg +[l3]: https://docs.rs/crossterm/ + +[s3]: https://docs.rs/crossterm/badge.svg +[l3]: https://docs.rs/crossterm/ + +[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord +[l5]: https://discord.gg/K4nyTDB + +[s6]: https://tokei.rs/b1/github/crossterm-rs/crossterm?category=code +[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master diff --git a/vendor/crossterm/docs/CONTRIBUTING.md b/vendor/crossterm/docs/CONTRIBUTING.md index d99d4e2..f86617f 100644 --- a/vendor/crossterm/docs/CONTRIBUTING.md +++ b/vendor/crossterm/docs/CONTRIBUTING.md @@ -1,65 +1,65 @@ -# Contributing - -I would appreciate any contributions to this crate. However, some things are handy to know. - -## Code Style - -### Import Order - -All imports are semantically grouped and ordered. The order is: - -- standard library (`use std::...`) -- external crates (`use rand::...`) -- current crate (`use crate::...`) -- parent module (`use super::..`) -- current module (`use self::...`) -- module declaration (`mod ...`) - -There must be an empty line between groups. An example: - -```rust -use crossterm_utils::{csi, write_cout, Result}; - -use crate::sys::{get_cursor_position, show_cursor}; - -use super::Cursor; -``` - -#### CLion Tips - -The CLion IDE does this for you (_Menu_ -> _Code_ -> _Optimize Imports_). Be aware that the CLion sorts -imports in a group in a different way when compared to the `rustfmt`. It's effectively two steps operation -to get proper grouping & sorting: - -* _Menu_ -> _Code_ -> _Optimize Imports_ - group & semantically order imports -* `cargo fmt` - fix ordering within the group - -Second step can be automated via _CLion_ -> _Preferences_ -> -_Languages & Frameworks_ -> _Rust_ -> _Rustfmt_ -> _Run rustfmt on save_. - -### Max Line Length - -| Type | Max line length | -|:---------------------|----------------:| -| Code | 100 | -| Comments in the code | 120 | -| Documentation | 120 | - -100 is the [`max_width`](https://github.com/rust-lang/rustfmt/blob/master/Configurations.md#max_width) -default value. - -120 is because of the GitHub. The editor & viewer width there is +- 123 characters. - -### Warnings - -The code must be warning free. It's quite hard to find an error if the build logs are polluted with warnings. -If you decide to silent a warning with (`#[allow(...)]`), please add a comment why it's required. - -Always consult the [Travis CI](https://travis-ci.org/crossterm-rs/crossterm/pull_requests) build logs. - -### Forbidden Warnings - -Search for `#![deny(...)]` in the code: - -* `unused_must_use` -* `unused_imports` +# Contributing + +I would appreciate any contributions to this crate. However, some things are handy to know. + +## Code Style + +### Import Order + +All imports are semantically grouped and ordered. The order is: + +- standard library (`use std::...`) +- external crates (`use rand::...`) +- current crate (`use crate::...`) +- parent module (`use super::..`) +- current module (`use self::...`) +- module declaration (`mod ...`) + +There must be an empty line between groups. An example: + +```rust +use crossterm_utils::{csi, write_cout, Result}; + +use crate::sys::{get_cursor_position, show_cursor}; + +use super::Cursor; +``` + +#### CLion Tips + +The CLion IDE does this for you (_Menu_ -> _Code_ -> _Optimize Imports_). Be aware that the CLion sorts +imports in a group in a different way when compared to the `rustfmt`. It's effectively two steps operation +to get proper grouping & sorting: + +* _Menu_ -> _Code_ -> _Optimize Imports_ - group & semantically order imports +* `cargo fmt` - fix ordering within the group + +Second step can be automated via _CLion_ -> _Preferences_ -> +_Languages & Frameworks_ -> _Rust_ -> _Rustfmt_ -> _Run rustfmt on save_. + +### Max Line Length + +| Type | Max line length | +|:---------------------|----------------:| +| Code | 100 | +| Comments in the code | 120 | +| Documentation | 120 | + +100 is the [`max_width`](https://github.com/rust-lang/rustfmt/blob/master/Configurations.md#max_width) +default value. + +120 is because of the GitHub. The editor & viewer width there is +- 123 characters. + +### Warnings + +The code must be warning free. It's quite hard to find an error if the build logs are polluted with warnings. +If you decide to silent a warning with (`#[allow(...)]`), please add a comment why it's required. + +Always consult the [Travis CI](https://travis-ci.org/crossterm-rs/crossterm/pull_requests) build logs. + +### Forbidden Warnings + +Search for `#![deny(...)]` in the code: + +* `unused_must_use` +* `unused_imports` diff --git a/vendor/crossterm/docs/crossterm_full.svg b/vendor/crossterm/docs/crossterm_full.svg index e8f9007..9492fe5 100644 --- a/vendor/crossterm/docs/crossterm_full.svg +++ b/vendor/crossterm/docs/crossterm_full.svg @@ -1,103 +1,103 @@ - - - - -Created by potrace 1.15, written by Peter Selinger 2001-2017 - - - - - - - - - - - - - + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + diff --git a/vendor/crossterm/docs/know-problems.md b/vendor/crossterm/docs/know-problems.md index 826aaee..5e7e99c 100644 --- a/vendor/crossterm/docs/know-problems.md +++ b/vendor/crossterm/docs/know-problems.md @@ -1,14 +1,14 @@ -# Known Problems - -There are some problems I discovered during development. -And I don't think it has to do anything with crossterm but it has to do whit how terminals handle ANSI or WinApi. - -## WinAPI - -- Power shell does not interpreter 'DarkYellow' and is instead using gray instead, cmd is working perfectly fine. -- Power shell inserts an '\n' (enter) when the program starts, this enter is the one you pressed when running the command. -- After the program ran, power shell will reset the background and foreground colors. - -## UNIX-terminals - -The Arc and Manjaro KDE Konsole's are not seeming to resize the terminal instead they are resizing the buffer. +# Known Problems + +There are some problems I discovered during development. +And I don't think it has to do anything with crossterm but it has to do whit how terminals handle ANSI or WinApi. + +## WinAPI + +- Power shell does not interpreter 'DarkYellow' and is instead using gray instead, cmd is working perfectly fine. +- Power shell inserts an '\n' (enter) when the program starts, this enter is the one you pressed when running the command. +- After the program ran, power shell will reset the background and foreground colors. + +## UNIX-terminals + +The Arc and Manjaro KDE Konsole's are not seeming to resize the terminal instead they are resizing the buffer. diff --git a/vendor/crossterm/examples/README.md b/vendor/crossterm/examples/README.md index c9884ec..f59750a 100644 --- a/vendor/crossterm/examples/README.md +++ b/vendor/crossterm/examples/README.md @@ -1,40 +1,40 @@ -![Lines of Code][s7] [![MIT][s2]][l2] [![Join us on Discord][s5]][l5] - -# Crossterm Examples - -The examples are compatible with the latest release. - -## Structure - -``` -├── examples -│   └── interactive-test -│   └── event-* -│   └── stderr -``` -| File Name | Description | Topics | -|:----------------------------|:-------------------------------|:------------------------------------------| -| `examples/interactive-test` | interactive, walk through, demo | cursor, style, event | -| `event-*` | event reading demos | (async) event reading | -| `stderr` | crossterm over stderr demo | raw mode, alternate screen, custom output | -| `is_tty` | Is this instance a tty ? | tty | - -## Run examples - -```bash -$ cargo run --example [file name] -``` - -To run the interactive-demo go into the folder `examples/interactive-demo` and run `cargo run`. - -## License - -This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details. - -[s2]: https://img.shields.io/badge/license-MIT-blue.svg -[l2]: LICENSE - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s7]: https://travis-ci.org/crossterm-rs/examples.svg?branch=master +![Lines of Code][s7] [![MIT][s2]][l2] [![Join us on Discord][s5]][l5] + +# Crossterm Examples + +The examples are compatible with the latest release. + +## Structure + +``` +├── examples +│   └── interactive-test +│   └── event-* +│   └── stderr +``` +| File Name | Description | Topics | +|:----------------------------|:-------------------------------|:------------------------------------------| +| `examples/interactive-test` | interactive, walk through, demo | cursor, style, event | +| `event-*` | event reading demos | (async) event reading | +| `stderr` | crossterm over stderr demo | raw mode, alternate screen, custom output | +| `is_tty` | Is this instance a tty ? | tty | + +## Run examples + +```bash +$ cargo run --example [file name] +``` + +To run the interactive-demo go into the folder `examples/interactive-demo` and run `cargo run`. + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details. + +[s2]: https://img.shields.io/badge/license-MIT-blue.svg +[l2]: LICENSE + +[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord +[l5]: https://discord.gg/K4nyTDB + +[s7]: https://travis-ci.org/crossterm-rs/examples.svg?branch=master diff --git a/vendor/crossterm/examples/event-match-modifiers.rs b/vendor/crossterm/examples/event-match-modifiers.rs index c3f75e9..d5e0cbb 100644 --- a/vendor/crossterm/examples/event-match-modifiers.rs +++ b/vendor/crossterm/examples/event-match-modifiers.rs @@ -1,68 +1,68 @@ -//! Demonstrates how to match on modifiers like: Control, alt, shift. -//! -//! cargo run --example event-match-modifiers - -use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; - -fn match_event(read_event: Event) { - match read_event { - // Match one one modifier: - Event::Key(KeyEvent { - modifiers: KeyModifiers::CONTROL, - code, - .. - }) => { - println!("Control + {:?}", code); - } - Event::Key(KeyEvent { - modifiers: KeyModifiers::SHIFT, - code, - .. - }) => { - println!("Shift + {:?}", code); - } - Event::Key(KeyEvent { - modifiers: KeyModifiers::ALT, - code, - .. - }) => { - println!("Alt + {:?}", code); - } - - // Match on multiple modifiers: - Event::Key(KeyEvent { - code, modifiers, .. - }) => { - if modifiers == (KeyModifiers::ALT | KeyModifiers::SHIFT) { - println!("Alt + Shift {:?}", code); - } else { - println!("({:?}) with key: {:?}", modifiers, code) - } - } - - _ => {} - } -} - -fn main() { - match_event(Event::Key(KeyEvent::new( - KeyCode::Char('z'), - KeyModifiers::CONTROL, - ))); - match_event(Event::Key(KeyEvent::new( - KeyCode::Left, - KeyModifiers::SHIFT, - ))); - match_event(Event::Key(KeyEvent::new( - KeyCode::Delete, - KeyModifiers::ALT, - ))); - match_event(Event::Key(KeyEvent::new( - KeyCode::Right, - KeyModifiers::ALT | KeyModifiers::SHIFT, - ))); - match_event(Event::Key(KeyEvent::new( - KeyCode::Home, - KeyModifiers::ALT | KeyModifiers::CONTROL, - ))); -} +//! Demonstrates how to match on modifiers like: Control, alt, shift. +//! +//! cargo run --example event-match-modifiers + +use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; + +fn match_event(read_event: Event) { + match read_event { + // Match one one modifier: + Event::Key(KeyEvent { + modifiers: KeyModifiers::CONTROL, + code, + .. + }) => { + println!("Control + {:?}", code); + } + Event::Key(KeyEvent { + modifiers: KeyModifiers::SHIFT, + code, + .. + }) => { + println!("Shift + {:?}", code); + } + Event::Key(KeyEvent { + modifiers: KeyModifiers::ALT, + code, + .. + }) => { + println!("Alt + {:?}", code); + } + + // Match on multiple modifiers: + Event::Key(KeyEvent { + code, modifiers, .. + }) => { + if modifiers == (KeyModifiers::ALT | KeyModifiers::SHIFT) { + println!("Alt + Shift {:?}", code); + } else { + println!("({:?}) with key: {:?}", modifiers, code) + } + } + + _ => {} + } +} + +fn main() { + match_event(Event::Key(KeyEvent::new( + KeyCode::Char('z'), + KeyModifiers::CONTROL, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Left, + KeyModifiers::SHIFT, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Delete, + KeyModifiers::ALT, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Right, + KeyModifiers::ALT | KeyModifiers::SHIFT, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Home, + KeyModifiers::ALT | KeyModifiers::CONTROL, + ))); +} diff --git a/vendor/crossterm/examples/event-poll-read.rs b/vendor/crossterm/examples/event-poll-read.rs index a901182..f9dd913 100644 --- a/vendor/crossterm/examples/event-poll-read.rs +++ b/vendor/crossterm/examples/event-poll-read.rs @@ -1,62 +1,62 @@ -//! Demonstrates how to match on modifiers like: Control, alt, shift. -//! -//! cargo run --example event-poll-read - -use std::{io::stdout, time::Duration}; - -use crossterm::{ - cursor::position, - event::{poll, read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, - execute, - terminal::{disable_raw_mode, enable_raw_mode}, - Result, -}; - -const HELP: &str = r#"Blocking poll() & non-blocking read() - - Keyboard, mouse and terminal resize events enabled - - Prints "." every second if there's no event - - Hit "c" to print current cursor position - - Use Esc to quit -"#; - -fn print_events() -> Result<()> { - loop { - // Wait up to 1s for another event - if poll(Duration::from_millis(1_000))? { - // It's guaranteed that read() won't block if `poll` returns `Ok(true)` - let event = read()?; - - println!("Event::{:?}\r", event); - - if event == Event::Key(KeyCode::Char('c').into()) { - println!("Cursor position: {:?}\r", position()); - } - - if event == Event::Key(KeyCode::Esc.into()) { - break; - } - } else { - // Timeout expired, no event for 1s - println!(".\r"); - } - } - - Ok(()) -} - -fn main() -> Result<()> { - println!("{}", HELP); - - enable_raw_mode()?; - - let mut stdout = stdout(); - execute!(stdout, EnableMouseCapture)?; - - if let Err(e) = print_events() { - println!("Error: {:?}\r", e); - } - - execute!(stdout, DisableMouseCapture)?; - - disable_raw_mode() -} +//! Demonstrates how to match on modifiers like: Control, alt, shift. +//! +//! cargo run --example event-poll-read + +use std::{io::stdout, time::Duration}; + +use crossterm::{ + cursor::position, + event::{poll, read, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, + execute, + terminal::{disable_raw_mode, enable_raw_mode}, + Result, +}; + +const HELP: &str = r#"Blocking poll() & non-blocking read() + - Keyboard, mouse and terminal resize events enabled + - Prints "." every second if there's no event + - Hit "c" to print current cursor position + - Use Esc to quit +"#; + +fn print_events() -> Result<()> { + loop { + // Wait up to 1s for another event + if poll(Duration::from_millis(1_000))? { + // It's guaranteed that read() won't block if `poll` returns `Ok(true)` + let event = read()?; + + println!("Event::{:?}\r", event); + + if event == Event::Key(KeyCode::Char('c').into()) { + println!("Cursor position: {:?}\r", position()); + } + + if event == Event::Key(KeyCode::Esc.into()) { + break; + } + } else { + // Timeout expired, no event for 1s + println!(".\r"); + } + } + + Ok(()) +} + +fn main() -> Result<()> { + println!("{}", HELP); + + enable_raw_mode()?; + + let mut stdout = stdout(); + execute!(stdout, EnableMouseCapture)?; + + if let Err(e) = print_events() { + println!("Error: {:?}\r", e); + } + + execute!(stdout, DisableMouseCapture)?; + + disable_raw_mode() +} diff --git a/vendor/crossterm/examples/event-read-char-line.rs b/vendor/crossterm/examples/event-read-char-line.rs index c6ef38b..bcf6976 100644 --- a/vendor/crossterm/examples/event-read-char-line.rs +++ b/vendor/crossterm/examples/event-read-char-line.rs @@ -1,45 +1,45 @@ -//! Demonstrates how to block read characters or a full line. -//! Just note that crossterm is not required to do this and can be done with `io::stdin()`. -//! -//! cargo run --example event-read-char-line - -use crossterm::{ - event::{self, Event, KeyCode, KeyEvent}, - Result, -}; - -pub fn read_char() -> Result { - loop { - if let Event::Key(KeyEvent { - code: KeyCode::Char(c), - .. - }) = event::read()? - { - return Ok(c); - } - } -} - -pub fn read_line() -> Result { - let mut line = String::new(); - while let Event::Key(KeyEvent { code, .. }) = event::read()? { - match code { - KeyCode::Enter => { - break; - } - KeyCode::Char(c) => { - line.push(c); - } - _ => {} - } - } - - Ok(line) -} - -fn main() { - println!("read line:"); - println!("{:?}", read_line()); - println!("read char:"); - println!("{:?}", read_char()); -} +//! Demonstrates how to block read characters or a full line. +//! Just note that crossterm is not required to do this and can be done with `io::stdin()`. +//! +//! cargo run --example event-read-char-line + +use crossterm::{ + event::{self, Event, KeyCode, KeyEvent}, + Result, +}; + +pub fn read_char() -> Result { + loop { + if let Event::Key(KeyEvent { + code: KeyCode::Char(c), + .. + }) = event::read()? + { + return Ok(c); + } + } +} + +pub fn read_line() -> Result { + let mut line = String::new(); + while let Event::Key(KeyEvent { code, .. }) = event::read()? { + match code { + KeyCode::Enter => { + break; + } + KeyCode::Char(c) => { + line.push(c); + } + _ => {} + } + } + + Ok(line) +} + +fn main() { + println!("read line:"); + println!("{:?}", read_line()); + println!("read char:"); + println!("{:?}", read_char()); +} diff --git a/vendor/crossterm/examples/event-read.rs b/vendor/crossterm/examples/event-read.rs index a111fd8..7178d0c 100644 --- a/vendor/crossterm/examples/event-read.rs +++ b/vendor/crossterm/examples/event-read.rs @@ -1,97 +1,97 @@ -//! Demonstrates how to block read events. -//! -//! cargo run --example event-read - -use std::io::stdout; - -use crossterm::event::{ - poll, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, -}; -use crossterm::{ - cursor::position, - event::{ - read, DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, - EnableFocusChange, EnableMouseCapture, Event, KeyCode, - }, - execute, - terminal::{disable_raw_mode, enable_raw_mode}, - Result, -}; -use std::time::Duration; - -const HELP: &str = r#"Blocking read() - - Keyboard, mouse, focus and terminal resize events enabled - - Hit "c" to print current cursor position - - Use Esc to quit -"#; - -fn print_events() -> Result<()> { - loop { - // Blocking read - let event = read()?; - - println!("Event: {:?}\r", event); - - if event == Event::Key(KeyCode::Char('c').into()) { - println!("Cursor position: {:?}\r", position()); - } - - if let Event::Resize(x, y) = event { - let (original_size, new_size) = flush_resize_events((x, y)); - println!("Resize from: {:?}, to: {:?}\r", original_size, new_size); - } - - if event == Event::Key(KeyCode::Esc.into()) { - break; - } - } - - Ok(()) -} - -// Resize events can occur in batches. -// With a simple loop they can be flushed. -// This function will keep the first and last resize event. -fn flush_resize_events(first_resize: (u16, u16)) -> ((u16, u16), (u16, u16)) { - let mut last_resize = first_resize; - while let Ok(true) = poll(Duration::from_millis(50)) { - if let Ok(Event::Resize(x, y)) = read() { - last_resize = (x, y); - } - } - - return (first_resize, last_resize); -} - -fn main() -> Result<()> { - println!("{}", HELP); - - enable_raw_mode()?; - - let mut stdout = stdout(); - execute!( - stdout, - EnableBracketedPaste, - EnableFocusChange, - EnableMouseCapture, - PushKeyboardEnhancementFlags( - KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES - | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES - | KeyboardEnhancementFlags::REPORT_EVENT_TYPES - ) - )?; - - if let Err(e) = print_events() { - println!("Error: {:?}\r", e); - } - - execute!( - stdout, - DisableBracketedPaste, - PopKeyboardEnhancementFlags, - DisableFocusChange, - DisableMouseCapture - )?; - - disable_raw_mode() -} +//! Demonstrates how to block read events. +//! +//! cargo run --example event-read + +use std::io::stdout; + +use crossterm::event::{ + poll, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, +}; +use crossterm::{ + cursor::position, + event::{ + read, DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, + EnableFocusChange, EnableMouseCapture, Event, KeyCode, + }, + execute, + terminal::{disable_raw_mode, enable_raw_mode}, + Result, +}; +use std::time::Duration; + +const HELP: &str = r#"Blocking read() + - Keyboard, mouse, focus and terminal resize events enabled + - Hit "c" to print current cursor position + - Use Esc to quit +"#; + +fn print_events() -> Result<()> { + loop { + // Blocking read + let event = read()?; + + println!("Event: {:?}\r", event); + + if event == Event::Key(KeyCode::Char('c').into()) { + println!("Cursor position: {:?}\r", position()); + } + + if let Event::Resize(x, y) = event { + let (original_size, new_size) = flush_resize_events((x, y)); + println!("Resize from: {:?}, to: {:?}\r", original_size, new_size); + } + + if event == Event::Key(KeyCode::Esc.into()) { + break; + } + } + + Ok(()) +} + +// Resize events can occur in batches. +// With a simple loop they can be flushed. +// This function will keep the first and last resize event. +fn flush_resize_events(first_resize: (u16, u16)) -> ((u16, u16), (u16, u16)) { + let mut last_resize = first_resize; + while let Ok(true) = poll(Duration::from_millis(50)) { + if let Ok(Event::Resize(x, y)) = read() { + last_resize = (x, y); + } + } + + return (first_resize, last_resize); +} + +fn main() -> Result<()> { + println!("{}", HELP); + + enable_raw_mode()?; + + let mut stdout = stdout(); + execute!( + stdout, + EnableBracketedPaste, + EnableFocusChange, + EnableMouseCapture, + PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_EVENT_TYPES + ) + )?; + + if let Err(e) = print_events() { + println!("Error: {:?}\r", e); + } + + execute!( + stdout, + DisableBracketedPaste, + PopKeyboardEnhancementFlags, + DisableFocusChange, + DisableMouseCapture + )?; + + disable_raw_mode() +} diff --git a/vendor/crossterm/examples/event-stream-async-std.rs b/vendor/crossterm/examples/event-stream-async-std.rs index 9df2f68..f2ae59c 100644 --- a/vendor/crossterm/examples/event-stream-async-std.rs +++ b/vendor/crossterm/examples/event-stream-async-std.rs @@ -1,68 +1,68 @@ -//! Demonstrates how to read events asynchronously with async-std. -//! -//! cargo run --features="event-stream" --example event-stream-async-std - -use std::{io::stdout, time::Duration}; - -use futures::{future::FutureExt, select, StreamExt}; -use futures_timer::Delay; - -use crossterm::{ - cursor::position, - event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode}, - execute, - terminal::{disable_raw_mode, enable_raw_mode}, - Result, -}; - -const HELP: &str = r#"EventStream based on futures_util::stream::Stream with async-std - - Keyboard, mouse and terminal resize events enabled - - Prints "." every second if there's no event - - Hit "c" to print current cursor position - - Use Esc to quit -"#; - -async fn print_events() { - let mut reader = EventStream::new(); - - loop { - let mut delay = Delay::new(Duration::from_millis(1_000)).fuse(); - let mut event = reader.next().fuse(); - - select! { - _ = delay => { println!(".\r"); }, - maybe_event = event => { - match maybe_event { - Some(Ok(event)) => { - println!("Event::{:?}\r", event); - - if event == Event::Key(KeyCode::Char('c').into()) { - println!("Cursor position: {:?}\r", position()); - } - - if event == Event::Key(KeyCode::Esc.into()) { - break; - } - } - Some(Err(e)) => println!("Error: {:?}\r", e), - None => break, - } - } - }; - } -} - -fn main() -> Result<()> { - println!("{}", HELP); - - enable_raw_mode()?; - - let mut stdout = stdout(); - execute!(stdout, EnableMouseCapture)?; - - async_std::task::block_on(print_events()); - - execute!(stdout, DisableMouseCapture)?; - - disable_raw_mode() -} +//! Demonstrates how to read events asynchronously with async-std. +//! +//! cargo run --features="event-stream" --example event-stream-async-std + +use std::{io::stdout, time::Duration}; + +use futures::{future::FutureExt, select, StreamExt}; +use futures_timer::Delay; + +use crossterm::{ + cursor::position, + event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode}, + execute, + terminal::{disable_raw_mode, enable_raw_mode}, + Result, +}; + +const HELP: &str = r#"EventStream based on futures_util::stream::Stream with async-std + - Keyboard, mouse and terminal resize events enabled + - Prints "." every second if there's no event + - Hit "c" to print current cursor position + - Use Esc to quit +"#; + +async fn print_events() { + let mut reader = EventStream::new(); + + loop { + let mut delay = Delay::new(Duration::from_millis(1_000)).fuse(); + let mut event = reader.next().fuse(); + + select! { + _ = delay => { println!(".\r"); }, + maybe_event = event => { + match maybe_event { + Some(Ok(event)) => { + println!("Event::{:?}\r", event); + + if event == Event::Key(KeyCode::Char('c').into()) { + println!("Cursor position: {:?}\r", position()); + } + + if event == Event::Key(KeyCode::Esc.into()) { + break; + } + } + Some(Err(e)) => println!("Error: {:?}\r", e), + None => break, + } + } + }; + } +} + +fn main() -> Result<()> { + println!("{}", HELP); + + enable_raw_mode()?; + + let mut stdout = stdout(); + execute!(stdout, EnableMouseCapture)?; + + async_std::task::block_on(print_events()); + + execute!(stdout, DisableMouseCapture)?; + + disable_raw_mode() +} diff --git a/vendor/crossterm/examples/event-stream-tokio.rs b/vendor/crossterm/examples/event-stream-tokio.rs index 10e4e63..642eb54 100644 --- a/vendor/crossterm/examples/event-stream-tokio.rs +++ b/vendor/crossterm/examples/event-stream-tokio.rs @@ -1,69 +1,69 @@ -//! Demonstrates how to read events asynchronously with tokio. -//! -//! cargo run --features="event-stream" --example event-stream-tokio - -use std::{io::stdout, time::Duration}; - -use futures::{future::FutureExt, select, StreamExt}; -use futures_timer::Delay; - -use crossterm::{ - cursor::position, - event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode}, - execute, - terminal::{disable_raw_mode, enable_raw_mode}, - Result, -}; - -const HELP: &str = r#"EventStream based on futures_util::Stream with tokio - - Keyboard, mouse and terminal resize events enabled - - Prints "." every second if there's no event - - Hit "c" to print current cursor position - - Use Esc to quit -"#; - -async fn print_events() { - let mut reader = EventStream::new(); - - loop { - let mut delay = Delay::new(Duration::from_millis(1_000)).fuse(); - let mut event = reader.next().fuse(); - - select! { - _ = delay => { println!(".\r"); }, - maybe_event = event => { - match maybe_event { - Some(Ok(event)) => { - println!("Event::{:?}\r", event); - - if event == Event::Key(KeyCode::Char('c').into()) { - println!("Cursor position: {:?}\r", position()); - } - - if event == Event::Key(KeyCode::Esc.into()) { - break; - } - } - Some(Err(e)) => println!("Error: {:?}\r", e), - None => break, - } - } - }; - } -} - -#[tokio::main] -async fn main() -> Result<()> { - println!("{}", HELP); - - enable_raw_mode()?; - - let mut stdout = stdout(); - execute!(stdout, EnableMouseCapture)?; - - print_events().await; - - execute!(stdout, DisableMouseCapture)?; - - disable_raw_mode() -} +//! Demonstrates how to read events asynchronously with tokio. +//! +//! cargo run --features="event-stream" --example event-stream-tokio + +use std::{io::stdout, time::Duration}; + +use futures::{future::FutureExt, select, StreamExt}; +use futures_timer::Delay; + +use crossterm::{ + cursor::position, + event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode}, + execute, + terminal::{disable_raw_mode, enable_raw_mode}, + Result, +}; + +const HELP: &str = r#"EventStream based on futures_util::Stream with tokio + - Keyboard, mouse and terminal resize events enabled + - Prints "." every second if there's no event + - Hit "c" to print current cursor position + - Use Esc to quit +"#; + +async fn print_events() { + let mut reader = EventStream::new(); + + loop { + let mut delay = Delay::new(Duration::from_millis(1_000)).fuse(); + let mut event = reader.next().fuse(); + + select! { + _ = delay => { println!(".\r"); }, + maybe_event = event => { + match maybe_event { + Some(Ok(event)) => { + println!("Event::{:?}\r", event); + + if event == Event::Key(KeyCode::Char('c').into()) { + println!("Cursor position: {:?}\r", position()); + } + + if event == Event::Key(KeyCode::Esc.into()) { + break; + } + } + Some(Err(e)) => println!("Error: {:?}\r", e), + None => break, + } + } + }; + } +} + +#[tokio::main] +async fn main() -> Result<()> { + println!("{}", HELP); + + enable_raw_mode()?; + + let mut stdout = stdout(); + execute!(stdout, EnableMouseCapture)?; + + print_events().await; + + execute!(stdout, DisableMouseCapture)?; + + disable_raw_mode() +} diff --git a/vendor/crossterm/examples/is_tty.rs b/vendor/crossterm/examples/is_tty.rs index 628d9a7..fb7d0f2 100644 --- a/vendor/crossterm/examples/is_tty.rs +++ b/vendor/crossterm/examples/is_tty.rs @@ -1,18 +1,18 @@ -use crossterm::{ - execute, - terminal::{size, SetSize}, - tty::IsTty, -}; -use std::io::{stdin, stdout}; - -pub fn main() { - println!("{:?}", size().unwrap()); - execute!(stdout(), SetSize(10, 10)).unwrap(); - println!("{:?}", size().unwrap()); - - if stdin().is_tty() { - println!("Is TTY"); - } else { - println!("Is not TTY"); - } -} +use crossterm::{ + execute, + terminal::{size, SetSize}, + tty::IsTty, +}; +use std::io::{stdin, stdout}; + +pub fn main() { + println!("{:?}", size().unwrap()); + execute!(stdout(), SetSize(10, 10)).unwrap(); + println!("{:?}", size().unwrap()); + + if stdin().is_tty() { + println!("Is TTY"); + } else { + println!("Is not TTY"); + } +} diff --git a/vendor/crossterm/examples/stderr.rs b/vendor/crossterm/examples/stderr.rs index 6b68188..6697115 100644 --- a/vendor/crossterm/examples/stderr.rs +++ b/vendor/crossterm/examples/stderr.rs @@ -1,96 +1,96 @@ -//! This shows how an application can write on stderr -//! instead of stdout, thus making it possible to -//! the command API instead of the "old style" direct -//! unbuffered API. -//! -//! This particular example is only suited to Unix -//! for now. -//! -//! cargo run --example stderr - -use std::io::{stderr, Write}; - -use crossterm::{ - cursor::{Hide, MoveTo, Show}, - event, - event::{Event, KeyCode, KeyEvent}, - execute, queue, - style::Print, - terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}, - Result, -}; - -const TEXT: &str = r#" -This screen is ran on stderr. -And when you hit enter, it prints on stdout. -This makes it possible to run an application and choose what will -be sent to any application calling yours. - -For example, assuming you build this example with - - cargo build --bin stderr - -and then you run it with - - cd "$(target/debug/stderr)" - -what the application prints on stdout is used as argument to cd. - -Try it out. - -Hit any key to quit this screen: - -1 will print `..` -2 will print `/` -3 will print `~` -Any other key will print this text (so that you may copy-paste) -"#; - -fn run_app(write: &mut W) -> Result -where - W: Write, -{ - queue!( - write, - EnterAlternateScreen, // enter alternate screen - Hide // hide the cursor - )?; - - let mut y = 1; - for line in TEXT.split('\n') { - queue!(write, MoveTo(1, y), Print(line.to_string()))?; - y += 1; - } - - write.flush()?; - - terminal::enable_raw_mode()?; - let user_char = read_char()?; // we wait for the user to hit a key - execute!(write, Show, LeaveAlternateScreen)?; // restore the cursor and leave the alternate screen - - terminal::disable_raw_mode()?; - - Ok(user_char) -} - -pub fn read_char() -> Result { - loop { - if let Event::Key(KeyEvent { - code: KeyCode::Char(c), - .. - }) = event::read()? - { - return Ok(c); - } - } -} - -// cargo run --example stderr -fn main() { - match run_app(&mut stderr()).unwrap() { - '1' => print!(".."), - '2' => print!("/"), - '3' => print!("~"), - _ => println!("{}", TEXT), - } -} +//! This shows how an application can write on stderr +//! instead of stdout, thus making it possible to +//! the command API instead of the "old style" direct +//! unbuffered API. +//! +//! This particular example is only suited to Unix +//! for now. +//! +//! cargo run --example stderr + +use std::io::{stderr, Write}; + +use crossterm::{ + cursor::{Hide, MoveTo, Show}, + event, + event::{Event, KeyCode, KeyEvent}, + execute, queue, + style::Print, + terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}, + Result, +}; + +const TEXT: &str = r#" +This screen is ran on stderr. +And when you hit enter, it prints on stdout. +This makes it possible to run an application and choose what will +be sent to any application calling yours. + +For example, assuming you build this example with + + cargo build --bin stderr + +and then you run it with + + cd "$(target/debug/stderr)" + +what the application prints on stdout is used as argument to cd. + +Try it out. + +Hit any key to quit this screen: + +1 will print `..` +2 will print `/` +3 will print `~` +Any other key will print this text (so that you may copy-paste) +"#; + +fn run_app(write: &mut W) -> Result +where + W: Write, +{ + queue!( + write, + EnterAlternateScreen, // enter alternate screen + Hide // hide the cursor + )?; + + let mut y = 1; + for line in TEXT.split('\n') { + queue!(write, MoveTo(1, y), Print(line.to_string()))?; + y += 1; + } + + write.flush()?; + + terminal::enable_raw_mode()?; + let user_char = read_char()?; // we wait for the user to hit a key + execute!(write, Show, LeaveAlternateScreen)?; // restore the cursor and leave the alternate screen + + terminal::disable_raw_mode()?; + + Ok(user_char) +} + +pub fn read_char() -> Result { + loop { + if let Event::Key(KeyEvent { + code: KeyCode::Char(c), + .. + }) = event::read()? + { + return Ok(c); + } + } +} + +// cargo run --example stderr +fn main() { + match run_app(&mut stderr()).unwrap() { + '1' => print!(".."), + '2' => print!("/"), + '3' => print!("~"), + _ => println!("{}", TEXT), + } +} diff --git a/vendor/crossterm/src/ansi_support.rs b/vendor/crossterm/src/ansi_support.rs index 35081f2..a3416c1 100644 --- a/vendor/crossterm/src/ansi_support.rs +++ b/vendor/crossterm/src/ansi_support.rs @@ -1,48 +1,48 @@ -use std::sync::atomic::{AtomicBool, Ordering}; - -use crossterm_winapi::{ConsoleMode, Handle}; -use parking_lot::Once; -use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; - -use crate::Result; - -/// Enable virtual terminal processing. -/// -/// This method attempts to enable virtual terminal processing for this -/// console. If there was a problem enabling it, then an error returned. -/// On success, the caller may assume that enabling it was successful. -/// -/// When virtual terminal processing is enabled, characters emitted to the -/// console are parsed for VT100 and similar control character sequences -/// that control color and other similar operations. -fn enable_vt_processing() -> Result<()> { - let mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING; - - let console_mode = ConsoleMode::from(Handle::current_out_handle()?); - let old_mode = console_mode.mode()?; - - if old_mode & mask == 0 { - console_mode.set_mode(old_mode | mask)?; - } - - Ok(()) -} - -static SUPPORTS_ANSI_ESCAPE_CODES: AtomicBool = AtomicBool::new(false); -static INITIALIZER: Once = Once::new(); - -/// Checks if the current terminal supports ANSI escape sequences -pub fn supports_ansi() -> bool { - INITIALIZER.call_once(|| { - // Some terminals on Windows like GitBash can't use WinAPI calls directly - // so when we try to enable the ANSI-flag for Windows this won't work. - // Because of that we should check first if the TERM-variable is set - // and see if the current terminal is a terminal who does support ANSI. - let supported = enable_vt_processing().is_ok() - || std::env::var("TERM").map_or(false, |term| term != "dumb"); - - SUPPORTS_ANSI_ESCAPE_CODES.store(supported, Ordering::SeqCst); - }); - - SUPPORTS_ANSI_ESCAPE_CODES.load(Ordering::SeqCst) -} +use std::sync::atomic::{AtomicBool, Ordering}; + +use crossterm_winapi::{ConsoleMode, Handle}; +use parking_lot::Once; +use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; + +use crate::Result; + +/// Enable virtual terminal processing. +/// +/// This method attempts to enable virtual terminal processing for this +/// console. If there was a problem enabling it, then an error returned. +/// On success, the caller may assume that enabling it was successful. +/// +/// When virtual terminal processing is enabled, characters emitted to the +/// console are parsed for VT100 and similar control character sequences +/// that control color and other similar operations. +fn enable_vt_processing() -> Result<()> { + let mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING; + + let console_mode = ConsoleMode::from(Handle::current_out_handle()?); + let old_mode = console_mode.mode()?; + + if old_mode & mask == 0 { + console_mode.set_mode(old_mode | mask)?; + } + + Ok(()) +} + +static SUPPORTS_ANSI_ESCAPE_CODES: AtomicBool = AtomicBool::new(false); +static INITIALIZER: Once = Once::new(); + +/// Checks if the current terminal supports ANSI escape sequences +pub fn supports_ansi() -> bool { + INITIALIZER.call_once(|| { + // Some terminals on Windows like GitBash can't use WinAPI calls directly + // so when we try to enable the ANSI-flag for Windows this won't work. + // Because of that we should check first if the TERM-variable is set + // and see if the current terminal is a terminal who does support ANSI. + let supported = enable_vt_processing().is_ok() + || std::env::var("TERM").map_or(false, |term| term != "dumb"); + + SUPPORTS_ANSI_ESCAPE_CODES.store(supported, Ordering::SeqCst); + }); + + SUPPORTS_ANSI_ESCAPE_CODES.load(Ordering::SeqCst) +} diff --git a/vendor/crossterm/src/command.rs b/vendor/crossterm/src/command.rs index e3cec4b..5766483 100644 --- a/vendor/crossterm/src/command.rs +++ b/vendor/crossterm/src/command.rs @@ -1,230 +1,230 @@ -use std::fmt; -use std::io::{self, Write}; - -use super::error::Result; - -/// An interface for a command that performs an action on the terminal. -/// -/// Crossterm provides a set of commands, -/// and there is no immediate reason to implement a command yourself. -/// In order to understand how to use and execute commands, -/// it is recommended that you take a look at [Command API](./index.html#command-api) chapter. -pub trait Command { - /// Write an ANSI representation of this command to the given writer. - /// An ANSI code can manipulate the terminal by writing it to the terminal buffer. - /// However, only Windows 10 and UNIX systems support this. - /// - /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api) - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result; - - /// Execute this command. - /// - /// Windows versions lower than windows 10 do not support ANSI escape codes, - /// therefore a direct WinAPI call is made. - /// - /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api) - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()>; - - /// Returns whether the ANSI code representation of this command is supported by windows. - /// - /// A list of supported ANSI escape codes - /// can be found [here](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences). - #[cfg(windows)] - fn is_ansi_code_supported(&self) -> bool { - super::ansi_support::supports_ansi() - } -} - -impl Command for &T { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - (**self).write_ansi(f) - } - - #[inline] - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - T::execute_winapi(self) - } - - #[cfg(windows)] - #[inline] - fn is_ansi_code_supported(&self) -> bool { - T::is_ansi_code_supported(self) - } -} - -/// An interface for types that can queue commands for further execution. -pub trait QueueableCommand { - /// Queues the given command for further execution. - fn queue(&mut self, command: impl Command) -> Result<&mut Self>; -} - -/// An interface for types that can directly execute commands. -pub trait ExecutableCommand { - /// Executes the given command directly. - fn execute(&mut self, command: impl Command) -> Result<&mut Self>; -} - -impl QueueableCommand for T { - /// Queues the given command for further execution. - /// - /// Queued commands will be executed in the following cases: - /// - /// * When `flush` is called manually on the given type implementing `io::Write`. - /// * The terminal will `flush` automatically if the buffer is full. - /// * Each line is flushed in case of `stdout`, because it is line buffered. - /// - /// # Arguments - /// - /// - [Command](./trait.Command.html) - /// - /// The command that you want to queue for later execution. - /// - /// # Examples - /// - /// ```rust - /// use std::io::{Write, stdout}; - /// - /// use crossterm::{Result, QueueableCommand, style::Print}; - /// - /// fn main() -> Result<()> { - /// let mut stdout = stdout(); - /// - /// // `Print` will executed executed when `flush` is called. - /// stdout - /// .queue(Print("foo 1\n".to_string()))? - /// .queue(Print("foo 2".to_string()))?; - /// - /// // some other code (no execution happening here) ... - /// - /// // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed. - /// stdout.flush()?; - /// - /// Ok(()) - /// - /// // ==== Output ==== - /// // foo 1 - /// // foo 2 - /// } - /// ``` - /// - /// Have a look over at the [Command API](./index.html#command-api) for more details. - /// - /// # Notes - /// - /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'. - /// * In case of Windows versions lower than 10, a direct WinAPI call will be made. - /// The reason for this is that Windows versions lower than 10 do not support ANSI codes, - /// and can therefore not be written to the given `writer`. - /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html) - /// and [queue](./trait.QueueableCommand.html) for those old Windows versions. - fn queue(&mut self, command: impl Command) -> Result<&mut Self> { - #[cfg(windows)] - if !command.is_ansi_code_supported() { - // There may be queued commands in this writer, but `execute_winapi` will execute the - // command immediately. To prevent commands being executed out of order we flush the - // writer now. - self.flush()?; - command.execute_winapi()?; - return Ok(self); - } - - write_command_ansi(self, command)?; - Ok(self) - } -} - -impl ExecutableCommand for T { - /// Executes the given command directly. - /// - /// The given command its ANSI escape code will be written and flushed onto `Self`. - /// - /// # Arguments - /// - /// - [Command](./trait.Command.html) - /// - /// The command that you want to execute directly. - /// - /// # Example - /// - /// ```rust - /// use std::io::{Write, stdout}; - /// - /// use crossterm::{Result, ExecutableCommand, style::Print}; - /// - /// fn main() -> Result<()> { - /// // will be executed directly - /// stdout() - /// .execute(Print("sum:\n".to_string()))? - /// .execute(Print(format!("1 + 1= {} ", 1 + 1)))?; - /// - /// Ok(()) - /// - /// // ==== Output ==== - /// // sum: - /// // 1 + 1 = 2 - /// } - /// ``` - /// - /// Have a look over at the [Command API](./index.html#command-api) for more details. - /// - /// # Notes - /// - /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'. - /// * In case of Windows versions lower than 10, a direct WinAPI call will be made. - /// The reason for this is that Windows versions lower than 10 do not support ANSI codes, - /// and can therefore not be written to the given `writer`. - /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html) - /// and [queue](./trait.QueueableCommand.html) for those old Windows versions. - fn execute(&mut self, command: impl Command) -> Result<&mut Self> { - self.queue(command)?; - self.flush()?; - Ok(self) - } -} - -/// Writes the ANSI representation of a command to the given writer. -fn write_command_ansi( - io: &mut (impl io::Write + ?Sized), - command: C, -) -> io::Result<()> { - struct Adapter { - inner: T, - res: io::Result<()>, - } - - impl fmt::Write for Adapter { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.inner.write_all(s.as_bytes()).map_err(|e| { - self.res = Err(e); - fmt::Error - }) - } - } - - let mut adapter = Adapter { - inner: io, - res: Ok(()), - }; - - command - .write_ansi(&mut adapter) - .map_err(|fmt::Error| match adapter.res { - Ok(()) => panic!( - "<{}>::write_ansi incorrectly errored", - std::any::type_name::() - ), - Err(e) => e, - }) -} - -/// Executes the ANSI representation of a command, using the given `fmt::Write`. -pub(crate) fn execute_fmt(f: &mut impl fmt::Write, command: impl Command) -> fmt::Result { - #[cfg(windows)] - if !command.is_ansi_code_supported() { - return command.execute_winapi().map_err(|_| fmt::Error); - } - - command.write_ansi(f) -} +use std::fmt; +use std::io::{self, Write}; + +use super::error::Result; + +/// An interface for a command that performs an action on the terminal. +/// +/// Crossterm provides a set of commands, +/// and there is no immediate reason to implement a command yourself. +/// In order to understand how to use and execute commands, +/// it is recommended that you take a look at [Command API](./index.html#command-api) chapter. +pub trait Command { + /// Write an ANSI representation of this command to the given writer. + /// An ANSI code can manipulate the terminal by writing it to the terminal buffer. + /// However, only Windows 10 and UNIX systems support this. + /// + /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api) + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result; + + /// Execute this command. + /// + /// Windows versions lower than windows 10 do not support ANSI escape codes, + /// therefore a direct WinAPI call is made. + /// + /// This method does not need to be accessed manually, as it is used by the crossterm's [Command API](./index.html#command-api) + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()>; + + /// Returns whether the ANSI code representation of this command is supported by windows. + /// + /// A list of supported ANSI escape codes + /// can be found [here](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences). + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + super::ansi_support::supports_ansi() + } +} + +impl Command for &T { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + (**self).write_ansi(f) + } + + #[inline] + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + T::execute_winapi(self) + } + + #[cfg(windows)] + #[inline] + fn is_ansi_code_supported(&self) -> bool { + T::is_ansi_code_supported(self) + } +} + +/// An interface for types that can queue commands for further execution. +pub trait QueueableCommand { + /// Queues the given command for further execution. + fn queue(&mut self, command: impl Command) -> Result<&mut Self>; +} + +/// An interface for types that can directly execute commands. +pub trait ExecutableCommand { + /// Executes the given command directly. + fn execute(&mut self, command: impl Command) -> Result<&mut Self>; +} + +impl QueueableCommand for T { + /// Queues the given command for further execution. + /// + /// Queued commands will be executed in the following cases: + /// + /// * When `flush` is called manually on the given type implementing `io::Write`. + /// * The terminal will `flush` automatically if the buffer is full. + /// * Each line is flushed in case of `stdout`, because it is line buffered. + /// + /// # Arguments + /// + /// - [Command](./trait.Command.html) + /// + /// The command that you want to queue for later execution. + /// + /// # Examples + /// + /// ```rust + /// use std::io::{Write, stdout}; + /// + /// use crossterm::{Result, QueueableCommand, style::Print}; + /// + /// fn main() -> Result<()> { + /// let mut stdout = stdout(); + /// + /// // `Print` will executed executed when `flush` is called. + /// stdout + /// .queue(Print("foo 1\n".to_string()))? + /// .queue(Print("foo 2".to_string()))?; + /// + /// // some other code (no execution happening here) ... + /// + /// // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed. + /// stdout.flush()?; + /// + /// Ok(()) + /// + /// // ==== Output ==== + /// // foo 1 + /// // foo 2 + /// } + /// ``` + /// + /// Have a look over at the [Command API](./index.html#command-api) for more details. + /// + /// # Notes + /// + /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'. + /// * In case of Windows versions lower than 10, a direct WinAPI call will be made. + /// The reason for this is that Windows versions lower than 10 do not support ANSI codes, + /// and can therefore not be written to the given `writer`. + /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html) + /// and [queue](./trait.QueueableCommand.html) for those old Windows versions. + fn queue(&mut self, command: impl Command) -> Result<&mut Self> { + #[cfg(windows)] + if !command.is_ansi_code_supported() { + // There may be queued commands in this writer, but `execute_winapi` will execute the + // command immediately. To prevent commands being executed out of order we flush the + // writer now. + self.flush()?; + command.execute_winapi()?; + return Ok(self); + } + + write_command_ansi(self, command)?; + Ok(self) + } +} + +impl ExecutableCommand for T { + /// Executes the given command directly. + /// + /// The given command its ANSI escape code will be written and flushed onto `Self`. + /// + /// # Arguments + /// + /// - [Command](./trait.Command.html) + /// + /// The command that you want to execute directly. + /// + /// # Example + /// + /// ```rust + /// use std::io::{Write, stdout}; + /// + /// use crossterm::{Result, ExecutableCommand, style::Print}; + /// + /// fn main() -> Result<()> { + /// // will be executed directly + /// stdout() + /// .execute(Print("sum:\n".to_string()))? + /// .execute(Print(format!("1 + 1= {} ", 1 + 1)))?; + /// + /// Ok(()) + /// + /// // ==== Output ==== + /// // sum: + /// // 1 + 1 = 2 + /// } + /// ``` + /// + /// Have a look over at the [Command API](./index.html#command-api) for more details. + /// + /// # Notes + /// + /// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'. + /// * In case of Windows versions lower than 10, a direct WinAPI call will be made. + /// The reason for this is that Windows versions lower than 10 do not support ANSI codes, + /// and can therefore not be written to the given `writer`. + /// Therefore, there is no difference between [execute](./trait.ExecutableCommand.html) + /// and [queue](./trait.QueueableCommand.html) for those old Windows versions. + fn execute(&mut self, command: impl Command) -> Result<&mut Self> { + self.queue(command)?; + self.flush()?; + Ok(self) + } +} + +/// Writes the ANSI representation of a command to the given writer. +fn write_command_ansi( + io: &mut (impl io::Write + ?Sized), + command: C, +) -> io::Result<()> { + struct Adapter { + inner: T, + res: io::Result<()>, + } + + impl fmt::Write for Adapter { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.inner.write_all(s.as_bytes()).map_err(|e| { + self.res = Err(e); + fmt::Error + }) + } + } + + let mut adapter = Adapter { + inner: io, + res: Ok(()), + }; + + command + .write_ansi(&mut adapter) + .map_err(|fmt::Error| match adapter.res { + Ok(()) => panic!( + "<{}>::write_ansi incorrectly errored", + std::any::type_name::() + ), + Err(e) => e, + }) +} + +/// Executes the ANSI representation of a command, using the given `fmt::Write`. +pub(crate) fn execute_fmt(f: &mut impl fmt::Write, command: impl Command) -> fmt::Result { + #[cfg(windows)] + if !command.is_ansi_code_supported() { + return command.execute_winapi().map_err(|_| fmt::Error); + } + + command.write_ansi(f) +} diff --git a/vendor/crossterm/src/cursor.rs b/vendor/crossterm/src/cursor.rs index 7ffd60b..12128c4 100644 --- a/vendor/crossterm/src/cursor.rs +++ b/vendor/crossterm/src/cursor.rs @@ -1,499 +1,499 @@ -//! # Cursor -//! -//! The `cursor` module provides functionality to work with the terminal cursor. -//! -//! This documentation does not contain a lot of examples. The reason is that it's fairly -//! obvious how to use this crate. Although, we do provide -//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository -//! to demonstrate the capabilities. -//! -//! ## Examples -//! -//! Cursor actions can be performed with commands. -//! Please have a look at [command documentation](../index.html#command-api) for a more detailed documentation. -//! -//! ```no_run -//! use std::io::{stdout, Write}; -//! -//! use crossterm::{ -//! ExecutableCommand, execute, Result, -//! cursor::{DisableBlinking, EnableBlinking, MoveTo, RestorePosition, SavePosition} -//! }; -//! -//! fn main() -> Result<()> { -//! // with macro -//! execute!( -//! stdout(), -//! SavePosition, -//! MoveTo(10, 10), -//! EnableBlinking, -//! DisableBlinking, -//! RestorePosition -//! ); -//! -//! // with function -//! stdout() -//! .execute(MoveTo(11,11))? -//! .execute(RestorePosition); -//! -//! Ok(()) -//! } -//! ``` -//! -//! For manual execution control check out [crossterm::queue](../macro.queue.html). - -use std::fmt; - -#[cfg(windows)] -use crate::Result; -use crate::{csi, impl_display, Command}; - -pub use sys::position; - -pub(crate) mod sys; - -/// A command that moves the terminal cursor to the given position (column, row). -/// -/// # Notes -/// * Top left cell is represented as `0,0`. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveTo(pub u16, pub u16); - -impl Command for MoveTo { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{};{}H"), self.1 + 1, self.0 + 1) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_to(self.0, self.1) - } -} - -/// A command that moves the terminal cursor down the given number of lines, -/// and moves it to the first column. -/// -/// # Notes -/// * This command is 1 based, meaning `MoveToNextLine(1)` moves to the next line. -/// * Most terminals default 0 argument to 1. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveToNextLine(pub u16); - -impl Command for MoveToNextLine { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}E"), self.0)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - if self.0 != 0 { - sys::move_to_next_line(self.0)?; - } - Ok(()) - } -} - -/// A command that moves the terminal cursor up the given number of lines, -/// and moves it to the first column. -/// -/// # Notes -/// * This command is 1 based, meaning `MoveToPreviousLine(1)` moves to the previous line. -/// * Most terminals default 0 argument to 1. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveToPreviousLine(pub u16); - -impl Command for MoveToPreviousLine { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}F"), self.0)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - if self.0 != 0 { - sys::move_to_previous_line(self.0)?; - } - Ok(()) - } -} - -/// A command that moves the terminal cursor to the given column on the current row. -/// -/// # Notes -/// * This command is 0 based, meaning 0 is the leftmost column. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveToColumn(pub u16); - -impl Command for MoveToColumn { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}G"), self.0 + 1)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_to_column(self.0) - } -} - -/// A command that moves the terminal cursor to the given row on the current column. -/// -/// # Notes -/// * This command is 0 based, meaning 0 is the topmost row. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveToRow(pub u16); - -impl Command for MoveToRow { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}d"), self.0 + 1)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_to_row(self.0) - } -} - -/// A command that moves the terminal cursor a given number of rows up. -/// -/// # Notes -/// * This command is 1 based, meaning `MoveUp(1)` moves the cursor up one cell. -/// * Most terminals default 0 argument to 1. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveUp(pub u16); - -impl Command for MoveUp { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}A"), self.0)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_up(self.0) - } -} - -/// A command that moves the terminal cursor a given number of columns to the right. -/// -/// # Notes -/// * This command is 1 based, meaning `MoveRight(1)` moves the cursor right one cell. -/// * Most terminals default 0 argument to 1. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveRight(pub u16); - -impl Command for MoveRight { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}C"), self.0)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_right(self.0) - } -} - -/// A command that moves the terminal cursor a given number of rows down. -/// -/// # Notes -/// * This command is 1 based, meaning `MoveDown(1)` moves the cursor down one cell. -/// * Most terminals default 0 argument to 1. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveDown(pub u16); - -impl Command for MoveDown { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}B"), self.0)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_down(self.0) - } -} - -/// A command that moves the terminal cursor a given number of columns to the left. -/// -/// # Notes -/// * This command is 1 based, meaning `MoveLeft(1)` moves the cursor left one cell. -/// * Most terminals default 0 argument to 1. -/// * Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MoveLeft(pub u16); - -impl Command for MoveLeft { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}D"), self.0)?; - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_left(self.0) - } -} - -/// A command that saves the current terminal cursor position. -/// -/// See the [RestorePosition](./struct.RestorePosition.html) command. -/// -/// # Notes -/// -/// - The cursor position is stored globally. -/// - Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SavePosition; - -impl Command for SavePosition { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str("\x1B7") - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::save_position() - } -} - -/// A command that restores the saved terminal cursor position. -/// -/// See the [SavePosition](./struct.SavePosition.html) command. -/// -/// # Notes -/// -/// - The cursor position is stored globally. -/// - Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct RestorePosition; - -impl Command for RestorePosition { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str("\x1B8") - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::restore_position() - } -} - -/// A command that hides the terminal cursor. -/// -/// # Notes -/// -/// - Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Hide; - -impl Command for Hide { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?25l")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::show_cursor(false) - } -} - -/// A command that shows the terminal cursor. -/// -/// # Notes -/// -/// - Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Show; - -impl Command for Show { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?25h")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::show_cursor(true) - } -} - -/// A command that enables blinking of the terminal cursor. -/// -/// # Notes -/// -/// - Windows versions lower than Windows 10 do not support this functionality. -/// - Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EnableBlinking; - -impl Command for EnableBlinking { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?12h")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Ok(()) - } -} - -/// A command that disables blinking of the terminal cursor. -/// -/// # Notes -/// -/// - Windows versions lower than Windows 10 do not support this functionality. -/// - Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DisableBlinking; - -impl Command for DisableBlinking { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?12l")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Ok(()) - } -} - -/// All supported cursor shapes -/// -/// # Note -/// -/// - Used with SetCursorShape -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CursorShape { - UnderScore, - Line, - Block, -} - -/// A command that sets the shape of the cursor -/// -/// # Note -/// -/// - Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetCursorShape(pub CursorShape); - -impl Command for SetCursorShape { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - use CursorShape::*; - match self.0 { - UnderScore => f.write_str("\x1b[3 q"), - Line => f.write_str("\x1b[5 q"), - Block => f.write_str("\x1b[2 q"), - } - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Ok(()) - } -} - -impl_display!(for MoveTo); -impl_display!(for MoveToColumn); -impl_display!(for MoveToRow); -impl_display!(for MoveToNextLine); -impl_display!(for MoveToPreviousLine); -impl_display!(for MoveUp); -impl_display!(for MoveDown); -impl_display!(for MoveLeft); -impl_display!(for MoveRight); -impl_display!(for SavePosition); -impl_display!(for RestorePosition); -impl_display!(for Hide); -impl_display!(for Show); -impl_display!(for EnableBlinking); -impl_display!(for DisableBlinking); -impl_display!(for SetCursorShape); - -#[cfg(test)] -mod tests { - use std::io::{self, stdout}; - - use crate::execute; - - use super::{ - position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition, - }; - - // Test is disabled, because it's failing on Travis - #[test] - #[ignore] - fn test_move_to() { - let (saved_x, saved_y) = position().unwrap(); - - execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap(); - assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1)); - - execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap(); - assert_eq!(position().unwrap(), (saved_x, saved_y)); - } - - // Test is disabled, because it's failing on Travis - #[test] - #[ignore] - fn test_move_right() { - let (saved_x, saved_y) = position().unwrap(); - execute!(io::stdout(), MoveRight(1)).unwrap(); - assert_eq!(position().unwrap(), (saved_x + 1, saved_y)); - } - - // Test is disabled, because it's failing on Travis - #[test] - #[ignore] - fn test_move_left() { - execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap(); - assert_eq!(position().unwrap(), (0, 0)); - } - - // Test is disabled, because it's failing on Travis - #[test] - #[ignore] - fn test_move_up() { - execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap(); - assert_eq!(position().unwrap(), (0, 0)); - } - - // Test is disabled, because it's failing on Travis - #[test] - #[ignore] - fn test_move_down() { - execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap(); - - assert_eq!(position().unwrap(), (0, 2)); - } - - // Test is disabled, because it's failing on Travis - #[test] - #[ignore] - fn test_save_restore_position() { - let (saved_x, saved_y) = position().unwrap(); - - execute!( - stdout(), - SavePosition, - MoveTo(saved_x + 1, saved_y + 1), - RestorePosition - ) - .unwrap(); - - let (x, y) = position().unwrap(); - - assert_eq!(x, saved_x); - assert_eq!(y, saved_y); - } -} +//! # Cursor +//! +//! The `cursor` module provides functionality to work with the terminal cursor. +//! +//! This documentation does not contain a lot of examples. The reason is that it's fairly +//! obvious how to use this crate. Although, we do provide +//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository +//! to demonstrate the capabilities. +//! +//! ## Examples +//! +//! Cursor actions can be performed with commands. +//! Please have a look at [command documentation](../index.html#command-api) for a more detailed documentation. +//! +//! ```no_run +//! use std::io::{stdout, Write}; +//! +//! use crossterm::{ +//! ExecutableCommand, execute, Result, +//! cursor::{DisableBlinking, EnableBlinking, MoveTo, RestorePosition, SavePosition} +//! }; +//! +//! fn main() -> Result<()> { +//! // with macro +//! execute!( +//! stdout(), +//! SavePosition, +//! MoveTo(10, 10), +//! EnableBlinking, +//! DisableBlinking, +//! RestorePosition +//! ); +//! +//! // with function +//! stdout() +//! .execute(MoveTo(11,11))? +//! .execute(RestorePosition); +//! +//! Ok(()) +//! } +//! ``` +//! +//! For manual execution control check out [crossterm::queue](../macro.queue.html). + +use std::fmt; + +#[cfg(windows)] +use crate::Result; +use crate::{csi, impl_display, Command}; + +pub use sys::position; + +pub(crate) mod sys; + +/// A command that moves the terminal cursor to the given position (column, row). +/// +/// # Notes +/// * Top left cell is represented as `0,0`. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveTo(pub u16, pub u16); + +impl Command for MoveTo { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{};{}H"), self.1 + 1, self.0 + 1) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_to(self.0, self.1) + } +} + +/// A command that moves the terminal cursor down the given number of lines, +/// and moves it to the first column. +/// +/// # Notes +/// * This command is 1 based, meaning `MoveToNextLine(1)` moves to the next line. +/// * Most terminals default 0 argument to 1. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveToNextLine(pub u16); + +impl Command for MoveToNextLine { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}E"), self.0)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + if self.0 != 0 { + sys::move_to_next_line(self.0)?; + } + Ok(()) + } +} + +/// A command that moves the terminal cursor up the given number of lines, +/// and moves it to the first column. +/// +/// # Notes +/// * This command is 1 based, meaning `MoveToPreviousLine(1)` moves to the previous line. +/// * Most terminals default 0 argument to 1. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveToPreviousLine(pub u16); + +impl Command for MoveToPreviousLine { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}F"), self.0)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + if self.0 != 0 { + sys::move_to_previous_line(self.0)?; + } + Ok(()) + } +} + +/// A command that moves the terminal cursor to the given column on the current row. +/// +/// # Notes +/// * This command is 0 based, meaning 0 is the leftmost column. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveToColumn(pub u16); + +impl Command for MoveToColumn { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}G"), self.0 + 1)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_to_column(self.0) + } +} + +/// A command that moves the terminal cursor to the given row on the current column. +/// +/// # Notes +/// * This command is 0 based, meaning 0 is the topmost row. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveToRow(pub u16); + +impl Command for MoveToRow { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}d"), self.0 + 1)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_to_row(self.0) + } +} + +/// A command that moves the terminal cursor a given number of rows up. +/// +/// # Notes +/// * This command is 1 based, meaning `MoveUp(1)` moves the cursor up one cell. +/// * Most terminals default 0 argument to 1. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveUp(pub u16); + +impl Command for MoveUp { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}A"), self.0)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_up(self.0) + } +} + +/// A command that moves the terminal cursor a given number of columns to the right. +/// +/// # Notes +/// * This command is 1 based, meaning `MoveRight(1)` moves the cursor right one cell. +/// * Most terminals default 0 argument to 1. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveRight(pub u16); + +impl Command for MoveRight { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}C"), self.0)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_right(self.0) + } +} + +/// A command that moves the terminal cursor a given number of rows down. +/// +/// # Notes +/// * This command is 1 based, meaning `MoveDown(1)` moves the cursor down one cell. +/// * Most terminals default 0 argument to 1. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveDown(pub u16); + +impl Command for MoveDown { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}B"), self.0)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_down(self.0) + } +} + +/// A command that moves the terminal cursor a given number of columns to the left. +/// +/// # Notes +/// * This command is 1 based, meaning `MoveLeft(1)` moves the cursor left one cell. +/// * Most terminals default 0 argument to 1. +/// * Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MoveLeft(pub u16); + +impl Command for MoveLeft { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}D"), self.0)?; + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_left(self.0) + } +} + +/// A command that saves the current terminal cursor position. +/// +/// See the [RestorePosition](./struct.RestorePosition.html) command. +/// +/// # Notes +/// +/// - The cursor position is stored globally. +/// - Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SavePosition; + +impl Command for SavePosition { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str("\x1B7") + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::save_position() + } +} + +/// A command that restores the saved terminal cursor position. +/// +/// See the [SavePosition](./struct.SavePosition.html) command. +/// +/// # Notes +/// +/// - The cursor position is stored globally. +/// - Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RestorePosition; + +impl Command for RestorePosition { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str("\x1B8") + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::restore_position() + } +} + +/// A command that hides the terminal cursor. +/// +/// # Notes +/// +/// - Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Hide; + +impl Command for Hide { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?25l")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::show_cursor(false) + } +} + +/// A command that shows the terminal cursor. +/// +/// # Notes +/// +/// - Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Show; + +impl Command for Show { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?25h")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::show_cursor(true) + } +} + +/// A command that enables blinking of the terminal cursor. +/// +/// # Notes +/// +/// - Windows versions lower than Windows 10 do not support this functionality. +/// - Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EnableBlinking; + +impl Command for EnableBlinking { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?12h")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Ok(()) + } +} + +/// A command that disables blinking of the terminal cursor. +/// +/// # Notes +/// +/// - Windows versions lower than Windows 10 do not support this functionality. +/// - Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DisableBlinking; + +impl Command for DisableBlinking { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?12l")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Ok(()) + } +} + +/// All supported cursor shapes +/// +/// # Note +/// +/// - Used with SetCursorShape +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CursorShape { + UnderScore, + Line, + Block, +} + +/// A command that sets the shape of the cursor +/// +/// # Note +/// +/// - Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetCursorShape(pub CursorShape); + +impl Command for SetCursorShape { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + use CursorShape::*; + match self.0 { + UnderScore => f.write_str("\x1b[3 q"), + Line => f.write_str("\x1b[5 q"), + Block => f.write_str("\x1b[2 q"), + } + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Ok(()) + } +} + +impl_display!(for MoveTo); +impl_display!(for MoveToColumn); +impl_display!(for MoveToRow); +impl_display!(for MoveToNextLine); +impl_display!(for MoveToPreviousLine); +impl_display!(for MoveUp); +impl_display!(for MoveDown); +impl_display!(for MoveLeft); +impl_display!(for MoveRight); +impl_display!(for SavePosition); +impl_display!(for RestorePosition); +impl_display!(for Hide); +impl_display!(for Show); +impl_display!(for EnableBlinking); +impl_display!(for DisableBlinking); +impl_display!(for SetCursorShape); + +#[cfg(test)] +mod tests { + use std::io::{self, stdout}; + + use crate::execute; + + use super::{ + position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition, + }; + + // Test is disabled, because it's failing on Travis + #[test] + #[ignore] + fn test_move_to() { + let (saved_x, saved_y) = position().unwrap(); + + execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap(); + assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1)); + + execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap(); + assert_eq!(position().unwrap(), (saved_x, saved_y)); + } + + // Test is disabled, because it's failing on Travis + #[test] + #[ignore] + fn test_move_right() { + let (saved_x, saved_y) = position().unwrap(); + execute!(io::stdout(), MoveRight(1)).unwrap(); + assert_eq!(position().unwrap(), (saved_x + 1, saved_y)); + } + + // Test is disabled, because it's failing on Travis + #[test] + #[ignore] + fn test_move_left() { + execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap(); + assert_eq!(position().unwrap(), (0, 0)); + } + + // Test is disabled, because it's failing on Travis + #[test] + #[ignore] + fn test_move_up() { + execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap(); + assert_eq!(position().unwrap(), (0, 0)); + } + + // Test is disabled, because it's failing on Travis + #[test] + #[ignore] + fn test_move_down() { + execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap(); + + assert_eq!(position().unwrap(), (0, 2)); + } + + // Test is disabled, because it's failing on Travis + #[test] + #[ignore] + fn test_save_restore_position() { + let (saved_x, saved_y) = position().unwrap(); + + execute!( + stdout(), + SavePosition, + MoveTo(saved_x + 1, saved_y + 1), + RestorePosition + ) + .unwrap(); + + let (x, y) = position().unwrap(); + + assert_eq!(x, saved_x); + assert_eq!(y, saved_y); + } +} diff --git a/vendor/crossterm/src/cursor/sys.rs b/vendor/crossterm/src/cursor/sys.rs index b63a2bb..fd4de44 100644 --- a/vendor/crossterm/src/cursor/sys.rs +++ b/vendor/crossterm/src/cursor/sys.rs @@ -1,17 +1,17 @@ -//! This module provides platform related functions. - -#[cfg(unix)] -pub use self::unix::position; -#[cfg(windows)] -pub use self::windows::position; -#[cfg(windows)] -pub(crate) use self::windows::{ - move_down, move_left, move_right, move_to, move_to_column, move_to_next_line, - move_to_previous_line, move_to_row, move_up, restore_position, save_position, show_cursor, -}; - -#[cfg(windows)] -pub(crate) mod windows; - -#[cfg(unix)] -pub(crate) mod unix; +//! This module provides platform related functions. + +#[cfg(unix)] +pub use self::unix::position; +#[cfg(windows)] +pub use self::windows::position; +#[cfg(windows)] +pub(crate) use self::windows::{ + move_down, move_left, move_right, move_to, move_to_column, move_to_next_line, + move_to_previous_line, move_to_row, move_up, restore_position, save_position, show_cursor, +}; + +#[cfg(windows)] +pub(crate) mod windows; + +#[cfg(unix)] +pub(crate) mod unix; diff --git a/vendor/crossterm/src/cursor/sys/unix.rs b/vendor/crossterm/src/cursor/sys/unix.rs index f610be1..5f9254d 100644 --- a/vendor/crossterm/src/cursor/sys/unix.rs +++ b/vendor/crossterm/src/cursor/sys/unix.rs @@ -1,57 +1,57 @@ -use std::{ - io::{self, Error, ErrorKind, Write}, - time::Duration, -}; - -use crate::{ - event::{filter::CursorPositionFilter, poll_internal, read_internal, InternalEvent}, - terminal::{disable_raw_mode, enable_raw_mode, sys::is_raw_mode_enabled}, - Result, -}; - -/// Returns the cursor position (column, row). -/// -/// The top left cell is represented as `(0, 0)`. -/// -/// On unix systems, this function will block and possibly time out while -/// [`crossterm::event::read`](crate::event::read) or [`crossterm::event::poll`](crate::event::poll) are being called. -pub fn position() -> Result<(u16, u16)> { - if is_raw_mode_enabled() { - read_position_raw() - } else { - read_position() - } -} - -fn read_position() -> Result<(u16, u16)> { - enable_raw_mode()?; - let pos = read_position_raw(); - disable_raw_mode()?; - pos -} - -fn read_position_raw() -> Result<(u16, u16)> { - // Use `ESC [ 6 n` to and retrieve the cursor position. - let mut stdout = io::stdout(); - stdout.write_all(b"\x1B[6n")?; - stdout.flush()?; - - loop { - match poll_internal(Some(Duration::from_millis(2000)), &CursorPositionFilter) { - Ok(true) => { - if let Ok(InternalEvent::CursorPosition(x, y)) = - read_internal(&CursorPositionFilter) - { - return Ok((x, y)); - } - } - Ok(false) => { - return Err(Error::new( - ErrorKind::Other, - "The cursor position could not be read within a normal duration", - )); - } - Err(_) => {} - } - } -} +use std::{ + io::{self, Error, ErrorKind, Write}, + time::Duration, +}; + +use crate::{ + event::{filter::CursorPositionFilter, poll_internal, read_internal, InternalEvent}, + terminal::{disable_raw_mode, enable_raw_mode, sys::is_raw_mode_enabled}, + Result, +}; + +/// Returns the cursor position (column, row). +/// +/// The top left cell is represented as `(0, 0)`. +/// +/// On unix systems, this function will block and possibly time out while +/// [`crossterm::event::read`](crate::event::read) or [`crossterm::event::poll`](crate::event::poll) are being called. +pub fn position() -> Result<(u16, u16)> { + if is_raw_mode_enabled() { + read_position_raw() + } else { + read_position() + } +} + +fn read_position() -> Result<(u16, u16)> { + enable_raw_mode()?; + let pos = read_position_raw(); + disable_raw_mode()?; + pos +} + +fn read_position_raw() -> Result<(u16, u16)> { + // Use `ESC [ 6 n` to and retrieve the cursor position. + let mut stdout = io::stdout(); + stdout.write_all(b"\x1B[6n")?; + stdout.flush()?; + + loop { + match poll_internal(Some(Duration::from_millis(2000)), &CursorPositionFilter) { + Ok(true) => { + if let Ok(InternalEvent::CursorPosition(x, y)) = + read_internal(&CursorPositionFilter) + { + return Ok((x, y)); + } + } + Ok(false) => { + return Err(Error::new( + ErrorKind::Other, + "The cursor position could not be read within a normal duration", + )); + } + Err(_) => {} + } + } +} diff --git a/vendor/crossterm/src/cursor/sys/windows.rs b/vendor/crossterm/src/cursor/sys/windows.rs index 06da423..05f5ad6 100644 --- a/vendor/crossterm/src/cursor/sys/windows.rs +++ b/vendor/crossterm/src/cursor/sys/windows.rs @@ -1,317 +1,317 @@ -//! WinAPI related logic to cursor manipulation. - -use std::convert::TryFrom; -use std::io; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crossterm_winapi::{result, Coord, Handle, HandleType, ScreenBuffer}; -use winapi::{ - shared::minwindef::{FALSE, TRUE}, - um::wincon::{SetConsoleCursorInfo, SetConsoleCursorPosition, CONSOLE_CURSOR_INFO, COORD}, -}; - -use crate::Result; - -/// The position of the cursor, written when you save the cursor's position. -/// -/// This is `u64::MAX` initially. Otherwise, it stores the cursor's x position bit-shifted left 16 -/// times or-ed with the cursor's y position, where both are `i16`s. -static SAVED_CURSOR_POS: AtomicU64 = AtomicU64::new(u64::MAX); - -// The 'y' position of the cursor is not relative to the window but absolute to screen buffer. -// We can calculate the relative cursor position by subtracting the top position of the terminal window from the y position. -// This results in an 1-based coord zo subtract 1 to make cursor position 0-based. -pub fn parse_relative_y(y: i16) -> Result { - let window = ScreenBuffer::current()?.info()?; - - let window_size = window.terminal_window(); - let screen_size = window.terminal_size(); - - if y <= screen_size.height { - Ok(y) - } else { - Ok(y - window_size.top) - } -} - -/// Returns the cursor position (column, row). -/// -/// The top left cell is represented `0,0`. -pub fn position() -> Result<(u16, u16)> { - let cursor = ScreenBufferCursor::output()?; - let mut position = cursor.position()?; - // if position.y != 0 { - position.y = parse_relative_y(position.y)?; - // } - Ok(position.into()) -} - -pub(crate) fn show_cursor(show_cursor: bool) -> Result<()> { - ScreenBufferCursor::from(Handle::current_out_handle()?).set_visibility(show_cursor) -} - -pub(crate) fn move_to(column: u16, row: u16) -> Result<()> { - let cursor = ScreenBufferCursor::output()?; - cursor.move_to(column as i16, row as i16)?; - Ok(()) -} - -pub(crate) fn move_up(count: u16) -> Result<()> { - let (column, row) = position()?; - move_to(column, row - count)?; - Ok(()) -} - -pub(crate) fn move_right(count: u16) -> Result<()> { - let (column, row) = position()?; - move_to(column + count, row)?; - Ok(()) -} - -pub(crate) fn move_down(count: u16) -> Result<()> { - let (column, row) = position()?; - move_to(column, row + count)?; - Ok(()) -} - -pub(crate) fn move_left(count: u16) -> Result<()> { - let (column, row) = position()?; - move_to(column - count, row)?; - Ok(()) -} - -pub(crate) fn move_to_column(new_column: u16) -> Result<()> { - let (_, row) = position()?; - move_to(new_column, row)?; - Ok(()) -} - -pub(crate) fn move_to_row(new_row: u16) -> Result<()> { - let (col, _) = position()?; - move_to(col, new_row)?; - Ok(()) -} - -pub(crate) fn move_to_next_line(count: u16) -> Result<()> { - let (_, row) = position()?; - move_to(0, row + count)?; - Ok(()) -} - -pub(crate) fn move_to_previous_line(count: u16) -> Result<()> { - let (_, row) = position()?; - move_to(0, row - count)?; - Ok(()) -} - -pub(crate) fn save_position() -> Result<()> { - ScreenBufferCursor::output()?.save_position()?; - Ok(()) -} - -pub(crate) fn restore_position() -> Result<()> { - ScreenBufferCursor::output()?.restore_position()?; - Ok(()) -} - -/// WinAPI wrapper over terminal cursor behaviour. -struct ScreenBufferCursor { - screen_buffer: ScreenBuffer, -} - -impl ScreenBufferCursor { - fn output() -> Result { - Ok(ScreenBufferCursor { - screen_buffer: ScreenBuffer::from(Handle::new(HandleType::CurrentOutputHandle)?), - }) - } - - fn position(&self) -> Result { - Ok(self.screen_buffer.info()?.cursor_pos()) - } - - fn move_to(&self, x: i16, y: i16) -> Result<()> { - if x < 0 { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Argument Out of Range Exception when setting cursor position to X: {}", - x - ), - )); - } - - if y < 0 { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Argument Out of Range Exception when setting cursor position to Y: {}", - y - ), - )); - } - - let position = COORD { X: x, Y: y }; - - unsafe { - if result(SetConsoleCursorPosition( - **self.screen_buffer.handle(), - position, - )) - .is_err() - { - return Err(io::Error::last_os_error()); - } - } - Ok(()) - } - - fn set_visibility(&self, visible: bool) -> Result<()> { - let cursor_info = CONSOLE_CURSOR_INFO { - dwSize: 100, - bVisible: if visible { TRUE } else { FALSE }, - }; - - unsafe { - if result(SetConsoleCursorInfo( - **self.screen_buffer.handle(), - &cursor_info, - )) - .is_err() - { - return Err(io::Error::last_os_error()); - } - } - Ok(()) - } - - fn restore_position(&self) -> Result<()> { - if let Ok(val) = u32::try_from(SAVED_CURSOR_POS.load(Ordering::Relaxed)) { - let x = (val >> 16) as i16; - let y = val as i16; - self.move_to(x, y)?; - } - - Ok(()) - } - - fn save_position(&self) -> Result<()> { - let position = self.position()?; - - let bits = u64::from(u32::from(position.x as u16) << 16 | u32::from(position.y as u16)); - SAVED_CURSOR_POS.store(bits, Ordering::Relaxed); - - Ok(()) - } -} - -impl From for ScreenBufferCursor { - fn from(handle: Handle) -> Self { - ScreenBufferCursor { - screen_buffer: ScreenBuffer::from(handle), - } - } -} - -#[cfg(test)] -mod tests { - use super::{ - move_down, move_left, move_right, move_to, move_to_column, move_to_next_line, - move_to_previous_line, move_to_row, move_up, position, restore_position, save_position, - }; - - #[test] - fn test_move_to_winapi() { - let (saved_x, saved_y) = position().unwrap(); - - move_to(saved_x + 1, saved_y + 1).unwrap(); - assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1)); - - move_to(saved_x, saved_y).unwrap(); - assert_eq!(position().unwrap(), (saved_x, saved_y)); - } - - #[test] - fn test_move_right_winapi() { - let (saved_x, saved_y) = position().unwrap(); - move_right(1).unwrap(); - assert_eq!(position().unwrap(), (saved_x + 1, saved_y)); - } - - #[test] - fn test_move_left_winapi() { - move_to(2, 0).unwrap(); - - move_left(2).unwrap(); - - assert_eq!(position().unwrap(), (0, 0)); - } - - #[test] - fn test_move_up_winapi() { - move_to(0, 2).unwrap(); - - move_up(2).unwrap(); - - assert_eq!(position().unwrap(), (0, 0)); - } - - #[test] - fn test_move_to_next_line_winapi() { - move_to(0, 2).unwrap(); - - move_to_next_line(2).unwrap(); - - assert_eq!(position().unwrap(), (0, 4)); - } - - #[test] - fn test_move_to_previous_line_winapi() { - move_to(0, 2).unwrap(); - - move_to_previous_line(2).unwrap(); - - assert_eq!(position().unwrap(), (0, 0)); - } - - #[test] - fn test_move_to_column_winapi() { - move_to(0, 2).unwrap(); - - move_to_column(12).unwrap(); - - assert_eq!(position().unwrap(), (12, 2)); - } - - #[test] - fn test_move_to_row_winapi() { - move_to(0, 2).unwrap(); - - move_to_row(5).unwrap(); - - assert_eq!(position().unwrap(), (0, 5)); - } - - #[test] - fn test_move_down_winapi() { - move_to(0, 0).unwrap(); - - move_down(2).unwrap(); - - assert_eq!(position().unwrap(), (0, 2)); - } - - #[test] - fn test_save_restore_position_winapi() { - let (saved_x, saved_y) = position().unwrap(); - - save_position().unwrap(); - move_to(saved_x + 1, saved_y + 1).unwrap(); - restore_position().unwrap(); - - let (x, y) = position().unwrap(); - - assert_eq!(x, saved_x); - assert_eq!(y, saved_y); - } -} +//! WinAPI related logic to cursor manipulation. + +use std::convert::TryFrom; +use std::io; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crossterm_winapi::{result, Coord, Handle, HandleType, ScreenBuffer}; +use winapi::{ + shared::minwindef::{FALSE, TRUE}, + um::wincon::{SetConsoleCursorInfo, SetConsoleCursorPosition, CONSOLE_CURSOR_INFO, COORD}, +}; + +use crate::Result; + +/// The position of the cursor, written when you save the cursor's position. +/// +/// This is `u64::MAX` initially. Otherwise, it stores the cursor's x position bit-shifted left 16 +/// times or-ed with the cursor's y position, where both are `i16`s. +static SAVED_CURSOR_POS: AtomicU64 = AtomicU64::new(u64::MAX); + +// The 'y' position of the cursor is not relative to the window but absolute to screen buffer. +// We can calculate the relative cursor position by subtracting the top position of the terminal window from the y position. +// This results in an 1-based coord zo subtract 1 to make cursor position 0-based. +pub fn parse_relative_y(y: i16) -> Result { + let window = ScreenBuffer::current()?.info()?; + + let window_size = window.terminal_window(); + let screen_size = window.terminal_size(); + + if y <= screen_size.height { + Ok(y) + } else { + Ok(y - window_size.top) + } +} + +/// Returns the cursor position (column, row). +/// +/// The top left cell is represented `0,0`. +pub fn position() -> Result<(u16, u16)> { + let cursor = ScreenBufferCursor::output()?; + let mut position = cursor.position()?; + // if position.y != 0 { + position.y = parse_relative_y(position.y)?; + // } + Ok(position.into()) +} + +pub(crate) fn show_cursor(show_cursor: bool) -> Result<()> { + ScreenBufferCursor::from(Handle::current_out_handle()?).set_visibility(show_cursor) +} + +pub(crate) fn move_to(column: u16, row: u16) -> Result<()> { + let cursor = ScreenBufferCursor::output()?; + cursor.move_to(column as i16, row as i16)?; + Ok(()) +} + +pub(crate) fn move_up(count: u16) -> Result<()> { + let (column, row) = position()?; + move_to(column, row - count)?; + Ok(()) +} + +pub(crate) fn move_right(count: u16) -> Result<()> { + let (column, row) = position()?; + move_to(column + count, row)?; + Ok(()) +} + +pub(crate) fn move_down(count: u16) -> Result<()> { + let (column, row) = position()?; + move_to(column, row + count)?; + Ok(()) +} + +pub(crate) fn move_left(count: u16) -> Result<()> { + let (column, row) = position()?; + move_to(column - count, row)?; + Ok(()) +} + +pub(crate) fn move_to_column(new_column: u16) -> Result<()> { + let (_, row) = position()?; + move_to(new_column, row)?; + Ok(()) +} + +pub(crate) fn move_to_row(new_row: u16) -> Result<()> { + let (col, _) = position()?; + move_to(col, new_row)?; + Ok(()) +} + +pub(crate) fn move_to_next_line(count: u16) -> Result<()> { + let (_, row) = position()?; + move_to(0, row + count)?; + Ok(()) +} + +pub(crate) fn move_to_previous_line(count: u16) -> Result<()> { + let (_, row) = position()?; + move_to(0, row - count)?; + Ok(()) +} + +pub(crate) fn save_position() -> Result<()> { + ScreenBufferCursor::output()?.save_position()?; + Ok(()) +} + +pub(crate) fn restore_position() -> Result<()> { + ScreenBufferCursor::output()?.restore_position()?; + Ok(()) +} + +/// WinAPI wrapper over terminal cursor behaviour. +struct ScreenBufferCursor { + screen_buffer: ScreenBuffer, +} + +impl ScreenBufferCursor { + fn output() -> Result { + Ok(ScreenBufferCursor { + screen_buffer: ScreenBuffer::from(Handle::new(HandleType::CurrentOutputHandle)?), + }) + } + + fn position(&self) -> Result { + Ok(self.screen_buffer.info()?.cursor_pos()) + } + + fn move_to(&self, x: i16, y: i16) -> Result<()> { + if x < 0 { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Argument Out of Range Exception when setting cursor position to X: {}", + x + ), + )); + } + + if y < 0 { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Argument Out of Range Exception when setting cursor position to Y: {}", + y + ), + )); + } + + let position = COORD { X: x, Y: y }; + + unsafe { + if result(SetConsoleCursorPosition( + **self.screen_buffer.handle(), + position, + )) + .is_err() + { + return Err(io::Error::last_os_error()); + } + } + Ok(()) + } + + fn set_visibility(&self, visible: bool) -> Result<()> { + let cursor_info = CONSOLE_CURSOR_INFO { + dwSize: 100, + bVisible: if visible { TRUE } else { FALSE }, + }; + + unsafe { + if result(SetConsoleCursorInfo( + **self.screen_buffer.handle(), + &cursor_info, + )) + .is_err() + { + return Err(io::Error::last_os_error()); + } + } + Ok(()) + } + + fn restore_position(&self) -> Result<()> { + if let Ok(val) = u32::try_from(SAVED_CURSOR_POS.load(Ordering::Relaxed)) { + let x = (val >> 16) as i16; + let y = val as i16; + self.move_to(x, y)?; + } + + Ok(()) + } + + fn save_position(&self) -> Result<()> { + let position = self.position()?; + + let bits = u64::from(u32::from(position.x as u16) << 16 | u32::from(position.y as u16)); + SAVED_CURSOR_POS.store(bits, Ordering::Relaxed); + + Ok(()) + } +} + +impl From for ScreenBufferCursor { + fn from(handle: Handle) -> Self { + ScreenBufferCursor { + screen_buffer: ScreenBuffer::from(handle), + } + } +} + +#[cfg(test)] +mod tests { + use super::{ + move_down, move_left, move_right, move_to, move_to_column, move_to_next_line, + move_to_previous_line, move_to_row, move_up, position, restore_position, save_position, + }; + + #[test] + fn test_move_to_winapi() { + let (saved_x, saved_y) = position().unwrap(); + + move_to(saved_x + 1, saved_y + 1).unwrap(); + assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1)); + + move_to(saved_x, saved_y).unwrap(); + assert_eq!(position().unwrap(), (saved_x, saved_y)); + } + + #[test] + fn test_move_right_winapi() { + let (saved_x, saved_y) = position().unwrap(); + move_right(1).unwrap(); + assert_eq!(position().unwrap(), (saved_x + 1, saved_y)); + } + + #[test] + fn test_move_left_winapi() { + move_to(2, 0).unwrap(); + + move_left(2).unwrap(); + + assert_eq!(position().unwrap(), (0, 0)); + } + + #[test] + fn test_move_up_winapi() { + move_to(0, 2).unwrap(); + + move_up(2).unwrap(); + + assert_eq!(position().unwrap(), (0, 0)); + } + + #[test] + fn test_move_to_next_line_winapi() { + move_to(0, 2).unwrap(); + + move_to_next_line(2).unwrap(); + + assert_eq!(position().unwrap(), (0, 4)); + } + + #[test] + fn test_move_to_previous_line_winapi() { + move_to(0, 2).unwrap(); + + move_to_previous_line(2).unwrap(); + + assert_eq!(position().unwrap(), (0, 0)); + } + + #[test] + fn test_move_to_column_winapi() { + move_to(0, 2).unwrap(); + + move_to_column(12).unwrap(); + + assert_eq!(position().unwrap(), (12, 2)); + } + + #[test] + fn test_move_to_row_winapi() { + move_to(0, 2).unwrap(); + + move_to_row(5).unwrap(); + + assert_eq!(position().unwrap(), (0, 5)); + } + + #[test] + fn test_move_down_winapi() { + move_to(0, 0).unwrap(); + + move_down(2).unwrap(); + + assert_eq!(position().unwrap(), (0, 2)); + } + + #[test] + fn test_save_restore_position_winapi() { + let (saved_x, saved_y) = position().unwrap(); + + save_position().unwrap(); + move_to(saved_x + 1, saved_y + 1).unwrap(); + restore_position().unwrap(); + + let (x, y) = position().unwrap(); + + assert_eq!(x, saved_x); + assert_eq!(y, saved_y); + } +} diff --git a/vendor/crossterm/src/error.rs b/vendor/crossterm/src/error.rs index 500c9fa..f6a4d12 100644 --- a/vendor/crossterm/src/error.rs +++ b/vendor/crossterm/src/error.rs @@ -1,8 +1,8 @@ -//! Module containing error handling logic. - -use std::io; - -/// The `crossterm` result type. -pub type Result = std::result::Result; - -pub type ErrorKind = io::Error; +//! Module containing error handling logic. + +use std::io; + +/// The `crossterm` result type. +pub type Result = std::result::Result; + +pub type ErrorKind = io::Error; diff --git a/vendor/crossterm/src/event.rs b/vendor/crossterm/src/event.rs index ab89575..d353654 100644 --- a/vendor/crossterm/src/event.rs +++ b/vendor/crossterm/src/event.rs @@ -1,973 +1,973 @@ -//! # Event -//! -//! The `event` module provides the functionality to read keyboard, mouse and terminal resize events. -//! -//! * The [`read`](fn.read.html) function returns an [`Event`](enum.Event.html) immediately -//! (if available) or blocks until an [`Event`](enum.Event.html) is available. -//! -//! * The [`poll`](fn.poll.html) function allows you to check if there is or isn't an [`Event`](enum.Event.html) available -//! within the given period of time. In other words - if subsequent call to the [`read`](fn.read.html) -//! function will block or not. -//! -//! It's **not allowed** to call these functions from different threads or combine them with the -//! [`EventStream`](struct.EventStream.html). You're allowed to either: -//! -//! * use the [`read`](fn.read.html) & [`poll`](fn.poll.html) functions on any, but same, thread -//! * or the [`EventStream`](struct.EventStream.html). -//! -//! **Make sure to enable [raw mode](../terminal/index.html#raw-mode) in order for keyboard events to work properly** -//! -//! ## Mouse Events -//! -//! Mouse events are not enabled by default. You have to enable them with the -//! [`EnableMouseCapture`](struct.EnableMouseCapture.html) command. See [Command API](../index.html#command-api) -//! for more information. -//! -//! ## Examples -//! -//! Blocking read: -//! -//! ```no_run -//! use crossterm::event::{read, Event}; -//! -//! fn print_events() -> crossterm::Result<()> { -//! loop { -//! // `read()` blocks until an `Event` is available -//! match read()? { -//! Event::FocusGained => println!("FocusGained"), -//! Event::FocusLost => println!("FocusLost"), -//! Event::Key(event) => println!("{:?}", event), -//! Event::Mouse(event) => println!("{:?}", event), -//! #[cfg(feature = "bracketed-paste")] -//! Event::Paste(data) => println!("{:?}", data), -//! Event::Resize(width, height) => println!("New size {}x{}", width, height), -//! } -//! } -//! Ok(()) -//! } -//! ``` -//! -//! Non-blocking read: -//! -//! ```no_run -//! use std::time::Duration; -//! -//! use crossterm::event::{poll, read, Event}; -//! -//! fn print_events() -> crossterm::Result<()> { -//! loop { -//! // `poll()` waits for an `Event` for a given time period -//! if poll(Duration::from_millis(500))? { -//! // It's guaranteed that the `read()` won't block when the `poll()` -//! // function returns `true` -//! match read()? { -//! Event::FocusGained => println!("FocusGained"), -//! Event::FocusLost => println!("FocusLost"), -//! Event::Key(event) => println!("{:?}", event), -//! Event::Mouse(event) => println!("{:?}", event), -//! #[cfg(feature = "bracketed-paste")] -//! Event::Paste(data) => println!("Pasted {:?}", data), -//! Event::Resize(width, height) => println!("New size {}x{}", width, height), -//! } -//! } else { -//! // Timeout expired and no `Event` is available -//! } -//! } -//! Ok(()) -//! } -//! ``` -//! -//! Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder for more of -//! them (`event-*`). - -use std::fmt; -use std::hash::{Hash, Hasher}; -#[cfg(windows)] -use std::io; -use std::time::Duration; - -use bitflags::bitflags; -use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use crate::{csi, Command, Result}; -use filter::{EventFilter, Filter}; -use read::InternalEventReader; -#[cfg(feature = "event-stream")] -pub use stream::EventStream; -use timeout::PollTimeout; - -pub(crate) mod filter; -mod read; -mod source; -#[cfg(feature = "event-stream")] -mod stream; -pub(crate) mod sys; -mod timeout; - -/// Static instance of `InternalEventReader`. -/// This needs to be static because there can be one event reader. -static INTERNAL_EVENT_READER: Mutex> = parking_lot::const_mutex(None); - -fn lock_internal_event_reader() -> MappedMutexGuard<'static, InternalEventReader> { - MutexGuard::map(INTERNAL_EVENT_READER.lock(), |reader| { - reader.get_or_insert_with(InternalEventReader::default) - }) -} -fn try_lock_internal_event_reader_for( - duration: Duration, -) -> Option> { - Some(MutexGuard::map( - INTERNAL_EVENT_READER.try_lock_for(duration)?, - |reader| reader.get_or_insert_with(InternalEventReader::default), - )) -} - -/// Checks if there is an [`Event`](enum.Event.html) available. -/// -/// Returns `Ok(true)` if an [`Event`](enum.Event.html) is available otherwise it returns `Ok(false)`. -/// -/// `Ok(true)` guarantees that subsequent call to the [`read`](fn.read.html) function -/// won't block. -/// -/// # Arguments -/// -/// * `timeout` - maximum waiting time for event availability -/// -/// # Examples -/// -/// Return immediately: -/// -/// ```no_run -/// use std::time::Duration; -/// -/// use crossterm::{event::poll, Result}; -/// -/// fn is_event_available() -> Result { -/// // Zero duration says that the `poll` function must return immediately -/// // with an `Event` availability information -/// poll(Duration::from_secs(0)) -/// } -/// ``` -/// -/// Wait up to 100ms: -/// -/// ```no_run -/// use std::time::Duration; -/// -/// use crossterm::{event::poll, Result}; -/// -/// fn is_event_available() -> Result { -/// // Wait for an `Event` availability for 100ms. It returns immediately -/// // if an `Event` is/becomes available. -/// poll(Duration::from_millis(100)) -/// } -/// ``` -pub fn poll(timeout: Duration) -> Result { - poll_internal(Some(timeout), &EventFilter) -} - -/// Reads a single [`Event`](enum.Event.html). -/// -/// This function blocks until an [`Event`](enum.Event.html) is available. Combine it with the -/// [`poll`](fn.poll.html) function to get non-blocking reads. -/// -/// # Examples -/// -/// Blocking read: -/// -/// ```no_run -/// use crossterm::{event::read, Result}; -/// -/// fn print_events() -> Result { -/// loop { -/// // Blocks until an `Event` is available -/// println!("{:?}", read()?); -/// } -/// } -/// ``` -/// -/// Non-blocking read: -/// -/// ```no_run -/// use std::time::Duration; -/// -/// use crossterm::{event::{read, poll}, Result}; -/// -/// fn print_events() -> Result { -/// loop { -/// if poll(Duration::from_millis(100))? { -/// // It's guaranteed that `read` won't block, because `poll` returned -/// // `Ok(true)`. -/// println!("{:?}", read()?); -/// } else { -/// // Timeout expired, no `Event` is available -/// } -/// } -/// } -/// ``` -pub fn read() -> Result { - match read_internal(&EventFilter)? { - InternalEvent::Event(event) => Ok(event), - #[cfg(unix)] - _ => unreachable!(), - } -} - -/// Polls to check if there are any `InternalEvent`s that can be read within the given duration. -pub(crate) fn poll_internal(timeout: Option, filter: &F) -> Result -where - F: Filter, -{ - let (mut reader, timeout) = if let Some(timeout) = timeout { - let poll_timeout = PollTimeout::new(Some(timeout)); - if let Some(reader) = try_lock_internal_event_reader_for(timeout) { - (reader, poll_timeout.leftover()) - } else { - return Ok(false); - } - } else { - (lock_internal_event_reader(), None) - }; - reader.poll(timeout, filter) -} - -/// Reads a single `InternalEvent`. -pub(crate) fn read_internal(filter: &F) -> Result -where - F: Filter, -{ - let mut reader = lock_internal_event_reader(); - reader.read(filter) -} - -/// A command that enables mouse event capturing. -/// -/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EnableMouseCapture; - -impl Command for EnableMouseCapture { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(concat!( - // Normal tracking: Send mouse X & Y on button press and release - csi!("?1000h"), - // Button-event tracking: Report button motion events (dragging) - csi!("?1002h"), - // Any-event tracking: Report all motion events - csi!("?1003h"), - // RXVT mouse mode: Allows mouse coordinates of >223 - csi!("?1015h"), - // SGR mouse mode: Allows mouse coordinates of >223, preferred over RXVT mode - csi!("?1006h"), - )) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::windows::enable_mouse_capture() - } - - #[cfg(windows)] - fn is_ansi_code_supported(&self) -> bool { - false - } -} - -/// A command that disables mouse event capturing. -/// -/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DisableMouseCapture; - -impl Command for DisableMouseCapture { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(concat!( - // The inverse commands of EnableMouseCapture, in reverse order. - csi!("?1006l"), - csi!("?1015l"), - csi!("?1003l"), - csi!("?1002l"), - csi!("?1000l"), - )) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::windows::disable_mouse_capture() - } - - #[cfg(windows)] - fn is_ansi_code_supported(&self) -> bool { - false - } -} - -bitflags! { - /// Represents special flags that tell compatible terminals to add extra information to keyboard events. - /// - /// See for more information. - /// - /// Alternate keys and Unicode codepoints are not yet supported by crossterm. - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - pub struct KeyboardEnhancementFlags: u8 { - /// Represent Escape and modified keys using CSI-u sequences, so they can be unambiguously - /// read. - const DISAMBIGUATE_ESCAPE_CODES = 0b0000_0001; - /// Add extra events with [`KeyEvent.kind`] set to [`KeyEventKind::Repeat`] or - /// [`KeyEventKind::Release`] when keys are autorepeated or released. - const REPORT_EVENT_TYPES = 0b0000_0010; - // Send [alternate keycodes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-codes) - // in addition to the base keycode. - // - // *Note*: these are not yet supported by crossterm. - // const REPORT_ALTERNATE_KEYS = 0b0000_0100; - /// Represent all keyboard events as CSI-u sequences. This is required to get repeat/release - /// events for plain-text keys. - const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 0b0000_1000; - // Send the Unicode codepoint as well as the keycode. - // - // *Note*: this is not yet supported by crossterm. - // const REPORT_ASSOCIATED_TEXT = 0b0001_0000; - } -} - -/// A command that enables the [kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/), which adds extra information to keyboard events and removes ambiguity for modifier keys. -/// -/// It should be paired with [`PopKeyboardEnhancementFlags`] at the end of execution. -/// -/// Example usage: -/// ```no_run -/// use std::io::{Write, stdout}; -/// use crossterm::execute; -/// use crossterm::event::{ -/// KeyboardEnhancementFlags, -/// PushKeyboardEnhancementFlags, -/// PopKeyboardEnhancementFlags -/// }; -/// -/// let mut stdout = stdout(); -/// -/// execute!( -/// stdout, -/// PushKeyboardEnhancementFlags( -/// KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES -/// ) -/// ); -/// -/// // ... -/// -/// execute!(stdout, PopKeyboardEnhancementFlags); -/// ``` -/// -/// Note that, currently, only the following support this protocol: -/// * [kitty terminal](https://sw.kovidgoyal.net/kitty/) -/// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319) -/// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html) -/// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131) -/// * [neovim text editor](https://github.com/neovim/neovim/pull/18181) -/// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103) -/// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138) -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct PushKeyboardEnhancementFlags(pub KeyboardEnhancementFlags); - -impl Command for PushKeyboardEnhancementFlags { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, "{}{}u", csi!(">"), self.0.bits()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Err(io::Error::new( - io::ErrorKind::Unsupported, - "Keyboard progressive enhancement not implemented for the legacy Windows API.", - )) - } - - #[cfg(windows)] - fn is_ansi_code_supported(&self) -> bool { - false - } -} - -/// A command that disables extra kinds of keyboard events. -/// -/// Specifically, it pops one level of keyboard enhancement flags. -/// -/// See [`PushKeyboardEnhancementFlags`] and for more information. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct PopKeyboardEnhancementFlags; - -impl Command for PopKeyboardEnhancementFlags { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("<1u")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Err(io::Error::new( - io::ErrorKind::Unsupported, - "Keyboard progressive enhancement not implemented for the legacy Windows API.", - )) - } - - #[cfg(windows)] - fn is_ansi_code_supported(&self) -> bool { - false - } -} - -/// A command that enables focus event emission. -/// -/// It should be paired with [`DisableFocusChange`] at the end of execution. -/// -/// Focus events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EnableFocusChange; - -impl Command for EnableFocusChange { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?1004h")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // Focus events are always enabled on Windows - Ok(()) - } -} - -/// A command that disables focus event emission. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DisableFocusChange; - -impl Command for DisableFocusChange { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?1004l")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // Focus events can't be disabled on Windows - Ok(()) - } -} - -/// A command that enables [bracketed paste mode](https://en.wikipedia.org/wiki/Bracketed-paste). -/// -/// It should be paired with [`DisableBracketedPaste`] at the end of execution. -/// -/// This is not supported in older Windows terminals without -/// [virtual terminal sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences). -#[cfg(feature = "bracketed-paste")] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EnableBracketedPaste; - -#[cfg(feature = "bracketed-paste")] -impl Command for EnableBracketedPaste { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?2004h")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Err(io::Error::new( - io::ErrorKind::Unsupported, - "Bracketed paste not implemented in the legacy Windows API.", - )) - } -} - -/// A command that disables bracketed paste mode. -#[cfg(feature = "bracketed-paste")] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DisableBracketedPaste; - -#[cfg(feature = "bracketed-paste")] -impl Command for DisableBracketedPaste { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?2004l")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Ok(()) - } -} - -/// Represents an event. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(not(feature = "bracketed-paste"), derive(Copy))] -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)] -pub enum Event { - /// The terminal gained focus - FocusGained, - /// The terminal lost focus - FocusLost, - /// A single key event with additional pressed modifiers. - Key(KeyEvent), - /// A single mouse event with additional pressed modifiers. - Mouse(MouseEvent), - /// A string that was pasted into the terminal. Only emitted if bracketed paste has been - /// enabled. - #[cfg(feature = "bracketed-paste")] - Paste(String), - /// An resize event with new dimensions after resize (columns, rows). - /// **Note** that resize events can be occur in batches. - Resize(u16, u16), -} - -/// Represents a mouse event. -/// -/// # Platform-specific Notes -/// -/// ## Mouse Buttons -/// -/// Some platforms/terminals do not report mouse button for the -/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left` -/// is returned if we don't know which button was used. -/// -/// ## Key Modifiers -/// -/// Some platforms/terminals does not report all key modifiers -/// combinations for all mouse event types. For example - macOS reports -/// `Ctrl` + left mouse button click as a right mouse button click. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -pub struct MouseEvent { - /// The kind of mouse event that was caused. - pub kind: MouseEventKind, - /// The column that the event occurred on. - pub column: u16, - /// The row that the event occurred on. - pub row: u16, - /// The key modifiers active when the event occurred. - pub modifiers: KeyModifiers, -} - -/// A mouse event kind. -/// -/// # Platform-specific Notes -/// -/// ## Mouse Buttons -/// -/// Some platforms/terminals do not report mouse button for the -/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left` -/// is returned if we don't know which button was used. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -pub enum MouseEventKind { - /// Pressed mouse button. Contains the button that was pressed. - Down(MouseButton), - /// Released mouse button. Contains the button that was released. - Up(MouseButton), - /// Moved the mouse cursor while pressing the contained mouse button. - Drag(MouseButton), - /// Moved the mouse cursor while not pressing a mouse button. - Moved, - /// Scrolled mouse wheel downwards (towards the user). - ScrollDown, - /// Scrolled mouse wheel upwards (away from the user). - ScrollUp, -} - -/// Represents a mouse button. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -pub enum MouseButton { - /// Left mouse button. - Left, - /// Right mouse button. - Right, - /// Middle mouse button. - Middle, -} - -bitflags! { - /// Represents key modifiers (shift, control, alt, etc.). - /// - /// **Note:** `SUPER`, `HYPER`, and `META` can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - pub struct KeyModifiers: u8 { - const SHIFT = 0b0000_0001; - const CONTROL = 0b0000_0010; - const ALT = 0b0000_0100; - const SUPER = 0b0000_1000; - const HYPER = 0b0001_0000; - const META = 0b0010_0000; - const NONE = 0b0000_0000; - } -} - -/// Represents a keyboard event kind. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -pub enum KeyEventKind { - Press, - Repeat, - Release, -} - -bitflags! { - /// Represents extra state about the key event. - /// - /// **Note:** This state can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - pub struct KeyEventState: u8 { - /// The key event origins from the keypad. - const KEYPAD = 0b0000_0001; - /// Caps Lock was enabled for this key event. - /// - /// **Note:** this is set for the initial press of Num Lock itself. - const CAPS_LOCK = 0b0000_1000; - /// Num Lock was enabled for this key event. - /// - /// **Note:** this is set for the initial press of Num Lock itself. - const NUM_LOCK = 0b0000_1000; - const NONE = 0b0000_0000; - } -} - -/// Represents a key event. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, Clone, Copy)] -pub struct KeyEvent { - /// The key itself. - pub code: KeyCode, - /// Additional key modifiers. - pub modifiers: KeyModifiers, - /// Kind of event. - pub kind: KeyEventKind, - /// Keyboard state. - /// - /// Only set if [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - pub state: KeyEventState, -} - -impl KeyEvent { - pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent { - KeyEvent { - code, - modifiers, - kind: KeyEventKind::Press, - state: KeyEventState::empty(), - } - } - - pub const fn new_with_kind( - code: KeyCode, - modifiers: KeyModifiers, - kind: KeyEventKind, - ) -> KeyEvent { - KeyEvent { - code, - modifiers, - kind, - state: KeyEventState::empty(), - } - } - - pub const fn new_with_kind_and_state( - code: KeyCode, - modifiers: KeyModifiers, - kind: KeyEventKind, - state: KeyEventState, - ) -> KeyEvent { - KeyEvent { - code, - modifiers, - kind, - state, - } - } - - // modifies the KeyEvent, - // so that KeyModifiers::SHIFT is present iff - // an uppercase char is present. - fn normalize_case(mut self) -> KeyEvent { - let c = match self.code { - KeyCode::Char(c) => c, - _ => return self, - }; - - if c.is_ascii_uppercase() { - self.modifiers.insert(KeyModifiers::SHIFT); - } else if self.modifiers.contains(KeyModifiers::SHIFT) { - self.code = KeyCode::Char(c.to_ascii_uppercase()) - } - self - } -} - -impl From for KeyEvent { - fn from(code: KeyCode) -> Self { - KeyEvent { - code, - modifiers: KeyModifiers::empty(), - kind: KeyEventKind::Press, - state: KeyEventState::empty(), - } - } -} - -impl PartialEq for KeyEvent { - fn eq(&self, other: &KeyEvent) -> bool { - let KeyEvent { - code: lhs_code, - modifiers: lhs_modifiers, - kind: lhs_kind, - state: lhs_state, - } = self.normalize_case(); - let KeyEvent { - code: rhs_code, - modifiers: rhs_modifiers, - kind: rhs_kind, - state: rhs_state, - } = other.normalize_case(); - (lhs_code == rhs_code) - && (lhs_modifiers == rhs_modifiers) - && (lhs_kind == rhs_kind) - && (lhs_state == rhs_state) - } -} - -impl Eq for KeyEvent {} - -impl Hash for KeyEvent { - fn hash(&self, hash_state: &mut H) { - let KeyEvent { - code, - modifiers, - kind, - state, - } = self.normalize_case(); - code.hash(hash_state); - modifiers.hash(hash_state); - kind.hash(hash_state); - state.hash(hash_state); - } -} - -/// Represents a media key (as part of [`KeyCode::Media`]). -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MediaKeyCode { - /// Play media key. - Play, - /// Pause media key. - Pause, - /// Play/Pause media key. - PlayPause, - /// Reverse media key. - Reverse, - /// Stop media key. - Stop, - /// Fast-forward media key. - FastForward, - /// Rewind media key. - Rewind, - /// Next-track media key. - TrackNext, - /// Previous-track media key. - TrackPrevious, - /// Record media key. - Record, - /// Lower-volume media key. - LowerVolume, - /// Raise-volume media key. - RaiseVolume, - /// Mute media key. - MuteVolume, -} - -/// Represents a modifier key (as part of [`KeyCode::Modifier`]). -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum ModifierKeyCode { - /// Left Shift key. - LeftShift, - /// Left Control key. - LeftControl, - /// Left Alt key. - LeftAlt, - /// Left Super key. - LeftSuper, - /// Left Hyper key. - LeftHyper, - /// Left Meta key. - LeftMeta, - /// Right Shift key. - RightShift, - /// Right Control key. - RightControl, - /// Right Alt key. - RightAlt, - /// Right Super key. - RightSuper, - /// Right Hyper key. - RightHyper, - /// Right Meta key. - RightMeta, - /// Iso Level3 Shift key. - IsoLevel3Shift, - /// Iso Level5 Shift key. - IsoLevel5Shift, -} - -/// Represents a key. -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum KeyCode { - /// Backspace key. - Backspace, - /// Enter key. - Enter, - /// Left arrow key. - Left, - /// Right arrow key. - Right, - /// Up arrow key. - Up, - /// Down arrow key. - Down, - /// Home key. - Home, - /// End key. - End, - /// Page up key. - PageUp, - /// Page down key. - PageDown, - /// Tab key. - Tab, - /// Shift + Tab key. - BackTab, - /// Delete key. - Delete, - /// Insert key. - Insert, - /// F key. - /// - /// `KeyCode::F(1)` represents F1 key, etc. - F(u8), - /// A character. - /// - /// `KeyCode::Char('c')` represents `c` character, etc. - Char(char), - /// Null. - Null, - /// Escape key. - Esc, - /// Caps Lock key. - /// - /// **Note:** this key can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - CapsLock, - /// Scroll Lock key. - /// - /// **Note:** this key can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - ScrollLock, - /// Num Lock key. - /// - /// **Note:** this key can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - NumLock, - /// Print Screen key. - /// - /// **Note:** this key can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - PrintScreen, - /// Pause key. - /// - /// **Note:** this key can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - Pause, - /// Menu key. - /// - /// **Note:** this key can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - Menu, - /// The "Begin" key (often mapped to the 5 key when Num Lock is turned on). - /// - /// **Note:** this key can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - KeypadBegin, - /// A media key. - /// - /// **Note:** these keys can only be read if - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with - /// [`PushKeyboardEnhancementFlags`]. - Media(MediaKeyCode), - /// A modifier key. - /// - /// **Note:** these keys can only be read if **both** - /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] and - /// [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] have been enabled with - /// [`PushKeyboardEnhancementFlags`]. - Modifier(ModifierKeyCode), -} - -/// An internal event. -/// -/// Encapsulates publicly available `Event` with additional internal -/// events that shouldn't be publicly available to the crate users. -#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Eq)] -pub(crate) enum InternalEvent { - /// An event. - Event(Event), - /// A cursor position (`col`, `row`). - #[cfg(unix)] - CursorPosition(u16, u16), -} - -#[cfg(test)] -mod tests { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - - use super::{KeyCode, KeyEvent, KeyModifiers}; - - #[test] - fn test_equality() { - let lowercase_d_with_shift = KeyEvent::new(KeyCode::Char('d'), KeyModifiers::SHIFT); - let uppercase_d_with_shift = KeyEvent::new(KeyCode::Char('D'), KeyModifiers::SHIFT); - let uppercase_d = KeyEvent::new(KeyCode::Char('D'), KeyModifiers::NONE); - assert_eq!(lowercase_d_with_shift, uppercase_d_with_shift); - assert_eq!(uppercase_d, uppercase_d_with_shift); - } - - #[test] - fn test_hash() { - let lowercase_d_with_shift_hash = { - let mut hasher = DefaultHasher::new(); - KeyEvent::new(KeyCode::Char('d'), KeyModifiers::SHIFT).hash(&mut hasher); - hasher.finish() - }; - let uppercase_d_with_shift_hash = { - let mut hasher = DefaultHasher::new(); - KeyEvent::new(KeyCode::Char('D'), KeyModifiers::SHIFT).hash(&mut hasher); - hasher.finish() - }; - let uppercase_d_hash = { - let mut hasher = DefaultHasher::new(); - KeyEvent::new(KeyCode::Char('D'), KeyModifiers::NONE).hash(&mut hasher); - hasher.finish() - }; - assert_eq!(lowercase_d_with_shift_hash, uppercase_d_with_shift_hash); - assert_eq!(uppercase_d_hash, uppercase_d_with_shift_hash); - } -} +//! # Event +//! +//! The `event` module provides the functionality to read keyboard, mouse and terminal resize events. +//! +//! * The [`read`](fn.read.html) function returns an [`Event`](enum.Event.html) immediately +//! (if available) or blocks until an [`Event`](enum.Event.html) is available. +//! +//! * The [`poll`](fn.poll.html) function allows you to check if there is or isn't an [`Event`](enum.Event.html) available +//! within the given period of time. In other words - if subsequent call to the [`read`](fn.read.html) +//! function will block or not. +//! +//! It's **not allowed** to call these functions from different threads or combine them with the +//! [`EventStream`](struct.EventStream.html). You're allowed to either: +//! +//! * use the [`read`](fn.read.html) & [`poll`](fn.poll.html) functions on any, but same, thread +//! * or the [`EventStream`](struct.EventStream.html). +//! +//! **Make sure to enable [raw mode](../terminal/index.html#raw-mode) in order for keyboard events to work properly** +//! +//! ## Mouse Events +//! +//! Mouse events are not enabled by default. You have to enable them with the +//! [`EnableMouseCapture`](struct.EnableMouseCapture.html) command. See [Command API](../index.html#command-api) +//! for more information. +//! +//! ## Examples +//! +//! Blocking read: +//! +//! ```no_run +//! use crossterm::event::{read, Event}; +//! +//! fn print_events() -> crossterm::Result<()> { +//! loop { +//! // `read()` blocks until an `Event` is available +//! match read()? { +//! Event::FocusGained => println!("FocusGained"), +//! Event::FocusLost => println!("FocusLost"), +//! Event::Key(event) => println!("{:?}", event), +//! Event::Mouse(event) => println!("{:?}", event), +//! #[cfg(feature = "bracketed-paste")] +//! Event::Paste(data) => println!("{:?}", data), +//! Event::Resize(width, height) => println!("New size {}x{}", width, height), +//! } +//! } +//! Ok(()) +//! } +//! ``` +//! +//! Non-blocking read: +//! +//! ```no_run +//! use std::time::Duration; +//! +//! use crossterm::event::{poll, read, Event}; +//! +//! fn print_events() -> crossterm::Result<()> { +//! loop { +//! // `poll()` waits for an `Event` for a given time period +//! if poll(Duration::from_millis(500))? { +//! // It's guaranteed that the `read()` won't block when the `poll()` +//! // function returns `true` +//! match read()? { +//! Event::FocusGained => println!("FocusGained"), +//! Event::FocusLost => println!("FocusLost"), +//! Event::Key(event) => println!("{:?}", event), +//! Event::Mouse(event) => println!("{:?}", event), +//! #[cfg(feature = "bracketed-paste")] +//! Event::Paste(data) => println!("Pasted {:?}", data), +//! Event::Resize(width, height) => println!("New size {}x{}", width, height), +//! } +//! } else { +//! // Timeout expired and no `Event` is available +//! } +//! } +//! Ok(()) +//! } +//! ``` +//! +//! Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder for more of +//! them (`event-*`). + +use std::fmt; +use std::hash::{Hash, Hasher}; +#[cfg(windows)] +use std::io; +use std::time::Duration; + +use bitflags::bitflags; +use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use crate::{csi, Command, Result}; +use filter::{EventFilter, Filter}; +use read::InternalEventReader; +#[cfg(feature = "event-stream")] +pub use stream::EventStream; +use timeout::PollTimeout; + +pub(crate) mod filter; +mod read; +mod source; +#[cfg(feature = "event-stream")] +mod stream; +pub(crate) mod sys; +mod timeout; + +/// Static instance of `InternalEventReader`. +/// This needs to be static because there can be one event reader. +static INTERNAL_EVENT_READER: Mutex> = parking_lot::const_mutex(None); + +fn lock_internal_event_reader() -> MappedMutexGuard<'static, InternalEventReader> { + MutexGuard::map(INTERNAL_EVENT_READER.lock(), |reader| { + reader.get_or_insert_with(InternalEventReader::default) + }) +} +fn try_lock_internal_event_reader_for( + duration: Duration, +) -> Option> { + Some(MutexGuard::map( + INTERNAL_EVENT_READER.try_lock_for(duration)?, + |reader| reader.get_or_insert_with(InternalEventReader::default), + )) +} + +/// Checks if there is an [`Event`](enum.Event.html) available. +/// +/// Returns `Ok(true)` if an [`Event`](enum.Event.html) is available otherwise it returns `Ok(false)`. +/// +/// `Ok(true)` guarantees that subsequent call to the [`read`](fn.read.html) function +/// won't block. +/// +/// # Arguments +/// +/// * `timeout` - maximum waiting time for event availability +/// +/// # Examples +/// +/// Return immediately: +/// +/// ```no_run +/// use std::time::Duration; +/// +/// use crossterm::{event::poll, Result}; +/// +/// fn is_event_available() -> Result { +/// // Zero duration says that the `poll` function must return immediately +/// // with an `Event` availability information +/// poll(Duration::from_secs(0)) +/// } +/// ``` +/// +/// Wait up to 100ms: +/// +/// ```no_run +/// use std::time::Duration; +/// +/// use crossterm::{event::poll, Result}; +/// +/// fn is_event_available() -> Result { +/// // Wait for an `Event` availability for 100ms. It returns immediately +/// // if an `Event` is/becomes available. +/// poll(Duration::from_millis(100)) +/// } +/// ``` +pub fn poll(timeout: Duration) -> Result { + poll_internal(Some(timeout), &EventFilter) +} + +/// Reads a single [`Event`](enum.Event.html). +/// +/// This function blocks until an [`Event`](enum.Event.html) is available. Combine it with the +/// [`poll`](fn.poll.html) function to get non-blocking reads. +/// +/// # Examples +/// +/// Blocking read: +/// +/// ```no_run +/// use crossterm::{event::read, Result}; +/// +/// fn print_events() -> Result { +/// loop { +/// // Blocks until an `Event` is available +/// println!("{:?}", read()?); +/// } +/// } +/// ``` +/// +/// Non-blocking read: +/// +/// ```no_run +/// use std::time::Duration; +/// +/// use crossterm::{event::{read, poll}, Result}; +/// +/// fn print_events() -> Result { +/// loop { +/// if poll(Duration::from_millis(100))? { +/// // It's guaranteed that `read` won't block, because `poll` returned +/// // `Ok(true)`. +/// println!("{:?}", read()?); +/// } else { +/// // Timeout expired, no `Event` is available +/// } +/// } +/// } +/// ``` +pub fn read() -> Result { + match read_internal(&EventFilter)? { + InternalEvent::Event(event) => Ok(event), + #[cfg(unix)] + _ => unreachable!(), + } +} + +/// Polls to check if there are any `InternalEvent`s that can be read within the given duration. +pub(crate) fn poll_internal(timeout: Option, filter: &F) -> Result +where + F: Filter, +{ + let (mut reader, timeout) = if let Some(timeout) = timeout { + let poll_timeout = PollTimeout::new(Some(timeout)); + if let Some(reader) = try_lock_internal_event_reader_for(timeout) { + (reader, poll_timeout.leftover()) + } else { + return Ok(false); + } + } else { + (lock_internal_event_reader(), None) + }; + reader.poll(timeout, filter) +} + +/// Reads a single `InternalEvent`. +pub(crate) fn read_internal(filter: &F) -> Result +where + F: Filter, +{ + let mut reader = lock_internal_event_reader(); + reader.read(filter) +} + +/// A command that enables mouse event capturing. +/// +/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EnableMouseCapture; + +impl Command for EnableMouseCapture { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(concat!( + // Normal tracking: Send mouse X & Y on button press and release + csi!("?1000h"), + // Button-event tracking: Report button motion events (dragging) + csi!("?1002h"), + // Any-event tracking: Report all motion events + csi!("?1003h"), + // RXVT mouse mode: Allows mouse coordinates of >223 + csi!("?1015h"), + // SGR mouse mode: Allows mouse coordinates of >223, preferred over RXVT mode + csi!("?1006h"), + )) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::windows::enable_mouse_capture() + } + + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + false + } +} + +/// A command that disables mouse event capturing. +/// +/// Mouse events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DisableMouseCapture; + +impl Command for DisableMouseCapture { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(concat!( + // The inverse commands of EnableMouseCapture, in reverse order. + csi!("?1006l"), + csi!("?1015l"), + csi!("?1003l"), + csi!("?1002l"), + csi!("?1000l"), + )) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::windows::disable_mouse_capture() + } + + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + false + } +} + +bitflags! { + /// Represents special flags that tell compatible terminals to add extra information to keyboard events. + /// + /// See for more information. + /// + /// Alternate keys and Unicode codepoints are not yet supported by crossterm. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct KeyboardEnhancementFlags: u8 { + /// Represent Escape and modified keys using CSI-u sequences, so they can be unambiguously + /// read. + const DISAMBIGUATE_ESCAPE_CODES = 0b0000_0001; + /// Add extra events with [`KeyEvent.kind`] set to [`KeyEventKind::Repeat`] or + /// [`KeyEventKind::Release`] when keys are autorepeated or released. + const REPORT_EVENT_TYPES = 0b0000_0010; + // Send [alternate keycodes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-codes) + // in addition to the base keycode. + // + // *Note*: these are not yet supported by crossterm. + // const REPORT_ALTERNATE_KEYS = 0b0000_0100; + /// Represent all keyboard events as CSI-u sequences. This is required to get repeat/release + /// events for plain-text keys. + const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 0b0000_1000; + // Send the Unicode codepoint as well as the keycode. + // + // *Note*: this is not yet supported by crossterm. + // const REPORT_ASSOCIATED_TEXT = 0b0001_0000; + } +} + +/// A command that enables the [kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/), which adds extra information to keyboard events and removes ambiguity for modifier keys. +/// +/// It should be paired with [`PopKeyboardEnhancementFlags`] at the end of execution. +/// +/// Example usage: +/// ```no_run +/// use std::io::{Write, stdout}; +/// use crossterm::execute; +/// use crossterm::event::{ +/// KeyboardEnhancementFlags, +/// PushKeyboardEnhancementFlags, +/// PopKeyboardEnhancementFlags +/// }; +/// +/// let mut stdout = stdout(); +/// +/// execute!( +/// stdout, +/// PushKeyboardEnhancementFlags( +/// KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES +/// ) +/// ); +/// +/// // ... +/// +/// execute!(stdout, PopKeyboardEnhancementFlags); +/// ``` +/// +/// Note that, currently, only the following support this protocol: +/// * [kitty terminal](https://sw.kovidgoyal.net/kitty/) +/// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319) +/// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html) +/// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131) +/// * [neovim text editor](https://github.com/neovim/neovim/pull/18181) +/// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103) +/// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PushKeyboardEnhancementFlags(pub KeyboardEnhancementFlags); + +impl Command for PushKeyboardEnhancementFlags { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "{}{}u", csi!(">"), self.0.bits()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Keyboard progressive enhancement not implemented for the legacy Windows API.", + )) + } + + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + false + } +} + +/// A command that disables extra kinds of keyboard events. +/// +/// Specifically, it pops one level of keyboard enhancement flags. +/// +/// See [`PushKeyboardEnhancementFlags`] and for more information. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PopKeyboardEnhancementFlags; + +impl Command for PopKeyboardEnhancementFlags { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("<1u")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Keyboard progressive enhancement not implemented for the legacy Windows API.", + )) + } + + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + false + } +} + +/// A command that enables focus event emission. +/// +/// It should be paired with [`DisableFocusChange`] at the end of execution. +/// +/// Focus events can be captured with [read](./fn.read.html)/[poll](./fn.poll.html). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EnableFocusChange; + +impl Command for EnableFocusChange { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?1004h")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // Focus events are always enabled on Windows + Ok(()) + } +} + +/// A command that disables focus event emission. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DisableFocusChange; + +impl Command for DisableFocusChange { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?1004l")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // Focus events can't be disabled on Windows + Ok(()) + } +} + +/// A command that enables [bracketed paste mode](https://en.wikipedia.org/wiki/Bracketed-paste). +/// +/// It should be paired with [`DisableBracketedPaste`] at the end of execution. +/// +/// This is not supported in older Windows terminals without +/// [virtual terminal sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences). +#[cfg(feature = "bracketed-paste")] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EnableBracketedPaste; + +#[cfg(feature = "bracketed-paste")] +impl Command for EnableBracketedPaste { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?2004h")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Bracketed paste not implemented in the legacy Windows API.", + )) + } +} + +/// A command that disables bracketed paste mode. +#[cfg(feature = "bracketed-paste")] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DisableBracketedPaste; + +#[cfg(feature = "bracketed-paste")] +impl Command for DisableBracketedPaste { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?2004l")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Ok(()) + } +} + +/// Represents an event. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(not(feature = "bracketed-paste"), derive(Copy))] +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)] +pub enum Event { + /// The terminal gained focus + FocusGained, + /// The terminal lost focus + FocusLost, + /// A single key event with additional pressed modifiers. + Key(KeyEvent), + /// A single mouse event with additional pressed modifiers. + Mouse(MouseEvent), + /// A string that was pasted into the terminal. Only emitted if bracketed paste has been + /// enabled. + #[cfg(feature = "bracketed-paste")] + Paste(String), + /// An resize event with new dimensions after resize (columns, rows). + /// **Note** that resize events can be occur in batches. + Resize(u16, u16), +} + +/// Represents a mouse event. +/// +/// # Platform-specific Notes +/// +/// ## Mouse Buttons +/// +/// Some platforms/terminals do not report mouse button for the +/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left` +/// is returned if we don't know which button was used. +/// +/// ## Key Modifiers +/// +/// Some platforms/terminals does not report all key modifiers +/// combinations for all mouse event types. For example - macOS reports +/// `Ctrl` + left mouse button click as a right mouse button click. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +pub struct MouseEvent { + /// The kind of mouse event that was caused. + pub kind: MouseEventKind, + /// The column that the event occurred on. + pub column: u16, + /// The row that the event occurred on. + pub row: u16, + /// The key modifiers active when the event occurred. + pub modifiers: KeyModifiers, +} + +/// A mouse event kind. +/// +/// # Platform-specific Notes +/// +/// ## Mouse Buttons +/// +/// Some platforms/terminals do not report mouse button for the +/// `MouseEventKind::Up` and `MouseEventKind::Drag` events. `MouseButton::Left` +/// is returned if we don't know which button was used. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +pub enum MouseEventKind { + /// Pressed mouse button. Contains the button that was pressed. + Down(MouseButton), + /// Released mouse button. Contains the button that was released. + Up(MouseButton), + /// Moved the mouse cursor while pressing the contained mouse button. + Drag(MouseButton), + /// Moved the mouse cursor while not pressing a mouse button. + Moved, + /// Scrolled mouse wheel downwards (towards the user). + ScrollDown, + /// Scrolled mouse wheel upwards (away from the user). + ScrollUp, +} + +/// Represents a mouse button. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +pub enum MouseButton { + /// Left mouse button. + Left, + /// Right mouse button. + Right, + /// Middle mouse button. + Middle, +} + +bitflags! { + /// Represents key modifiers (shift, control, alt, etc.). + /// + /// **Note:** `SUPER`, `HYPER`, and `META` can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct KeyModifiers: u8 { + const SHIFT = 0b0000_0001; + const CONTROL = 0b0000_0010; + const ALT = 0b0000_0100; + const SUPER = 0b0000_1000; + const HYPER = 0b0001_0000; + const META = 0b0010_0000; + const NONE = 0b0000_0000; + } +} + +/// Represents a keyboard event kind. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +pub enum KeyEventKind { + Press, + Repeat, + Release, +} + +bitflags! { + /// Represents extra state about the key event. + /// + /// **Note:** This state can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct KeyEventState: u8 { + /// The key event origins from the keypad. + const KEYPAD = 0b0000_0001; + /// Caps Lock was enabled for this key event. + /// + /// **Note:** this is set for the initial press of Num Lock itself. + const CAPS_LOCK = 0b0000_1000; + /// Num Lock was enabled for this key event. + /// + /// **Note:** this is set for the initial press of Num Lock itself. + const NUM_LOCK = 0b0000_1000; + const NONE = 0b0000_0000; + } +} + +/// Represents a key event. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialOrd, Clone, Copy)] +pub struct KeyEvent { + /// The key itself. + pub code: KeyCode, + /// Additional key modifiers. + pub modifiers: KeyModifiers, + /// Kind of event. + pub kind: KeyEventKind, + /// Keyboard state. + /// + /// Only set if [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + pub state: KeyEventState, +} + +impl KeyEvent { + pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> KeyEvent { + KeyEvent { + code, + modifiers, + kind: KeyEventKind::Press, + state: KeyEventState::empty(), + } + } + + pub const fn new_with_kind( + code: KeyCode, + modifiers: KeyModifiers, + kind: KeyEventKind, + ) -> KeyEvent { + KeyEvent { + code, + modifiers, + kind, + state: KeyEventState::empty(), + } + } + + pub const fn new_with_kind_and_state( + code: KeyCode, + modifiers: KeyModifiers, + kind: KeyEventKind, + state: KeyEventState, + ) -> KeyEvent { + KeyEvent { + code, + modifiers, + kind, + state, + } + } + + // modifies the KeyEvent, + // so that KeyModifiers::SHIFT is present iff + // an uppercase char is present. + fn normalize_case(mut self) -> KeyEvent { + let c = match self.code { + KeyCode::Char(c) => c, + _ => return self, + }; + + if c.is_ascii_uppercase() { + self.modifiers.insert(KeyModifiers::SHIFT); + } else if self.modifiers.contains(KeyModifiers::SHIFT) { + self.code = KeyCode::Char(c.to_ascii_uppercase()) + } + self + } +} + +impl From for KeyEvent { + fn from(code: KeyCode) -> Self { + KeyEvent { + code, + modifiers: KeyModifiers::empty(), + kind: KeyEventKind::Press, + state: KeyEventState::empty(), + } + } +} + +impl PartialEq for KeyEvent { + fn eq(&self, other: &KeyEvent) -> bool { + let KeyEvent { + code: lhs_code, + modifiers: lhs_modifiers, + kind: lhs_kind, + state: lhs_state, + } = self.normalize_case(); + let KeyEvent { + code: rhs_code, + modifiers: rhs_modifiers, + kind: rhs_kind, + state: rhs_state, + } = other.normalize_case(); + (lhs_code == rhs_code) + && (lhs_modifiers == rhs_modifiers) + && (lhs_kind == rhs_kind) + && (lhs_state == rhs_state) + } +} + +impl Eq for KeyEvent {} + +impl Hash for KeyEvent { + fn hash(&self, hash_state: &mut H) { + let KeyEvent { + code, + modifiers, + kind, + state, + } = self.normalize_case(); + code.hash(hash_state); + modifiers.hash(hash_state); + kind.hash(hash_state); + state.hash(hash_state); + } +} + +/// Represents a media key (as part of [`KeyCode::Media`]). +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum MediaKeyCode { + /// Play media key. + Play, + /// Pause media key. + Pause, + /// Play/Pause media key. + PlayPause, + /// Reverse media key. + Reverse, + /// Stop media key. + Stop, + /// Fast-forward media key. + FastForward, + /// Rewind media key. + Rewind, + /// Next-track media key. + TrackNext, + /// Previous-track media key. + TrackPrevious, + /// Record media key. + Record, + /// Lower-volume media key. + LowerVolume, + /// Raise-volume media key. + RaiseVolume, + /// Mute media key. + MuteVolume, +} + +/// Represents a modifier key (as part of [`KeyCode::Modifier`]). +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum ModifierKeyCode { + /// Left Shift key. + LeftShift, + /// Left Control key. + LeftControl, + /// Left Alt key. + LeftAlt, + /// Left Super key. + LeftSuper, + /// Left Hyper key. + LeftHyper, + /// Left Meta key. + LeftMeta, + /// Right Shift key. + RightShift, + /// Right Control key. + RightControl, + /// Right Alt key. + RightAlt, + /// Right Super key. + RightSuper, + /// Right Hyper key. + RightHyper, + /// Right Meta key. + RightMeta, + /// Iso Level3 Shift key. + IsoLevel3Shift, + /// Iso Level5 Shift key. + IsoLevel5Shift, +} + +/// Represents a key. +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum KeyCode { + /// Backspace key. + Backspace, + /// Enter key. + Enter, + /// Left arrow key. + Left, + /// Right arrow key. + Right, + /// Up arrow key. + Up, + /// Down arrow key. + Down, + /// Home key. + Home, + /// End key. + End, + /// Page up key. + PageUp, + /// Page down key. + PageDown, + /// Tab key. + Tab, + /// Shift + Tab key. + BackTab, + /// Delete key. + Delete, + /// Insert key. + Insert, + /// F key. + /// + /// `KeyCode::F(1)` represents F1 key, etc. + F(u8), + /// A character. + /// + /// `KeyCode::Char('c')` represents `c` character, etc. + Char(char), + /// Null. + Null, + /// Escape key. + Esc, + /// Caps Lock key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + CapsLock, + /// Scroll Lock key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + ScrollLock, + /// Num Lock key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + NumLock, + /// Print Screen key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + PrintScreen, + /// Pause key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + Pause, + /// Menu key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + Menu, + /// The "Begin" key (often mapped to the 5 key when Num Lock is turned on). + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + KeypadBegin, + /// A media key. + /// + /// **Note:** these keys can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + Media(MediaKeyCode), + /// A modifier key. + /// + /// **Note:** these keys can only be read if **both** + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] and + /// [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] have been enabled with + /// [`PushKeyboardEnhancementFlags`]. + Modifier(ModifierKeyCode), +} + +/// An internal event. +/// +/// Encapsulates publicly available `Event` with additional internal +/// events that shouldn't be publicly available to the crate users. +#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Eq)] +pub(crate) enum InternalEvent { + /// An event. + Event(Event), + /// A cursor position (`col`, `row`). + #[cfg(unix)] + CursorPosition(u16, u16), +} + +#[cfg(test)] +mod tests { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + use super::{KeyCode, KeyEvent, KeyModifiers}; + + #[test] + fn test_equality() { + let lowercase_d_with_shift = KeyEvent::new(KeyCode::Char('d'), KeyModifiers::SHIFT); + let uppercase_d_with_shift = KeyEvent::new(KeyCode::Char('D'), KeyModifiers::SHIFT); + let uppercase_d = KeyEvent::new(KeyCode::Char('D'), KeyModifiers::NONE); + assert_eq!(lowercase_d_with_shift, uppercase_d_with_shift); + assert_eq!(uppercase_d, uppercase_d_with_shift); + } + + #[test] + fn test_hash() { + let lowercase_d_with_shift_hash = { + let mut hasher = DefaultHasher::new(); + KeyEvent::new(KeyCode::Char('d'), KeyModifiers::SHIFT).hash(&mut hasher); + hasher.finish() + }; + let uppercase_d_with_shift_hash = { + let mut hasher = DefaultHasher::new(); + KeyEvent::new(KeyCode::Char('D'), KeyModifiers::SHIFT).hash(&mut hasher); + hasher.finish() + }; + let uppercase_d_hash = { + let mut hasher = DefaultHasher::new(); + KeyEvent::new(KeyCode::Char('D'), KeyModifiers::NONE).hash(&mut hasher); + hasher.finish() + }; + assert_eq!(lowercase_d_with_shift_hash, uppercase_d_with_shift_hash); + assert_eq!(uppercase_d_hash, uppercase_d_with_shift_hash); + } +} diff --git a/vendor/crossterm/src/event/filter.rs b/vendor/crossterm/src/event/filter.rs index 2b7e292..971de6e 100644 --- a/vendor/crossterm/src/event/filter.rs +++ b/vendor/crossterm/src/event/filter.rs @@ -1,68 +1,68 @@ -use crate::event::InternalEvent; - -/// Interface for filtering an `InternalEvent`. -pub(crate) trait Filter: Send + Sync + 'static { - /// Returns whether the given event fulfills the filter. - fn eval(&self, event: &InternalEvent) -> bool; -} - -#[cfg(unix)] -#[derive(Debug, Clone)] -pub(crate) struct CursorPositionFilter; - -#[cfg(unix)] -impl Filter for CursorPositionFilter { - fn eval(&self, event: &InternalEvent) -> bool { - matches!(*event, InternalEvent::CursorPosition(_, _)) - } -} - -#[derive(Debug, Clone)] -pub(crate) struct EventFilter; - -impl Filter for EventFilter { - #[cfg(unix)] - fn eval(&self, event: &InternalEvent) -> bool { - matches!(*event, InternalEvent::Event(_)) - } - - #[cfg(windows)] - fn eval(&self, _: &InternalEvent) -> bool { - true - } -} - -#[derive(Debug, Clone)] -pub(crate) struct InternalEventFilter; - -impl Filter for InternalEventFilter { - fn eval(&self, _: &InternalEvent) -> bool { - true - } -} - -#[cfg(test)] -#[cfg(unix)] -mod tests { - use super::{ - super::Event, CursorPositionFilter, EventFilter, Filter, InternalEvent, InternalEventFilter, - }; - - #[test] - fn test_cursor_position_filter_filters_cursor_position() { - assert!(!CursorPositionFilter.eval(&InternalEvent::Event(Event::Resize(10, 10)))); - assert!(CursorPositionFilter.eval(&InternalEvent::CursorPosition(0, 0))); - } - - #[test] - fn test_event_filter_filters_events() { - assert!(EventFilter.eval(&InternalEvent::Event(Event::Resize(10, 10)))); - assert!(!EventFilter.eval(&InternalEvent::CursorPosition(0, 0))); - } - - #[test] - fn test_event_filter_filters_internal_events() { - assert!(InternalEventFilter.eval(&InternalEvent::Event(Event::Resize(10, 10)))); - assert!(InternalEventFilter.eval(&InternalEvent::CursorPosition(0, 0))); - } -} +use crate::event::InternalEvent; + +/// Interface for filtering an `InternalEvent`. +pub(crate) trait Filter: Send + Sync + 'static { + /// Returns whether the given event fulfills the filter. + fn eval(&self, event: &InternalEvent) -> bool; +} + +#[cfg(unix)] +#[derive(Debug, Clone)] +pub(crate) struct CursorPositionFilter; + +#[cfg(unix)] +impl Filter for CursorPositionFilter { + fn eval(&self, event: &InternalEvent) -> bool { + matches!(*event, InternalEvent::CursorPosition(_, _)) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct EventFilter; + +impl Filter for EventFilter { + #[cfg(unix)] + fn eval(&self, event: &InternalEvent) -> bool { + matches!(*event, InternalEvent::Event(_)) + } + + #[cfg(windows)] + fn eval(&self, _: &InternalEvent) -> bool { + true + } +} + +#[derive(Debug, Clone)] +pub(crate) struct InternalEventFilter; + +impl Filter for InternalEventFilter { + fn eval(&self, _: &InternalEvent) -> bool { + true + } +} + +#[cfg(test)] +#[cfg(unix)] +mod tests { + use super::{ + super::Event, CursorPositionFilter, EventFilter, Filter, InternalEvent, InternalEventFilter, + }; + + #[test] + fn test_cursor_position_filter_filters_cursor_position() { + assert!(!CursorPositionFilter.eval(&InternalEvent::Event(Event::Resize(10, 10)))); + assert!(CursorPositionFilter.eval(&InternalEvent::CursorPosition(0, 0))); + } + + #[test] + fn test_event_filter_filters_events() { + assert!(EventFilter.eval(&InternalEvent::Event(Event::Resize(10, 10)))); + assert!(!EventFilter.eval(&InternalEvent::CursorPosition(0, 0))); + } + + #[test] + fn test_event_filter_filters_internal_events() { + assert!(InternalEventFilter.eval(&InternalEvent::Event(Event::Resize(10, 10)))); + assert!(InternalEventFilter.eval(&InternalEvent::CursorPosition(0, 0))); + } +} diff --git a/vendor/crossterm/src/event/read.rs b/vendor/crossterm/src/event/read.rs index 72b0830..961c51b 100644 --- a/vendor/crossterm/src/event/read.rs +++ b/vendor/crossterm/src/event/read.rs @@ -1,445 +1,445 @@ -use std::{collections::vec_deque::VecDeque, io, time::Duration}; - -#[cfg(unix)] -use super::source::unix::UnixInternalEventSource; -#[cfg(windows)] -use super::source::windows::WindowsEventSource; -#[cfg(feature = "event-stream")] -use super::sys::Waker; -use super::{filter::Filter, source::EventSource, timeout::PollTimeout, InternalEvent, Result}; -/// Can be used to read `InternalEvent`s. -pub(crate) struct InternalEventReader { - events: VecDeque, - source: Option>, - skipped_events: Vec, -} - -impl Default for InternalEventReader { - fn default() -> Self { - #[cfg(windows)] - let source = WindowsEventSource::new(); - #[cfg(unix)] - let source = UnixInternalEventSource::new(); - - let source = source.ok().map(|x| Box::new(x) as Box); - - InternalEventReader { - source, - events: VecDeque::with_capacity(32), - skipped_events: Vec::with_capacity(32), - } - } -} - -impl InternalEventReader { - /// Returns a `Waker` allowing to wake/force the `poll` method to return `Ok(false)`. - #[cfg(feature = "event-stream")] - pub(crate) fn waker(&self) -> Waker { - self.source.as_ref().expect("reader source not set").waker() - } - - pub(crate) fn poll(&mut self, timeout: Option, filter: &F) -> Result - where - F: Filter, - { - for event in &self.events { - if filter.eval(event) { - return Ok(true); - } - } - - let event_source = match self.source.as_mut() { - Some(source) => source, - None => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to initialize input reader", - )) - } - }; - - let poll_timeout = PollTimeout::new(timeout); - - loop { - let maybe_event = match event_source.try_read(poll_timeout.leftover()) { - Ok(None) => None, - Ok(Some(event)) => { - if filter.eval(&event) { - Some(event) - } else { - self.skipped_events.push(event); - None - } - } - Err(e) => { - if e.kind() == io::ErrorKind::Interrupted { - return Ok(false); - } - - return Err(e); - } - }; - - if poll_timeout.elapsed() || maybe_event.is_some() { - self.events.extend(self.skipped_events.drain(..)); - - if let Some(event) = maybe_event { - self.events.push_front(event); - return Ok(true); - } - - return Ok(false); - } - } - } - - pub(crate) fn read(&mut self, filter: &F) -> Result - where - F: Filter, - { - let mut skipped_events = VecDeque::new(); - - loop { - while let Some(event) = self.events.pop_front() { - if filter.eval(&event) { - while let Some(event) = skipped_events.pop_front() { - self.events.push_back(event); - } - - return Ok(event); - } else { - // We can not directly write events back to `self.events`. - // If we did, we would put our self's into an endless loop - // that would enqueue -> dequeue -> enqueue etc. - // This happens because `poll` in this function will always return true if there are events in it's. - // And because we just put the non-fulfilling event there this is going to be the case. - // Instead we can store them into the temporary buffer, - // and then when the filter is fulfilled write all events back in order. - skipped_events.push_back(event); - } - } - - let _ = self.poll(None, filter)?; - } - } -} - -#[cfg(test)] -mod tests { - use std::io; - use std::{collections::VecDeque, time::Duration}; - - use crate::ErrorKind; - - #[cfg(unix)] - use super::super::filter::CursorPositionFilter; - use super::{ - super::{filter::InternalEventFilter, Event}, - EventSource, InternalEvent, InternalEventReader, - }; - - #[test] - fn test_poll_fails_without_event_source() { - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: None, - skipped_events: Vec::with_capacity(32), - }; - - assert!(reader.poll(None, &InternalEventFilter).is_err()); - assert!(reader - .poll(Some(Duration::from_secs(0)), &InternalEventFilter) - .is_err()); - assert!(reader - .poll(Some(Duration::from_secs(10)), &InternalEventFilter) - .is_err()); - } - - #[test] - fn test_poll_returns_true_for_matching_event_in_queue_at_front() { - let mut reader = InternalEventReader { - events: vec![InternalEvent::Event(Event::Resize(10, 10))].into(), - source: None, - skipped_events: Vec::with_capacity(32), - }; - - assert!(reader.poll(None, &InternalEventFilter).unwrap()); - } - - #[test] - #[cfg(unix)] - fn test_poll_returns_true_for_matching_event_in_queue_at_back() { - let mut reader = InternalEventReader { - events: vec![ - InternalEvent::Event(Event::Resize(10, 10)), - InternalEvent::CursorPosition(10, 20), - ] - .into(), - source: None, - skipped_events: Vec::with_capacity(32), - }; - - assert!(reader.poll(None, &CursorPositionFilter).unwrap()); - } - - #[test] - fn test_read_returns_matching_event_in_queue_at_front() { - const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); - - let mut reader = InternalEventReader { - events: vec![EVENT].into(), - source: None, - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - } - - #[test] - #[cfg(unix)] - fn test_read_returns_matching_event_in_queue_at_back() { - const CURSOR_EVENT: InternalEvent = InternalEvent::CursorPosition(10, 20); - - let mut reader = InternalEventReader { - events: vec![InternalEvent::Event(Event::Resize(10, 10)), CURSOR_EVENT].into(), - source: None, - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&CursorPositionFilter).unwrap(), CURSOR_EVENT); - } - - #[test] - #[cfg(unix)] - fn test_read_does_not_consume_skipped_event() { - const SKIPPED_EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); - const CURSOR_EVENT: InternalEvent = InternalEvent::CursorPosition(10, 20); - - let mut reader = InternalEventReader { - events: vec![SKIPPED_EVENT, CURSOR_EVENT].into(), - source: None, - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&CursorPositionFilter).unwrap(), CURSOR_EVENT); - assert_eq!(reader.read(&InternalEventFilter).unwrap(), SKIPPED_EVENT); - } - - #[test] - fn test_poll_timeouts_if_source_has_no_events() { - let source = FakeSource::default(); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert!(!reader - .poll(Some(Duration::from_secs(0)), &InternalEventFilter) - .unwrap()); - } - - #[test] - fn test_poll_returns_true_if_source_has_at_least_one_event() { - let source = FakeSource::with_events(&[InternalEvent::Event(Event::Resize(10, 10))]); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert!(reader.poll(None, &InternalEventFilter).unwrap()); - assert!(reader - .poll(Some(Duration::from_secs(0)), &InternalEventFilter) - .unwrap()); - } - - #[test] - fn test_reads_returns_event_if_source_has_at_least_one_event() { - const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); - - let source = FakeSource::with_events(&[EVENT]); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - } - - #[test] - fn test_read_returns_events_if_source_has_events() { - const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); - - let source = FakeSource::with_events(&[EVENT, EVENT, EVENT]); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - } - - #[test] - fn test_poll_returns_false_after_all_source_events_are_consumed() { - const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); - - let source = FakeSource::with_events(&[EVENT, EVENT, EVENT]); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - assert!(!reader - .poll(Some(Duration::from_secs(0)), &InternalEventFilter) - .unwrap()); - } - - #[test] - fn test_poll_propagates_error() { - let source = FakeSource::with_error(ErrorKind::from(io::ErrorKind::Other)); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!( - reader - .poll(Some(Duration::from_secs(0)), &InternalEventFilter) - .err() - .map(|e| format!("{:?}", &e)), - Some(format!("{:?}", ErrorKind::from(io::ErrorKind::Other))) - ); - } - - #[test] - fn test_read_propagates_error() { - let source = FakeSource::with_error(ErrorKind::from(io::ErrorKind::Other)); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!( - reader - .read(&InternalEventFilter) - .err() - .map(|e| format!("{:?}", &e)), - Some(format!("{:?}", ErrorKind::from(io::ErrorKind::Other))) - ); - } - - #[test] - fn test_poll_continues_after_error() { - const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); - - let source = FakeSource::new(&[EVENT, EVENT], ErrorKind::from(io::ErrorKind::Other)); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - assert!(reader.read(&InternalEventFilter).is_err()); - assert!(reader - .poll(Some(Duration::from_secs(0)), &InternalEventFilter) - .unwrap()); - } - - #[test] - fn test_read_continues_after_error() { - const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); - - let source = FakeSource::new(&[EVENT, EVENT], ErrorKind::from(io::ErrorKind::Other)); - - let mut reader = InternalEventReader { - events: VecDeque::new(), - source: Some(Box::new(source)), - skipped_events: Vec::with_capacity(32), - }; - - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - assert!(reader.read(&InternalEventFilter).is_err()); - assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); - } - - #[derive(Default)] - struct FakeSource { - events: VecDeque, - error: Option, - } - - impl FakeSource { - fn new(events: &[InternalEvent], error: ErrorKind) -> FakeSource { - FakeSource { - events: events.to_vec().into(), - error: Some(error), - } - } - - fn with_events(events: &[InternalEvent]) -> FakeSource { - FakeSource { - events: events.to_vec().into(), - error: None, - } - } - - fn with_error(error: ErrorKind) -> FakeSource { - FakeSource { - events: VecDeque::new(), - error: Some(error), - } - } - } - - impl EventSource for FakeSource { - fn try_read( - &mut self, - _timeout: Option, - ) -> Result, ErrorKind> { - // Return error if set in case there's just one remaining event - if self.events.len() == 1 { - if let Some(error) = self.error.take() { - return Err(error); - } - } - - // Return all events from the queue - if let Some(event) = self.events.pop_front() { - return Ok(Some(event)); - } - - // Return error if there're no more events - if let Some(error) = self.error.take() { - return Err(error); - } - - // Timeout - Ok(None) - } - - #[cfg(feature = "event-stream")] - fn waker(&self) -> super::super::sys::Waker { - unimplemented!(); - } - } -} +use std::{collections::vec_deque::VecDeque, io, time::Duration}; + +#[cfg(unix)] +use super::source::unix::UnixInternalEventSource; +#[cfg(windows)] +use super::source::windows::WindowsEventSource; +#[cfg(feature = "event-stream")] +use super::sys::Waker; +use super::{filter::Filter, source::EventSource, timeout::PollTimeout, InternalEvent, Result}; +/// Can be used to read `InternalEvent`s. +pub(crate) struct InternalEventReader { + events: VecDeque, + source: Option>, + skipped_events: Vec, +} + +impl Default for InternalEventReader { + fn default() -> Self { + #[cfg(windows)] + let source = WindowsEventSource::new(); + #[cfg(unix)] + let source = UnixInternalEventSource::new(); + + let source = source.ok().map(|x| Box::new(x) as Box); + + InternalEventReader { + source, + events: VecDeque::with_capacity(32), + skipped_events: Vec::with_capacity(32), + } + } +} + +impl InternalEventReader { + /// Returns a `Waker` allowing to wake/force the `poll` method to return `Ok(false)`. + #[cfg(feature = "event-stream")] + pub(crate) fn waker(&self) -> Waker { + self.source.as_ref().expect("reader source not set").waker() + } + + pub(crate) fn poll(&mut self, timeout: Option, filter: &F) -> Result + where + F: Filter, + { + for event in &self.events { + if filter.eval(event) { + return Ok(true); + } + } + + let event_source = match self.source.as_mut() { + Some(source) => source, + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Failed to initialize input reader", + )) + } + }; + + let poll_timeout = PollTimeout::new(timeout); + + loop { + let maybe_event = match event_source.try_read(poll_timeout.leftover()) { + Ok(None) => None, + Ok(Some(event)) => { + if filter.eval(&event) { + Some(event) + } else { + self.skipped_events.push(event); + None + } + } + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + return Ok(false); + } + + return Err(e); + } + }; + + if poll_timeout.elapsed() || maybe_event.is_some() { + self.events.extend(self.skipped_events.drain(..)); + + if let Some(event) = maybe_event { + self.events.push_front(event); + return Ok(true); + } + + return Ok(false); + } + } + } + + pub(crate) fn read(&mut self, filter: &F) -> Result + where + F: Filter, + { + let mut skipped_events = VecDeque::new(); + + loop { + while let Some(event) = self.events.pop_front() { + if filter.eval(&event) { + while let Some(event) = skipped_events.pop_front() { + self.events.push_back(event); + } + + return Ok(event); + } else { + // We can not directly write events back to `self.events`. + // If we did, we would put our self's into an endless loop + // that would enqueue -> dequeue -> enqueue etc. + // This happens because `poll` in this function will always return true if there are events in it's. + // And because we just put the non-fulfilling event there this is going to be the case. + // Instead we can store them into the temporary buffer, + // and then when the filter is fulfilled write all events back in order. + skipped_events.push_back(event); + } + } + + let _ = self.poll(None, filter)?; + } + } +} + +#[cfg(test)] +mod tests { + use std::io; + use std::{collections::VecDeque, time::Duration}; + + use crate::ErrorKind; + + #[cfg(unix)] + use super::super::filter::CursorPositionFilter; + use super::{ + super::{filter::InternalEventFilter, Event}, + EventSource, InternalEvent, InternalEventReader, + }; + + #[test] + fn test_poll_fails_without_event_source() { + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: None, + skipped_events: Vec::with_capacity(32), + }; + + assert!(reader.poll(None, &InternalEventFilter).is_err()); + assert!(reader + .poll(Some(Duration::from_secs(0)), &InternalEventFilter) + .is_err()); + assert!(reader + .poll(Some(Duration::from_secs(10)), &InternalEventFilter) + .is_err()); + } + + #[test] + fn test_poll_returns_true_for_matching_event_in_queue_at_front() { + let mut reader = InternalEventReader { + events: vec![InternalEvent::Event(Event::Resize(10, 10))].into(), + source: None, + skipped_events: Vec::with_capacity(32), + }; + + assert!(reader.poll(None, &InternalEventFilter).unwrap()); + } + + #[test] + #[cfg(unix)] + fn test_poll_returns_true_for_matching_event_in_queue_at_back() { + let mut reader = InternalEventReader { + events: vec![ + InternalEvent::Event(Event::Resize(10, 10)), + InternalEvent::CursorPosition(10, 20), + ] + .into(), + source: None, + skipped_events: Vec::with_capacity(32), + }; + + assert!(reader.poll(None, &CursorPositionFilter).unwrap()); + } + + #[test] + fn test_read_returns_matching_event_in_queue_at_front() { + const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); + + let mut reader = InternalEventReader { + events: vec![EVENT].into(), + source: None, + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + } + + #[test] + #[cfg(unix)] + fn test_read_returns_matching_event_in_queue_at_back() { + const CURSOR_EVENT: InternalEvent = InternalEvent::CursorPosition(10, 20); + + let mut reader = InternalEventReader { + events: vec![InternalEvent::Event(Event::Resize(10, 10)), CURSOR_EVENT].into(), + source: None, + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&CursorPositionFilter).unwrap(), CURSOR_EVENT); + } + + #[test] + #[cfg(unix)] + fn test_read_does_not_consume_skipped_event() { + const SKIPPED_EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); + const CURSOR_EVENT: InternalEvent = InternalEvent::CursorPosition(10, 20); + + let mut reader = InternalEventReader { + events: vec![SKIPPED_EVENT, CURSOR_EVENT].into(), + source: None, + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&CursorPositionFilter).unwrap(), CURSOR_EVENT); + assert_eq!(reader.read(&InternalEventFilter).unwrap(), SKIPPED_EVENT); + } + + #[test] + fn test_poll_timeouts_if_source_has_no_events() { + let source = FakeSource::default(); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert!(!reader + .poll(Some(Duration::from_secs(0)), &InternalEventFilter) + .unwrap()); + } + + #[test] + fn test_poll_returns_true_if_source_has_at_least_one_event() { + let source = FakeSource::with_events(&[InternalEvent::Event(Event::Resize(10, 10))]); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert!(reader.poll(None, &InternalEventFilter).unwrap()); + assert!(reader + .poll(Some(Duration::from_secs(0)), &InternalEventFilter) + .unwrap()); + } + + #[test] + fn test_reads_returns_event_if_source_has_at_least_one_event() { + const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); + + let source = FakeSource::with_events(&[EVENT]); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + } + + #[test] + fn test_read_returns_events_if_source_has_events() { + const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); + + let source = FakeSource::with_events(&[EVENT, EVENT, EVENT]); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + } + + #[test] + fn test_poll_returns_false_after_all_source_events_are_consumed() { + const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); + + let source = FakeSource::with_events(&[EVENT, EVENT, EVENT]); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + assert!(!reader + .poll(Some(Duration::from_secs(0)), &InternalEventFilter) + .unwrap()); + } + + #[test] + fn test_poll_propagates_error() { + let source = FakeSource::with_error(ErrorKind::from(io::ErrorKind::Other)); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!( + reader + .poll(Some(Duration::from_secs(0)), &InternalEventFilter) + .err() + .map(|e| format!("{:?}", &e)), + Some(format!("{:?}", ErrorKind::from(io::ErrorKind::Other))) + ); + } + + #[test] + fn test_read_propagates_error() { + let source = FakeSource::with_error(ErrorKind::from(io::ErrorKind::Other)); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!( + reader + .read(&InternalEventFilter) + .err() + .map(|e| format!("{:?}", &e)), + Some(format!("{:?}", ErrorKind::from(io::ErrorKind::Other))) + ); + } + + #[test] + fn test_poll_continues_after_error() { + const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); + + let source = FakeSource::new(&[EVENT, EVENT], ErrorKind::from(io::ErrorKind::Other)); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + assert!(reader.read(&InternalEventFilter).is_err()); + assert!(reader + .poll(Some(Duration::from_secs(0)), &InternalEventFilter) + .unwrap()); + } + + #[test] + fn test_read_continues_after_error() { + const EVENT: InternalEvent = InternalEvent::Event(Event::Resize(10, 10)); + + let source = FakeSource::new(&[EVENT, EVENT], ErrorKind::from(io::ErrorKind::Other)); + + let mut reader = InternalEventReader { + events: VecDeque::new(), + source: Some(Box::new(source)), + skipped_events: Vec::with_capacity(32), + }; + + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + assert!(reader.read(&InternalEventFilter).is_err()); + assert_eq!(reader.read(&InternalEventFilter).unwrap(), EVENT); + } + + #[derive(Default)] + struct FakeSource { + events: VecDeque, + error: Option, + } + + impl FakeSource { + fn new(events: &[InternalEvent], error: ErrorKind) -> FakeSource { + FakeSource { + events: events.to_vec().into(), + error: Some(error), + } + } + + fn with_events(events: &[InternalEvent]) -> FakeSource { + FakeSource { + events: events.to_vec().into(), + error: None, + } + } + + fn with_error(error: ErrorKind) -> FakeSource { + FakeSource { + events: VecDeque::new(), + error: Some(error), + } + } + } + + impl EventSource for FakeSource { + fn try_read( + &mut self, + _timeout: Option, + ) -> Result, ErrorKind> { + // Return error if set in case there's just one remaining event + if self.events.len() == 1 { + if let Some(error) = self.error.take() { + return Err(error); + } + } + + // Return all events from the queue + if let Some(event) = self.events.pop_front() { + return Ok(Some(event)); + } + + // Return error if there're no more events + if let Some(error) = self.error.take() { + return Err(error); + } + + // Timeout + Ok(None) + } + + #[cfg(feature = "event-stream")] + fn waker(&self) -> super::super::sys::Waker { + unimplemented!(); + } + } +} diff --git a/vendor/crossterm/src/event/source.rs b/vendor/crossterm/src/event/source.rs index a9c2f65..16b1171 100644 --- a/vendor/crossterm/src/event/source.rs +++ b/vendor/crossterm/src/event/source.rs @@ -1,27 +1,27 @@ -use std::time::Duration; - -#[cfg(feature = "event-stream")] -use super::sys::Waker; -use super::InternalEvent; - -#[cfg(unix)] -pub(crate) mod unix; -#[cfg(windows)] -pub(crate) mod windows; - -/// An interface for trying to read an `InternalEvent` within an optional `Duration`. -pub(crate) trait EventSource: Sync + Send { - /// Tries to read an `InternalEvent` within the given duration. - /// - /// # Arguments - /// - /// * `timeout` - `None` block indefinitely until an event is available, `Some(duration)` blocks - /// for the given timeout - /// - /// Returns `Ok(None)` if there's no event available and timeout expires. - fn try_read(&mut self, timeout: Option) -> crate::Result>; - - /// Returns a `Waker` allowing to wake/force the `try_read` method to return `Ok(None)`. - #[cfg(feature = "event-stream")] - fn waker(&self) -> Waker; -} +use std::time::Duration; + +#[cfg(feature = "event-stream")] +use super::sys::Waker; +use super::InternalEvent; + +#[cfg(unix)] +pub(crate) mod unix; +#[cfg(windows)] +pub(crate) mod windows; + +/// An interface for trying to read an `InternalEvent` within an optional `Duration`. +pub(crate) trait EventSource: Sync + Send { + /// Tries to read an `InternalEvent` within the given duration. + /// + /// # Arguments + /// + /// * `timeout` - `None` block indefinitely until an event is available, `Some(duration)` blocks + /// for the given timeout + /// + /// Returns `Ok(None)` if there's no event available and timeout expires. + fn try_read(&mut self, timeout: Option) -> crate::Result>; + + /// Returns a `Waker` allowing to wake/force the `try_read` method to return `Ok(None)`. + #[cfg(feature = "event-stream")] + fn waker(&self) -> Waker; +} diff --git a/vendor/crossterm/src/event/source/unix.rs b/vendor/crossterm/src/event/source/unix.rs index 8a44d5a..32a3c5c 100644 --- a/vendor/crossterm/src/event/source/unix.rs +++ b/vendor/crossterm/src/event/source/unix.rs @@ -1,241 +1,241 @@ -use std::{collections::VecDeque, io, time::Duration}; - -use mio::{unix::SourceFd, Events, Interest, Poll, Token}; -use signal_hook_mio::v0_8::Signals; - -use crate::Result; - -#[cfg(feature = "event-stream")] -use super::super::sys::Waker; -use super::super::{ - source::EventSource, - sys::unix::{ - file_descriptor::{tty_fd, FileDesc}, - parse::parse_event, - }, - timeout::PollTimeout, - Event, InternalEvent, -}; - -// Tokens to identify file descriptor -const TTY_TOKEN: Token = Token(0); -const SIGNAL_TOKEN: Token = Token(1); -#[cfg(feature = "event-stream")] -const WAKE_TOKEN: Token = Token(2); - -// I (@zrzka) wasn't able to read more than 1_022 bytes when testing -// reading on macOS/Linux -> we don't need bigger buffer and 1k of bytes -// is enough. -const TTY_BUFFER_SIZE: usize = 1_024; - -pub(crate) struct UnixInternalEventSource { - poll: Poll, - events: Events, - parser: Parser, - tty_buffer: [u8; TTY_BUFFER_SIZE], - tty_fd: FileDesc, - signals: Signals, - #[cfg(feature = "event-stream")] - waker: Waker, -} - -impl UnixInternalEventSource { - pub fn new() -> Result { - UnixInternalEventSource::from_file_descriptor(tty_fd()?) - } - - pub(crate) fn from_file_descriptor(input_fd: FileDesc) -> Result { - let poll = Poll::new()?; - let registry = poll.registry(); - - let tty_raw_fd = input_fd.raw_fd(); - let mut tty_ev = SourceFd(&tty_raw_fd); - registry.register(&mut tty_ev, TTY_TOKEN, Interest::READABLE)?; - - let mut signals = Signals::new(&[signal_hook::consts::SIGWINCH])?; - registry.register(&mut signals, SIGNAL_TOKEN, Interest::READABLE)?; - - #[cfg(feature = "event-stream")] - let waker = Waker::new(registry, WAKE_TOKEN)?; - - Ok(UnixInternalEventSource { - poll, - events: Events::with_capacity(3), - parser: Parser::default(), - tty_buffer: [0u8; TTY_BUFFER_SIZE], - tty_fd: input_fd, - signals, - #[cfg(feature = "event-stream")] - waker, - }) - } -} - -impl EventSource for UnixInternalEventSource { - fn try_read(&mut self, timeout: Option) -> Result> { - if let Some(event) = self.parser.next() { - return Ok(Some(event)); - } - - let timeout = PollTimeout::new(timeout); - - loop { - if let Err(e) = self.poll.poll(&mut self.events, timeout.leftover()) { - // Mio will throw an interrupted error in case of cursor position retrieval. We need to retry until it succeeds. - // Previous versions of Mio (< 0.7) would automatically retry the poll call if it was interrupted (if EINTR was returned). - // https://docs.rs/mio/0.7.0/mio/struct.Poll.html#notes - if e.kind() == io::ErrorKind::Interrupted { - continue; - } else { - return Err(e); - } - }; - - if self.events.is_empty() { - // No readiness events = timeout - return Ok(None); - } - - for token in self.events.iter().map(|x| x.token()) { - match token { - TTY_TOKEN => { - loop { - match self.tty_fd.read(&mut self.tty_buffer, TTY_BUFFER_SIZE) { - Ok(read_count) => { - if read_count > 0 { - self.parser.advance( - &self.tty_buffer[..read_count], - read_count == TTY_BUFFER_SIZE, - ); - } - } - Err(e) => { - // No more data to read at the moment. We will receive another event - if e.kind() == io::ErrorKind::WouldBlock { - break; - } - // once more data is available to read. - else if e.kind() == io::ErrorKind::Interrupted { - continue; - } - } - }; - - if let Some(event) = self.parser.next() { - return Ok(Some(event)); - } - } - } - SIGNAL_TOKEN => { - for signal in self.signals.pending() { - match signal { - signal_hook::consts::SIGWINCH => { - // TODO Should we remove tput? - // - // This can take a really long time, because terminal::size can - // launch new process (tput) and then it parses its output. It's - // not a really long time from the absolute time point of view, but - // it's a really long time from the mio, async-std/tokio executor, ... - // point of view. - let new_size = crate::terminal::size()?; - return Ok(Some(InternalEvent::Event(Event::Resize( - new_size.0, new_size.1, - )))); - } - _ => unreachable!("Synchronize signal registration & handling"), - }; - } - } - #[cfg(feature = "event-stream")] - WAKE_TOKEN => { - return Err(std::io::Error::new( - std::io::ErrorKind::Interrupted, - "Poll operation was woken up by `Waker::wake`", - )); - } - _ => unreachable!("Synchronize Evented handle registration & token handling"), - } - } - - // Processing above can take some time, check if timeout expired - if timeout.elapsed() { - return Ok(None); - } - } - } - - #[cfg(feature = "event-stream")] - fn waker(&self) -> Waker { - self.waker.clone() - } -} - -// -// Following `Parser` structure exists for two reasons: -// -// * mimic anes Parser interface -// * move the advancing, parsing, ... stuff out of the `try_read` method -// -#[derive(Debug)] -struct Parser { - buffer: Vec, - internal_events: VecDeque, -} - -impl Default for Parser { - fn default() -> Self { - Parser { - // This buffer is used for -> 1 <- ANSI escape sequence. Are we - // aware of any ANSI escape sequence that is bigger? Can we make - // it smaller? - // - // Probably not worth spending more time on this as "there's a plan" - // to use the anes crate parser. - buffer: Vec::with_capacity(256), - // TTY_BUFFER_SIZE is 1_024 bytes. How many ANSI escape sequences can - // fit? What is an average sequence length? Let's guess here - // and say that the average ANSI escape sequence length is 8 bytes. Thus - // the buffer size should be 1024/8=128 to avoid additional allocations - // when processing large amounts of data. - // - // There's no need to make it bigger, because when you look at the `try_read` - // method implementation, all events are consumed before the next TTY_BUFFER - // is processed -> events pushed. - internal_events: VecDeque::with_capacity(128), - } - } -} - -impl Parser { - fn advance(&mut self, buffer: &[u8], more: bool) { - for (idx, byte) in buffer.iter().enumerate() { - let more = idx + 1 < buffer.len() || more; - - self.buffer.push(*byte); - - match parse_event(&self.buffer, more) { - Ok(Some(ie)) => { - self.internal_events.push_back(ie); - self.buffer.clear(); - } - Ok(None) => { - // Event can't be parsed, because we don't have enough bytes for - // the current sequence. Keep the buffer and process next bytes. - } - Err(_) => { - // Event can't be parsed (not enough parameters, parameter is not a number, ...). - // Clear the buffer and continue with another sequence. - self.buffer.clear(); - } - } - } - } -} - -impl Iterator for Parser { - type Item = InternalEvent; - - fn next(&mut self) -> Option { - self.internal_events.pop_front() - } -} +use std::{collections::VecDeque, io, time::Duration}; + +use mio::{unix::SourceFd, Events, Interest, Poll, Token}; +use signal_hook_mio::v0_8::Signals; + +use crate::Result; + +#[cfg(feature = "event-stream")] +use super::super::sys::Waker; +use super::super::{ + source::EventSource, + sys::unix::{ + file_descriptor::{tty_fd, FileDesc}, + parse::parse_event, + }, + timeout::PollTimeout, + Event, InternalEvent, +}; + +// Tokens to identify file descriptor +const TTY_TOKEN: Token = Token(0); +const SIGNAL_TOKEN: Token = Token(1); +#[cfg(feature = "event-stream")] +const WAKE_TOKEN: Token = Token(2); + +// I (@zrzka) wasn't able to read more than 1_022 bytes when testing +// reading on macOS/Linux -> we don't need bigger buffer and 1k of bytes +// is enough. +const TTY_BUFFER_SIZE: usize = 1_024; + +pub(crate) struct UnixInternalEventSource { + poll: Poll, + events: Events, + parser: Parser, + tty_buffer: [u8; TTY_BUFFER_SIZE], + tty_fd: FileDesc, + signals: Signals, + #[cfg(feature = "event-stream")] + waker: Waker, +} + +impl UnixInternalEventSource { + pub fn new() -> Result { + UnixInternalEventSource::from_file_descriptor(tty_fd()?) + } + + pub(crate) fn from_file_descriptor(input_fd: FileDesc) -> Result { + let poll = Poll::new()?; + let registry = poll.registry(); + + let tty_raw_fd = input_fd.raw_fd(); + let mut tty_ev = SourceFd(&tty_raw_fd); + registry.register(&mut tty_ev, TTY_TOKEN, Interest::READABLE)?; + + let mut signals = Signals::new(&[signal_hook::consts::SIGWINCH])?; + registry.register(&mut signals, SIGNAL_TOKEN, Interest::READABLE)?; + + #[cfg(feature = "event-stream")] + let waker = Waker::new(registry, WAKE_TOKEN)?; + + Ok(UnixInternalEventSource { + poll, + events: Events::with_capacity(3), + parser: Parser::default(), + tty_buffer: [0u8; TTY_BUFFER_SIZE], + tty_fd: input_fd, + signals, + #[cfg(feature = "event-stream")] + waker, + }) + } +} + +impl EventSource for UnixInternalEventSource { + fn try_read(&mut self, timeout: Option) -> Result> { + if let Some(event) = self.parser.next() { + return Ok(Some(event)); + } + + let timeout = PollTimeout::new(timeout); + + loop { + if let Err(e) = self.poll.poll(&mut self.events, timeout.leftover()) { + // Mio will throw an interrupted error in case of cursor position retrieval. We need to retry until it succeeds. + // Previous versions of Mio (< 0.7) would automatically retry the poll call if it was interrupted (if EINTR was returned). + // https://docs.rs/mio/0.7.0/mio/struct.Poll.html#notes + if e.kind() == io::ErrorKind::Interrupted { + continue; + } else { + return Err(e); + } + }; + + if self.events.is_empty() { + // No readiness events = timeout + return Ok(None); + } + + for token in self.events.iter().map(|x| x.token()) { + match token { + TTY_TOKEN => { + loop { + match self.tty_fd.read(&mut self.tty_buffer, TTY_BUFFER_SIZE) { + Ok(read_count) => { + if read_count > 0 { + self.parser.advance( + &self.tty_buffer[..read_count], + read_count == TTY_BUFFER_SIZE, + ); + } + } + Err(e) => { + // No more data to read at the moment. We will receive another event + if e.kind() == io::ErrorKind::WouldBlock { + break; + } + // once more data is available to read. + else if e.kind() == io::ErrorKind::Interrupted { + continue; + } + } + }; + + if let Some(event) = self.parser.next() { + return Ok(Some(event)); + } + } + } + SIGNAL_TOKEN => { + for signal in self.signals.pending() { + match signal { + signal_hook::consts::SIGWINCH => { + // TODO Should we remove tput? + // + // This can take a really long time, because terminal::size can + // launch new process (tput) and then it parses its output. It's + // not a really long time from the absolute time point of view, but + // it's a really long time from the mio, async-std/tokio executor, ... + // point of view. + let new_size = crate::terminal::size()?; + return Ok(Some(InternalEvent::Event(Event::Resize( + new_size.0, new_size.1, + )))); + } + _ => unreachable!("Synchronize signal registration & handling"), + }; + } + } + #[cfg(feature = "event-stream")] + WAKE_TOKEN => { + return Err(std::io::Error::new( + std::io::ErrorKind::Interrupted, + "Poll operation was woken up by `Waker::wake`", + )); + } + _ => unreachable!("Synchronize Evented handle registration & token handling"), + } + } + + // Processing above can take some time, check if timeout expired + if timeout.elapsed() { + return Ok(None); + } + } + } + + #[cfg(feature = "event-stream")] + fn waker(&self) -> Waker { + self.waker.clone() + } +} + +// +// Following `Parser` structure exists for two reasons: +// +// * mimic anes Parser interface +// * move the advancing, parsing, ... stuff out of the `try_read` method +// +#[derive(Debug)] +struct Parser { + buffer: Vec, + internal_events: VecDeque, +} + +impl Default for Parser { + fn default() -> Self { + Parser { + // This buffer is used for -> 1 <- ANSI escape sequence. Are we + // aware of any ANSI escape sequence that is bigger? Can we make + // it smaller? + // + // Probably not worth spending more time on this as "there's a plan" + // to use the anes crate parser. + buffer: Vec::with_capacity(256), + // TTY_BUFFER_SIZE is 1_024 bytes. How many ANSI escape sequences can + // fit? What is an average sequence length? Let's guess here + // and say that the average ANSI escape sequence length is 8 bytes. Thus + // the buffer size should be 1024/8=128 to avoid additional allocations + // when processing large amounts of data. + // + // There's no need to make it bigger, because when you look at the `try_read` + // method implementation, all events are consumed before the next TTY_BUFFER + // is processed -> events pushed. + internal_events: VecDeque::with_capacity(128), + } + } +} + +impl Parser { + fn advance(&mut self, buffer: &[u8], more: bool) { + for (idx, byte) in buffer.iter().enumerate() { + let more = idx + 1 < buffer.len() || more; + + self.buffer.push(*byte); + + match parse_event(&self.buffer, more) { + Ok(Some(ie)) => { + self.internal_events.push_back(ie); + self.buffer.clear(); + } + Ok(None) => { + // Event can't be parsed, because we don't have enough bytes for + // the current sequence. Keep the buffer and process next bytes. + } + Err(_) => { + // Event can't be parsed (not enough parameters, parameter is not a number, ...). + // Clear the buffer and continue with another sequence. + self.buffer.clear(); + } + } + } + } +} + +impl Iterator for Parser { + type Item = InternalEvent; + + fn next(&mut self) -> Option { + self.internal_events.pop_front() + } +} diff --git a/vendor/crossterm/src/event/source/windows.rs b/vendor/crossterm/src/event/source/windows.rs index 752f753..a0a667b 100644 --- a/vendor/crossterm/src/event/source/windows.rs +++ b/vendor/crossterm/src/event/source/windows.rs @@ -1,96 +1,96 @@ -use std::time::Duration; - -use crossterm_winapi::{Console, Handle, InputRecord}; - -use crate::event::{ - sys::windows::{parse::MouseButtonsPressed, poll::WinApiPoll}, - Event, -}; - -#[cfg(feature = "event-stream")] -use super::super::sys::Waker; -use super::super::{ - source::EventSource, - sys::windows::parse::{handle_key_event, handle_mouse_event}, - timeout::PollTimeout, - InternalEvent, Result, -}; - -pub(crate) struct WindowsEventSource { - console: Console, - poll: WinApiPoll, - surrogate_buffer: Option, - mouse_buttons_pressed: MouseButtonsPressed, -} - -impl WindowsEventSource { - pub(crate) fn new() -> Result { - let console = Console::from(Handle::current_in_handle()?); - Ok(WindowsEventSource { - console, - - #[cfg(not(feature = "event-stream"))] - poll: WinApiPoll::new(), - #[cfg(feature = "event-stream")] - poll: WinApiPoll::new()?, - - surrogate_buffer: None, - mouse_buttons_pressed: MouseButtonsPressed::default(), - }) - } -} - -impl EventSource for WindowsEventSource { - fn try_read(&mut self, timeout: Option) -> Result> { - let poll_timeout = PollTimeout::new(timeout); - - loop { - if let Some(event_ready) = self.poll.poll(poll_timeout.leftover())? { - let number = self.console.number_of_console_input_events()?; - if event_ready && number != 0 { - let event = match self.console.read_single_input_event()? { - InputRecord::KeyEvent(record) => { - handle_key_event(record, &mut self.surrogate_buffer) - } - InputRecord::MouseEvent(record) => { - let mouse_event = - handle_mouse_event(record, &self.mouse_buttons_pressed); - self.mouse_buttons_pressed = MouseButtonsPressed { - left: record.button_state.left_button(), - right: record.button_state.right_button(), - middle: record.button_state.middle_button(), - }; - - mouse_event - } - InputRecord::WindowBufferSizeEvent(record) => { - Some(Event::Resize(record.size.x as u16, record.size.y as u16)) - } - InputRecord::FocusEvent(record) => { - let event = if record.set_focus { - Event::FocusGained - } else { - Event::FocusLost - }; - Some(event) - } - _ => None, - }; - - if let Some(event) = event { - return Ok(Some(InternalEvent::Event(event))); - } - } - } - - if poll_timeout.elapsed() { - return Ok(None); - } - } - } - - #[cfg(feature = "event-stream")] - fn waker(&self) -> Waker { - self.poll.waker() - } -} +use std::time::Duration; + +use crossterm_winapi::{Console, Handle, InputRecord}; + +use crate::event::{ + sys::windows::{parse::MouseButtonsPressed, poll::WinApiPoll}, + Event, +}; + +#[cfg(feature = "event-stream")] +use super::super::sys::Waker; +use super::super::{ + source::EventSource, + sys::windows::parse::{handle_key_event, handle_mouse_event}, + timeout::PollTimeout, + InternalEvent, Result, +}; + +pub(crate) struct WindowsEventSource { + console: Console, + poll: WinApiPoll, + surrogate_buffer: Option, + mouse_buttons_pressed: MouseButtonsPressed, +} + +impl WindowsEventSource { + pub(crate) fn new() -> Result { + let console = Console::from(Handle::current_in_handle()?); + Ok(WindowsEventSource { + console, + + #[cfg(not(feature = "event-stream"))] + poll: WinApiPoll::new(), + #[cfg(feature = "event-stream")] + poll: WinApiPoll::new()?, + + surrogate_buffer: None, + mouse_buttons_pressed: MouseButtonsPressed::default(), + }) + } +} + +impl EventSource for WindowsEventSource { + fn try_read(&mut self, timeout: Option) -> Result> { + let poll_timeout = PollTimeout::new(timeout); + + loop { + if let Some(event_ready) = self.poll.poll(poll_timeout.leftover())? { + let number = self.console.number_of_console_input_events()?; + if event_ready && number != 0 { + let event = match self.console.read_single_input_event()? { + InputRecord::KeyEvent(record) => { + handle_key_event(record, &mut self.surrogate_buffer) + } + InputRecord::MouseEvent(record) => { + let mouse_event = + handle_mouse_event(record, &self.mouse_buttons_pressed); + self.mouse_buttons_pressed = MouseButtonsPressed { + left: record.button_state.left_button(), + right: record.button_state.right_button(), + middle: record.button_state.middle_button(), + }; + + mouse_event + } + InputRecord::WindowBufferSizeEvent(record) => { + Some(Event::Resize(record.size.x as u16, record.size.y as u16)) + } + InputRecord::FocusEvent(record) => { + let event = if record.set_focus { + Event::FocusGained + } else { + Event::FocusLost + }; + Some(event) + } + _ => None, + }; + + if let Some(event) = event { + return Ok(Some(InternalEvent::Event(event))); + } + } + } + + if poll_timeout.elapsed() { + return Ok(None); + } + } + } + + #[cfg(feature = "event-stream")] + fn waker(&self) -> Waker { + self.poll.waker() + } +} diff --git a/vendor/crossterm/src/event/stream.rs b/vendor/crossterm/src/event/stream.rs index 602d54a..e3f35c4 100644 --- a/vendor/crossterm/src/event/stream.rs +++ b/vendor/crossterm/src/event/stream.rs @@ -1,147 +1,147 @@ -use std::{ - pin::Pin, - sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{self, SyncSender}, - Arc, - }, - task::{Context, Poll}, - thread, - time::Duration, -}; - -use futures_core::stream::Stream; - -use crate::Result; - -use super::{ - filter::EventFilter, lock_internal_event_reader, poll_internal, read_internal, sys::Waker, - Event, InternalEvent, -}; - -/// A stream of `Result`. -/// -/// **This type is not available by default. You have to use the `event-stream` feature flag -/// to make it available.** -/// -/// It implements the [Stream](futures_core::stream::Stream) -/// trait and allows you to receive [`Event`]s with [`async-std`](https://crates.io/crates/async-std) -/// or [`tokio`](https://crates.io/crates/tokio) crates. -/// -/// Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder to see how to use -/// it (`event-stream-*`). -#[derive(Debug)] -pub struct EventStream { - poll_internal_waker: Waker, - stream_wake_task_executed: Arc, - stream_wake_task_should_shutdown: Arc, - task_sender: SyncSender, -} - -impl Default for EventStream { - fn default() -> Self { - let (task_sender, receiver) = mpsc::sync_channel::(1); - - thread::spawn(move || { - while let Ok(task) = receiver.recv() { - loop { - if let Ok(true) = poll_internal(None, &EventFilter) { - break; - } - - if task.stream_wake_task_should_shutdown.load(Ordering::SeqCst) { - break; - } - } - task.stream_wake_task_executed - .store(false, Ordering::SeqCst); - task.stream_waker.wake(); - } - }); - - EventStream { - poll_internal_waker: lock_internal_event_reader().waker(), - stream_wake_task_executed: Arc::new(AtomicBool::new(false)), - stream_wake_task_should_shutdown: Arc::new(AtomicBool::new(false)), - task_sender, - } - } -} - -impl EventStream { - /// Constructs a new instance of `EventStream`. - pub fn new() -> EventStream { - EventStream::default() - } -} - -struct Task { - stream_waker: std::task::Waker, - stream_wake_task_executed: Arc, - stream_wake_task_should_shutdown: Arc, -} - -// Note to future me -// -// We need two wakers in order to implement EventStream correctly. -// -// 1. futures::Stream waker -// -// Stream::poll_next can return Poll::Pending which means that there's no -// event available. We are going to spawn a thread with the -// poll_internal(None, &EventFilter) call. This call blocks until an -// event is available and then we have to wake up the executor with notification -// that the task can be resumed. -// -// 2. poll_internal waker -// -// There's no event available, Poll::Pending was returned, stream waker thread -// is up and sitting in the poll_internal. User wants to drop the EventStream. -// We have to wake up the poll_internal (force it to return Ok(false)) and quit -// the thread before we drop. -impl Stream for EventStream { - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let result = match poll_internal(Some(Duration::from_secs(0)), &EventFilter) { - Ok(true) => match read_internal(&EventFilter) { - Ok(InternalEvent::Event(event)) => Poll::Ready(Some(Ok(event))), - Err(e) => Poll::Ready(Some(Err(e))), - #[cfg(unix)] - _ => unreachable!(), - }, - Ok(false) => { - if !self - .stream_wake_task_executed - .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) - // https://github.com/rust-lang/rust/issues/80486#issuecomment-752244166 - .unwrap_or_else(|x| x) - { - let stream_waker = cx.waker().clone(); - let stream_wake_task_executed = self.stream_wake_task_executed.clone(); - let stream_wake_task_should_shutdown = - self.stream_wake_task_should_shutdown.clone(); - - stream_wake_task_should_shutdown.store(false, Ordering::SeqCst); - - let _ = self.task_sender.send(Task { - stream_waker, - stream_wake_task_executed, - stream_wake_task_should_shutdown, - }); - } - Poll::Pending - } - Err(e) => Poll::Ready(Some(Err(e))), - }; - result - } -} - -impl Drop for EventStream { - fn drop(&mut self) { - self.stream_wake_task_should_shutdown - .store(true, Ordering::SeqCst); - let _ = self.poll_internal_waker.wake(); - } -} +use std::{ + pin::Pin, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{self, SyncSender}, + Arc, + }, + task::{Context, Poll}, + thread, + time::Duration, +}; + +use futures_core::stream::Stream; + +use crate::Result; + +use super::{ + filter::EventFilter, lock_internal_event_reader, poll_internal, read_internal, sys::Waker, + Event, InternalEvent, +}; + +/// A stream of `Result`. +/// +/// **This type is not available by default. You have to use the `event-stream` feature flag +/// to make it available.** +/// +/// It implements the [Stream](futures_core::stream::Stream) +/// trait and allows you to receive [`Event`]s with [`async-std`](https://crates.io/crates/async-std) +/// or [`tokio`](https://crates.io/crates/tokio) crates. +/// +/// Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder to see how to use +/// it (`event-stream-*`). +#[derive(Debug)] +pub struct EventStream { + poll_internal_waker: Waker, + stream_wake_task_executed: Arc, + stream_wake_task_should_shutdown: Arc, + task_sender: SyncSender, +} + +impl Default for EventStream { + fn default() -> Self { + let (task_sender, receiver) = mpsc::sync_channel::(1); + + thread::spawn(move || { + while let Ok(task) = receiver.recv() { + loop { + if let Ok(true) = poll_internal(None, &EventFilter) { + break; + } + + if task.stream_wake_task_should_shutdown.load(Ordering::SeqCst) { + break; + } + } + task.stream_wake_task_executed + .store(false, Ordering::SeqCst); + task.stream_waker.wake(); + } + }); + + EventStream { + poll_internal_waker: lock_internal_event_reader().waker(), + stream_wake_task_executed: Arc::new(AtomicBool::new(false)), + stream_wake_task_should_shutdown: Arc::new(AtomicBool::new(false)), + task_sender, + } + } +} + +impl EventStream { + /// Constructs a new instance of `EventStream`. + pub fn new() -> EventStream { + EventStream::default() + } +} + +struct Task { + stream_waker: std::task::Waker, + stream_wake_task_executed: Arc, + stream_wake_task_should_shutdown: Arc, +} + +// Note to future me +// +// We need two wakers in order to implement EventStream correctly. +// +// 1. futures::Stream waker +// +// Stream::poll_next can return Poll::Pending which means that there's no +// event available. We are going to spawn a thread with the +// poll_internal(None, &EventFilter) call. This call blocks until an +// event is available and then we have to wake up the executor with notification +// that the task can be resumed. +// +// 2. poll_internal waker +// +// There's no event available, Poll::Pending was returned, stream waker thread +// is up and sitting in the poll_internal. User wants to drop the EventStream. +// We have to wake up the poll_internal (force it to return Ok(false)) and quit +// the thread before we drop. +impl Stream for EventStream { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let result = match poll_internal(Some(Duration::from_secs(0)), &EventFilter) { + Ok(true) => match read_internal(&EventFilter) { + Ok(InternalEvent::Event(event)) => Poll::Ready(Some(Ok(event))), + Err(e) => Poll::Ready(Some(Err(e))), + #[cfg(unix)] + _ => unreachable!(), + }, + Ok(false) => { + if !self + .stream_wake_task_executed + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + // https://github.com/rust-lang/rust/issues/80486#issuecomment-752244166 + .unwrap_or_else(|x| x) + { + let stream_waker = cx.waker().clone(); + let stream_wake_task_executed = self.stream_wake_task_executed.clone(); + let stream_wake_task_should_shutdown = + self.stream_wake_task_should_shutdown.clone(); + + stream_wake_task_should_shutdown.store(false, Ordering::SeqCst); + + let _ = self.task_sender.send(Task { + stream_waker, + stream_wake_task_executed, + stream_wake_task_should_shutdown, + }); + } + Poll::Pending + } + Err(e) => Poll::Ready(Some(Err(e))), + }; + result + } +} + +impl Drop for EventStream { + fn drop(&mut self) { + self.stream_wake_task_should_shutdown + .store(true, Ordering::SeqCst); + let _ = self.poll_internal_waker.wake(); + } +} diff --git a/vendor/crossterm/src/event/sys.rs b/vendor/crossterm/src/event/sys.rs index bd79307..e8e619f 100644 --- a/vendor/crossterm/src/event/sys.rs +++ b/vendor/crossterm/src/event/sys.rs @@ -1,9 +1,9 @@ -#[cfg(all(unix, feature = "event-stream"))] -pub(crate) use unix::waker::Waker; -#[cfg(all(windows, feature = "event-stream"))] -pub(crate) use windows::waker::Waker; - -#[cfg(unix)] -pub(crate) mod unix; -#[cfg(windows)] -pub(crate) mod windows; +#[cfg(all(unix, feature = "event-stream"))] +pub(crate) use unix::waker::Waker; +#[cfg(all(windows, feature = "event-stream"))] +pub(crate) use windows::waker::Waker; + +#[cfg(unix)] +pub(crate) mod unix; +#[cfg(windows)] +pub(crate) mod windows; diff --git a/vendor/crossterm/src/event/sys/unix.rs b/vendor/crossterm/src/event/sys/unix.rs index 19321f6..d39d5d4 100644 --- a/vendor/crossterm/src/event/sys/unix.rs +++ b/vendor/crossterm/src/event/sys/unix.rs @@ -1,5 +1,5 @@ -#[cfg(feature = "event-stream")] -pub(crate) mod waker; - -pub(crate) mod file_descriptor; -pub(crate) mod parse; +#[cfg(feature = "event-stream")] +pub(crate) mod waker; + +pub(crate) mod file_descriptor; +pub(crate) mod parse; diff --git a/vendor/crossterm/src/event/sys/unix/file_descriptor.rs b/vendor/crossterm/src/event/sys/unix/file_descriptor.rs index 8b48176..19677e3 100644 --- a/vendor/crossterm/src/event/sys/unix/file_descriptor.rs +++ b/vendor/crossterm/src/event/sys/unix/file_descriptor.rs @@ -1,82 +1,82 @@ -use std::{ - fs, io, - os::unix::io::{IntoRawFd, RawFd}, -}; - -use libc::size_t; - -use crate::Result; - -/// A file descriptor wrapper. -/// -/// It allows to retrieve raw file descriptor, write to the file descriptor and -/// mainly it closes the file descriptor once dropped. -#[derive(Debug)] -pub struct FileDesc { - fd: RawFd, - close_on_drop: bool, -} - -impl FileDesc { - /// Constructs a new `FileDesc` with the given `RawFd`. - /// - /// # Arguments - /// - /// * `fd` - raw file descriptor - /// * `close_on_drop` - specify if the raw file descriptor should be closed once the `FileDesc` is dropped - pub fn new(fd: RawFd, close_on_drop: bool) -> FileDesc { - FileDesc { fd, close_on_drop } - } - - pub fn read(&self, buffer: &mut [u8], size: usize) -> Result { - let result = unsafe { - libc::read( - self.fd, - buffer.as_mut_ptr() as *mut libc::c_void, - size as size_t, - ) as isize - }; - - if result < 0 { - Err(io::Error::last_os_error()) - } else { - Ok(result as usize) - } - } - - /// Returns the underlying file descriptor. - pub fn raw_fd(&self) -> RawFd { - self.fd - } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - if self.close_on_drop { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // opened after we closed ours. - let _ = unsafe { libc::close(self.fd) }; - } - } -} - -/// Creates a file descriptor pointing to the standard input or `/dev/tty`. -pub fn tty_fd() -> Result { - let (fd, close_on_drop) = if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } { - (libc::STDIN_FILENO, false) - } else { - ( - fs::OpenOptions::new() - .read(true) - .write(true) - .open("/dev/tty")? - .into_raw_fd(), - true, - ) - }; - - Ok(FileDesc::new(fd, close_on_drop)) -} +use std::{ + fs, io, + os::unix::io::{IntoRawFd, RawFd}, +}; + +use libc::size_t; + +use crate::Result; + +/// A file descriptor wrapper. +/// +/// It allows to retrieve raw file descriptor, write to the file descriptor and +/// mainly it closes the file descriptor once dropped. +#[derive(Debug)] +pub struct FileDesc { + fd: RawFd, + close_on_drop: bool, +} + +impl FileDesc { + /// Constructs a new `FileDesc` with the given `RawFd`. + /// + /// # Arguments + /// + /// * `fd` - raw file descriptor + /// * `close_on_drop` - specify if the raw file descriptor should be closed once the `FileDesc` is dropped + pub fn new(fd: RawFd, close_on_drop: bool) -> FileDesc { + FileDesc { fd, close_on_drop } + } + + pub fn read(&self, buffer: &mut [u8], size: usize) -> Result { + let result = unsafe { + libc::read( + self.fd, + buffer.as_mut_ptr() as *mut libc::c_void, + size as size_t, + ) as isize + }; + + if result < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(result as usize) + } + } + + /// Returns the underlying file descriptor. + pub fn raw_fd(&self) -> RawFd { + self.fd + } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + if self.close_on_drop { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // opened after we closed ours. + let _ = unsafe { libc::close(self.fd) }; + } + } +} + +/// Creates a file descriptor pointing to the standard input or `/dev/tty`. +pub fn tty_fd() -> Result { + let (fd, close_on_drop) = if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } { + (libc::STDIN_FILENO, false) + } else { + ( + fs::OpenOptions::new() + .read(true) + .write(true) + .open("/dev/tty")? + .into_raw_fd(), + true, + ) + }; + + Ok(FileDesc::new(fd, close_on_drop)) +} diff --git a/vendor/crossterm/src/event/sys/unix/parse.rs b/vendor/crossterm/src/event/sys/unix/parse.rs index e5b2459..4560fdd 100644 --- a/vendor/crossterm/src/event/sys/unix/parse.rs +++ b/vendor/crossterm/src/event/sys/unix/parse.rs @@ -1,1367 +1,1367 @@ -use std::io; - -use crate::{ - event::{ - Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, MediaKeyCode, - ModifierKeyCode, MouseButton, MouseEvent, MouseEventKind, - }, - ErrorKind, Result, -}; - -use super::super::super::InternalEvent; - -// Event parsing -// -// This code (& previous one) are kind of ugly. We have to think about this, -// because it's really not maintainable, no tests, etc. -// -// Every fn returns Result> -// -// Ok(None) -> wait for more bytes -// Err(_) -> failed to parse event, clear the buffer -// Ok(Some(event)) -> we have event, clear the buffer -// - -fn could_not_parse_event_error() -> ErrorKind { - io::Error::new(io::ErrorKind::Other, "Could not parse an event.") -} - -pub(crate) fn parse_event(buffer: &[u8], input_available: bool) -> Result> { - if buffer.is_empty() { - return Ok(None); - } - - match buffer[0] { - b'\x1B' => { - if buffer.len() == 1 { - if input_available { - // Possible Esc sequence - Ok(None) - } else { - Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into())))) - } - } else { - match buffer[1] { - b'O' => { - if buffer.len() == 2 { - Ok(None) - } else { - match buffer[2] { - b'D' => { - Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Left.into())))) - } - b'C' => Ok(Some(InternalEvent::Event(Event::Key( - KeyCode::Right.into(), - )))), - b'A' => { - Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Up.into())))) - } - b'B' => { - Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Down.into())))) - } - b'H' => { - Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Home.into())))) - } - b'F' => { - Ok(Some(InternalEvent::Event(Event::Key(KeyCode::End.into())))) - } - // F1-F4 - val @ b'P'..=b'S' => Ok(Some(InternalEvent::Event(Event::Key( - KeyCode::F(1 + val - b'P').into(), - )))), - _ => Err(could_not_parse_event_error()), - } - } - } - b'[' => parse_csi(buffer), - b'\x1B' => Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into())))), - _ => parse_event(&buffer[1..], input_available).map(|event_option| { - event_option.map(|event| { - if let InternalEvent::Event(Event::Key(key_event)) = event { - let mut alt_key_event = key_event; - alt_key_event.modifiers |= KeyModifiers::ALT; - InternalEvent::Event(Event::Key(alt_key_event)) - } else { - event - } - }) - }), - } - } - } - b'\r' => Ok(Some(InternalEvent::Event(Event::Key( - KeyCode::Enter.into(), - )))), - // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get - // newlines as input is because the terminal converts \r into \n for us. When we - // enter raw mode, we disable that, so \n no longer has any meaning - it's better to - // use Ctrl+J. Waiting to handle it here means it gets picked up later - b'\n' if !crate::terminal::sys::is_raw_mode_enabled() => Ok(Some(InternalEvent::Event( - Event::Key(KeyCode::Enter.into()), - ))), - b'\t' => Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into())))), - b'\x7F' => Ok(Some(InternalEvent::Event(Event::Key( - KeyCode::Backspace.into(), - )))), - c @ b'\x01'..=b'\x1A' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char((c as u8 - 0x1 + b'a') as char), - KeyModifiers::CONTROL, - ))))), - c @ b'\x1C'..=b'\x1F' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char((c as u8 - 0x1C + b'4') as char), - KeyModifiers::CONTROL, - ))))), - b'\0' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char(' '), - KeyModifiers::CONTROL, - ))))), - _ => parse_utf8_char(buffer).map(|maybe_char| { - maybe_char - .map(KeyCode::Char) - .map(char_code_to_event) - .map(Event::Key) - .map(InternalEvent::Event) - }), - } -} - -// converts KeyCode to KeyEvent (adds shift modifier in case of uppercase characters) -fn char_code_to_event(code: KeyCode) -> KeyEvent { - let modifiers = match code { - KeyCode::Char(c) if c.is_uppercase() => KeyModifiers::SHIFT, - _ => KeyModifiers::empty(), - }; - KeyEvent::new(code, modifiers) -} - -pub(crate) fn parse_csi(buffer: &[u8]) -> Result> { - assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ - - if buffer.len() == 2 { - return Ok(None); - } - - let input_event = match buffer[2] { - b'[' => { - if buffer.len() == 3 { - None - } else { - match buffer[3] { - // NOTE (@imdaveho): cannot find when this occurs; - // having another '[' after ESC[ not a likely scenario - val @ b'A'..=b'E' => Some(Event::Key(KeyCode::F(1 + val - b'A').into())), - _ => return Err(could_not_parse_event_error()), - } - } - } - b'D' => Some(Event::Key(KeyCode::Left.into())), - b'C' => Some(Event::Key(KeyCode::Right.into())), - b'A' => Some(Event::Key(KeyCode::Up.into())), - b'B' => Some(Event::Key(KeyCode::Down.into())), - b'H' => Some(Event::Key(KeyCode::Home.into())), - b'F' => Some(Event::Key(KeyCode::End.into())), - b'Z' => Some(Event::Key(KeyEvent::new_with_kind( - KeyCode::BackTab, - KeyModifiers::SHIFT, - KeyEventKind::Press, - ))), - b'M' => return parse_csi_normal_mouse(buffer), - b'<' => return parse_csi_sgr_mouse(buffer), - b'I' => Some(Event::FocusGained), - b'O' => Some(Event::FocusLost), - b';' => return parse_csi_modifier_key_code(buffer), - b'0'..=b'9' => { - // Numbered escape code. - if buffer.len() == 3 { - None - } else { - // The final byte of a CSI sequence can be in the range 64-126, so - // let's keep reading anything else. - let last_byte = buffer[buffer.len() - 1]; - if !(64..=126).contains(&last_byte) { - None - } else { - #[cfg(feature = "bracketed-paste")] - if buffer.starts_with(b"\x1B[200~") { - return parse_csi_bracketed_paste(buffer); - } - match last_byte { - b'M' => return parse_csi_rxvt_mouse(buffer), - b'~' => return parse_csi_special_key_code(buffer), - b'u' => return parse_csi_u_encoded_key_code(buffer), - b'R' => return parse_csi_cursor_position(buffer), - _ => return parse_csi_modifier_key_code(buffer), - } - } - } - } - _ => return Err(could_not_parse_event_error()), - }; - - Ok(input_event.map(InternalEvent::Event)) -} - -pub(crate) fn next_parsed(iter: &mut dyn Iterator) -> Result -where - T: std::str::FromStr, -{ - iter.next() - .ok_or_else(could_not_parse_event_error)? - .parse::() - .map_err(|_| could_not_parse_event_error()) -} - -fn modifier_and_kind_parsed(iter: &mut dyn Iterator) -> Result<(u8, u8)> { - let mut sub_split = iter - .next() - .ok_or_else(could_not_parse_event_error)? - .split(':'); - - let modifier_mask = next_parsed::(&mut sub_split)?; - - if let Ok(kind_code) = next_parsed::(&mut sub_split) { - Ok((modifier_mask, kind_code)) - } else { - Ok((modifier_mask, 1)) - } -} - -pub(crate) fn parse_csi_cursor_position(buffer: &[u8]) -> Result> { - // ESC [ Cy ; Cx R - // Cy - cursor row number (starting from 1) - // Cx - cursor column number (starting from 1) - assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ - assert!(buffer.ends_with(&[b'R'])); - - let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) - .map_err(|_| could_not_parse_event_error())?; - - let mut split = s.split(';'); - - let y = next_parsed::(&mut split)? - 1; - let x = next_parsed::(&mut split)? - 1; - - Ok(Some(InternalEvent::CursorPosition(x, y))) -} - -fn parse_modifiers(mask: u8) -> KeyModifiers { - let modifier_mask = mask.saturating_sub(1); - let mut modifiers = KeyModifiers::empty(); - if modifier_mask & 1 != 0 { - modifiers |= KeyModifiers::SHIFT; - } - if modifier_mask & 2 != 0 { - modifiers |= KeyModifiers::ALT; - } - if modifier_mask & 4 != 0 { - modifiers |= KeyModifiers::CONTROL; - } - if modifier_mask & 8 != 0 { - modifiers |= KeyModifiers::SUPER; - } - if modifier_mask & 16 != 0 { - modifiers |= KeyModifiers::HYPER; - } - if modifier_mask & 32 != 0 { - modifiers |= KeyModifiers::META; - } - modifiers -} - -fn parse_modifiers_to_state(mask: u8) -> KeyEventState { - let modifier_mask = mask.saturating_sub(1); - let mut state = KeyEventState::empty(); - if modifier_mask & 64 != 0 { - state |= KeyEventState::CAPS_LOCK; - } - if modifier_mask & 128 != 0 { - state |= KeyEventState::NUM_LOCK; - } - state -} - -fn parse_key_event_kind(kind: u8) -> KeyEventKind { - match kind { - 1 => KeyEventKind::Press, - 2 => KeyEventKind::Repeat, - 3 => KeyEventKind::Release, - _ => KeyEventKind::Press, - } -} - -pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result> { - assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ - // - let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) - .map_err(|_| could_not_parse_event_error())?; - let mut split = s.split(';'); - - split.next(); - - let (modifiers, kind) = - if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) { - ( - parse_modifiers(modifier_mask), - parse_key_event_kind(kind_code), - ) - } else if buffer.len() > 3 { - ( - parse_modifiers( - (buffer[buffer.len() - 2] as char) - .to_digit(10) - .ok_or_else(could_not_parse_event_error)? as u8, - ), - KeyEventKind::Press, - ) - } else { - (KeyModifiers::NONE, KeyEventKind::Press) - }; - let key = buffer[buffer.len() - 1]; - - let keycode = match key { - b'A' => KeyCode::Up, - b'B' => KeyCode::Down, - b'C' => KeyCode::Right, - b'D' => KeyCode::Left, - b'F' => KeyCode::End, - b'H' => KeyCode::Home, - b'P' => KeyCode::F(1), - b'Q' => KeyCode::F(2), - b'R' => KeyCode::F(3), - b'S' => KeyCode::F(4), - _ => return Err(could_not_parse_event_error()), - }; - - let input_event = Event::Key(KeyEvent::new_with_kind(keycode, modifiers, kind)); - - Ok(Some(InternalEvent::Event(input_event))) -} - -fn translate_functional_key_code(codepoint: u32) -> Option<(KeyCode, KeyEventState)> { - if let Some(keycode) = match codepoint { - 57399 => Some(KeyCode::Char('0')), - 57400 => Some(KeyCode::Char('1')), - 57401 => Some(KeyCode::Char('2')), - 57402 => Some(KeyCode::Char('3')), - 57403 => Some(KeyCode::Char('4')), - 57404 => Some(KeyCode::Char('5')), - 57405 => Some(KeyCode::Char('6')), - 57406 => Some(KeyCode::Char('7')), - 57407 => Some(KeyCode::Char('8')), - 57408 => Some(KeyCode::Char('9')), - 57409 => Some(KeyCode::Char('.')), - 57410 => Some(KeyCode::Char('/')), - 57411 => Some(KeyCode::Char('*')), - 57412 => Some(KeyCode::Char('-')), - 57413 => Some(KeyCode::Char('+')), - 57414 => Some(KeyCode::Enter), - 57415 => Some(KeyCode::Char('=')), - 57416 => Some(KeyCode::Char(',')), - 57417 => Some(KeyCode::Left), - 57418 => Some(KeyCode::Right), - 57419 => Some(KeyCode::Up), - 57420 => Some(KeyCode::Down), - 57421 => Some(KeyCode::PageUp), - 57422 => Some(KeyCode::PageDown), - 57423 => Some(KeyCode::Home), - 57424 => Some(KeyCode::End), - 57425 => Some(KeyCode::Insert), - 57426 => Some(KeyCode::Delete), - 57427 => Some(KeyCode::KeypadBegin), - _ => None, - } { - return Some((keycode, KeyEventState::KEYPAD)); - } - - if let Some(keycode) = match codepoint { - 57358 => Some(KeyCode::CapsLock), - 57359 => Some(KeyCode::ScrollLock), - 57360 => Some(KeyCode::NumLock), - 57361 => Some(KeyCode::PrintScreen), - 57362 => Some(KeyCode::Pause), - 57363 => Some(KeyCode::Menu), - 57376 => Some(KeyCode::F(13)), - 57377 => Some(KeyCode::F(14)), - 57378 => Some(KeyCode::F(15)), - 57379 => Some(KeyCode::F(16)), - 57380 => Some(KeyCode::F(17)), - 57381 => Some(KeyCode::F(18)), - 57382 => Some(KeyCode::F(19)), - 57383 => Some(KeyCode::F(20)), - 57384 => Some(KeyCode::F(21)), - 57385 => Some(KeyCode::F(22)), - 57386 => Some(KeyCode::F(23)), - 57387 => Some(KeyCode::F(24)), - 57388 => Some(KeyCode::F(25)), - 57389 => Some(KeyCode::F(26)), - 57390 => Some(KeyCode::F(27)), - 57391 => Some(KeyCode::F(28)), - 57392 => Some(KeyCode::F(29)), - 57393 => Some(KeyCode::F(30)), - 57394 => Some(KeyCode::F(31)), - 57395 => Some(KeyCode::F(32)), - 57396 => Some(KeyCode::F(33)), - 57397 => Some(KeyCode::F(34)), - 57398 => Some(KeyCode::F(35)), - 57428 => Some(KeyCode::Media(MediaKeyCode::Play)), - 57429 => Some(KeyCode::Media(MediaKeyCode::Pause)), - 57430 => Some(KeyCode::Media(MediaKeyCode::PlayPause)), - 57431 => Some(KeyCode::Media(MediaKeyCode::Reverse)), - 57432 => Some(KeyCode::Media(MediaKeyCode::Stop)), - 57433 => Some(KeyCode::Media(MediaKeyCode::FastForward)), - 57434 => Some(KeyCode::Media(MediaKeyCode::Rewind)), - 57435 => Some(KeyCode::Media(MediaKeyCode::TrackNext)), - 57436 => Some(KeyCode::Media(MediaKeyCode::TrackPrevious)), - 57437 => Some(KeyCode::Media(MediaKeyCode::Record)), - 57438 => Some(KeyCode::Media(MediaKeyCode::LowerVolume)), - 57439 => Some(KeyCode::Media(MediaKeyCode::RaiseVolume)), - 57440 => Some(KeyCode::Media(MediaKeyCode::MuteVolume)), - 57441 => Some(KeyCode::Modifier(ModifierKeyCode::LeftShift)), - 57442 => Some(KeyCode::Modifier(ModifierKeyCode::LeftControl)), - 57443 => Some(KeyCode::Modifier(ModifierKeyCode::LeftAlt)), - 57444 => Some(KeyCode::Modifier(ModifierKeyCode::LeftSuper)), - 57445 => Some(KeyCode::Modifier(ModifierKeyCode::LeftHyper)), - 57446 => Some(KeyCode::Modifier(ModifierKeyCode::LeftMeta)), - 57447 => Some(KeyCode::Modifier(ModifierKeyCode::RightShift)), - 57448 => Some(KeyCode::Modifier(ModifierKeyCode::RightControl)), - 57449 => Some(KeyCode::Modifier(ModifierKeyCode::RightAlt)), - 57450 => Some(KeyCode::Modifier(ModifierKeyCode::RightSuper)), - 57451 => Some(KeyCode::Modifier(ModifierKeyCode::RightHyper)), - 57452 => Some(KeyCode::Modifier(ModifierKeyCode::RightMeta)), - 57453 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift)), - 57454 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift)), - _ => None, - } { - return Some((keycode, KeyEventState::empty())); - } - - None -} - -pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result> { - assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ - assert!(buffer.ends_with(&[b'u'])); - - let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) - .map_err(|_| could_not_parse_event_error())?; - let mut split = s.split(';'); - - // This CSI sequence a tuple of semicolon-separated numbers. - // CSI [codepoint];[modifiers] u - // codepoint: ASCII Dec value - let codepoint = next_parsed::(&mut split)?; - - let (mut modifiers, kind, state_from_modifiers) = - if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) { - ( - parse_modifiers(modifier_mask), - parse_key_event_kind(kind_code), - parse_modifiers_to_state(modifier_mask), - ) - } else { - (KeyModifiers::NONE, KeyEventKind::Press, KeyEventState::NONE) - }; - - let (keycode, state_from_keycode) = { - if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) { - (special_key_code, state) - } else if let Some(c) = char::from_u32(codepoint) { - ( - match c { - '\x1B' => KeyCode::Esc, - '\r' => KeyCode::Enter, - // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get - // newlines as input is because the terminal converts \r into \n for us. When we - // enter raw mode, we disable that, so \n no longer has any meaning - it's better to - // use Ctrl+J. Waiting to handle it here means it gets picked up later - '\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter, - '\t' => { - if modifiers.contains(KeyModifiers::SHIFT) { - KeyCode::BackTab - } else { - KeyCode::Tab - } - } - '\x7F' => KeyCode::Backspace, - _ => KeyCode::Char(c), - }, - KeyEventState::empty(), - ) - } else { - return Err(could_not_parse_event_error()); - } - }; - - if let KeyCode::Modifier(modifier_keycode) = keycode { - match modifier_keycode { - ModifierKeyCode::LeftAlt | ModifierKeyCode::RightAlt => { - modifiers.set(KeyModifiers::ALT, true) - } - ModifierKeyCode::LeftControl | ModifierKeyCode::RightControl => { - modifiers.set(KeyModifiers::CONTROL, true) - } - ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => { - modifiers.set(KeyModifiers::SHIFT, true) - } - ModifierKeyCode::LeftSuper | ModifierKeyCode::RightSuper => { - modifiers.set(KeyModifiers::SUPER, true) - } - ModifierKeyCode::LeftHyper | ModifierKeyCode::RightHyper => { - modifiers.set(KeyModifiers::HYPER, true) - } - ModifierKeyCode::LeftMeta | ModifierKeyCode::RightMeta => { - modifiers.set(KeyModifiers::META, true) - } - _ => {} - } - } - - let input_event = Event::Key(KeyEvent::new_with_kind_and_state( - keycode, - modifiers, - kind, - state_from_keycode | state_from_modifiers, - )); - - Ok(Some(InternalEvent::Event(input_event))) -} - -pub(crate) fn parse_csi_special_key_code(buffer: &[u8]) -> Result> { - assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ - assert!(buffer.ends_with(&[b'~'])); - - let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) - .map_err(|_| could_not_parse_event_error())?; - let mut split = s.split(';'); - - // This CSI sequence can be a list of semicolon-separated numbers. - let first = next_parsed::(&mut split)?; - - let modifiers = if let Ok(modifier_mask) = next_parsed::(&mut split) { - parse_modifiers(modifier_mask) - } else { - KeyModifiers::NONE - }; - - let keycode = match first { - 1 | 7 => KeyCode::Home, - 2 => KeyCode::Insert, - 3 => KeyCode::Delete, - 4 | 8 => KeyCode::End, - 5 => KeyCode::PageUp, - 6 => KeyCode::PageDown, - v @ 11..=15 => KeyCode::F(v - 10), - v @ 17..=21 => KeyCode::F(v - 11), - v @ 23..=26 => KeyCode::F(v - 12), - v @ 28..=29 => KeyCode::F(v - 15), - v @ 31..=34 => KeyCode::F(v - 17), - _ => return Err(could_not_parse_event_error()), - }; - - let input_event = Event::Key(KeyEvent::new(keycode, modifiers)); - - Ok(Some(InternalEvent::Event(input_event))) -} - -pub(crate) fn parse_csi_rxvt_mouse(buffer: &[u8]) -> Result> { - // rxvt mouse encoding: - // ESC [ Cb ; Cx ; Cy ; M - - assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ - assert!(buffer.ends_with(&[b'M'])); - - let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) - .map_err(|_| could_not_parse_event_error())?; - let mut split = s.split(';'); - - let cb = next_parsed::(&mut split)? - .checked_sub(32) - .ok_or_else(could_not_parse_event_error)?; - let (kind, modifiers) = parse_cb(cb)?; - - let cx = next_parsed::(&mut split)? - 1; - let cy = next_parsed::(&mut split)? - 1; - - Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind, - column: cx, - row: cy, - modifiers, - })))) -} - -pub(crate) fn parse_csi_normal_mouse(buffer: &[u8]) -> Result> { - // Normal mouse encoding: ESC [ M CB Cx Cy (6 characters only). - - assert!(buffer.starts_with(&[b'\x1B', b'[', b'M'])); // ESC [ M - - if buffer.len() < 6 { - return Ok(None); - } - - let cb = buffer[3] - .checked_sub(32) - .ok_or_else(could_not_parse_event_error)?; - let (kind, modifiers) = parse_cb(cb)?; - - // See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking - // The upper left character position on the terminal is denoted as 1,1. - // Subtract 1 to keep it synced with cursor - let cx = u16::from(buffer[4].saturating_sub(32)) - 1; - let cy = u16::from(buffer[5].saturating_sub(32)) - 1; - - Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind, - column: cx, - row: cy, - modifiers, - })))) -} - -pub(crate) fn parse_csi_sgr_mouse(buffer: &[u8]) -> Result> { - // ESC [ < Cb ; Cx ; Cy (;) (M or m) - - assert!(buffer.starts_with(&[b'\x1B', b'[', b'<'])); // ESC [ < - - if !buffer.ends_with(&[b'm']) && !buffer.ends_with(&[b'M']) { - return Ok(None); - } - - let s = std::str::from_utf8(&buffer[3..buffer.len() - 1]) - .map_err(|_| could_not_parse_event_error())?; - let mut split = s.split(';'); - - let cb = next_parsed::(&mut split)?; - let (kind, modifiers) = parse_cb(cb)?; - - // See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking - // The upper left character position on the terminal is denoted as 1,1. - // Subtract 1 to keep it synced with cursor - let cx = next_parsed::(&mut split)? - 1; - let cy = next_parsed::(&mut split)? - 1; - - // When button 3 in Cb is used to represent mouse release, you can't tell which button was - // released. SGR mode solves this by having the sequence end with a lowercase m if it's a - // button release and an uppercase M if it's a button press. - // - // We've already checked that the last character is a lowercase or uppercase M at the start of - // this function, so we just need one if. - let kind = if buffer.last() == Some(&b'm') { - match kind { - MouseEventKind::Down(button) => MouseEventKind::Up(button), - other => other, - } - } else { - kind - }; - - Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind, - column: cx, - row: cy, - modifiers, - })))) -} - -/// Cb is the byte of a mouse input that contains the button being used, the key modifiers being -/// held and whether the mouse is dragging or not. -/// -/// Bit layout of cb, from low to high: -/// -/// - button number -/// - button number -/// - shift -/// - meta (alt) -/// - control -/// - mouse is dragging -/// - button number -/// - button number -fn parse_cb(cb: u8) -> Result<(MouseEventKind, KeyModifiers)> { - let button_number = (cb & 0b0000_0011) | ((cb & 0b1100_0000) >> 4); - let dragging = cb & 0b0010_0000 == 0b0010_0000; - - let kind = match (button_number, dragging) { - (0, false) => MouseEventKind::Down(MouseButton::Left), - (1, false) => MouseEventKind::Down(MouseButton::Middle), - (2, false) => MouseEventKind::Down(MouseButton::Right), - (0, true) => MouseEventKind::Drag(MouseButton::Left), - (1, true) => MouseEventKind::Drag(MouseButton::Middle), - (2, true) => MouseEventKind::Drag(MouseButton::Right), - (3, false) => MouseEventKind::Up(MouseButton::Left), - (3, true) | (4, true) | (5, true) => MouseEventKind::Moved, - (4, false) => MouseEventKind::ScrollUp, - (5, false) => MouseEventKind::ScrollDown, - // We do not support other buttons. - _ => return Err(could_not_parse_event_error()), - }; - - let mut modifiers = KeyModifiers::empty(); - - if cb & 0b0000_0100 == 0b0000_0100 { - modifiers |= KeyModifiers::SHIFT; - } - if cb & 0b0000_1000 == 0b0000_1000 { - modifiers |= KeyModifiers::ALT; - } - if cb & 0b0001_0000 == 0b0001_0000 { - modifiers |= KeyModifiers::CONTROL; - } - - Ok((kind, modifiers)) -} - -#[cfg(feature = "bracketed-paste")] -pub(crate) fn parse_csi_bracketed_paste(buffer: &[u8]) -> Result> { - // ESC [ 2 0 0 ~ pasted text ESC 2 0 1 ~ - assert!(buffer.starts_with(b"\x1B[200~")); - - if !buffer.ends_with(b"\x1b[201~") { - Ok(None) - } else { - let paste = String::from_utf8_lossy(&buffer[6..buffer.len() - 6]).to_string(); - Ok(Some(InternalEvent::Event(Event::Paste(paste)))) - } -} - -pub(crate) fn parse_utf8_char(buffer: &[u8]) -> Result> { - match std::str::from_utf8(buffer) { - Ok(s) => { - let ch = s.chars().next().ok_or_else(could_not_parse_event_error)?; - - Ok(Some(ch)) - } - Err(_) => { - // from_utf8 failed, but we have to check if we need more bytes for code point - // and if all the bytes we have no are valid - - let required_bytes = match buffer[0] { - // https://en.wikipedia.org/wiki/UTF-8#Description - (0x00..=0x7F) => 1, // 0xxxxxxx - (0xC0..=0xDF) => 2, // 110xxxxx 10xxxxxx - (0xE0..=0xEF) => 3, // 1110xxxx 10xxxxxx 10xxxxxx - (0xF0..=0xF7) => 4, // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - (0x80..=0xBF) | (0xF8..=0xFF) => return Err(could_not_parse_event_error()), - }; - - // More than 1 byte, check them for 10xxxxxx pattern - if required_bytes > 1 && buffer.len() > 1 { - for byte in &buffer[1..] { - if byte & !0b0011_1111 != 0b1000_0000 { - return Err(could_not_parse_event_error()); - } - } - } - - if buffer.len() < required_bytes { - // All bytes looks good so far, but we need more of them - Ok(None) - } else { - Err(could_not_parse_event_error()) - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::event::{KeyEventState, KeyModifiers, MouseButton, MouseEvent}; - - use super::*; - - #[test] - fn test_esc_key() { - assert_eq!( - parse_event(b"\x1B", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into()))), - ); - } - - #[test] - fn test_possible_esc_sequence() { - assert_eq!(parse_event(b"\x1B", true).unwrap(), None,); - } - - #[test] - fn test_alt_key() { - assert_eq!( - parse_event(b"\x1Bc", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('c'), - KeyModifiers::ALT - )))), - ); - } - - #[test] - fn test_alt_shift() { - assert_eq!( - parse_event(b"\x1BH", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('H'), - KeyModifiers::ALT | KeyModifiers::SHIFT - )))), - ); - } - - #[test] - fn test_alt_ctrl() { - assert_eq!( - parse_event(b"\x1B\x14", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('t'), - KeyModifiers::ALT | KeyModifiers::CONTROL - )))), - ); - } - - #[test] - fn test_parse_event_subsequent_calls() { - // The main purpose of this test is to check if we're passing - // correct slice to other parse_ functions. - - // parse_csi_cursor_position - assert_eq!( - parse_event(b"\x1B[20;10R", false).unwrap(), - Some(InternalEvent::CursorPosition(9, 19)) - ); - - // parse_csi - assert_eq!( - parse_event(b"\x1B[D", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))), - ); - - // parse_csi_modifier_key_code - assert_eq!( - parse_event(b"\x1B[2D", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Left, - KeyModifiers::SHIFT - )))) - ); - - // parse_csi_special_key_code - assert_eq!( - parse_event(b"\x1B[3~", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))), - ); - - // parse_csi_bracketed_paste - #[cfg(feature = "bracketed-paste")] - assert_eq!( - parse_event(b"\x1B[200~on and on and on\x1B[201~", false).unwrap(), - Some(InternalEvent::Event(Event::Paste( - "on and on and on".to_string() - ))), - ); - - // parse_csi_rxvt_mouse - assert_eq!( - parse_event(b"\x1B[32;30;40;M", false).unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Down(MouseButton::Left), - column: 29, - row: 39, - modifiers: KeyModifiers::empty(), - }))) - ); - - // parse_csi_normal_mouse - assert_eq!( - parse_event(b"\x1B[M0\x60\x70", false).unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Down(MouseButton::Left), - column: 63, - row: 79, - modifiers: KeyModifiers::CONTROL, - }))) - ); - - // parse_csi_sgr_mouse - assert_eq!( - parse_event(b"\x1B[<0;20;10;M", false).unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Down(MouseButton::Left), - column: 19, - row: 9, - modifiers: KeyModifiers::empty(), - }))) - ); - - // parse_utf8_char - assert_eq!( - parse_event("Ž".as_bytes(), false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('Ž'), - KeyModifiers::SHIFT - )))), - ); - } - - #[test] - fn test_parse_event() { - assert_eq!( - parse_event(b"\t", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into()))), - ); - } - - #[test] - fn test_parse_csi_cursor_position() { - assert_eq!( - parse_csi_cursor_position(b"\x1B[20;10R").unwrap(), - Some(InternalEvent::CursorPosition(9, 19)) - ); - } - - #[test] - fn test_parse_csi() { - assert_eq!( - parse_csi(b"\x1B[D").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))), - ); - } - - #[test] - fn test_parse_csi_modifier_key_code() { - assert_eq!( - parse_csi_modifier_key_code(b"\x1B[2D").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Left, - KeyModifiers::SHIFT - )))), - ); - } - - #[test] - fn test_parse_csi_special_key_code() { - assert_eq!( - parse_csi_special_key_code(b"\x1B[3~").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))), - ); - } - - #[test] - fn test_parse_csi_special_key_code_multiple_values_not_supported() { - assert_eq!( - parse_csi_special_key_code(b"\x1B[3;2~").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Delete, - KeyModifiers::SHIFT - )))), - ); - } - - #[cfg(feature = "bracketed-paste")] - #[test] - fn test_parse_csi_bracketed_paste() { - // - assert_eq!( - parse_event(b"\x1B[200~o", false).unwrap(), - None, - "A partial bracketed paste isn't parsed" - ); - assert_eq!( - parse_event(b"\x1B[200~o\x1B[2D", false).unwrap(), - None, - "A partial bracketed paste containing another escape code isn't parsed" - ); - assert_eq!( - parse_event(b"\x1B[200~o\x1B[2D\x1B[201~", false).unwrap(), - Some(InternalEvent::Event(Event::Paste("o\x1B[2D".to_string()))) - ); - } - - #[test] - fn test_parse_csi_focus() { - assert_eq!( - parse_csi(b"\x1B[O").unwrap(), - Some(InternalEvent::Event(Event::FocusLost)) - ); - } - - #[test] - fn test_parse_csi_rxvt_mouse() { - assert_eq!( - parse_csi_rxvt_mouse(b"\x1B[32;30;40;M").unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Down(MouseButton::Left), - column: 29, - row: 39, - modifiers: KeyModifiers::empty(), - }))) - ); - } - - #[test] - fn test_parse_csi_normal_mouse() { - assert_eq!( - parse_csi_normal_mouse(b"\x1B[M0\x60\x70").unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Down(MouseButton::Left), - column: 63, - row: 79, - modifiers: KeyModifiers::CONTROL, - }))) - ); - } - - #[test] - fn test_parse_csi_sgr_mouse() { - assert_eq!( - parse_csi_sgr_mouse(b"\x1B[<0;20;10;M").unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Down(MouseButton::Left), - column: 19, - row: 9, - modifiers: KeyModifiers::empty(), - }))) - ); - assert_eq!( - parse_csi_sgr_mouse(b"\x1B[<0;20;10M").unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Down(MouseButton::Left), - column: 19, - row: 9, - modifiers: KeyModifiers::empty(), - }))) - ); - assert_eq!( - parse_csi_sgr_mouse(b"\x1B[<0;20;10;m").unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Up(MouseButton::Left), - column: 19, - row: 9, - modifiers: KeyModifiers::empty(), - }))) - ); - assert_eq!( - parse_csi_sgr_mouse(b"\x1B[<0;20;10m").unwrap(), - Some(InternalEvent::Event(Event::Mouse(MouseEvent { - kind: MouseEventKind::Up(MouseButton::Left), - column: 19, - row: 9, - modifiers: KeyModifiers::empty(), - }))) - ); - } - - #[test] - fn test_utf8() { - // https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805 - - // 'Valid ASCII' => "a", - assert_eq!(parse_utf8_char(b"a").unwrap(), Some('a'),); - - // 'Valid 2 Octet Sequence' => "\xc3\xb1", - assert_eq!(parse_utf8_char(&[0xC3, 0xB1]).unwrap(), Some('ñ'),); - - // 'Invalid 2 Octet Sequence' => "\xc3\x28", - assert!(parse_utf8_char(&[0xC3, 0x28]).is_err()); - - // 'Invalid Sequence Identifier' => "\xa0\xa1", - assert!(parse_utf8_char(&[0xA0, 0xA1]).is_err()); - - // 'Valid 3 Octet Sequence' => "\xe2\x82\xa1", - assert_eq!( - parse_utf8_char(&[0xE2, 0x81, 0xA1]).unwrap(), - Some('\u{2061}'), - ); - - // 'Invalid 3 Octet Sequence (in 2nd Octet)' => "\xe2\x28\xa1", - assert!(parse_utf8_char(&[0xE2, 0x28, 0xA1]).is_err()); - - // 'Invalid 3 Octet Sequence (in 3rd Octet)' => "\xe2\x82\x28", - assert!(parse_utf8_char(&[0xE2, 0x82, 0x28]).is_err()); - - // 'Valid 4 Octet Sequence' => "\xf0\x90\x8c\xbc", - assert_eq!( - parse_utf8_char(&[0xF0, 0x90, 0x8C, 0xBC]).unwrap(), - Some('𐌼'), - ); - - // 'Invalid 4 Octet Sequence (in 2nd Octet)' => "\xf0\x28\x8c\xbc", - assert!(parse_utf8_char(&[0xF0, 0x28, 0x8C, 0xBC]).is_err()); - - // 'Invalid 4 Octet Sequence (in 3rd Octet)' => "\xf0\x90\x28\xbc", - assert!(parse_utf8_char(&[0xF0, 0x90, 0x28, 0xBC]).is_err()); - - // 'Invalid 4 Octet Sequence (in 4th Octet)' => "\xf0\x28\x8c\x28", - assert!(parse_utf8_char(&[0xF0, 0x28, 0x8C, 0x28]).is_err()); - } - - #[test] - fn test_parse_char_event_lowercase() { - assert_eq!( - parse_event(b"c", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('c'), - KeyModifiers::empty() - )))), - ); - } - - #[test] - fn test_parse_char_event_uppercase() { - assert_eq!( - parse_event(b"C", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('C'), - KeyModifiers::SHIFT - )))), - ); - } - - #[test] - fn test_parse_basic_csi_u_encoded_key_code() { - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('a'), - KeyModifiers::empty() - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;2u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('A'), - KeyModifiers::SHIFT - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;7u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('a'), - KeyModifiers::ALT | KeyModifiers::CONTROL - )))), - ); - } - - #[test] - fn test_parse_basic_csi_u_encoded_key_code_special_keys() { - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[13u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Enter, - KeyModifiers::empty() - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[27u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Esc, - KeyModifiers::empty() - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57358u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::CapsLock, - KeyModifiers::empty() - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57376u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::F(13), - KeyModifiers::empty() - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57428u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Media(MediaKeyCode::Play), - KeyModifiers::empty() - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57441u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Modifier(ModifierKeyCode::LeftShift), - KeyModifiers::SHIFT, - )))), - ); - } - - #[test] - fn test_parse_csi_u_encoded_keypad_code() { - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57399u").unwrap(), - Some(InternalEvent::Event(Event::Key( - KeyEvent::new_with_kind_and_state( - KeyCode::Char('0'), - KeyModifiers::empty(), - KeyEventKind::Press, - KeyEventState::KEYPAD, - ) - ))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57419u").unwrap(), - Some(InternalEvent::Event(Event::Key( - KeyEvent::new_with_kind_and_state( - KeyCode::Up, - KeyModifiers::empty(), - KeyEventKind::Press, - KeyEventState::KEYPAD, - ) - ))), - ); - } - - #[test] - fn test_parse_csi_u_encoded_key_code_with_types() { - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;1u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Char('a'), - KeyModifiers::empty(), - KeyEventKind::Press, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;1:1u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Char('a'), - KeyModifiers::empty(), - KeyEventKind::Press, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;5:1u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Char('a'), - KeyModifiers::CONTROL, - KeyEventKind::Press, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;1:2u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Char('a'), - KeyModifiers::empty(), - KeyEventKind::Repeat, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;1:3u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Char('a'), - KeyModifiers::empty(), - KeyEventKind::Release, - )))), - ); - } - - #[test] - fn test_parse_csi_u_encoded_key_code_has_modifier_on_modifier_press() { - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57449u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Modifier(ModifierKeyCode::RightAlt), - KeyModifiers::ALT, - KeyEventKind::Press, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57449;3:3u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Modifier(ModifierKeyCode::RightAlt), - KeyModifiers::ALT, - KeyEventKind::Release, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57450u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Modifier(ModifierKeyCode::RightSuper), - KeyModifiers::SUPER, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57451u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Modifier(ModifierKeyCode::RightHyper), - KeyModifiers::HYPER, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57452u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Modifier(ModifierKeyCode::RightMeta), - KeyModifiers::META, - )))), - ); - } - - #[test] - fn test_parse_csi_u_encoded_key_code_with_extra_modifiers() { - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;9u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('a'), - KeyModifiers::SUPER - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;17u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('a'), - KeyModifiers::HYPER, - )))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;33u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Char('a'), - KeyModifiers::META, - )))), - ); - } - - #[test] - fn test_parse_csi_u_encoded_key_code_with_extra_state() { - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[97;65u").unwrap(), - Some(InternalEvent::Event(Event::Key( - KeyEvent::new_with_kind_and_state( - KeyCode::Char('a'), - KeyModifiers::empty(), - KeyEventKind::Press, - KeyEventState::CAPS_LOCK, - ) - ))), - ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[49;129u").unwrap(), - Some(InternalEvent::Event(Event::Key( - KeyEvent::new_with_kind_and_state( - KeyCode::Char('1'), - KeyModifiers::empty(), - KeyEventKind::Press, - KeyEventState::NUM_LOCK, - ) - ))), - ); - } - - #[test] - fn test_parse_csi_special_key_code_with_types() { - assert_eq!( - parse_event(b"\x1B[;1:3B", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Down, - KeyModifiers::empty(), - KeyEventKind::Release, - )))), - ); - assert_eq!( - parse_event(b"\x1B[1;1:3B", false).unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( - KeyCode::Down, - KeyModifiers::empty(), - KeyEventKind::Release, - )))), - ); - } -} +use std::io; + +use crate::{ + event::{ + Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, MediaKeyCode, + ModifierKeyCode, MouseButton, MouseEvent, MouseEventKind, + }, + ErrorKind, Result, +}; + +use super::super::super::InternalEvent; + +// Event parsing +// +// This code (& previous one) are kind of ugly. We have to think about this, +// because it's really not maintainable, no tests, etc. +// +// Every fn returns Result> +// +// Ok(None) -> wait for more bytes +// Err(_) -> failed to parse event, clear the buffer +// Ok(Some(event)) -> we have event, clear the buffer +// + +fn could_not_parse_event_error() -> ErrorKind { + io::Error::new(io::ErrorKind::Other, "Could not parse an event.") +} + +pub(crate) fn parse_event(buffer: &[u8], input_available: bool) -> Result> { + if buffer.is_empty() { + return Ok(None); + } + + match buffer[0] { + b'\x1B' => { + if buffer.len() == 1 { + if input_available { + // Possible Esc sequence + Ok(None) + } else { + Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into())))) + } + } else { + match buffer[1] { + b'O' => { + if buffer.len() == 2 { + Ok(None) + } else { + match buffer[2] { + b'D' => { + Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Left.into())))) + } + b'C' => Ok(Some(InternalEvent::Event(Event::Key( + KeyCode::Right.into(), + )))), + b'A' => { + Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Up.into())))) + } + b'B' => { + Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Down.into())))) + } + b'H' => { + Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Home.into())))) + } + b'F' => { + Ok(Some(InternalEvent::Event(Event::Key(KeyCode::End.into())))) + } + // F1-F4 + val @ b'P'..=b'S' => Ok(Some(InternalEvent::Event(Event::Key( + KeyCode::F(1 + val - b'P').into(), + )))), + _ => Err(could_not_parse_event_error()), + } + } + } + b'[' => parse_csi(buffer), + b'\x1B' => Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into())))), + _ => parse_event(&buffer[1..], input_available).map(|event_option| { + event_option.map(|event| { + if let InternalEvent::Event(Event::Key(key_event)) = event { + let mut alt_key_event = key_event; + alt_key_event.modifiers |= KeyModifiers::ALT; + InternalEvent::Event(Event::Key(alt_key_event)) + } else { + event + } + }) + }), + } + } + } + b'\r' => Ok(Some(InternalEvent::Event(Event::Key( + KeyCode::Enter.into(), + )))), + // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get + // newlines as input is because the terminal converts \r into \n for us. When we + // enter raw mode, we disable that, so \n no longer has any meaning - it's better to + // use Ctrl+J. Waiting to handle it here means it gets picked up later + b'\n' if !crate::terminal::sys::is_raw_mode_enabled() => Ok(Some(InternalEvent::Event( + Event::Key(KeyCode::Enter.into()), + ))), + b'\t' => Ok(Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into())))), + b'\x7F' => Ok(Some(InternalEvent::Event(Event::Key( + KeyCode::Backspace.into(), + )))), + c @ b'\x01'..=b'\x1A' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char((c as u8 - 0x1 + b'a') as char), + KeyModifiers::CONTROL, + ))))), + c @ b'\x1C'..=b'\x1F' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char((c as u8 - 0x1C + b'4') as char), + KeyModifiers::CONTROL, + ))))), + b'\0' => Ok(Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char(' '), + KeyModifiers::CONTROL, + ))))), + _ => parse_utf8_char(buffer).map(|maybe_char| { + maybe_char + .map(KeyCode::Char) + .map(char_code_to_event) + .map(Event::Key) + .map(InternalEvent::Event) + }), + } +} + +// converts KeyCode to KeyEvent (adds shift modifier in case of uppercase characters) +fn char_code_to_event(code: KeyCode) -> KeyEvent { + let modifiers = match code { + KeyCode::Char(c) if c.is_uppercase() => KeyModifiers::SHIFT, + _ => KeyModifiers::empty(), + }; + KeyEvent::new(code, modifiers) +} + +pub(crate) fn parse_csi(buffer: &[u8]) -> Result> { + assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ + + if buffer.len() == 2 { + return Ok(None); + } + + let input_event = match buffer[2] { + b'[' => { + if buffer.len() == 3 { + None + } else { + match buffer[3] { + // NOTE (@imdaveho): cannot find when this occurs; + // having another '[' after ESC[ not a likely scenario + val @ b'A'..=b'E' => Some(Event::Key(KeyCode::F(1 + val - b'A').into())), + _ => return Err(could_not_parse_event_error()), + } + } + } + b'D' => Some(Event::Key(KeyCode::Left.into())), + b'C' => Some(Event::Key(KeyCode::Right.into())), + b'A' => Some(Event::Key(KeyCode::Up.into())), + b'B' => Some(Event::Key(KeyCode::Down.into())), + b'H' => Some(Event::Key(KeyCode::Home.into())), + b'F' => Some(Event::Key(KeyCode::End.into())), + b'Z' => Some(Event::Key(KeyEvent::new_with_kind( + KeyCode::BackTab, + KeyModifiers::SHIFT, + KeyEventKind::Press, + ))), + b'M' => return parse_csi_normal_mouse(buffer), + b'<' => return parse_csi_sgr_mouse(buffer), + b'I' => Some(Event::FocusGained), + b'O' => Some(Event::FocusLost), + b';' => return parse_csi_modifier_key_code(buffer), + b'0'..=b'9' => { + // Numbered escape code. + if buffer.len() == 3 { + None + } else { + // The final byte of a CSI sequence can be in the range 64-126, so + // let's keep reading anything else. + let last_byte = buffer[buffer.len() - 1]; + if !(64..=126).contains(&last_byte) { + None + } else { + #[cfg(feature = "bracketed-paste")] + if buffer.starts_with(b"\x1B[200~") { + return parse_csi_bracketed_paste(buffer); + } + match last_byte { + b'M' => return parse_csi_rxvt_mouse(buffer), + b'~' => return parse_csi_special_key_code(buffer), + b'u' => return parse_csi_u_encoded_key_code(buffer), + b'R' => return parse_csi_cursor_position(buffer), + _ => return parse_csi_modifier_key_code(buffer), + } + } + } + } + _ => return Err(could_not_parse_event_error()), + }; + + Ok(input_event.map(InternalEvent::Event)) +} + +pub(crate) fn next_parsed(iter: &mut dyn Iterator) -> Result +where + T: std::str::FromStr, +{ + iter.next() + .ok_or_else(could_not_parse_event_error)? + .parse::() + .map_err(|_| could_not_parse_event_error()) +} + +fn modifier_and_kind_parsed(iter: &mut dyn Iterator) -> Result<(u8, u8)> { + let mut sub_split = iter + .next() + .ok_or_else(could_not_parse_event_error)? + .split(':'); + + let modifier_mask = next_parsed::(&mut sub_split)?; + + if let Ok(kind_code) = next_parsed::(&mut sub_split) { + Ok((modifier_mask, kind_code)) + } else { + Ok((modifier_mask, 1)) + } +} + +pub(crate) fn parse_csi_cursor_position(buffer: &[u8]) -> Result> { + // ESC [ Cy ; Cx R + // Cy - cursor row number (starting from 1) + // Cx - cursor column number (starting from 1) + assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ + assert!(buffer.ends_with(&[b'R'])); + + let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) + .map_err(|_| could_not_parse_event_error())?; + + let mut split = s.split(';'); + + let y = next_parsed::(&mut split)? - 1; + let x = next_parsed::(&mut split)? - 1; + + Ok(Some(InternalEvent::CursorPosition(x, y))) +} + +fn parse_modifiers(mask: u8) -> KeyModifiers { + let modifier_mask = mask.saturating_sub(1); + let mut modifiers = KeyModifiers::empty(); + if modifier_mask & 1 != 0 { + modifiers |= KeyModifiers::SHIFT; + } + if modifier_mask & 2 != 0 { + modifiers |= KeyModifiers::ALT; + } + if modifier_mask & 4 != 0 { + modifiers |= KeyModifiers::CONTROL; + } + if modifier_mask & 8 != 0 { + modifiers |= KeyModifiers::SUPER; + } + if modifier_mask & 16 != 0 { + modifiers |= KeyModifiers::HYPER; + } + if modifier_mask & 32 != 0 { + modifiers |= KeyModifiers::META; + } + modifiers +} + +fn parse_modifiers_to_state(mask: u8) -> KeyEventState { + let modifier_mask = mask.saturating_sub(1); + let mut state = KeyEventState::empty(); + if modifier_mask & 64 != 0 { + state |= KeyEventState::CAPS_LOCK; + } + if modifier_mask & 128 != 0 { + state |= KeyEventState::NUM_LOCK; + } + state +} + +fn parse_key_event_kind(kind: u8) -> KeyEventKind { + match kind { + 1 => KeyEventKind::Press, + 2 => KeyEventKind::Repeat, + 3 => KeyEventKind::Release, + _ => KeyEventKind::Press, + } +} + +pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result> { + assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ + // + let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) + .map_err(|_| could_not_parse_event_error())?; + let mut split = s.split(';'); + + split.next(); + + let (modifiers, kind) = + if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) { + ( + parse_modifiers(modifier_mask), + parse_key_event_kind(kind_code), + ) + } else if buffer.len() > 3 { + ( + parse_modifiers( + (buffer[buffer.len() - 2] as char) + .to_digit(10) + .ok_or_else(could_not_parse_event_error)? as u8, + ), + KeyEventKind::Press, + ) + } else { + (KeyModifiers::NONE, KeyEventKind::Press) + }; + let key = buffer[buffer.len() - 1]; + + let keycode = match key { + b'A' => KeyCode::Up, + b'B' => KeyCode::Down, + b'C' => KeyCode::Right, + b'D' => KeyCode::Left, + b'F' => KeyCode::End, + b'H' => KeyCode::Home, + b'P' => KeyCode::F(1), + b'Q' => KeyCode::F(2), + b'R' => KeyCode::F(3), + b'S' => KeyCode::F(4), + _ => return Err(could_not_parse_event_error()), + }; + + let input_event = Event::Key(KeyEvent::new_with_kind(keycode, modifiers, kind)); + + Ok(Some(InternalEvent::Event(input_event))) +} + +fn translate_functional_key_code(codepoint: u32) -> Option<(KeyCode, KeyEventState)> { + if let Some(keycode) = match codepoint { + 57399 => Some(KeyCode::Char('0')), + 57400 => Some(KeyCode::Char('1')), + 57401 => Some(KeyCode::Char('2')), + 57402 => Some(KeyCode::Char('3')), + 57403 => Some(KeyCode::Char('4')), + 57404 => Some(KeyCode::Char('5')), + 57405 => Some(KeyCode::Char('6')), + 57406 => Some(KeyCode::Char('7')), + 57407 => Some(KeyCode::Char('8')), + 57408 => Some(KeyCode::Char('9')), + 57409 => Some(KeyCode::Char('.')), + 57410 => Some(KeyCode::Char('/')), + 57411 => Some(KeyCode::Char('*')), + 57412 => Some(KeyCode::Char('-')), + 57413 => Some(KeyCode::Char('+')), + 57414 => Some(KeyCode::Enter), + 57415 => Some(KeyCode::Char('=')), + 57416 => Some(KeyCode::Char(',')), + 57417 => Some(KeyCode::Left), + 57418 => Some(KeyCode::Right), + 57419 => Some(KeyCode::Up), + 57420 => Some(KeyCode::Down), + 57421 => Some(KeyCode::PageUp), + 57422 => Some(KeyCode::PageDown), + 57423 => Some(KeyCode::Home), + 57424 => Some(KeyCode::End), + 57425 => Some(KeyCode::Insert), + 57426 => Some(KeyCode::Delete), + 57427 => Some(KeyCode::KeypadBegin), + _ => None, + } { + return Some((keycode, KeyEventState::KEYPAD)); + } + + if let Some(keycode) = match codepoint { + 57358 => Some(KeyCode::CapsLock), + 57359 => Some(KeyCode::ScrollLock), + 57360 => Some(KeyCode::NumLock), + 57361 => Some(KeyCode::PrintScreen), + 57362 => Some(KeyCode::Pause), + 57363 => Some(KeyCode::Menu), + 57376 => Some(KeyCode::F(13)), + 57377 => Some(KeyCode::F(14)), + 57378 => Some(KeyCode::F(15)), + 57379 => Some(KeyCode::F(16)), + 57380 => Some(KeyCode::F(17)), + 57381 => Some(KeyCode::F(18)), + 57382 => Some(KeyCode::F(19)), + 57383 => Some(KeyCode::F(20)), + 57384 => Some(KeyCode::F(21)), + 57385 => Some(KeyCode::F(22)), + 57386 => Some(KeyCode::F(23)), + 57387 => Some(KeyCode::F(24)), + 57388 => Some(KeyCode::F(25)), + 57389 => Some(KeyCode::F(26)), + 57390 => Some(KeyCode::F(27)), + 57391 => Some(KeyCode::F(28)), + 57392 => Some(KeyCode::F(29)), + 57393 => Some(KeyCode::F(30)), + 57394 => Some(KeyCode::F(31)), + 57395 => Some(KeyCode::F(32)), + 57396 => Some(KeyCode::F(33)), + 57397 => Some(KeyCode::F(34)), + 57398 => Some(KeyCode::F(35)), + 57428 => Some(KeyCode::Media(MediaKeyCode::Play)), + 57429 => Some(KeyCode::Media(MediaKeyCode::Pause)), + 57430 => Some(KeyCode::Media(MediaKeyCode::PlayPause)), + 57431 => Some(KeyCode::Media(MediaKeyCode::Reverse)), + 57432 => Some(KeyCode::Media(MediaKeyCode::Stop)), + 57433 => Some(KeyCode::Media(MediaKeyCode::FastForward)), + 57434 => Some(KeyCode::Media(MediaKeyCode::Rewind)), + 57435 => Some(KeyCode::Media(MediaKeyCode::TrackNext)), + 57436 => Some(KeyCode::Media(MediaKeyCode::TrackPrevious)), + 57437 => Some(KeyCode::Media(MediaKeyCode::Record)), + 57438 => Some(KeyCode::Media(MediaKeyCode::LowerVolume)), + 57439 => Some(KeyCode::Media(MediaKeyCode::RaiseVolume)), + 57440 => Some(KeyCode::Media(MediaKeyCode::MuteVolume)), + 57441 => Some(KeyCode::Modifier(ModifierKeyCode::LeftShift)), + 57442 => Some(KeyCode::Modifier(ModifierKeyCode::LeftControl)), + 57443 => Some(KeyCode::Modifier(ModifierKeyCode::LeftAlt)), + 57444 => Some(KeyCode::Modifier(ModifierKeyCode::LeftSuper)), + 57445 => Some(KeyCode::Modifier(ModifierKeyCode::LeftHyper)), + 57446 => Some(KeyCode::Modifier(ModifierKeyCode::LeftMeta)), + 57447 => Some(KeyCode::Modifier(ModifierKeyCode::RightShift)), + 57448 => Some(KeyCode::Modifier(ModifierKeyCode::RightControl)), + 57449 => Some(KeyCode::Modifier(ModifierKeyCode::RightAlt)), + 57450 => Some(KeyCode::Modifier(ModifierKeyCode::RightSuper)), + 57451 => Some(KeyCode::Modifier(ModifierKeyCode::RightHyper)), + 57452 => Some(KeyCode::Modifier(ModifierKeyCode::RightMeta)), + 57453 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift)), + 57454 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift)), + _ => None, + } { + return Some((keycode, KeyEventState::empty())); + } + + None +} + +pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result> { + assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ + assert!(buffer.ends_with(&[b'u'])); + + let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) + .map_err(|_| could_not_parse_event_error())?; + let mut split = s.split(';'); + + // This CSI sequence a tuple of semicolon-separated numbers. + // CSI [codepoint];[modifiers] u + // codepoint: ASCII Dec value + let codepoint = next_parsed::(&mut split)?; + + let (mut modifiers, kind, state_from_modifiers) = + if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) { + ( + parse_modifiers(modifier_mask), + parse_key_event_kind(kind_code), + parse_modifiers_to_state(modifier_mask), + ) + } else { + (KeyModifiers::NONE, KeyEventKind::Press, KeyEventState::NONE) + }; + + let (keycode, state_from_keycode) = { + if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) { + (special_key_code, state) + } else if let Some(c) = char::from_u32(codepoint) { + ( + match c { + '\x1B' => KeyCode::Esc, + '\r' => KeyCode::Enter, + // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get + // newlines as input is because the terminal converts \r into \n for us. When we + // enter raw mode, we disable that, so \n no longer has any meaning - it's better to + // use Ctrl+J. Waiting to handle it here means it gets picked up later + '\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter, + '\t' => { + if modifiers.contains(KeyModifiers::SHIFT) { + KeyCode::BackTab + } else { + KeyCode::Tab + } + } + '\x7F' => KeyCode::Backspace, + _ => KeyCode::Char(c), + }, + KeyEventState::empty(), + ) + } else { + return Err(could_not_parse_event_error()); + } + }; + + if let KeyCode::Modifier(modifier_keycode) = keycode { + match modifier_keycode { + ModifierKeyCode::LeftAlt | ModifierKeyCode::RightAlt => { + modifiers.set(KeyModifiers::ALT, true) + } + ModifierKeyCode::LeftControl | ModifierKeyCode::RightControl => { + modifiers.set(KeyModifiers::CONTROL, true) + } + ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => { + modifiers.set(KeyModifiers::SHIFT, true) + } + ModifierKeyCode::LeftSuper | ModifierKeyCode::RightSuper => { + modifiers.set(KeyModifiers::SUPER, true) + } + ModifierKeyCode::LeftHyper | ModifierKeyCode::RightHyper => { + modifiers.set(KeyModifiers::HYPER, true) + } + ModifierKeyCode::LeftMeta | ModifierKeyCode::RightMeta => { + modifiers.set(KeyModifiers::META, true) + } + _ => {} + } + } + + let input_event = Event::Key(KeyEvent::new_with_kind_and_state( + keycode, + modifiers, + kind, + state_from_keycode | state_from_modifiers, + )); + + Ok(Some(InternalEvent::Event(input_event))) +} + +pub(crate) fn parse_csi_special_key_code(buffer: &[u8]) -> Result> { + assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ + assert!(buffer.ends_with(&[b'~'])); + + let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) + .map_err(|_| could_not_parse_event_error())?; + let mut split = s.split(';'); + + // This CSI sequence can be a list of semicolon-separated numbers. + let first = next_parsed::(&mut split)?; + + let modifiers = if let Ok(modifier_mask) = next_parsed::(&mut split) { + parse_modifiers(modifier_mask) + } else { + KeyModifiers::NONE + }; + + let keycode = match first { + 1 | 7 => KeyCode::Home, + 2 => KeyCode::Insert, + 3 => KeyCode::Delete, + 4 | 8 => KeyCode::End, + 5 => KeyCode::PageUp, + 6 => KeyCode::PageDown, + v @ 11..=15 => KeyCode::F(v - 10), + v @ 17..=21 => KeyCode::F(v - 11), + v @ 23..=26 => KeyCode::F(v - 12), + v @ 28..=29 => KeyCode::F(v - 15), + v @ 31..=34 => KeyCode::F(v - 17), + _ => return Err(could_not_parse_event_error()), + }; + + let input_event = Event::Key(KeyEvent::new(keycode, modifiers)); + + Ok(Some(InternalEvent::Event(input_event))) +} + +pub(crate) fn parse_csi_rxvt_mouse(buffer: &[u8]) -> Result> { + // rxvt mouse encoding: + // ESC [ Cb ; Cx ; Cy ; M + + assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ + assert!(buffer.ends_with(&[b'M'])); + + let s = std::str::from_utf8(&buffer[2..buffer.len() - 1]) + .map_err(|_| could_not_parse_event_error())?; + let mut split = s.split(';'); + + let cb = next_parsed::(&mut split)? + .checked_sub(32) + .ok_or_else(could_not_parse_event_error)?; + let (kind, modifiers) = parse_cb(cb)?; + + let cx = next_parsed::(&mut split)? - 1; + let cy = next_parsed::(&mut split)? - 1; + + Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind, + column: cx, + row: cy, + modifiers, + })))) +} + +pub(crate) fn parse_csi_normal_mouse(buffer: &[u8]) -> Result> { + // Normal mouse encoding: ESC [ M CB Cx Cy (6 characters only). + + assert!(buffer.starts_with(&[b'\x1B', b'[', b'M'])); // ESC [ M + + if buffer.len() < 6 { + return Ok(None); + } + + let cb = buffer[3] + .checked_sub(32) + .ok_or_else(could_not_parse_event_error)?; + let (kind, modifiers) = parse_cb(cb)?; + + // See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking + // The upper left character position on the terminal is denoted as 1,1. + // Subtract 1 to keep it synced with cursor + let cx = u16::from(buffer[4].saturating_sub(32)) - 1; + let cy = u16::from(buffer[5].saturating_sub(32)) - 1; + + Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind, + column: cx, + row: cy, + modifiers, + })))) +} + +pub(crate) fn parse_csi_sgr_mouse(buffer: &[u8]) -> Result> { + // ESC [ < Cb ; Cx ; Cy (;) (M or m) + + assert!(buffer.starts_with(&[b'\x1B', b'[', b'<'])); // ESC [ < + + if !buffer.ends_with(&[b'm']) && !buffer.ends_with(&[b'M']) { + return Ok(None); + } + + let s = std::str::from_utf8(&buffer[3..buffer.len() - 1]) + .map_err(|_| could_not_parse_event_error())?; + let mut split = s.split(';'); + + let cb = next_parsed::(&mut split)?; + let (kind, modifiers) = parse_cb(cb)?; + + // See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking + // The upper left character position on the terminal is denoted as 1,1. + // Subtract 1 to keep it synced with cursor + let cx = next_parsed::(&mut split)? - 1; + let cy = next_parsed::(&mut split)? - 1; + + // When button 3 in Cb is used to represent mouse release, you can't tell which button was + // released. SGR mode solves this by having the sequence end with a lowercase m if it's a + // button release and an uppercase M if it's a button press. + // + // We've already checked that the last character is a lowercase or uppercase M at the start of + // this function, so we just need one if. + let kind = if buffer.last() == Some(&b'm') { + match kind { + MouseEventKind::Down(button) => MouseEventKind::Up(button), + other => other, + } + } else { + kind + }; + + Ok(Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind, + column: cx, + row: cy, + modifiers, + })))) +} + +/// Cb is the byte of a mouse input that contains the button being used, the key modifiers being +/// held and whether the mouse is dragging or not. +/// +/// Bit layout of cb, from low to high: +/// +/// - button number +/// - button number +/// - shift +/// - meta (alt) +/// - control +/// - mouse is dragging +/// - button number +/// - button number +fn parse_cb(cb: u8) -> Result<(MouseEventKind, KeyModifiers)> { + let button_number = (cb & 0b0000_0011) | ((cb & 0b1100_0000) >> 4); + let dragging = cb & 0b0010_0000 == 0b0010_0000; + + let kind = match (button_number, dragging) { + (0, false) => MouseEventKind::Down(MouseButton::Left), + (1, false) => MouseEventKind::Down(MouseButton::Middle), + (2, false) => MouseEventKind::Down(MouseButton::Right), + (0, true) => MouseEventKind::Drag(MouseButton::Left), + (1, true) => MouseEventKind::Drag(MouseButton::Middle), + (2, true) => MouseEventKind::Drag(MouseButton::Right), + (3, false) => MouseEventKind::Up(MouseButton::Left), + (3, true) | (4, true) | (5, true) => MouseEventKind::Moved, + (4, false) => MouseEventKind::ScrollUp, + (5, false) => MouseEventKind::ScrollDown, + // We do not support other buttons. + _ => return Err(could_not_parse_event_error()), + }; + + let mut modifiers = KeyModifiers::empty(); + + if cb & 0b0000_0100 == 0b0000_0100 { + modifiers |= KeyModifiers::SHIFT; + } + if cb & 0b0000_1000 == 0b0000_1000 { + modifiers |= KeyModifiers::ALT; + } + if cb & 0b0001_0000 == 0b0001_0000 { + modifiers |= KeyModifiers::CONTROL; + } + + Ok((kind, modifiers)) +} + +#[cfg(feature = "bracketed-paste")] +pub(crate) fn parse_csi_bracketed_paste(buffer: &[u8]) -> Result> { + // ESC [ 2 0 0 ~ pasted text ESC 2 0 1 ~ + assert!(buffer.starts_with(b"\x1B[200~")); + + if !buffer.ends_with(b"\x1b[201~") { + Ok(None) + } else { + let paste = String::from_utf8_lossy(&buffer[6..buffer.len() - 6]).to_string(); + Ok(Some(InternalEvent::Event(Event::Paste(paste)))) + } +} + +pub(crate) fn parse_utf8_char(buffer: &[u8]) -> Result> { + match std::str::from_utf8(buffer) { + Ok(s) => { + let ch = s.chars().next().ok_or_else(could_not_parse_event_error)?; + + Ok(Some(ch)) + } + Err(_) => { + // from_utf8 failed, but we have to check if we need more bytes for code point + // and if all the bytes we have no are valid + + let required_bytes = match buffer[0] { + // https://en.wikipedia.org/wiki/UTF-8#Description + (0x00..=0x7F) => 1, // 0xxxxxxx + (0xC0..=0xDF) => 2, // 110xxxxx 10xxxxxx + (0xE0..=0xEF) => 3, // 1110xxxx 10xxxxxx 10xxxxxx + (0xF0..=0xF7) => 4, // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + (0x80..=0xBF) | (0xF8..=0xFF) => return Err(could_not_parse_event_error()), + }; + + // More than 1 byte, check them for 10xxxxxx pattern + if required_bytes > 1 && buffer.len() > 1 { + for byte in &buffer[1..] { + if byte & !0b0011_1111 != 0b1000_0000 { + return Err(could_not_parse_event_error()); + } + } + } + + if buffer.len() < required_bytes { + // All bytes looks good so far, but we need more of them + Ok(None) + } else { + Err(could_not_parse_event_error()) + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::event::{KeyEventState, KeyModifiers, MouseButton, MouseEvent}; + + use super::*; + + #[test] + fn test_esc_key() { + assert_eq!( + parse_event(b"\x1B", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyCode::Esc.into()))), + ); + } + + #[test] + fn test_possible_esc_sequence() { + assert_eq!(parse_event(b"\x1B", true).unwrap(), None,); + } + + #[test] + fn test_alt_key() { + assert_eq!( + parse_event(b"\x1Bc", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('c'), + KeyModifiers::ALT + )))), + ); + } + + #[test] + fn test_alt_shift() { + assert_eq!( + parse_event(b"\x1BH", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('H'), + KeyModifiers::ALT | KeyModifiers::SHIFT + )))), + ); + } + + #[test] + fn test_alt_ctrl() { + assert_eq!( + parse_event(b"\x1B\x14", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('t'), + KeyModifiers::ALT | KeyModifiers::CONTROL + )))), + ); + } + + #[test] + fn test_parse_event_subsequent_calls() { + // The main purpose of this test is to check if we're passing + // correct slice to other parse_ functions. + + // parse_csi_cursor_position + assert_eq!( + parse_event(b"\x1B[20;10R", false).unwrap(), + Some(InternalEvent::CursorPosition(9, 19)) + ); + + // parse_csi + assert_eq!( + parse_event(b"\x1B[D", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))), + ); + + // parse_csi_modifier_key_code + assert_eq!( + parse_event(b"\x1B[2D", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Left, + KeyModifiers::SHIFT + )))) + ); + + // parse_csi_special_key_code + assert_eq!( + parse_event(b"\x1B[3~", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))), + ); + + // parse_csi_bracketed_paste + #[cfg(feature = "bracketed-paste")] + assert_eq!( + parse_event(b"\x1B[200~on and on and on\x1B[201~", false).unwrap(), + Some(InternalEvent::Event(Event::Paste( + "on and on and on".to_string() + ))), + ); + + // parse_csi_rxvt_mouse + assert_eq!( + parse_event(b"\x1B[32;30;40;M", false).unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + column: 29, + row: 39, + modifiers: KeyModifiers::empty(), + }))) + ); + + // parse_csi_normal_mouse + assert_eq!( + parse_event(b"\x1B[M0\x60\x70", false).unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + column: 63, + row: 79, + modifiers: KeyModifiers::CONTROL, + }))) + ); + + // parse_csi_sgr_mouse + assert_eq!( + parse_event(b"\x1B[<0;20;10;M", false).unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + column: 19, + row: 9, + modifiers: KeyModifiers::empty(), + }))) + ); + + // parse_utf8_char + assert_eq!( + parse_event("Ž".as_bytes(), false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('Ž'), + KeyModifiers::SHIFT + )))), + ); + } + + #[test] + fn test_parse_event() { + assert_eq!( + parse_event(b"\t", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyCode::Tab.into()))), + ); + } + + #[test] + fn test_parse_csi_cursor_position() { + assert_eq!( + parse_csi_cursor_position(b"\x1B[20;10R").unwrap(), + Some(InternalEvent::CursorPosition(9, 19)) + ); + } + + #[test] + fn test_parse_csi() { + assert_eq!( + parse_csi(b"\x1B[D").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyCode::Left.into()))), + ); + } + + #[test] + fn test_parse_csi_modifier_key_code() { + assert_eq!( + parse_csi_modifier_key_code(b"\x1B[2D").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Left, + KeyModifiers::SHIFT + )))), + ); + } + + #[test] + fn test_parse_csi_special_key_code() { + assert_eq!( + parse_csi_special_key_code(b"\x1B[3~").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyCode::Delete.into()))), + ); + } + + #[test] + fn test_parse_csi_special_key_code_multiple_values_not_supported() { + assert_eq!( + parse_csi_special_key_code(b"\x1B[3;2~").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Delete, + KeyModifiers::SHIFT + )))), + ); + } + + #[cfg(feature = "bracketed-paste")] + #[test] + fn test_parse_csi_bracketed_paste() { + // + assert_eq!( + parse_event(b"\x1B[200~o", false).unwrap(), + None, + "A partial bracketed paste isn't parsed" + ); + assert_eq!( + parse_event(b"\x1B[200~o\x1B[2D", false).unwrap(), + None, + "A partial bracketed paste containing another escape code isn't parsed" + ); + assert_eq!( + parse_event(b"\x1B[200~o\x1B[2D\x1B[201~", false).unwrap(), + Some(InternalEvent::Event(Event::Paste("o\x1B[2D".to_string()))) + ); + } + + #[test] + fn test_parse_csi_focus() { + assert_eq!( + parse_csi(b"\x1B[O").unwrap(), + Some(InternalEvent::Event(Event::FocusLost)) + ); + } + + #[test] + fn test_parse_csi_rxvt_mouse() { + assert_eq!( + parse_csi_rxvt_mouse(b"\x1B[32;30;40;M").unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + column: 29, + row: 39, + modifiers: KeyModifiers::empty(), + }))) + ); + } + + #[test] + fn test_parse_csi_normal_mouse() { + assert_eq!( + parse_csi_normal_mouse(b"\x1B[M0\x60\x70").unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + column: 63, + row: 79, + modifiers: KeyModifiers::CONTROL, + }))) + ); + } + + #[test] + fn test_parse_csi_sgr_mouse() { + assert_eq!( + parse_csi_sgr_mouse(b"\x1B[<0;20;10;M").unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + column: 19, + row: 9, + modifiers: KeyModifiers::empty(), + }))) + ); + assert_eq!( + parse_csi_sgr_mouse(b"\x1B[<0;20;10M").unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Down(MouseButton::Left), + column: 19, + row: 9, + modifiers: KeyModifiers::empty(), + }))) + ); + assert_eq!( + parse_csi_sgr_mouse(b"\x1B[<0;20;10;m").unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Up(MouseButton::Left), + column: 19, + row: 9, + modifiers: KeyModifiers::empty(), + }))) + ); + assert_eq!( + parse_csi_sgr_mouse(b"\x1B[<0;20;10m").unwrap(), + Some(InternalEvent::Event(Event::Mouse(MouseEvent { + kind: MouseEventKind::Up(MouseButton::Left), + column: 19, + row: 9, + modifiers: KeyModifiers::empty(), + }))) + ); + } + + #[test] + fn test_utf8() { + // https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805 + + // 'Valid ASCII' => "a", + assert_eq!(parse_utf8_char(b"a").unwrap(), Some('a'),); + + // 'Valid 2 Octet Sequence' => "\xc3\xb1", + assert_eq!(parse_utf8_char(&[0xC3, 0xB1]).unwrap(), Some('ñ'),); + + // 'Invalid 2 Octet Sequence' => "\xc3\x28", + assert!(parse_utf8_char(&[0xC3, 0x28]).is_err()); + + // 'Invalid Sequence Identifier' => "\xa0\xa1", + assert!(parse_utf8_char(&[0xA0, 0xA1]).is_err()); + + // 'Valid 3 Octet Sequence' => "\xe2\x82\xa1", + assert_eq!( + parse_utf8_char(&[0xE2, 0x81, 0xA1]).unwrap(), + Some('\u{2061}'), + ); + + // 'Invalid 3 Octet Sequence (in 2nd Octet)' => "\xe2\x28\xa1", + assert!(parse_utf8_char(&[0xE2, 0x28, 0xA1]).is_err()); + + // 'Invalid 3 Octet Sequence (in 3rd Octet)' => "\xe2\x82\x28", + assert!(parse_utf8_char(&[0xE2, 0x82, 0x28]).is_err()); + + // 'Valid 4 Octet Sequence' => "\xf0\x90\x8c\xbc", + assert_eq!( + parse_utf8_char(&[0xF0, 0x90, 0x8C, 0xBC]).unwrap(), + Some('𐌼'), + ); + + // 'Invalid 4 Octet Sequence (in 2nd Octet)' => "\xf0\x28\x8c\xbc", + assert!(parse_utf8_char(&[0xF0, 0x28, 0x8C, 0xBC]).is_err()); + + // 'Invalid 4 Octet Sequence (in 3rd Octet)' => "\xf0\x90\x28\xbc", + assert!(parse_utf8_char(&[0xF0, 0x90, 0x28, 0xBC]).is_err()); + + // 'Invalid 4 Octet Sequence (in 4th Octet)' => "\xf0\x28\x8c\x28", + assert!(parse_utf8_char(&[0xF0, 0x28, 0x8C, 0x28]).is_err()); + } + + #[test] + fn test_parse_char_event_lowercase() { + assert_eq!( + parse_event(b"c", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('c'), + KeyModifiers::empty() + )))), + ); + } + + #[test] + fn test_parse_char_event_uppercase() { + assert_eq!( + parse_event(b"C", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('C'), + KeyModifiers::SHIFT + )))), + ); + } + + #[test] + fn test_parse_basic_csi_u_encoded_key_code() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('a'), + KeyModifiers::empty() + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;2u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('A'), + KeyModifiers::SHIFT + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;7u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('a'), + KeyModifiers::ALT | KeyModifiers::CONTROL + )))), + ); + } + + #[test] + fn test_parse_basic_csi_u_encoded_key_code_special_keys() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[13u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Enter, + KeyModifiers::empty() + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[27u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Esc, + KeyModifiers::empty() + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57358u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::CapsLock, + KeyModifiers::empty() + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57376u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::F(13), + KeyModifiers::empty() + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57428u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Media(MediaKeyCode::Play), + KeyModifiers::empty() + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57441u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Modifier(ModifierKeyCode::LeftShift), + KeyModifiers::SHIFT, + )))), + ); + } + + #[test] + fn test_parse_csi_u_encoded_keypad_code() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57399u").unwrap(), + Some(InternalEvent::Event(Event::Key( + KeyEvent::new_with_kind_and_state( + KeyCode::Char('0'), + KeyModifiers::empty(), + KeyEventKind::Press, + KeyEventState::KEYPAD, + ) + ))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57419u").unwrap(), + Some(InternalEvent::Event(Event::Key( + KeyEvent::new_with_kind_and_state( + KeyCode::Up, + KeyModifiers::empty(), + KeyEventKind::Press, + KeyEventState::KEYPAD, + ) + ))), + ); + } + + #[test] + fn test_parse_csi_u_encoded_key_code_with_types() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;1u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Char('a'), + KeyModifiers::empty(), + KeyEventKind::Press, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;1:1u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Char('a'), + KeyModifiers::empty(), + KeyEventKind::Press, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;5:1u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Char('a'), + KeyModifiers::CONTROL, + KeyEventKind::Press, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;1:2u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Char('a'), + KeyModifiers::empty(), + KeyEventKind::Repeat, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;1:3u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Char('a'), + KeyModifiers::empty(), + KeyEventKind::Release, + )))), + ); + } + + #[test] + fn test_parse_csi_u_encoded_key_code_has_modifier_on_modifier_press() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57449u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Modifier(ModifierKeyCode::RightAlt), + KeyModifiers::ALT, + KeyEventKind::Press, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57449;3:3u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Modifier(ModifierKeyCode::RightAlt), + KeyModifiers::ALT, + KeyEventKind::Release, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57450u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Modifier(ModifierKeyCode::RightSuper), + KeyModifiers::SUPER, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57451u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Modifier(ModifierKeyCode::RightHyper), + KeyModifiers::HYPER, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57452u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Modifier(ModifierKeyCode::RightMeta), + KeyModifiers::META, + )))), + ); + } + + #[test] + fn test_parse_csi_u_encoded_key_code_with_extra_modifiers() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;9u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('a'), + KeyModifiers::SUPER + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;17u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('a'), + KeyModifiers::HYPER, + )))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;33u").unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('a'), + KeyModifiers::META, + )))), + ); + } + + #[test] + fn test_parse_csi_u_encoded_key_code_with_extra_state() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[97;65u").unwrap(), + Some(InternalEvent::Event(Event::Key( + KeyEvent::new_with_kind_and_state( + KeyCode::Char('a'), + KeyModifiers::empty(), + KeyEventKind::Press, + KeyEventState::CAPS_LOCK, + ) + ))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[49;129u").unwrap(), + Some(InternalEvent::Event(Event::Key( + KeyEvent::new_with_kind_and_state( + KeyCode::Char('1'), + KeyModifiers::empty(), + KeyEventKind::Press, + KeyEventState::NUM_LOCK, + ) + ))), + ); + } + + #[test] + fn test_parse_csi_special_key_code_with_types() { + assert_eq!( + parse_event(b"\x1B[;1:3B", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Down, + KeyModifiers::empty(), + KeyEventKind::Release, + )))), + ); + assert_eq!( + parse_event(b"\x1B[1;1:3B", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind( + KeyCode::Down, + KeyModifiers::empty(), + KeyEventKind::Release, + )))), + ); + } +} diff --git a/vendor/crossterm/src/event/sys/unix/waker.rs b/vendor/crossterm/src/event/sys/unix/waker.rs index 4509e9b..d78c94a 100644 --- a/vendor/crossterm/src/event/sys/unix/waker.rs +++ b/vendor/crossterm/src/event/sys/unix/waker.rs @@ -1,36 +1,36 @@ -use std::sync::{Arc, Mutex}; - -use mio::{Registry, Token}; - -use crate::Result; - -/// Allows to wake up the `mio::Poll::poll()` method. -/// This type wraps `mio::Waker`, for more information see its documentation. -#[derive(Clone, Debug)] -pub(crate) struct Waker { - inner: Arc>, -} - -impl Waker { - /// Create a new `Waker`. - pub(crate) fn new(registry: &Registry, waker_token: Token) -> Result { - Ok(Self { - inner: Arc::new(Mutex::new(mio::Waker::new(registry, waker_token)?)), - }) - } - - /// Wake up the [`Poll`] associated with this `Waker`. - /// - /// Readiness is set to `Ready::readable()`. - pub(crate) fn wake(&self) -> Result<()> { - self.inner.lock().unwrap().wake() - } - - /// Resets the state so the same waker can be reused. - /// - /// This function is not impl - #[allow(dead_code, clippy::clippy::unnecessary_wraps)] - pub(crate) fn reset(&self) -> Result<()> { - Ok(()) - } -} +use std::sync::{Arc, Mutex}; + +use mio::{Registry, Token}; + +use crate::Result; + +/// Allows to wake up the `mio::Poll::poll()` method. +/// This type wraps `mio::Waker`, for more information see its documentation. +#[derive(Clone, Debug)] +pub(crate) struct Waker { + inner: Arc>, +} + +impl Waker { + /// Create a new `Waker`. + pub(crate) fn new(registry: &Registry, waker_token: Token) -> Result { + Ok(Self { + inner: Arc::new(Mutex::new(mio::Waker::new(registry, waker_token)?)), + }) + } + + /// Wake up the [`Poll`] associated with this `Waker`. + /// + /// Readiness is set to `Ready::readable()`. + pub(crate) fn wake(&self) -> Result<()> { + self.inner.lock().unwrap().wake() + } + + /// Resets the state so the same waker can be reused. + /// + /// This function is not impl + #[allow(dead_code, clippy::clippy::unnecessary_wraps)] + pub(crate) fn reset(&self) -> Result<()> { + Ok(()) + } +} diff --git a/vendor/crossterm/src/event/sys/windows.rs b/vendor/crossterm/src/event/sys/windows.rs index af20497..af02ae2 100644 --- a/vendor/crossterm/src/event/sys/windows.rs +++ b/vendor/crossterm/src/event/sys/windows.rs @@ -1,51 +1,51 @@ -//! This is a WINDOWS specific implementation for input related action. - -use std::convert::TryFrom; -use std::io; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crossterm_winapi::{ConsoleMode, Handle}; - -use crate::Result; - -#[cfg(feature = "event-stream")] -pub(crate) mod waker; - -pub(crate) mod parse; -pub(crate) mod poll; - -const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; - -/// This is a either `u64::MAX` if it's uninitialized or a valid `u32` that stores the original -/// console mode if it's initialized. -static ORIGINAL_CONSOLE_MODE: AtomicU64 = AtomicU64::new(u64::MAX); - -/// Initializes the default console color. It will will be skipped if it has already been initialized. -fn init_original_console_mode(original_mode: u32) { - let _ = ORIGINAL_CONSOLE_MODE.compare_exchange( - u64::MAX, - u64::from(original_mode), - Ordering::Relaxed, - Ordering::Relaxed, - ); -} - -/// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. -fn original_console_mode() -> Result { - u32::try_from(ORIGINAL_CONSOLE_MODE.load(Ordering::Relaxed)) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "Initial console modes not set")) -} - -pub(crate) fn enable_mouse_capture() -> Result<()> { - let mode = ConsoleMode::from(Handle::current_in_handle()?); - init_original_console_mode(mode.mode()?); - mode.set_mode(ENABLE_MOUSE_MODE)?; - - Ok(()) -} - -pub(crate) fn disable_mouse_capture() -> Result<()> { - let mode = ConsoleMode::from(Handle::current_in_handle()?); - mode.set_mode(original_console_mode()?)?; - Ok(()) -} +//! This is a WINDOWS specific implementation for input related action. + +use std::convert::TryFrom; +use std::io; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crossterm_winapi::{ConsoleMode, Handle}; + +use crate::Result; + +#[cfg(feature = "event-stream")] +pub(crate) mod waker; + +pub(crate) mod parse; +pub(crate) mod poll; + +const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; + +/// This is a either `u64::MAX` if it's uninitialized or a valid `u32` that stores the original +/// console mode if it's initialized. +static ORIGINAL_CONSOLE_MODE: AtomicU64 = AtomicU64::new(u64::MAX); + +/// Initializes the default console color. It will will be skipped if it has already been initialized. +fn init_original_console_mode(original_mode: u32) { + let _ = ORIGINAL_CONSOLE_MODE.compare_exchange( + u64::MAX, + u64::from(original_mode), + Ordering::Relaxed, + Ordering::Relaxed, + ); +} + +/// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. +fn original_console_mode() -> Result { + u32::try_from(ORIGINAL_CONSOLE_MODE.load(Ordering::Relaxed)) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "Initial console modes not set")) +} + +pub(crate) fn enable_mouse_capture() -> Result<()> { + let mode = ConsoleMode::from(Handle::current_in_handle()?); + init_original_console_mode(mode.mode()?); + mode.set_mode(ENABLE_MOUSE_MODE)?; + + Ok(()) +} + +pub(crate) fn disable_mouse_capture() -> Result<()> { + let mode = ConsoleMode::from(Handle::current_in_handle()?); + mode.set_mode(original_console_mode()?)?; + Ok(()) +} diff --git a/vendor/crossterm/src/event/sys/windows/parse.rs b/vendor/crossterm/src/event/sys/windows/parse.rs index 697a49a..38780fb 100644 --- a/vendor/crossterm/src/event/sys/windows/parse.rs +++ b/vendor/crossterm/src/event/sys/windows/parse.rs @@ -1,366 +1,366 @@ -use crossterm_winapi::{ControlKeyState, EventFlags, KeyEventRecord, MouseEvent, ScreenBuffer}; -use winapi::um::{ - wincon::{ - CAPSLOCK_ON, LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, - SHIFT_PRESSED, - }, - winuser::{ - GetForegroundWindow, GetKeyboardLayout, GetWindowThreadProcessId, ToUnicodeEx, VK_BACK, - VK_CONTROL, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_F24, VK_HOME, VK_INSERT, - VK_LEFT, VK_MENU, VK_NEXT, VK_NUMPAD0, VK_NUMPAD9, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SHIFT, - VK_TAB, VK_UP, - }, -}; - -use crate::{ - event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEventKind}, - Result, -}; - -#[derive(Default)] -pub struct MouseButtonsPressed { - pub(crate) left: bool, - pub(crate) right: bool, - pub(crate) middle: bool, -} - -pub(crate) fn handle_mouse_event( - mouse_event: MouseEvent, - buttons_pressed: &MouseButtonsPressed, -) -> Option { - if let Ok(Some(event)) = parse_mouse_event_record(&mouse_event, buttons_pressed) { - return Some(Event::Mouse(event)); - } - - None -} - -enum WindowsKeyEvent { - KeyEvent(KeyEvent), - Surrogate(u16), -} - -pub(crate) fn handle_key_event( - key_event: KeyEventRecord, - surrogate_buffer: &mut Option, -) -> Option { - let windows_key_event = parse_key_event_record(&key_event)?; - match windows_key_event { - WindowsKeyEvent::KeyEvent(key_event) => { - // Discard any buffered surrogate value if another valid key event comes before the - // next surrogate value. - *surrogate_buffer = None; - Some(Event::Key(key_event)) - } - WindowsKeyEvent::Surrogate(new_surrogate) => { - let ch = handle_surrogate(surrogate_buffer, new_surrogate)?; - let modifiers = KeyModifiers::from(&key_event.control_key_state); - let key_event = KeyEvent::new(KeyCode::Char(ch), modifiers); - Some(Event::Key(key_event)) - } - } -} - -fn handle_surrogate(surrogate_buffer: &mut Option, new_surrogate: u16) -> Option { - match *surrogate_buffer { - Some(buffered_surrogate) => { - *surrogate_buffer = None; - std::char::decode_utf16([buffered_surrogate, new_surrogate]) - .next() - .unwrap() - .ok() - } - None => { - *surrogate_buffer = Some(new_surrogate); - None - } - } -} - -impl From<&ControlKeyState> for KeyModifiers { - fn from(state: &ControlKeyState) -> Self { - let shift = state.has_state(SHIFT_PRESSED); - let alt = state.has_state(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED); - let control = state.has_state(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED); - - let mut modifier = KeyModifiers::empty(); - - if shift { - modifier |= KeyModifiers::SHIFT; - } - if control { - modifier |= KeyModifiers::CONTROL; - } - if alt { - modifier |= KeyModifiers::ALT; - } - - modifier - } -} - -enum CharCase { - LowerCase, - UpperCase, -} - -fn try_ensure_char_case(ch: char, desired_case: CharCase) -> char { - match desired_case { - CharCase::LowerCase if ch.is_uppercase() => { - let mut iter = ch.to_lowercase(); - // Unwrap is safe; iterator yields one or more chars. - let ch_lower = iter.next().unwrap(); - if iter.next() == None { - ch_lower - } else { - ch - } - } - CharCase::UpperCase if ch.is_lowercase() => { - let mut iter = ch.to_uppercase(); - // Unwrap is safe; iterator yields one or more chars. - let ch_upper = iter.next().unwrap(); - if iter.next() == None { - ch_upper - } else { - ch - } - } - _ => ch, - } -} - -// Attempts to return the character for a key event accounting for the user's keyboard layout. -// The returned character (if any) is capitalized (if applicable) based on shift and capslock state. -// Returns None if the key doesn't map to a character or if it is a dead key. -// We use the *currently* active keyboard layout (if it can be determined). This layout may not -// correspond to the keyboard layout that was active when the user typed their input, since console -// applications get their input asynchronously from the terminal. By the time a console application -// can process a key input, the user may have changed the active layout. In this case, the character -// returned might not correspond to what the user expects, but there is no way for a console -// application to know what the keyboard layout actually was for a key event, so this is our best -// effort. If a console application processes input in a timely fashion, then it is unlikely that a -// user has time to change their keyboard layout before a key event is processed. -fn get_char_for_key(key_event: &KeyEventRecord) -> Option { - let virtual_key_code = key_event.virtual_key_code as u32; - let virtual_scan_code = key_event.virtual_scan_code as u32; - let key_state = [0u8; 256]; - let mut utf16_buf = [0u16, 16]; - let dont_change_kernel_keyboard_state = 0x4; - - // Best-effort attempt at determining the currently active keyboard layout. - // At the time of writing, this works for a console application running in Windows Terminal, but - // doesn't work under a Conhost terminal. For Conhost, the window handle returned by - // GetForegroundWindow() does not appear to actually be the foreground window which has the - // keyboard layout associated with it (or perhaps it is, but also has special protection that - // doesn't allow us to query it). - // When this determination fails, the returned keyboard layout handle will be null, which is an - // acceptable input for ToUnicodeEx, as that argument is optional. In this case ToUnicodeEx - // appears to use the keyboard layout associated with the current thread, which will be the - // layout that was inherited when the console application started (or possibly when the current - // thread was spawned). This is then unfortunately not updated when the user changes their - // keyboard layout in the terminal, but it's what we get. - let active_keyboard_layout = unsafe { - let foreground_window = GetForegroundWindow(); - let foreground_thread = GetWindowThreadProcessId(foreground_window, std::ptr::null_mut()); - GetKeyboardLayout(foreground_thread) - }; - - let ret = unsafe { - ToUnicodeEx( - virtual_key_code, - virtual_scan_code, - key_state.as_ptr(), - utf16_buf.as_mut_ptr(), - utf16_buf.len() as i32, - dont_change_kernel_keyboard_state, - active_keyboard_layout, - ) - }; - - // -1 indicates a dead key. - // 0 indicates no character for this key. - if ret < 1 { - return None; - } - - let mut ch_iter = std::char::decode_utf16(utf16_buf.into_iter().take(ret as usize)); - let mut ch = ch_iter.next()?.ok()?; - if ch_iter.next() != None { - // Key doesn't map to a single char. - return None; - } - - let is_shift_pressed = key_event.control_key_state.has_state(SHIFT_PRESSED); - let is_capslock_on = key_event.control_key_state.has_state(CAPSLOCK_ON); - let desired_case = if is_shift_pressed ^ is_capslock_on { - CharCase::UpperCase - } else { - CharCase::LowerCase - }; - ch = try_ensure_char_case(ch, desired_case); - Some(ch) -} - -fn parse_key_event_record(key_event: &KeyEventRecord) -> Option { - let modifiers = KeyModifiers::from(&key_event.control_key_state); - let virtual_key_code = key_event.virtual_key_code as i32; - - // We normally ignore all key release events, but we will make an exception for an Alt key - // release if it carries a u_char value, as this indicates an Alt code. - let is_alt_code = virtual_key_code == VK_MENU && !key_event.key_down && key_event.u_char != 0; - if is_alt_code { - let utf16 = key_event.u_char; - match utf16 { - surrogate @ 0xD800..=0xDFFF => { - return Some(WindowsKeyEvent::Surrogate(surrogate)); - } - unicode_scalar_value => { - // Unwrap is safe: We tested for surrogate values above and those are the only - // u16 values that are invalid when directly interpreted as unicode scalar - // values. - let ch = std::char::from_u32(unicode_scalar_value as u32).unwrap(); - let key_code = KeyCode::Char(ch); - let key_event = KeyEvent::new(key_code, modifiers); - return Some(WindowsKeyEvent::KeyEvent(key_event)); - } - } - } - - // Don't generate events for numpad key presses when they're producing Alt codes. - let is_numpad_numeric_key = (VK_NUMPAD0..=VK_NUMPAD9).contains(&virtual_key_code); - let is_only_alt_modifier = modifiers.contains(KeyModifiers::ALT) - && !modifiers.contains(KeyModifiers::SHIFT | KeyModifiers::CONTROL); - if is_only_alt_modifier && is_numpad_numeric_key { - return None; - } - - if !key_event.key_down { - return None; - } - - let parse_result = match virtual_key_code { - VK_SHIFT | VK_CONTROL | VK_MENU => None, - VK_BACK => Some(KeyCode::Backspace), - VK_ESCAPE => Some(KeyCode::Esc), - VK_RETURN => Some(KeyCode::Enter), - VK_F1..=VK_F24 => Some(KeyCode::F((key_event.virtual_key_code - 111) as u8)), - VK_LEFT => Some(KeyCode::Left), - VK_UP => Some(KeyCode::Up), - VK_RIGHT => Some(KeyCode::Right), - VK_DOWN => Some(KeyCode::Down), - VK_PRIOR => Some(KeyCode::PageUp), - VK_NEXT => Some(KeyCode::PageDown), - VK_HOME => Some(KeyCode::Home), - VK_END => Some(KeyCode::End), - VK_DELETE => Some(KeyCode::Delete), - VK_INSERT => Some(KeyCode::Insert), - VK_TAB if modifiers.contains(KeyModifiers::SHIFT) => Some(KeyCode::BackTab), - VK_TAB => Some(KeyCode::Tab), - _ => { - let utf16 = key_event.u_char; - match utf16 { - 0x00..=0x1f => { - // Some key combinations generate either no u_char value or generate control - // codes. To deliver back a KeyCode::Char(...) event we want to know which - // character the key normally maps to on the user's keyboard layout. - // The keys that intentionally generate control codes (ESC, ENTER, TAB, etc.) - // are handled by their virtual key codes above. - get_char_for_key(key_event).map(KeyCode::Char) - } - surrogate @ 0xD800..=0xDFFF => { - return Some(WindowsKeyEvent::Surrogate(surrogate)); - } - unicode_scalar_value => { - // Unwrap is safe: We tested for surrogate values above and those are the only - // u16 values that are invalid when directly interpreted as unicode scalar - // values. - let ch = std::char::from_u32(unicode_scalar_value as u32).unwrap(); - Some(KeyCode::Char(ch)) - } - } - } - }; - - if let Some(key_code) = parse_result { - let key_event = KeyEvent::new(key_code, modifiers); - return Some(WindowsKeyEvent::KeyEvent(key_event)); - } - - None -} - -// The 'y' position of a mouse event or resize event is not relative to the window but absolute to screen buffer. -// This means that when the mouse cursor is at the top left it will be x: 0, y: 2295 (e.g. y = number of cells conting from the absolute buffer height) instead of relative x: 0, y: 0 to the window. -pub fn parse_relative_y(y: i16) -> Result { - let window_size = ScreenBuffer::current()?.info()?.terminal_window(); - Ok(y - window_size.top) -} - -fn parse_mouse_event_record( - event: &MouseEvent, - buttons_pressed: &MouseButtonsPressed, -) -> Result> { - let modifiers = KeyModifiers::from(&event.control_key_state); - - let xpos = event.mouse_position.x as u16; - let ypos = parse_relative_y(event.mouse_position.y)? as u16; - - let button_state = event.button_state; - - let kind = match event.event_flags { - EventFlags::PressOrRelease => { - if button_state.left_button() && !buttons_pressed.left { - Some(MouseEventKind::Down(MouseButton::Left)) - } else if !button_state.left_button() && buttons_pressed.left { - Some(MouseEventKind::Up(MouseButton::Left)) - } else if button_state.right_button() && !buttons_pressed.right { - Some(MouseEventKind::Down(MouseButton::Right)) - } else if !button_state.right_button() && buttons_pressed.right { - Some(MouseEventKind::Up(MouseButton::Right)) - } else if button_state.middle_button() && !buttons_pressed.middle { - Some(MouseEventKind::Down(MouseButton::Middle)) - } else if !button_state.middle_button() && buttons_pressed.middle { - Some(MouseEventKind::Up(MouseButton::Middle)) - } else { - None - } - } - EventFlags::MouseMoved => { - let button = if button_state.right_button() { - MouseButton::Right - } else if button_state.middle_button() { - MouseButton::Middle - } else { - MouseButton::Left - }; - if button_state.release_button() { - Some(MouseEventKind::Moved) - } else { - Some(MouseEventKind::Drag(button)) - } - } - EventFlags::MouseWheeled => { - // Vertical scroll - // from https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str - // if `button_state` is negative then the wheel was rotated backward, toward the user. - if button_state.scroll_down() { - Some(MouseEventKind::ScrollDown) - } else if button_state.scroll_up() { - Some(MouseEventKind::ScrollUp) - } else { - None - } - } - EventFlags::DoubleClick => None, // double click not supported by unix terminals - EventFlags::MouseHwheeled => None, // horizontal scroll not supported by unix terminals - _ => None, - }; - - Ok(kind.map(|kind| crate::event::MouseEvent { - kind, - column: xpos, - row: ypos, - modifiers, - })) -} +use crossterm_winapi::{ControlKeyState, EventFlags, KeyEventRecord, MouseEvent, ScreenBuffer}; +use winapi::um::{ + wincon::{ + CAPSLOCK_ON, LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, + SHIFT_PRESSED, + }, + winuser::{ + GetForegroundWindow, GetKeyboardLayout, GetWindowThreadProcessId, ToUnicodeEx, VK_BACK, + VK_CONTROL, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_F24, VK_HOME, VK_INSERT, + VK_LEFT, VK_MENU, VK_NEXT, VK_NUMPAD0, VK_NUMPAD9, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SHIFT, + VK_TAB, VK_UP, + }, +}; + +use crate::{ + event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEventKind}, + Result, +}; + +#[derive(Default)] +pub struct MouseButtonsPressed { + pub(crate) left: bool, + pub(crate) right: bool, + pub(crate) middle: bool, +} + +pub(crate) fn handle_mouse_event( + mouse_event: MouseEvent, + buttons_pressed: &MouseButtonsPressed, +) -> Option { + if let Ok(Some(event)) = parse_mouse_event_record(&mouse_event, buttons_pressed) { + return Some(Event::Mouse(event)); + } + + None +} + +enum WindowsKeyEvent { + KeyEvent(KeyEvent), + Surrogate(u16), +} + +pub(crate) fn handle_key_event( + key_event: KeyEventRecord, + surrogate_buffer: &mut Option, +) -> Option { + let windows_key_event = parse_key_event_record(&key_event)?; + match windows_key_event { + WindowsKeyEvent::KeyEvent(key_event) => { + // Discard any buffered surrogate value if another valid key event comes before the + // next surrogate value. + *surrogate_buffer = None; + Some(Event::Key(key_event)) + } + WindowsKeyEvent::Surrogate(new_surrogate) => { + let ch = handle_surrogate(surrogate_buffer, new_surrogate)?; + let modifiers = KeyModifiers::from(&key_event.control_key_state); + let key_event = KeyEvent::new(KeyCode::Char(ch), modifiers); + Some(Event::Key(key_event)) + } + } +} + +fn handle_surrogate(surrogate_buffer: &mut Option, new_surrogate: u16) -> Option { + match *surrogate_buffer { + Some(buffered_surrogate) => { + *surrogate_buffer = None; + std::char::decode_utf16([buffered_surrogate, new_surrogate]) + .next() + .unwrap() + .ok() + } + None => { + *surrogate_buffer = Some(new_surrogate); + None + } + } +} + +impl From<&ControlKeyState> for KeyModifiers { + fn from(state: &ControlKeyState) -> Self { + let shift = state.has_state(SHIFT_PRESSED); + let alt = state.has_state(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED); + let control = state.has_state(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED); + + let mut modifier = KeyModifiers::empty(); + + if shift { + modifier |= KeyModifiers::SHIFT; + } + if control { + modifier |= KeyModifiers::CONTROL; + } + if alt { + modifier |= KeyModifiers::ALT; + } + + modifier + } +} + +enum CharCase { + LowerCase, + UpperCase, +} + +fn try_ensure_char_case(ch: char, desired_case: CharCase) -> char { + match desired_case { + CharCase::LowerCase if ch.is_uppercase() => { + let mut iter = ch.to_lowercase(); + // Unwrap is safe; iterator yields one or more chars. + let ch_lower = iter.next().unwrap(); + if iter.next() == None { + ch_lower + } else { + ch + } + } + CharCase::UpperCase if ch.is_lowercase() => { + let mut iter = ch.to_uppercase(); + // Unwrap is safe; iterator yields one or more chars. + let ch_upper = iter.next().unwrap(); + if iter.next() == None { + ch_upper + } else { + ch + } + } + _ => ch, + } +} + +// Attempts to return the character for a key event accounting for the user's keyboard layout. +// The returned character (if any) is capitalized (if applicable) based on shift and capslock state. +// Returns None if the key doesn't map to a character or if it is a dead key. +// We use the *currently* active keyboard layout (if it can be determined). This layout may not +// correspond to the keyboard layout that was active when the user typed their input, since console +// applications get their input asynchronously from the terminal. By the time a console application +// can process a key input, the user may have changed the active layout. In this case, the character +// returned might not correspond to what the user expects, but there is no way for a console +// application to know what the keyboard layout actually was for a key event, so this is our best +// effort. If a console application processes input in a timely fashion, then it is unlikely that a +// user has time to change their keyboard layout before a key event is processed. +fn get_char_for_key(key_event: &KeyEventRecord) -> Option { + let virtual_key_code = key_event.virtual_key_code as u32; + let virtual_scan_code = key_event.virtual_scan_code as u32; + let key_state = [0u8; 256]; + let mut utf16_buf = [0u16, 16]; + let dont_change_kernel_keyboard_state = 0x4; + + // Best-effort attempt at determining the currently active keyboard layout. + // At the time of writing, this works for a console application running in Windows Terminal, but + // doesn't work under a Conhost terminal. For Conhost, the window handle returned by + // GetForegroundWindow() does not appear to actually be the foreground window which has the + // keyboard layout associated with it (or perhaps it is, but also has special protection that + // doesn't allow us to query it). + // When this determination fails, the returned keyboard layout handle will be null, which is an + // acceptable input for ToUnicodeEx, as that argument is optional. In this case ToUnicodeEx + // appears to use the keyboard layout associated with the current thread, which will be the + // layout that was inherited when the console application started (or possibly when the current + // thread was spawned). This is then unfortunately not updated when the user changes their + // keyboard layout in the terminal, but it's what we get. + let active_keyboard_layout = unsafe { + let foreground_window = GetForegroundWindow(); + let foreground_thread = GetWindowThreadProcessId(foreground_window, std::ptr::null_mut()); + GetKeyboardLayout(foreground_thread) + }; + + let ret = unsafe { + ToUnicodeEx( + virtual_key_code, + virtual_scan_code, + key_state.as_ptr(), + utf16_buf.as_mut_ptr(), + utf16_buf.len() as i32, + dont_change_kernel_keyboard_state, + active_keyboard_layout, + ) + }; + + // -1 indicates a dead key. + // 0 indicates no character for this key. + if ret < 1 { + return None; + } + + let mut ch_iter = std::char::decode_utf16(utf16_buf.into_iter().take(ret as usize)); + let mut ch = ch_iter.next()?.ok()?; + if ch_iter.next() != None { + // Key doesn't map to a single char. + return None; + } + + let is_shift_pressed = key_event.control_key_state.has_state(SHIFT_PRESSED); + let is_capslock_on = key_event.control_key_state.has_state(CAPSLOCK_ON); + let desired_case = if is_shift_pressed ^ is_capslock_on { + CharCase::UpperCase + } else { + CharCase::LowerCase + }; + ch = try_ensure_char_case(ch, desired_case); + Some(ch) +} + +fn parse_key_event_record(key_event: &KeyEventRecord) -> Option { + let modifiers = KeyModifiers::from(&key_event.control_key_state); + let virtual_key_code = key_event.virtual_key_code as i32; + + // We normally ignore all key release events, but we will make an exception for an Alt key + // release if it carries a u_char value, as this indicates an Alt code. + let is_alt_code = virtual_key_code == VK_MENU && !key_event.key_down && key_event.u_char != 0; + if is_alt_code { + let utf16 = key_event.u_char; + match utf16 { + surrogate @ 0xD800..=0xDFFF => { + return Some(WindowsKeyEvent::Surrogate(surrogate)); + } + unicode_scalar_value => { + // Unwrap is safe: We tested for surrogate values above and those are the only + // u16 values that are invalid when directly interpreted as unicode scalar + // values. + let ch = std::char::from_u32(unicode_scalar_value as u32).unwrap(); + let key_code = KeyCode::Char(ch); + let key_event = KeyEvent::new(key_code, modifiers); + return Some(WindowsKeyEvent::KeyEvent(key_event)); + } + } + } + + // Don't generate events for numpad key presses when they're producing Alt codes. + let is_numpad_numeric_key = (VK_NUMPAD0..=VK_NUMPAD9).contains(&virtual_key_code); + let is_only_alt_modifier = modifiers.contains(KeyModifiers::ALT) + && !modifiers.contains(KeyModifiers::SHIFT | KeyModifiers::CONTROL); + if is_only_alt_modifier && is_numpad_numeric_key { + return None; + } + + if !key_event.key_down { + return None; + } + + let parse_result = match virtual_key_code { + VK_SHIFT | VK_CONTROL | VK_MENU => None, + VK_BACK => Some(KeyCode::Backspace), + VK_ESCAPE => Some(KeyCode::Esc), + VK_RETURN => Some(KeyCode::Enter), + VK_F1..=VK_F24 => Some(KeyCode::F((key_event.virtual_key_code - 111) as u8)), + VK_LEFT => Some(KeyCode::Left), + VK_UP => Some(KeyCode::Up), + VK_RIGHT => Some(KeyCode::Right), + VK_DOWN => Some(KeyCode::Down), + VK_PRIOR => Some(KeyCode::PageUp), + VK_NEXT => Some(KeyCode::PageDown), + VK_HOME => Some(KeyCode::Home), + VK_END => Some(KeyCode::End), + VK_DELETE => Some(KeyCode::Delete), + VK_INSERT => Some(KeyCode::Insert), + VK_TAB if modifiers.contains(KeyModifiers::SHIFT) => Some(KeyCode::BackTab), + VK_TAB => Some(KeyCode::Tab), + _ => { + let utf16 = key_event.u_char; + match utf16 { + 0x00..=0x1f => { + // Some key combinations generate either no u_char value or generate control + // codes. To deliver back a KeyCode::Char(...) event we want to know which + // character the key normally maps to on the user's keyboard layout. + // The keys that intentionally generate control codes (ESC, ENTER, TAB, etc.) + // are handled by their virtual key codes above. + get_char_for_key(key_event).map(KeyCode::Char) + } + surrogate @ 0xD800..=0xDFFF => { + return Some(WindowsKeyEvent::Surrogate(surrogate)); + } + unicode_scalar_value => { + // Unwrap is safe: We tested for surrogate values above and those are the only + // u16 values that are invalid when directly interpreted as unicode scalar + // values. + let ch = std::char::from_u32(unicode_scalar_value as u32).unwrap(); + Some(KeyCode::Char(ch)) + } + } + } + }; + + if let Some(key_code) = parse_result { + let key_event = KeyEvent::new(key_code, modifiers); + return Some(WindowsKeyEvent::KeyEvent(key_event)); + } + + None +} + +// The 'y' position of a mouse event or resize event is not relative to the window but absolute to screen buffer. +// This means that when the mouse cursor is at the top left it will be x: 0, y: 2295 (e.g. y = number of cells conting from the absolute buffer height) instead of relative x: 0, y: 0 to the window. +pub fn parse_relative_y(y: i16) -> Result { + let window_size = ScreenBuffer::current()?.info()?.terminal_window(); + Ok(y - window_size.top) +} + +fn parse_mouse_event_record( + event: &MouseEvent, + buttons_pressed: &MouseButtonsPressed, +) -> Result> { + let modifiers = KeyModifiers::from(&event.control_key_state); + + let xpos = event.mouse_position.x as u16; + let ypos = parse_relative_y(event.mouse_position.y)? as u16; + + let button_state = event.button_state; + + let kind = match event.event_flags { + EventFlags::PressOrRelease => { + if button_state.left_button() && !buttons_pressed.left { + Some(MouseEventKind::Down(MouseButton::Left)) + } else if !button_state.left_button() && buttons_pressed.left { + Some(MouseEventKind::Up(MouseButton::Left)) + } else if button_state.right_button() && !buttons_pressed.right { + Some(MouseEventKind::Down(MouseButton::Right)) + } else if !button_state.right_button() && buttons_pressed.right { + Some(MouseEventKind::Up(MouseButton::Right)) + } else if button_state.middle_button() && !buttons_pressed.middle { + Some(MouseEventKind::Down(MouseButton::Middle)) + } else if !button_state.middle_button() && buttons_pressed.middle { + Some(MouseEventKind::Up(MouseButton::Middle)) + } else { + None + } + } + EventFlags::MouseMoved => { + let button = if button_state.right_button() { + MouseButton::Right + } else if button_state.middle_button() { + MouseButton::Middle + } else { + MouseButton::Left + }; + if button_state.release_button() { + Some(MouseEventKind::Moved) + } else { + Some(MouseEventKind::Drag(button)) + } + } + EventFlags::MouseWheeled => { + // Vertical scroll + // from https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str + // if `button_state` is negative then the wheel was rotated backward, toward the user. + if button_state.scroll_down() { + Some(MouseEventKind::ScrollDown) + } else if button_state.scroll_up() { + Some(MouseEventKind::ScrollUp) + } else { + None + } + } + EventFlags::DoubleClick => None, // double click not supported by unix terminals + EventFlags::MouseHwheeled => None, // horizontal scroll not supported by unix terminals + _ => None, + }; + + Ok(kind.map(|kind| crate::event::MouseEvent { + kind, + column: xpos, + row: ypos, + modifiers, + })) +} diff --git a/vendor/crossterm/src/event/sys/windows/poll.rs b/vendor/crossterm/src/event/sys/windows/poll.rs index 32511fb..12099fb 100644 --- a/vendor/crossterm/src/event/sys/windows/poll.rs +++ b/vendor/crossterm/src/event/sys/windows/poll.rs @@ -1,89 +1,89 @@ -use std::io; -use std::time::Duration; - -use crossterm_winapi::Handle; -use winapi::{ - shared::winerror::WAIT_TIMEOUT, - um::{ - synchapi::WaitForMultipleObjects, - winbase::{INFINITE, WAIT_ABANDONED_0, WAIT_FAILED, WAIT_OBJECT_0}, - }, -}; - -use crate::Result; - -#[cfg(feature = "event-stream")] -pub(crate) use super::waker::Waker; - -#[derive(Debug)] -pub(crate) struct WinApiPoll { - #[cfg(feature = "event-stream")] - waker: Waker, -} - -impl WinApiPoll { - #[cfg(not(feature = "event-stream"))] - pub(crate) fn new() -> WinApiPoll { - WinApiPoll {} - } - - #[cfg(feature = "event-stream")] - pub(crate) fn new() -> Result { - Ok(WinApiPoll { - waker: Waker::new()?, - }) - } -} - -impl WinApiPoll { - pub fn poll(&mut self, timeout: Option) -> Result> { - let dw_millis = if let Some(duration) = timeout { - duration.as_millis() as u32 - } else { - INFINITE - }; - - let console_handle = Handle::current_in_handle()?; - - #[cfg(feature = "event-stream")] - let semaphore = self.waker.semaphore(); - #[cfg(feature = "event-stream")] - let handles = &[*console_handle, **semaphore.handle()]; - #[cfg(not(feature = "event-stream"))] - let handles = &[*console_handle]; - - let output = - unsafe { WaitForMultipleObjects(handles.len() as u32, handles.as_ptr(), 0, dw_millis) }; - - match output { - output if output == WAIT_OBJECT_0 => { - // input handle triggered - Ok(Some(true)) - } - #[cfg(feature = "event-stream")] - output if output == WAIT_OBJECT_0 + 1 => { - // semaphore handle triggered - let _ = self.waker.reset(); - Err(io::Error::new( - io::ErrorKind::Interrupted, - "Poll operation was woken up by `Waker::wake`", - ) - .into()) - } - WAIT_TIMEOUT | WAIT_ABANDONED_0 => { - // timeout elapsed - Ok(None) - } - WAIT_FAILED => Err(io::Error::last_os_error()), - _ => Err(io::Error::new( - io::ErrorKind::Other, - "WaitForMultipleObjects returned unexpected result.", - )), - } - } - - #[cfg(feature = "event-stream")] - pub fn waker(&self) -> Waker { - self.waker.clone() - } -} +use std::io; +use std::time::Duration; + +use crossterm_winapi::Handle; +use winapi::{ + shared::winerror::WAIT_TIMEOUT, + um::{ + synchapi::WaitForMultipleObjects, + winbase::{INFINITE, WAIT_ABANDONED_0, WAIT_FAILED, WAIT_OBJECT_0}, + }, +}; + +use crate::Result; + +#[cfg(feature = "event-stream")] +pub(crate) use super::waker::Waker; + +#[derive(Debug)] +pub(crate) struct WinApiPoll { + #[cfg(feature = "event-stream")] + waker: Waker, +} + +impl WinApiPoll { + #[cfg(not(feature = "event-stream"))] + pub(crate) fn new() -> WinApiPoll { + WinApiPoll {} + } + + #[cfg(feature = "event-stream")] + pub(crate) fn new() -> Result { + Ok(WinApiPoll { + waker: Waker::new()?, + }) + } +} + +impl WinApiPoll { + pub fn poll(&mut self, timeout: Option) -> Result> { + let dw_millis = if let Some(duration) = timeout { + duration.as_millis() as u32 + } else { + INFINITE + }; + + let console_handle = Handle::current_in_handle()?; + + #[cfg(feature = "event-stream")] + let semaphore = self.waker.semaphore(); + #[cfg(feature = "event-stream")] + let handles = &[*console_handle, **semaphore.handle()]; + #[cfg(not(feature = "event-stream"))] + let handles = &[*console_handle]; + + let output = + unsafe { WaitForMultipleObjects(handles.len() as u32, handles.as_ptr(), 0, dw_millis) }; + + match output { + output if output == WAIT_OBJECT_0 => { + // input handle triggered + Ok(Some(true)) + } + #[cfg(feature = "event-stream")] + output if output == WAIT_OBJECT_0 + 1 => { + // semaphore handle triggered + let _ = self.waker.reset(); + Err(io::Error::new( + io::ErrorKind::Interrupted, + "Poll operation was woken up by `Waker::wake`", + ) + .into()) + } + WAIT_TIMEOUT | WAIT_ABANDONED_0 => { + // timeout elapsed + Ok(None) + } + WAIT_FAILED => Err(io::Error::last_os_error()), + _ => Err(io::Error::new( + io::ErrorKind::Other, + "WaitForMultipleObjects returned unexpected result.", + )), + } + } + + #[cfg(feature = "event-stream")] + pub fn waker(&self) -> Waker { + self.waker.clone() + } +} diff --git a/vendor/crossterm/src/event/sys/windows/waker.rs b/vendor/crossterm/src/event/sys/windows/waker.rs index 2b02794..992adab 100644 --- a/vendor/crossterm/src/event/sys/windows/waker.rs +++ b/vendor/crossterm/src/event/sys/windows/waker.rs @@ -1,42 +1,42 @@ -use std::sync::{Arc, Mutex}; - -use crossterm_winapi::Semaphore; - -use crate::Result; - -/// Allows to wake up the `WinApiPoll::poll()` method. -#[derive(Clone, Debug)] -pub(crate) struct Waker { - inner: Arc>, -} - -impl Waker { - /// Creates a new waker. - /// - /// `Waker` is based on the `Semaphore`. You have to use the semaphore - /// handle along with the `WaitForMultipleObjects`. - pub(crate) fn new() -> Result { - let inner = Semaphore::new()?; - - Ok(Self { - inner: Arc::new(Mutex::new(inner)), - }) - } - - /// Wakes the `WaitForMultipleObjects`. - pub(crate) fn wake(&self) -> Result<()> { - self.inner.lock().unwrap().release()?; - Ok(()) - } - - /// Replaces the current semaphore with a new one allowing us to reuse the same `Waker`. - pub(crate) fn reset(&self) -> Result<()> { - *self.inner.lock().unwrap() = Semaphore::new()?; - Ok(()) - } - - /// Returns the semaphore associated with the waker. - pub(crate) fn semaphore(&self) -> Semaphore { - self.inner.lock().unwrap().clone() - } -} +use std::sync::{Arc, Mutex}; + +use crossterm_winapi::Semaphore; + +use crate::Result; + +/// Allows to wake up the `WinApiPoll::poll()` method. +#[derive(Clone, Debug)] +pub(crate) struct Waker { + inner: Arc>, +} + +impl Waker { + /// Creates a new waker. + /// + /// `Waker` is based on the `Semaphore`. You have to use the semaphore + /// handle along with the `WaitForMultipleObjects`. + pub(crate) fn new() -> Result { + let inner = Semaphore::new()?; + + Ok(Self { + inner: Arc::new(Mutex::new(inner)), + }) + } + + /// Wakes the `WaitForMultipleObjects`. + pub(crate) fn wake(&self) -> Result<()> { + self.inner.lock().unwrap().release()?; + Ok(()) + } + + /// Replaces the current semaphore with a new one allowing us to reuse the same `Waker`. + pub(crate) fn reset(&self) -> Result<()> { + *self.inner.lock().unwrap() = Semaphore::new()?; + Ok(()) + } + + /// Returns the semaphore associated with the waker. + pub(crate) fn semaphore(&self) -> Semaphore { + self.inner.lock().unwrap().clone() + } +} diff --git a/vendor/crossterm/src/event/timeout.rs b/vendor/crossterm/src/event/timeout.rs index f266d28..5e41132 100644 --- a/vendor/crossterm/src/event/timeout.rs +++ b/vendor/crossterm/src/event/timeout.rs @@ -1,92 +1,92 @@ -use std::time::{Duration, Instant}; - -/// Keeps track of the elapsed time since the moment the polling started. -#[derive(Debug, Clone)] -pub struct PollTimeout { - timeout: Option, - start: Instant, -} - -impl PollTimeout { - /// Constructs a new `PollTimeout` with the given optional `Duration`. - pub fn new(timeout: Option) -> PollTimeout { - PollTimeout { - timeout, - start: Instant::now(), - } - } - - /// Returns whether the timeout has elapsed. - /// - /// It always returns `false` if the initial timeout was set to `None`. - pub fn elapsed(&self) -> bool { - self.timeout - .map(|timeout| self.start.elapsed() >= timeout) - .unwrap_or(false) - } - - /// Returns the timeout leftover (initial timeout duration - elapsed duration). - pub fn leftover(&self) -> Option { - self.timeout.map(|timeout| { - let elapsed = self.start.elapsed(); - - if elapsed >= timeout { - Duration::from_secs(0) - } else { - timeout - elapsed - } - }) - } -} - -#[cfg(test)] -mod tests { - use std::time::{Duration, Instant}; - - use super::PollTimeout; - - #[test] - pub fn test_timeout_without_duration_does_not_have_leftover() { - let timeout = PollTimeout::new(None); - assert_eq!(timeout.leftover(), None) - } - - #[test] - pub fn test_timeout_without_duration_never_elapses() { - let timeout = PollTimeout::new(None); - assert!(!timeout.elapsed()); - } - - #[test] - pub fn test_timeout_elapses() { - const TIMEOUT_MILLIS: u64 = 100; - - let timeout = PollTimeout { - timeout: Some(Duration::from_millis(TIMEOUT_MILLIS)), - start: Instant::now() - Duration::from_millis(2 * TIMEOUT_MILLIS), - }; - - assert!(timeout.elapsed()); - } - - #[test] - pub fn test_elapsed_timeout_has_zero_leftover() { - const TIMEOUT_MILLIS: u64 = 100; - - let timeout = PollTimeout { - timeout: Some(Duration::from_millis(TIMEOUT_MILLIS)), - start: Instant::now() - Duration::from_millis(2 * TIMEOUT_MILLIS), - }; - - assert!(timeout.elapsed()); - assert_eq!(timeout.leftover(), Some(Duration::from_millis(0))); - } - - #[test] - pub fn test_not_elapsed_timeout_has_positive_leftover() { - let timeout = PollTimeout::new(Some(Duration::from_secs(60))); - - assert!(!timeout.elapsed()); - assert!(timeout.leftover().unwrap() > Duration::from_secs(0)); - } -} +use std::time::{Duration, Instant}; + +/// Keeps track of the elapsed time since the moment the polling started. +#[derive(Debug, Clone)] +pub struct PollTimeout { + timeout: Option, + start: Instant, +} + +impl PollTimeout { + /// Constructs a new `PollTimeout` with the given optional `Duration`. + pub fn new(timeout: Option) -> PollTimeout { + PollTimeout { + timeout, + start: Instant::now(), + } + } + + /// Returns whether the timeout has elapsed. + /// + /// It always returns `false` if the initial timeout was set to `None`. + pub fn elapsed(&self) -> bool { + self.timeout + .map(|timeout| self.start.elapsed() >= timeout) + .unwrap_or(false) + } + + /// Returns the timeout leftover (initial timeout duration - elapsed duration). + pub fn leftover(&self) -> Option { + self.timeout.map(|timeout| { + let elapsed = self.start.elapsed(); + + if elapsed >= timeout { + Duration::from_secs(0) + } else { + timeout - elapsed + } + }) + } +} + +#[cfg(test)] +mod tests { + use std::time::{Duration, Instant}; + + use super::PollTimeout; + + #[test] + pub fn test_timeout_without_duration_does_not_have_leftover() { + let timeout = PollTimeout::new(None); + assert_eq!(timeout.leftover(), None) + } + + #[test] + pub fn test_timeout_without_duration_never_elapses() { + let timeout = PollTimeout::new(None); + assert!(!timeout.elapsed()); + } + + #[test] + pub fn test_timeout_elapses() { + const TIMEOUT_MILLIS: u64 = 100; + + let timeout = PollTimeout { + timeout: Some(Duration::from_millis(TIMEOUT_MILLIS)), + start: Instant::now() - Duration::from_millis(2 * TIMEOUT_MILLIS), + }; + + assert!(timeout.elapsed()); + } + + #[test] + pub fn test_elapsed_timeout_has_zero_leftover() { + const TIMEOUT_MILLIS: u64 = 100; + + let timeout = PollTimeout { + timeout: Some(Duration::from_millis(TIMEOUT_MILLIS)), + start: Instant::now() - Duration::from_millis(2 * TIMEOUT_MILLIS), + }; + + assert!(timeout.elapsed()); + assert_eq!(timeout.leftover(), Some(Duration::from_millis(0))); + } + + #[test] + pub fn test_not_elapsed_timeout_has_positive_leftover() { + let timeout = PollTimeout::new(Some(Duration::from_secs(60))); + + assert!(!timeout.elapsed()); + assert!(timeout.leftover().unwrap() > Duration::from_secs(0)); + } +} diff --git a/vendor/crossterm/src/lib.rs b/vendor/crossterm/src/lib.rs index 2ec7ad0..d5c8981 100644 --- a/vendor/crossterm/src/lib.rs +++ b/vendor/crossterm/src/lib.rs @@ -1,260 +1,260 @@ -#![deny(unused_imports, unused_must_use)] - -//! # Crossterm -//! -//! Have you ever been disappointed when a terminal library for rust was only written for UNIX systems? -//! Crossterm provides clearing, event (input) handling, styling, cursor movement, and terminal actions for both -//! Windows and UNIX systems. -//! -//! Crossterm aims to be simple and easy to call in code. Through the simplicity of Crossterm, you do not -//! have to worry about the platform you are working with. -//! -//! This crate supports all UNIX and Windows terminals down to Windows 7 (not all terminals are tested -//! see [Tested Terminals](https://github.com/crossterm-rs/crossterm#tested-terminals) -//! for more info). -//! -//! ## Command API -//! -//! The command API makes the use of `crossterm` much easier and offers more control over when and how a -//! command is executed. A command is just an action you can perform on the terminal e.g. cursor movement. -//! -//! The command API offers: -//! -//! * Better Performance. -//! * Complete control over when to flush. -//! * Complete control over where the ANSI escape commands are executed to. -//! * Way easier and nicer API. -//! -//! There are two ways to use the API command: -//! -//! * Functions can execute commands on types that implement Write. Functions are easier to use and debug. -//! There is a disadvantage, and that is that there is a boilerplate code involved. -//! * Macros are generally seen as more difficult and aren't always well supported by editors but offer an API with less boilerplate code. If you are -//! not afraid of macros, this is a recommendation. -//! -//! Linux and Windows 10 systems support ANSI escape codes. Those ANSI escape codes are strings or rather a -//! byte sequence. When we `write` and `flush` those to the terminal we can perform some action. -//! For older windows systems a WinAPI call is made. -//! -//! ### Supported Commands -//! -//! - Module [`cursor`](cursor/index.html) -//! - Visibility - [`Show`](cursor/struct.Show.html), [`Hide`](cursor/struct.Hide.html) -//! - Appearance - [`EnableBlinking`](cursor/struct.EnableBlinking.html), -//! [`DisableBlinking`](cursor/struct.DisableBlinking.html) -//! - Position - -//! [`SavePosition`](cursor/struct.SavePosition.html), [`RestorePosition`](cursor/struct.RestorePosition.html), -//! [`MoveUp`](cursor/struct.MoveUp.html), [`MoveDown`](cursor/struct.MoveDown.html), -//! [`MoveLeft`](cursor/struct.MoveLeft.html), [`MoveRight`](cursor/struct.MoveRight.html), -//! [`MoveTo`](cursor/struct.MoveTo.html), [`MoveToColumn`](cursor/struct.MoveToColumn.html),[`MoveToRow`](cursor/struct.MoveToRow.html), -//! [`MoveToNextLine`](cursor/struct.MoveToNextLine.html), [`MoveToPreviousLine`](cursor/struct.MoveToPreviousLine.html), -//! - Shape - -//! [`SetCursorShape`](cursor/struct.SetCursorShape.html) -//! - Module [`event`](event/index.html) -//! - Keyboard events - -//! [`PushKeyboardEnhancementFlags`](event/struct.PushKeyboardEnhancementFlags.html), -//! [`PopKeyboardEnhancementFlags`](event/struct.PopKeyboardEnhancementFlags.html) -//! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html), -//! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html) -//! - Module [`style`](style/index.html) -//! - Colors - [`SetForegroundColor`](style/struct.SetForegroundColor.html), -//! [`SetBackgroundColor`](style/struct.SetBackgroundColor.html), -//! [`ResetColor`](style/struct.ResetColor.html), [`SetColors`](style/struct.SetColors.html) -//! - Attributes - [`SetAttribute`](style/struct.SetAttribute.html), [`SetAttributes`](style/struct.SetAttributes.html), -//! [`PrintStyledContent`](style/struct.PrintStyledContent.html) -//! - Module [`terminal`](terminal/index.html) -//! - Scrolling - [`ScrollUp`](terminal/struct.ScrollUp.html), -//! [`ScrollDown`](terminal/struct.ScrollDown.html) -//! - Miscellaneous - [`Clear`](terminal/struct.Clear.html), -//! [`SetSize`](terminal/struct.SetSize.html) -//! [`SetTitle`](terminal/struct.SetTitle.html) -//! [`DisableLineWrap`](terminal/struct.DisableLineWrap.html) -//! [`EnableLineWrap`](terminal/struct.EnableLineWrap.html) -//! - Alternate screen - [`EnterAlternateScreen`](terminal/struct.EnterAlternateScreen.html), -//! [`LeaveAlternateScreen`](terminal/struct.LeaveAlternateScreen.html) -//! -//! ### Command Execution -//! -//! There are two different ways to execute commands: -//! -//! * [Lazy Execution](#lazy-execution) -//! * [Direct Execution](#direct-execution) -//! -//! #### Lazy Execution -//! -//! Flushing bytes to the terminal buffer is a heavy system call. If we perform a lot of actions with the terminal, -//! we want to do this periodically - like with a TUI editor - so that we can flush more data to the terminal buffer -//! at the same time. -//! -//! Crossterm offers the possibility to do this with `queue`. -//! With `queue` you can queue commands, and when you call [Write::flush][flush] these commands will be executed. -//! -//! You can pass a custom buffer implementing [std::io::Write][write] to this `queue` operation. -//! The commands will be executed on that buffer. -//! The most common buffer is [std::io::stdout][stdout] however, [std::io::stderr][stderr] is used sometimes as well. -//! -//! ##### Examples -//! -//! A simple demonstration that shows the command API in action with cursor commands. -//! -//! Functions: -//! -//! ```no_run -//! use std::io::{Write, stdout}; -//! use crossterm::{QueueableCommand, cursor}; -//! -//! let mut stdout = stdout(); -//! stdout.queue(cursor::MoveTo(5,5)); -//! -//! // some other code ... -//! -//! stdout.flush(); -//! ``` -//! -//! The [queue](./trait.QueueableCommand.html) function returns itself, therefore you can use this to queue another -//! command. Like `stdout.queue(Goto(5,5)).queue(Clear(ClearType::All))`. -//! -//! Macros: -//! -//! ```no_run -//! use std::io::{Write, stdout}; -//! use crossterm::{queue, QueueableCommand, cursor}; -//! -//! let mut stdout = stdout(); -//! queue!(stdout, cursor::MoveTo(5, 5)); -//! -//! // some other code ... -//! -//! // move operation is performed only if we flush the buffer. -//! stdout.flush(); -//! ``` -//! -//! You can pass more than one command into the [queue](./macro.queue.html) macro like -//! `queue!(stdout, MoveTo(5, 5), Clear(ClearType::All))` and -//! they will be executed in the given order from left to right. -//! -//! #### Direct Execution -//! -//! For many applications it is not at all important to be efficient with 'flush' operations. -//! For this use case there is the `execute` operation. -//! This operation executes the command immediately, and calls the `flush` under water. -//! -//! You can pass a custom buffer implementing [std::io::Write][write] to this `execute` operation. -//! The commands will be executed on that buffer. -//! The most common buffer is [std::io::stdout][stdout] however, [std::io::stderr][stderr] is used sometimes as well. -//! -//! ##### Examples -//! -//! Functions: -//! -//! ```no_run -//! use std::io::{Write, stdout}; -//! use crossterm::{ExecutableCommand, cursor}; -//! -//! let mut stdout = stdout(); -//! stdout.execute(cursor::MoveTo(5,5)); -//! ``` -//! The [execute](./trait.ExecutableCommand.html) function returns itself, therefore you can use this to queue -//! another command. Like `stdout.execute(Goto(5,5))?.execute(Clear(ClearType::All))`. -//! -//! Macros: -//! -//! ```no_run -//! use std::io::{Write, stdout}; -//! use crossterm::{execute, ExecutableCommand, cursor}; -//! -//! let mut stdout = stdout(); -//! execute!(stdout, cursor::MoveTo(5, 5)); -//! ``` -//! You can pass more than one command into the [execute](./macro.execute.html) macro like -//! `execute!(stdout, MoveTo(5, 5), Clear(ClearType::All))` and they will be executed in the given order from -//! left to right. -//! -//! ## Examples -//! -//! Print a rectangle colored with magenta and use both direct execution and lazy execution. -//! -//! Functions: -//! -//! ```no_run -//! use std::io::{stdout, Write}; -//! use crossterm::{ -//! ExecutableCommand, QueueableCommand, -//! terminal, cursor, style::{self, Stylize}, Result -//! }; -//! -//! fn main() -> Result<()> { -//! let mut stdout = stdout(); -//! -//! stdout.execute(terminal::Clear(terminal::ClearType::All))?; -//! -//! for y in 0..40 { -//! for x in 0..150 { -//! if (y == 0 || y == 40 - 1) || (x == 0 || x == 150 - 1) { -//! // in this loop we are more efficient by not flushing the buffer. -//! stdout -//! .queue(cursor::MoveTo(x,y))? -//! .queue(style::PrintStyledContent( "█".magenta()))?; -//! } -//! } -//! } -//! stdout.flush()?; -//! Ok(()) -//! } -//! ``` -//! -//! Macros: -//! -//! ```no_run -//! use std::io::{stdout, Write}; -//! use crossterm::{ -//! execute, queue, -//! style::{self, Stylize}, cursor, terminal, Result -//! }; -//! -//! fn main() -> Result<()> { -//! let mut stdout = stdout(); -//! -//! execute!(stdout, terminal::Clear(terminal::ClearType::All))?; -//! -//! for y in 0..40 { -//! for x in 0..150 { -//! if (y == 0 || y == 40 - 1) || (x == 0 || x == 150 - 1) { -//! // in this loop we are more efficient by not flushing the buffer. -//! queue!(stdout, cursor::MoveTo(x,y), style::PrintStyledContent( "█".magenta()))?; -//! } -//! } -//! } -//! stdout.flush()?; -//! Ok(()) -//! } -//!``` -//! -//! [write]: https://doc.rust-lang.org/std/io/trait.Write.html -//! [stdout]: https://doc.rust-lang.org/std/io/fn.stdout.html -//! [stderr]: https://doc.rust-lang.org/std/io/fn.stderr.html -//! [flush]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush - -pub use crate::{ - command::{Command, ExecutableCommand, QueueableCommand}, - error::{ErrorKind, Result}, -}; - -/// A module to work with the terminal cursor -pub mod cursor; -/// A module to read events. -pub mod event; -/// A module to apply attributes and colors on your text. -pub mod style; -/// A module to work with the terminal. -pub mod terminal; - -/// A module to query if the current instance is a tty. -pub mod tty; - -#[cfg(windows)] -/// A module that exposes one function to check if the current terminal supports ANSI sequences. -pub mod ansi_support; -mod command; -mod error; -pub(crate) mod macros; +#![deny(unused_imports, unused_must_use)] + +//! # Crossterm +//! +//! Have you ever been disappointed when a terminal library for rust was only written for UNIX systems? +//! Crossterm provides clearing, event (input) handling, styling, cursor movement, and terminal actions for both +//! Windows and UNIX systems. +//! +//! Crossterm aims to be simple and easy to call in code. Through the simplicity of Crossterm, you do not +//! have to worry about the platform you are working with. +//! +//! This crate supports all UNIX and Windows terminals down to Windows 7 (not all terminals are tested +//! see [Tested Terminals](https://github.com/crossterm-rs/crossterm#tested-terminals) +//! for more info). +//! +//! ## Command API +//! +//! The command API makes the use of `crossterm` much easier and offers more control over when and how a +//! command is executed. A command is just an action you can perform on the terminal e.g. cursor movement. +//! +//! The command API offers: +//! +//! * Better Performance. +//! * Complete control over when to flush. +//! * Complete control over where the ANSI escape commands are executed to. +//! * Way easier and nicer API. +//! +//! There are two ways to use the API command: +//! +//! * Functions can execute commands on types that implement Write. Functions are easier to use and debug. +//! There is a disadvantage, and that is that there is a boilerplate code involved. +//! * Macros are generally seen as more difficult and aren't always well supported by editors but offer an API with less boilerplate code. If you are +//! not afraid of macros, this is a recommendation. +//! +//! Linux and Windows 10 systems support ANSI escape codes. Those ANSI escape codes are strings or rather a +//! byte sequence. When we `write` and `flush` those to the terminal we can perform some action. +//! For older windows systems a WinAPI call is made. +//! +//! ### Supported Commands +//! +//! - Module [`cursor`](cursor/index.html) +//! - Visibility - [`Show`](cursor/struct.Show.html), [`Hide`](cursor/struct.Hide.html) +//! - Appearance - [`EnableBlinking`](cursor/struct.EnableBlinking.html), +//! [`DisableBlinking`](cursor/struct.DisableBlinking.html) +//! - Position - +//! [`SavePosition`](cursor/struct.SavePosition.html), [`RestorePosition`](cursor/struct.RestorePosition.html), +//! [`MoveUp`](cursor/struct.MoveUp.html), [`MoveDown`](cursor/struct.MoveDown.html), +//! [`MoveLeft`](cursor/struct.MoveLeft.html), [`MoveRight`](cursor/struct.MoveRight.html), +//! [`MoveTo`](cursor/struct.MoveTo.html), [`MoveToColumn`](cursor/struct.MoveToColumn.html),[`MoveToRow`](cursor/struct.MoveToRow.html), +//! [`MoveToNextLine`](cursor/struct.MoveToNextLine.html), [`MoveToPreviousLine`](cursor/struct.MoveToPreviousLine.html), +//! - Shape - +//! [`SetCursorShape`](cursor/struct.SetCursorShape.html) +//! - Module [`event`](event/index.html) +//! - Keyboard events - +//! [`PushKeyboardEnhancementFlags`](event/struct.PushKeyboardEnhancementFlags.html), +//! [`PopKeyboardEnhancementFlags`](event/struct.PopKeyboardEnhancementFlags.html) +//! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html), +//! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html) +//! - Module [`style`](style/index.html) +//! - Colors - [`SetForegroundColor`](style/struct.SetForegroundColor.html), +//! [`SetBackgroundColor`](style/struct.SetBackgroundColor.html), +//! [`ResetColor`](style/struct.ResetColor.html), [`SetColors`](style/struct.SetColors.html) +//! - Attributes - [`SetAttribute`](style/struct.SetAttribute.html), [`SetAttributes`](style/struct.SetAttributes.html), +//! [`PrintStyledContent`](style/struct.PrintStyledContent.html) +//! - Module [`terminal`](terminal/index.html) +//! - Scrolling - [`ScrollUp`](terminal/struct.ScrollUp.html), +//! [`ScrollDown`](terminal/struct.ScrollDown.html) +//! - Miscellaneous - [`Clear`](terminal/struct.Clear.html), +//! [`SetSize`](terminal/struct.SetSize.html) +//! [`SetTitle`](terminal/struct.SetTitle.html) +//! [`DisableLineWrap`](terminal/struct.DisableLineWrap.html) +//! [`EnableLineWrap`](terminal/struct.EnableLineWrap.html) +//! - Alternate screen - [`EnterAlternateScreen`](terminal/struct.EnterAlternateScreen.html), +//! [`LeaveAlternateScreen`](terminal/struct.LeaveAlternateScreen.html) +//! +//! ### Command Execution +//! +//! There are two different ways to execute commands: +//! +//! * [Lazy Execution](#lazy-execution) +//! * [Direct Execution](#direct-execution) +//! +//! #### Lazy Execution +//! +//! Flushing bytes to the terminal buffer is a heavy system call. If we perform a lot of actions with the terminal, +//! we want to do this periodically - like with a TUI editor - so that we can flush more data to the terminal buffer +//! at the same time. +//! +//! Crossterm offers the possibility to do this with `queue`. +//! With `queue` you can queue commands, and when you call [Write::flush][flush] these commands will be executed. +//! +//! You can pass a custom buffer implementing [std::io::Write][write] to this `queue` operation. +//! The commands will be executed on that buffer. +//! The most common buffer is [std::io::stdout][stdout] however, [std::io::stderr][stderr] is used sometimes as well. +//! +//! ##### Examples +//! +//! A simple demonstration that shows the command API in action with cursor commands. +//! +//! Functions: +//! +//! ```no_run +//! use std::io::{Write, stdout}; +//! use crossterm::{QueueableCommand, cursor}; +//! +//! let mut stdout = stdout(); +//! stdout.queue(cursor::MoveTo(5,5)); +//! +//! // some other code ... +//! +//! stdout.flush(); +//! ``` +//! +//! The [queue](./trait.QueueableCommand.html) function returns itself, therefore you can use this to queue another +//! command. Like `stdout.queue(Goto(5,5)).queue(Clear(ClearType::All))`. +//! +//! Macros: +//! +//! ```no_run +//! use std::io::{Write, stdout}; +//! use crossterm::{queue, QueueableCommand, cursor}; +//! +//! let mut stdout = stdout(); +//! queue!(stdout, cursor::MoveTo(5, 5)); +//! +//! // some other code ... +//! +//! // move operation is performed only if we flush the buffer. +//! stdout.flush(); +//! ``` +//! +//! You can pass more than one command into the [queue](./macro.queue.html) macro like +//! `queue!(stdout, MoveTo(5, 5), Clear(ClearType::All))` and +//! they will be executed in the given order from left to right. +//! +//! #### Direct Execution +//! +//! For many applications it is not at all important to be efficient with 'flush' operations. +//! For this use case there is the `execute` operation. +//! This operation executes the command immediately, and calls the `flush` under water. +//! +//! You can pass a custom buffer implementing [std::io::Write][write] to this `execute` operation. +//! The commands will be executed on that buffer. +//! The most common buffer is [std::io::stdout][stdout] however, [std::io::stderr][stderr] is used sometimes as well. +//! +//! ##### Examples +//! +//! Functions: +//! +//! ```no_run +//! use std::io::{Write, stdout}; +//! use crossterm::{ExecutableCommand, cursor}; +//! +//! let mut stdout = stdout(); +//! stdout.execute(cursor::MoveTo(5,5)); +//! ``` +//! The [execute](./trait.ExecutableCommand.html) function returns itself, therefore you can use this to queue +//! another command. Like `stdout.execute(Goto(5,5))?.execute(Clear(ClearType::All))`. +//! +//! Macros: +//! +//! ```no_run +//! use std::io::{Write, stdout}; +//! use crossterm::{execute, ExecutableCommand, cursor}; +//! +//! let mut stdout = stdout(); +//! execute!(stdout, cursor::MoveTo(5, 5)); +//! ``` +//! You can pass more than one command into the [execute](./macro.execute.html) macro like +//! `execute!(stdout, MoveTo(5, 5), Clear(ClearType::All))` and they will be executed in the given order from +//! left to right. +//! +//! ## Examples +//! +//! Print a rectangle colored with magenta and use both direct execution and lazy execution. +//! +//! Functions: +//! +//! ```no_run +//! use std::io::{stdout, Write}; +//! use crossterm::{ +//! ExecutableCommand, QueueableCommand, +//! terminal, cursor, style::{self, Stylize}, Result +//! }; +//! +//! fn main() -> Result<()> { +//! let mut stdout = stdout(); +//! +//! stdout.execute(terminal::Clear(terminal::ClearType::All))?; +//! +//! for y in 0..40 { +//! for x in 0..150 { +//! if (y == 0 || y == 40 - 1) || (x == 0 || x == 150 - 1) { +//! // in this loop we are more efficient by not flushing the buffer. +//! stdout +//! .queue(cursor::MoveTo(x,y))? +//! .queue(style::PrintStyledContent( "█".magenta()))?; +//! } +//! } +//! } +//! stdout.flush()?; +//! Ok(()) +//! } +//! ``` +//! +//! Macros: +//! +//! ```no_run +//! use std::io::{stdout, Write}; +//! use crossterm::{ +//! execute, queue, +//! style::{self, Stylize}, cursor, terminal, Result +//! }; +//! +//! fn main() -> Result<()> { +//! let mut stdout = stdout(); +//! +//! execute!(stdout, terminal::Clear(terminal::ClearType::All))?; +//! +//! for y in 0..40 { +//! for x in 0..150 { +//! if (y == 0 || y == 40 - 1) || (x == 0 || x == 150 - 1) { +//! // in this loop we are more efficient by not flushing the buffer. +//! queue!(stdout, cursor::MoveTo(x,y), style::PrintStyledContent( "█".magenta()))?; +//! } +//! } +//! } +//! stdout.flush()?; +//! Ok(()) +//! } +//!``` +//! +//! [write]: https://doc.rust-lang.org/std/io/trait.Write.html +//! [stdout]: https://doc.rust-lang.org/std/io/fn.stdout.html +//! [stderr]: https://doc.rust-lang.org/std/io/fn.stderr.html +//! [flush]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush + +pub use crate::{ + command::{Command, ExecutableCommand, QueueableCommand}, + error::{ErrorKind, Result}, +}; + +/// A module to work with the terminal cursor +pub mod cursor; +/// A module to read events. +pub mod event; +/// A module to apply attributes and colors on your text. +pub mod style; +/// A module to work with the terminal. +pub mod terminal; + +/// A module to query if the current instance is a tty. +pub mod tty; + +#[cfg(windows)] +/// A module that exposes one function to check if the current terminal supports ANSI sequences. +pub mod ansi_support; +mod command; +mod error; +pub(crate) mod macros; diff --git a/vendor/crossterm/src/macros.rs b/vendor/crossterm/src/macros.rs index ac102fe..e52c22e 100644 --- a/vendor/crossterm/src/macros.rs +++ b/vendor/crossterm/src/macros.rs @@ -1,372 +1,372 @@ -/// Append a the first few characters of an ANSI escape code to the given string. -#[macro_export] -#[doc(hidden)] -macro_rules! csi { - ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) }; -} - -/// Queues one or more command(s) for further execution. -/// -/// Queued commands must be flushed to the underlying device to be executed. -/// This generally happens in the following cases: -/// -/// * When `flush` is called manually on the given type implementing `io::Write`. -/// * The terminal will `flush` automatically if the buffer is full. -/// * Each line is flushed in case of `stdout`, because it is line buffered. -/// -/// # Arguments -/// -/// - [std::io::Writer](std::io::Write) -/// -/// ANSI escape codes are written on the given 'writer', after which they are flushed. -/// -/// - [Command](./trait.Command.html) -/// -/// One or more commands -/// -/// # Examples -/// -/// ```rust -/// use std::io::{Write, stdout}; -/// use crossterm::{queue, style::Print}; -/// -/// let mut stdout = stdout(); -/// -/// // `Print` will executed executed when `flush` is called. -/// queue!(stdout, Print("foo".to_string())); -/// -/// // some other code (no execution happening here) ... -/// -/// // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed. -/// stdout.flush(); -/// -/// // ==== Output ==== -/// // foo -/// ``` -/// -/// Have a look over at the [Command API](./index.html#command-api) for more details. -/// -/// # Notes -/// -/// In case of Windows versions lower than 10, a direct WinAPI call will be made. -/// The reason for this is that Windows versions lower than 10 do not support ANSI codes, -/// and can therefore not be written to the given `writer`. -/// Therefore, there is no difference between [execute](macro.execute.html) -/// and [queue](macro.queue.html) for those old Windows versions. -/// -#[macro_export] -macro_rules! queue { - ($writer:expr $(, $command:expr)* $(,)?) => {{ - use ::std::io::Write; - - // This allows the macro to take both mut impl Write and &mut impl Write. - Ok($writer.by_ref()) - $(.and_then(|writer| $crate::QueueableCommand::queue(writer, $command)))* - .map(|_| ()) - }} -} - -/// Executes one or more command(s). -/// -/// # Arguments -/// -/// - [std::io::Writer](std::io::Write) -/// -/// ANSI escape codes are written on the given 'writer', after which they are flushed. -/// -/// - [Command](./trait.Command.html) -/// -/// One or more commands -/// -/// # Examples -/// -/// ```rust -/// use std::io::{Write, stdout}; -/// use crossterm::{execute, style::Print}; -/// -/// // will be executed directly -/// execute!(stdout(), Print("sum:\n".to_string())); -/// -/// // will be executed directly -/// execute!(stdout(), Print("1 + 1= ".to_string()), Print((1+1).to_string())); -/// -/// // ==== Output ==== -/// // sum: -/// // 1 + 1 = 2 -/// ``` -/// -/// Have a look over at the [Command API](./index.html#command-api) for more details. -/// -/// # Notes -/// -/// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'. -/// * In case of Windows versions lower than 10, a direct WinAPI call will be made. -/// The reason for this is that Windows versions lower than 10 do not support ANSI codes, -/// and can therefore not be written to the given `writer`. -/// Therefore, there is no difference between [execute](macro.execute.html) -/// and [queue](macro.queue.html) for those old Windows versions. -#[macro_export] -macro_rules! execute { - ($writer:expr $(, $command:expr)* $(,)? ) => {{ - use ::std::io::Write; - - // Queue each command, then flush - $crate::queue!($writer $(, $command)*) - .and_then(|()| { - ::std::io::Write::flush($writer.by_ref()) - }) - }} -} - -#[doc(hidden)] -#[macro_export] -macro_rules! impl_display { - (for $($t:ty),+) => { - $(impl ::std::fmt::Display for $t { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - $crate::command::execute_fmt(f, self) - } - })* - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! impl_from { - ($from:path, $to:expr) => { - impl From<$from> for ErrorKind { - fn from(e: $from) -> Self { - $to(e) - } - } - }; -} - -#[cfg(test)] -mod tests { - use std::io; - use std::str; - - // Helper for execute tests to confirm flush - #[derive(Default, Debug, Clone)] - pub(self) struct FakeWrite { - buffer: String, - flushed: bool, - } - - impl io::Write for FakeWrite { - fn write(&mut self, content: &[u8]) -> io::Result { - let content = str::from_utf8(content) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - self.buffer.push_str(content); - self.flushed = false; - Ok(content.len()) - } - - fn flush(&mut self) -> io::Result<()> { - self.flushed = true; - Ok(()) - } - } - - #[cfg(not(windows))] - mod unix { - use std::fmt; - - use super::FakeWrite; - use crate::command::Command; - - pub struct FakeCommand; - - impl Command for FakeCommand { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str("cmd") - } - } - - #[test] - fn test_queue_one() { - let mut result = FakeWrite::default(); - queue!(&mut result, FakeCommand).unwrap(); - assert_eq!(&result.buffer, "cmd"); - assert!(!result.flushed); - } - - #[test] - fn test_queue_many() { - let mut result = FakeWrite::default(); - queue!(&mut result, FakeCommand, FakeCommand).unwrap(); - assert_eq!(&result.buffer, "cmdcmd"); - assert!(!result.flushed); - } - - #[test] - fn test_queue_trailing_comma() { - let mut result = FakeWrite::default(); - queue!(&mut result, FakeCommand, FakeCommand,).unwrap(); - assert_eq!(&result.buffer, "cmdcmd"); - assert!(!result.flushed); - } - - #[test] - fn test_execute_one() { - let mut result = FakeWrite::default(); - execute!(&mut result, FakeCommand).unwrap(); - assert_eq!(&result.buffer, "cmd"); - assert!(result.flushed); - } - - #[test] - fn test_execute_many() { - let mut result = FakeWrite::default(); - execute!(&mut result, FakeCommand, FakeCommand).unwrap(); - assert_eq!(&result.buffer, "cmdcmd"); - assert!(result.flushed); - } - - #[test] - fn test_execute_trailing_comma() { - let mut result = FakeWrite::default(); - execute!(&mut result, FakeCommand, FakeCommand,).unwrap(); - assert_eq!(&result.buffer, "cmdcmd"); - assert!(result.flushed); - } - } - - #[cfg(windows)] - mod windows { - use std::fmt; - - use std::cell::RefCell; - use std::fmt::Debug; - - use super::FakeWrite; - use crate::command::Command; - use crate::error::Result as CrosstermResult; - - // We need to test two different APIs: WinAPI and the write api. We - // don't know until runtime which we're supporting (via - // Command::is_ansi_code_supported), so we have to test them both. The - // CI environment hopefully includes both versions of windows. - - // WindowsEventStream is a place for execute_winapi to push strings, - // when called. - type WindowsEventStream = Vec<&'static str>; - - struct FakeCommand<'a> { - // Need to use a refcell because we want execute_winapi to be able - // push to the vector, but execute_winapi take &self. - stream: RefCell<&'a mut WindowsEventStream>, - value: &'static str, - } - - impl<'a> FakeCommand<'a> { - fn new(stream: &'a mut WindowsEventStream, value: &'static str) -> Self { - Self { - value, - stream: RefCell::new(stream), - } - } - } - - impl<'a> Command for FakeCommand<'a> { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(self.value) - } - - fn execute_winapi(&self) -> CrosstermResult<()> { - self.stream.borrow_mut().push(self.value); - Ok(()) - } - } - - // Helper function for running tests against either WinAPI or an - // io::Write. - // - // This function will execute the `test` function, which should - // queue some commands against the given FakeWrite and - // WindowsEventStream. It will then test that the correct data sink - // was populated. It does not currently check is_ansi_code_supported; - // for now it simply checks that one of the two streams was correctly - // populated. - // - // If the stream was populated, it tests that the two arrays are equal. - // If the writer was populated, it tests that the contents of the - // write buffer are equal to the concatenation of `stream_result`. - fn test_harness( - stream_result: &[&'static str], - test: impl FnOnce(&mut FakeWrite, &mut WindowsEventStream) -> Result<(), E>, - ) { - let mut stream = WindowsEventStream::default(); - let mut writer = FakeWrite::default(); - - if let Err(err) = test(&mut writer, &mut stream) { - panic!("Error returned from test function: {:?}", err); - } - - // We need this for type inference, for whatever reason. - const EMPTY_RESULT: [&str; 0] = []; - - // TODO: confirm that the correct sink was used, based on - // is_ansi_code_supported - match (writer.buffer.is_empty(), stream.is_empty()) { - (true, true) if stream_result == EMPTY_RESULT => {} - (true, true) => panic!( - "Neither the event stream nor the writer were populated. Expected {:?}", - stream_result - ), - - // writer is populated - (false, true) => { - // Concat the stream result to find the string result - let result: String = stream_result.iter().copied().collect(); - assert_eq!(result, writer.buffer); - assert_eq!(&stream, &EMPTY_RESULT); - } - - // stream is populated - (true, false) => { - assert_eq!(stream, stream_result); - assert_eq!(writer.buffer, ""); - } - - // Both are populated - (false, false) => panic!( - "Both the writer and the event stream were written to.\n\ - Only one should be used, based on is_ansi_code_supported.\n\ - stream: {stream:?}\n\ - writer: {writer:?}", - stream = stream, - writer = writer, - ), - } - } - - #[test] - fn test_queue_one() { - test_harness(&["cmd1"], |writer, stream| { - queue!(writer, FakeCommand::new(stream, "cmd1")) - }) - } - - #[test] - fn test_queue_some() { - test_harness(&["cmd1", "cmd2"], |writer, stream| { - queue!( - writer, - FakeCommand::new(stream, "cmd1"), - FakeCommand::new(stream, "cmd2"), - ) - }) - } - - #[test] - fn test_many_queues() { - test_harness(&["cmd1", "cmd2", "cmd3"], |writer, stream| { - queue!(writer, FakeCommand::new(stream, "cmd1"))?; - queue!(writer, FakeCommand::new(stream, "cmd2"))?; - queue!(writer, FakeCommand::new(stream, "cmd3")) - }) - } - } -} +/// Append a the first few characters of an ANSI escape code to the given string. +#[macro_export] +#[doc(hidden)] +macro_rules! csi { + ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) }; +} + +/// Queues one or more command(s) for further execution. +/// +/// Queued commands must be flushed to the underlying device to be executed. +/// This generally happens in the following cases: +/// +/// * When `flush` is called manually on the given type implementing `io::Write`. +/// * The terminal will `flush` automatically if the buffer is full. +/// * Each line is flushed in case of `stdout`, because it is line buffered. +/// +/// # Arguments +/// +/// - [std::io::Writer](std::io::Write) +/// +/// ANSI escape codes are written on the given 'writer', after which they are flushed. +/// +/// - [Command](./trait.Command.html) +/// +/// One or more commands +/// +/// # Examples +/// +/// ```rust +/// use std::io::{Write, stdout}; +/// use crossterm::{queue, style::Print}; +/// +/// let mut stdout = stdout(); +/// +/// // `Print` will executed executed when `flush` is called. +/// queue!(stdout, Print("foo".to_string())); +/// +/// // some other code (no execution happening here) ... +/// +/// // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed. +/// stdout.flush(); +/// +/// // ==== Output ==== +/// // foo +/// ``` +/// +/// Have a look over at the [Command API](./index.html#command-api) for more details. +/// +/// # Notes +/// +/// In case of Windows versions lower than 10, a direct WinAPI call will be made. +/// The reason for this is that Windows versions lower than 10 do not support ANSI codes, +/// and can therefore not be written to the given `writer`. +/// Therefore, there is no difference between [execute](macro.execute.html) +/// and [queue](macro.queue.html) for those old Windows versions. +/// +#[macro_export] +macro_rules! queue { + ($writer:expr $(, $command:expr)* $(,)?) => {{ + use ::std::io::Write; + + // This allows the macro to take both mut impl Write and &mut impl Write. + Ok($writer.by_ref()) + $(.and_then(|writer| $crate::QueueableCommand::queue(writer, $command)))* + .map(|_| ()) + }} +} + +/// Executes one or more command(s). +/// +/// # Arguments +/// +/// - [std::io::Writer](std::io::Write) +/// +/// ANSI escape codes are written on the given 'writer', after which they are flushed. +/// +/// - [Command](./trait.Command.html) +/// +/// One or more commands +/// +/// # Examples +/// +/// ```rust +/// use std::io::{Write, stdout}; +/// use crossterm::{execute, style::Print}; +/// +/// // will be executed directly +/// execute!(stdout(), Print("sum:\n".to_string())); +/// +/// // will be executed directly +/// execute!(stdout(), Print("1 + 1= ".to_string()), Print((1+1).to_string())); +/// +/// // ==== Output ==== +/// // sum: +/// // 1 + 1 = 2 +/// ``` +/// +/// Have a look over at the [Command API](./index.html#command-api) for more details. +/// +/// # Notes +/// +/// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'. +/// * In case of Windows versions lower than 10, a direct WinAPI call will be made. +/// The reason for this is that Windows versions lower than 10 do not support ANSI codes, +/// and can therefore not be written to the given `writer`. +/// Therefore, there is no difference between [execute](macro.execute.html) +/// and [queue](macro.queue.html) for those old Windows versions. +#[macro_export] +macro_rules! execute { + ($writer:expr $(, $command:expr)* $(,)? ) => {{ + use ::std::io::Write; + + // Queue each command, then flush + $crate::queue!($writer $(, $command)*) + .and_then(|()| { + ::std::io::Write::flush($writer.by_ref()) + }) + }} +} + +#[doc(hidden)] +#[macro_export] +macro_rules! impl_display { + (for $($t:ty),+) => { + $(impl ::std::fmt::Display for $t { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + $crate::command::execute_fmt(f, self) + } + })* + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! impl_from { + ($from:path, $to:expr) => { + impl From<$from> for ErrorKind { + fn from(e: $from) -> Self { + $to(e) + } + } + }; +} + +#[cfg(test)] +mod tests { + use std::io; + use std::str; + + // Helper for execute tests to confirm flush + #[derive(Default, Debug, Clone)] + pub(self) struct FakeWrite { + buffer: String, + flushed: bool, + } + + impl io::Write for FakeWrite { + fn write(&mut self, content: &[u8]) -> io::Result { + let content = str::from_utf8(content) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + self.buffer.push_str(content); + self.flushed = false; + Ok(content.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.flushed = true; + Ok(()) + } + } + + #[cfg(not(windows))] + mod unix { + use std::fmt; + + use super::FakeWrite; + use crate::command::Command; + + pub struct FakeCommand; + + impl Command for FakeCommand { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str("cmd") + } + } + + #[test] + fn test_queue_one() { + let mut result = FakeWrite::default(); + queue!(&mut result, FakeCommand).unwrap(); + assert_eq!(&result.buffer, "cmd"); + assert!(!result.flushed); + } + + #[test] + fn test_queue_many() { + let mut result = FakeWrite::default(); + queue!(&mut result, FakeCommand, FakeCommand).unwrap(); + assert_eq!(&result.buffer, "cmdcmd"); + assert!(!result.flushed); + } + + #[test] + fn test_queue_trailing_comma() { + let mut result = FakeWrite::default(); + queue!(&mut result, FakeCommand, FakeCommand,).unwrap(); + assert_eq!(&result.buffer, "cmdcmd"); + assert!(!result.flushed); + } + + #[test] + fn test_execute_one() { + let mut result = FakeWrite::default(); + execute!(&mut result, FakeCommand).unwrap(); + assert_eq!(&result.buffer, "cmd"); + assert!(result.flushed); + } + + #[test] + fn test_execute_many() { + let mut result = FakeWrite::default(); + execute!(&mut result, FakeCommand, FakeCommand).unwrap(); + assert_eq!(&result.buffer, "cmdcmd"); + assert!(result.flushed); + } + + #[test] + fn test_execute_trailing_comma() { + let mut result = FakeWrite::default(); + execute!(&mut result, FakeCommand, FakeCommand,).unwrap(); + assert_eq!(&result.buffer, "cmdcmd"); + assert!(result.flushed); + } + } + + #[cfg(windows)] + mod windows { + use std::fmt; + + use std::cell::RefCell; + use std::fmt::Debug; + + use super::FakeWrite; + use crate::command::Command; + use crate::error::Result as CrosstermResult; + + // We need to test two different APIs: WinAPI and the write api. We + // don't know until runtime which we're supporting (via + // Command::is_ansi_code_supported), so we have to test them both. The + // CI environment hopefully includes both versions of windows. + + // WindowsEventStream is a place for execute_winapi to push strings, + // when called. + type WindowsEventStream = Vec<&'static str>; + + struct FakeCommand<'a> { + // Need to use a refcell because we want execute_winapi to be able + // push to the vector, but execute_winapi take &self. + stream: RefCell<&'a mut WindowsEventStream>, + value: &'static str, + } + + impl<'a> FakeCommand<'a> { + fn new(stream: &'a mut WindowsEventStream, value: &'static str) -> Self { + Self { + value, + stream: RefCell::new(stream), + } + } + } + + impl<'a> Command for FakeCommand<'a> { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(self.value) + } + + fn execute_winapi(&self) -> CrosstermResult<()> { + self.stream.borrow_mut().push(self.value); + Ok(()) + } + } + + // Helper function for running tests against either WinAPI or an + // io::Write. + // + // This function will execute the `test` function, which should + // queue some commands against the given FakeWrite and + // WindowsEventStream. It will then test that the correct data sink + // was populated. It does not currently check is_ansi_code_supported; + // for now it simply checks that one of the two streams was correctly + // populated. + // + // If the stream was populated, it tests that the two arrays are equal. + // If the writer was populated, it tests that the contents of the + // write buffer are equal to the concatenation of `stream_result`. + fn test_harness( + stream_result: &[&'static str], + test: impl FnOnce(&mut FakeWrite, &mut WindowsEventStream) -> Result<(), E>, + ) { + let mut stream = WindowsEventStream::default(); + let mut writer = FakeWrite::default(); + + if let Err(err) = test(&mut writer, &mut stream) { + panic!("Error returned from test function: {:?}", err); + } + + // We need this for type inference, for whatever reason. + const EMPTY_RESULT: [&str; 0] = []; + + // TODO: confirm that the correct sink was used, based on + // is_ansi_code_supported + match (writer.buffer.is_empty(), stream.is_empty()) { + (true, true) if stream_result == EMPTY_RESULT => {} + (true, true) => panic!( + "Neither the event stream nor the writer were populated. Expected {:?}", + stream_result + ), + + // writer is populated + (false, true) => { + // Concat the stream result to find the string result + let result: String = stream_result.iter().copied().collect(); + assert_eq!(result, writer.buffer); + assert_eq!(&stream, &EMPTY_RESULT); + } + + // stream is populated + (true, false) => { + assert_eq!(stream, stream_result); + assert_eq!(writer.buffer, ""); + } + + // Both are populated + (false, false) => panic!( + "Both the writer and the event stream were written to.\n\ + Only one should be used, based on is_ansi_code_supported.\n\ + stream: {stream:?}\n\ + writer: {writer:?}", + stream = stream, + writer = writer, + ), + } + } + + #[test] + fn test_queue_one() { + test_harness(&["cmd1"], |writer, stream| { + queue!(writer, FakeCommand::new(stream, "cmd1")) + }) + } + + #[test] + fn test_queue_some() { + test_harness(&["cmd1", "cmd2"], |writer, stream| { + queue!( + writer, + FakeCommand::new(stream, "cmd1"), + FakeCommand::new(stream, "cmd2"), + ) + }) + } + + #[test] + fn test_many_queues() { + test_harness(&["cmd1", "cmd2", "cmd3"], |writer, stream| { + queue!(writer, FakeCommand::new(stream, "cmd1"))?; + queue!(writer, FakeCommand::new(stream, "cmd2"))?; + queue!(writer, FakeCommand::new(stream, "cmd3")) + }) + } + } +} diff --git a/vendor/crossterm/src/style.rs b/vendor/crossterm/src/style.rs index c886524..74696be 100644 --- a/vendor/crossterm/src/style.rs +++ b/vendor/crossterm/src/style.rs @@ -1,501 +1,501 @@ -//! # Style -//! -//! The `style` module provides a functionality to apply attributes and colors on your text. -//! -//! This documentation does not contain a lot of examples. The reason is that it's fairly -//! obvious how to use this crate. Although, we do provide -//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository -//! to demonstrate the capabilities. -//! -//! ## Platform-specific Notes -//! -//! Not all features are supported on all terminals/platforms. You should always consult -//! platform-specific notes of the following types: -//! -//! * [Color](enum.Color.html#platform-specific-notes) -//! * [Attribute](enum.Attribute.html#platform-specific-notes) -//! -//! ## Examples -//! -//! A few examples of how to use the style module. -//! -//! ### Colors -//! -//! How to change the terminal text color. -//! -//! Command API: -//! -//! Using the Command API to color text. -//! -//! ```no_run -//! use std::io::{stdout, Write}; -//! -//! use crossterm::{execute, Result}; -//! use crossterm::style::{Print, SetForegroundColor, SetBackgroundColor, ResetColor, Color, Attribute}; -//! -//! fn main() -> Result<()> { -//! execute!( -//! stdout(), -//! // Blue foreground -//! SetForegroundColor(Color::Blue), -//! // Red background -//! SetBackgroundColor(Color::Red), -//! // Print text -//! Print("Blue text on Red.".to_string()), -//! // Reset to default colors -//! ResetColor -//! ) -//! } -//! ``` -//! -//! Functions: -//! -//! Using functions from [`Stylize`](crate::style::Stylize) on a `String` or `&'static str` to color -//! it. -//! -//! ```no_run -//! use crossterm::style::Stylize; -//! -//! println!("{}", "Red foreground color & blue background.".red().on_blue()); -//! ``` -//! -//! ### Attributes -//! -//! How to apply terminal attributes to text. -//! -//! Command API: -//! -//! Using the Command API to set attributes. -//! -//! ```no_run -//! use std::io::{stdout, Write}; -//! -//! use crossterm::{execute, Result, style::Print}; -//! use crossterm::style::{SetAttribute, Attribute}; -//! -//! fn main() -> Result<()> { -//! execute!( -//! stdout(), -//! // Set to bold -//! SetAttribute(Attribute::Bold), -//! Print("Bold text here.".to_string()), -//! // Reset all attributes -//! SetAttribute(Attribute::Reset) -//! ) -//! } -//! ``` -//! -//! Functions: -//! -//! Using [`Stylize`](crate::style::Stylize) functions on a `String` or `&'static str` to set -//! attributes to it. -//! -//! ```no_run -//! use crossterm::style::Stylize; -//! -//! println!("{}", "Bold".bold()); -//! println!("{}", "Underlined".underlined()); -//! println!("{}", "Negative".negative()); -//! ``` -//! -//! Displayable: -//! -//! [`Attribute`](enum.Attribute.html) implements [Display](https://doc.rust-lang.org/beta/std/fmt/trait.Display.html) and therefore it can be formatted like: -//! -//! ```no_run -//! use crossterm::style::Attribute; -//! -//! println!( -//! "{} Underlined {} No Underline", -//! Attribute::Underlined, -//! Attribute::NoUnderline -//! ); -//! ``` - -use std::{ - env, - fmt::{self, Display}, -}; - -use crate::command::execute_fmt; -#[cfg(windows)] -use crate::Result; -use crate::{csi, impl_display, Command}; - -pub use self::{ - attributes::Attributes, - content_style::ContentStyle, - styled_content::StyledContent, - stylize::Stylize, - types::{Attribute, Color, Colored, Colors}, -}; - -mod attributes; -mod content_style; -mod styled_content; -mod stylize; -mod sys; -mod types; - -/// Creates a `StyledContent`. -/// -/// This could be used to style any type that implements `Display` with colors and text attributes. -/// -/// See [`StyledContent`](struct.StyledContent.html) for more info. -/// -/// # Examples -/// -/// ```no_run -/// use crossterm::style::{style, Stylize, Color}; -/// -/// let styled_content = style("Blue colored text on yellow background") -/// .with(Color::Blue) -/// .on(Color::Yellow); -/// -/// println!("{}", styled_content); -/// ``` -pub fn style(val: D) -> StyledContent { - ContentStyle::new().apply(val) -} - -/// Returns available color count. -/// -/// # Notes -/// -/// This does not always provide a good result. -pub fn available_color_count() -> u16 { - env::var("TERM") - .map(|x| if x.contains("256color") { 256 } else { 8 }) - .unwrap_or(8) -} - -/// A command that sets the the foreground color. -/// -/// See [`Color`](enum.Color.html) for more info. -/// -/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background -/// color in one command. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetForegroundColor(pub Color); - -impl Command for SetForegroundColor { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}m"), Colored::ForegroundColor(self.0)) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::windows::set_foreground_color(self.0) - } -} - -/// A command that sets the the background color. -/// -/// See [`Color`](enum.Color.html) for more info. -/// -/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background -/// color with one command. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetBackgroundColor(pub Color); - -impl Command for SetBackgroundColor { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}m"), Colored::BackgroundColor(self.0)) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::windows::set_background_color(self.0) - } -} - -/// A command that sets the the underline color. -/// -/// See [`Color`](enum.Color.html) for more info. -/// -/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background -/// color with one command. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetUnderlineColor(pub Color); - -impl Command for SetUnderlineColor { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}m"), Colored::UnderlineColor(self.0)) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - "SetUnderlineColor not supported by winapi.", - )) - } -} - -/// A command that optionally sets the foreground and/or background color. -/// -/// For example: -/// ```no_run -/// use std::io::{stdout, Write}; -/// -/// use crossterm::execute; -/// use crossterm::style::{Color::{Green, Black}, Colors, Print, SetColors}; -/// -/// execute!( -/// stdout(), -/// SetColors(Colors::new(Green, Black)), -/// Print("Hello, world!".to_string()), -/// ).unwrap(); -/// ``` -/// -/// See [`Colors`](struct.Colors.html) for more info. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetColors(pub Colors); - -impl Command for SetColors { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - if let Some(color) = self.0.foreground { - SetForegroundColor(color).write_ansi(f)?; - } - if let Some(color) = self.0.background { - SetBackgroundColor(color).write_ansi(f)?; - } - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - if let Some(color) = self.0.foreground { - sys::windows::set_foreground_color(color)?; - } - if let Some(color) = self.0.background { - sys::windows::set_background_color(color)?; - } - Ok(()) - } -} - -/// A command that sets an attribute. -/// -/// See [`Attribute`](enum.Attribute.html) for more info. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetAttribute(pub Attribute); - -impl Command for SetAttribute { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("{}m"), self.0.sgr()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // attributes are not supported by WinAPI. - Ok(()) - } -} - -/// A command that sets several attributes. -/// -/// See [`Attributes`](struct.Attributes.html) for more info. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetAttributes(pub Attributes); - -impl Command for SetAttributes { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - for attr in Attribute::iterator() { - if self.0.has(attr) { - SetAttribute(attr).write_ansi(f)?; - } - } - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // attributes are not supported by WinAPI. - Ok(()) - } -} - -/// A command that sets a style (colors and attributes). -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetStyle(pub ContentStyle); - -impl Command for SetStyle { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - if let Some(bg) = self.0.background_color { - execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; - } - if let Some(fg) = self.0.foreground_color { - execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?; - } - if let Some(ul) = self.0.underline_color { - execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?; - } - if !self.0.attributes.is_empty() { - execute_fmt(f, SetAttributes(self.0.attributes)).map_err(|_| fmt::Error)?; - } - - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - panic!("tried to execute SetStyle command using WinAPI, use ANSI instead"); - } - - #[cfg(windows)] - fn is_ansi_code_supported(&self) -> bool { - true - } -} - -/// A command that prints styled content. -/// -/// See [`StyledContent`](struct.StyledContent.html) for more info. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Copy, Clone)] -pub struct PrintStyledContent(pub StyledContent); - -impl Command for PrintStyledContent { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - let style = self.0.style(); - - let mut reset_background = false; - let mut reset_foreground = false; - let mut reset = false; - - if let Some(bg) = style.background_color { - execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; - reset_background = true; - } - if let Some(fg) = style.foreground_color { - execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?; - reset_foreground = true; - } - if let Some(ul) = style.underline_color { - execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?; - reset_foreground = true; - } - - if !style.attributes.is_empty() { - execute_fmt(f, SetAttributes(style.attributes)).map_err(|_| fmt::Error)?; - reset = true; - } - - write!(f, "{}", self.0.content())?; - - if reset { - // NOTE: This will reset colors even though self has no colors, hence produce unexpected - // resets. - // TODO: reset the set attributes only. - execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?; - } else { - // NOTE: Since the above bug, we do not need to reset colors when we reset attributes. - if reset_background { - execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?; - } - if reset_foreground { - execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?; - } - } - - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Ok(()) - } -} - -/// A command that resets the colors back to default. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ResetColor; - -impl Command for ResetColor { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("0m")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::windows::reset() - } -} - -/// A command that prints the given displayable type. -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Print(pub T); - -impl Command for Print { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, "{}", self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - panic!("tried to execute Print command using WinAPI, use ANSI instead"); - } - - #[cfg(windows)] - fn is_ansi_code_supported(&self) -> bool { - true - } -} - -impl Display for Print { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl_display!(for SetForegroundColor); -impl_display!(for SetBackgroundColor); -impl_display!(for SetColors); -impl_display!(for SetAttribute); -impl_display!(for PrintStyledContent); -impl_display!(for PrintStyledContent<&'static str>); -impl_display!(for ResetColor); - -/// Utility function for ANSI parsing in Color and Colored. -/// Gets the next element of `iter` and tries to parse it as a `u8`. -fn parse_next_u8<'a>(iter: &mut impl Iterator) -> Option { - iter.next().and_then(|s| s.parse().ok()) -} +//! # Style +//! +//! The `style` module provides a functionality to apply attributes and colors on your text. +//! +//! This documentation does not contain a lot of examples. The reason is that it's fairly +//! obvious how to use this crate. Although, we do provide +//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository +//! to demonstrate the capabilities. +//! +//! ## Platform-specific Notes +//! +//! Not all features are supported on all terminals/platforms. You should always consult +//! platform-specific notes of the following types: +//! +//! * [Color](enum.Color.html#platform-specific-notes) +//! * [Attribute](enum.Attribute.html#platform-specific-notes) +//! +//! ## Examples +//! +//! A few examples of how to use the style module. +//! +//! ### Colors +//! +//! How to change the terminal text color. +//! +//! Command API: +//! +//! Using the Command API to color text. +//! +//! ```no_run +//! use std::io::{stdout, Write}; +//! +//! use crossterm::{execute, Result}; +//! use crossterm::style::{Print, SetForegroundColor, SetBackgroundColor, ResetColor, Color, Attribute}; +//! +//! fn main() -> Result<()> { +//! execute!( +//! stdout(), +//! // Blue foreground +//! SetForegroundColor(Color::Blue), +//! // Red background +//! SetBackgroundColor(Color::Red), +//! // Print text +//! Print("Blue text on Red.".to_string()), +//! // Reset to default colors +//! ResetColor +//! ) +//! } +//! ``` +//! +//! Functions: +//! +//! Using functions from [`Stylize`](crate::style::Stylize) on a `String` or `&'static str` to color +//! it. +//! +//! ```no_run +//! use crossterm::style::Stylize; +//! +//! println!("{}", "Red foreground color & blue background.".red().on_blue()); +//! ``` +//! +//! ### Attributes +//! +//! How to apply terminal attributes to text. +//! +//! Command API: +//! +//! Using the Command API to set attributes. +//! +//! ```no_run +//! use std::io::{stdout, Write}; +//! +//! use crossterm::{execute, Result, style::Print}; +//! use crossterm::style::{SetAttribute, Attribute}; +//! +//! fn main() -> Result<()> { +//! execute!( +//! stdout(), +//! // Set to bold +//! SetAttribute(Attribute::Bold), +//! Print("Bold text here.".to_string()), +//! // Reset all attributes +//! SetAttribute(Attribute::Reset) +//! ) +//! } +//! ``` +//! +//! Functions: +//! +//! Using [`Stylize`](crate::style::Stylize) functions on a `String` or `&'static str` to set +//! attributes to it. +//! +//! ```no_run +//! use crossterm::style::Stylize; +//! +//! println!("{}", "Bold".bold()); +//! println!("{}", "Underlined".underlined()); +//! println!("{}", "Negative".negative()); +//! ``` +//! +//! Displayable: +//! +//! [`Attribute`](enum.Attribute.html) implements [Display](https://doc.rust-lang.org/beta/std/fmt/trait.Display.html) and therefore it can be formatted like: +//! +//! ```no_run +//! use crossterm::style::Attribute; +//! +//! println!( +//! "{} Underlined {} No Underline", +//! Attribute::Underlined, +//! Attribute::NoUnderline +//! ); +//! ``` + +use std::{ + env, + fmt::{self, Display}, +}; + +use crate::command::execute_fmt; +#[cfg(windows)] +use crate::Result; +use crate::{csi, impl_display, Command}; + +pub use self::{ + attributes::Attributes, + content_style::ContentStyle, + styled_content::StyledContent, + stylize::Stylize, + types::{Attribute, Color, Colored, Colors}, +}; + +mod attributes; +mod content_style; +mod styled_content; +mod stylize; +mod sys; +mod types; + +/// Creates a `StyledContent`. +/// +/// This could be used to style any type that implements `Display` with colors and text attributes. +/// +/// See [`StyledContent`](struct.StyledContent.html) for more info. +/// +/// # Examples +/// +/// ```no_run +/// use crossterm::style::{style, Stylize, Color}; +/// +/// let styled_content = style("Blue colored text on yellow background") +/// .with(Color::Blue) +/// .on(Color::Yellow); +/// +/// println!("{}", styled_content); +/// ``` +pub fn style(val: D) -> StyledContent { + ContentStyle::new().apply(val) +} + +/// Returns available color count. +/// +/// # Notes +/// +/// This does not always provide a good result. +pub fn available_color_count() -> u16 { + env::var("TERM") + .map(|x| if x.contains("256color") { 256 } else { 8 }) + .unwrap_or(8) +} + +/// A command that sets the the foreground color. +/// +/// See [`Color`](enum.Color.html) for more info. +/// +/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background +/// color in one command. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetForegroundColor(pub Color); + +impl Command for SetForegroundColor { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}m"), Colored::ForegroundColor(self.0)) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::windows::set_foreground_color(self.0) + } +} + +/// A command that sets the the background color. +/// +/// See [`Color`](enum.Color.html) for more info. +/// +/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background +/// color with one command. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetBackgroundColor(pub Color); + +impl Command for SetBackgroundColor { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}m"), Colored::BackgroundColor(self.0)) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::windows::set_background_color(self.0) + } +} + +/// A command that sets the the underline color. +/// +/// See [`Color`](enum.Color.html) for more info. +/// +/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background +/// color with one command. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetUnderlineColor(pub Color); + +impl Command for SetUnderlineColor { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}m"), Colored::UnderlineColor(self.0)) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "SetUnderlineColor not supported by winapi.", + )) + } +} + +/// A command that optionally sets the foreground and/or background color. +/// +/// For example: +/// ```no_run +/// use std::io::{stdout, Write}; +/// +/// use crossterm::execute; +/// use crossterm::style::{Color::{Green, Black}, Colors, Print, SetColors}; +/// +/// execute!( +/// stdout(), +/// SetColors(Colors::new(Green, Black)), +/// Print("Hello, world!".to_string()), +/// ).unwrap(); +/// ``` +/// +/// See [`Colors`](struct.Colors.html) for more info. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetColors(pub Colors); + +impl Command for SetColors { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + if let Some(color) = self.0.foreground { + SetForegroundColor(color).write_ansi(f)?; + } + if let Some(color) = self.0.background { + SetBackgroundColor(color).write_ansi(f)?; + } + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + if let Some(color) = self.0.foreground { + sys::windows::set_foreground_color(color)?; + } + if let Some(color) = self.0.background { + sys::windows::set_background_color(color)?; + } + Ok(()) + } +} + +/// A command that sets an attribute. +/// +/// See [`Attribute`](enum.Attribute.html) for more info. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetAttribute(pub Attribute); + +impl Command for SetAttribute { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("{}m"), self.0.sgr()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // attributes are not supported by WinAPI. + Ok(()) + } +} + +/// A command that sets several attributes. +/// +/// See [`Attributes`](struct.Attributes.html) for more info. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetAttributes(pub Attributes); + +impl Command for SetAttributes { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + for attr in Attribute::iterator() { + if self.0.has(attr) { + SetAttribute(attr).write_ansi(f)?; + } + } + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + // attributes are not supported by WinAPI. + Ok(()) + } +} + +/// A command that sets a style (colors and attributes). +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetStyle(pub ContentStyle); + +impl Command for SetStyle { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + if let Some(bg) = self.0.background_color { + execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; + } + if let Some(fg) = self.0.foreground_color { + execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?; + } + if let Some(ul) = self.0.underline_color { + execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?; + } + if !self.0.attributes.is_empty() { + execute_fmt(f, SetAttributes(self.0.attributes)).map_err(|_| fmt::Error)?; + } + + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + panic!("tried to execute SetStyle command using WinAPI, use ANSI instead"); + } + + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + true + } +} + +/// A command that prints styled content. +/// +/// See [`StyledContent`](struct.StyledContent.html) for more info. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Copy, Clone)] +pub struct PrintStyledContent(pub StyledContent); + +impl Command for PrintStyledContent { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + let style = self.0.style(); + + let mut reset_background = false; + let mut reset_foreground = false; + let mut reset = false; + + if let Some(bg) = style.background_color { + execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?; + reset_background = true; + } + if let Some(fg) = style.foreground_color { + execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?; + reset_foreground = true; + } + if let Some(ul) = style.underline_color { + execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?; + reset_foreground = true; + } + + if !style.attributes.is_empty() { + execute_fmt(f, SetAttributes(style.attributes)).map_err(|_| fmt::Error)?; + reset = true; + } + + write!(f, "{}", self.0.content())?; + + if reset { + // NOTE: This will reset colors even though self has no colors, hence produce unexpected + // resets. + // TODO: reset the set attributes only. + execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?; + } else { + // NOTE: Since the above bug, we do not need to reset colors when we reset attributes. + if reset_background { + execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?; + } + if reset_foreground { + execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?; + } + } + + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + Ok(()) + } +} + +/// A command that resets the colors back to default. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ResetColor; + +impl Command for ResetColor { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("0m")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::windows::reset() + } +} + +/// A command that prints the given displayable type. +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Print(pub T); + +impl Command for Print { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "{}", self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + panic!("tried to execute Print command using WinAPI, use ANSI instead"); + } + + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + true + } +} + +impl Display for Print { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl_display!(for SetForegroundColor); +impl_display!(for SetBackgroundColor); +impl_display!(for SetColors); +impl_display!(for SetAttribute); +impl_display!(for PrintStyledContent); +impl_display!(for PrintStyledContent<&'static str>); +impl_display!(for ResetColor); + +/// Utility function for ANSI parsing in Color and Colored. +/// Gets the next element of `iter` and tries to parse it as a `u8`. +fn parse_next_u8<'a>(iter: &mut impl Iterator) -> Option { + iter.next().and_then(|s| s.parse().ok()) +} diff --git a/vendor/crossterm/src/style/attributes.rs b/vendor/crossterm/src/style/attributes.rs index 7acd02e..ecc403c 100644 --- a/vendor/crossterm/src/style/attributes.rs +++ b/vendor/crossterm/src/style/attributes.rs @@ -1,120 +1,120 @@ -use std::ops::{BitAnd, BitOr, BitXor}; - -use crate::style::Attribute; - -/// a bitset for all possible attributes -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub struct Attributes(u32); - -impl From for Attributes { - fn from(attribute: Attribute) -> Self { - Self(attribute.bytes()) - } -} - -impl From<&[Attribute]> for Attributes { - fn from(arr: &[Attribute]) -> Self { - let mut attributes = Attributes::default(); - for &attr in arr { - attributes.set(attr); - } - attributes - } -} - -impl BitAnd for Attributes { - type Output = Self; - fn bitand(self, rhs: Attribute) -> Self { - Self(self.0 & rhs.bytes()) - } -} -impl BitAnd for Attributes { - type Output = Self; - fn bitand(self, rhs: Self) -> Self { - Self(self.0 & rhs.0) - } -} - -impl BitOr for Attributes { - type Output = Self; - fn bitor(self, rhs: Attribute) -> Self { - Self(self.0 | rhs.bytes()) - } -} -impl BitOr for Attributes { - type Output = Self; - fn bitor(self, rhs: Self) -> Self { - Self(self.0 | rhs.0) - } -} - -impl BitXor for Attributes { - type Output = Self; - fn bitxor(self, rhs: Attribute) -> Self { - Self(self.0 ^ rhs.bytes()) - } -} -impl BitXor for Attributes { - type Output = Self; - fn bitxor(self, rhs: Self) -> Self { - Self(self.0 ^ rhs.0) - } -} - -impl Attributes { - /// Sets the attribute. - /// If it's already set, this does nothing. - #[inline(always)] - pub fn set(&mut self, attribute: Attribute) { - self.0 |= attribute.bytes(); - } - - /// Unsets the attribute. - /// If it's not set, this changes nothing. - #[inline(always)] - pub fn unset(&mut self, attribute: Attribute) { - self.0 &= !attribute.bytes(); - } - - /// Sets the attribute if it's unset, unset it - /// if it is set. - #[inline(always)] - pub fn toggle(&mut self, attribute: Attribute) { - self.0 ^= attribute.bytes(); - } - - /// Returns whether the attribute is set. - #[inline(always)] - pub const fn has(self, attribute: Attribute) -> bool { - self.0 & attribute.bytes() != 0 - } - - /// Sets all the passed attributes. Removes none. - #[inline(always)] - pub fn extend(&mut self, attributes: Attributes) { - self.0 |= attributes.0; - } - - /// Returns whether there is no attribute set. - #[inline(always)] - pub const fn is_empty(self) -> bool { - self.0 == 0 - } -} - -#[cfg(test)] -mod tests { - use super::{Attribute, Attributes}; - - #[test] - fn test_attributes() { - let mut attributes: Attributes = Attribute::Bold.into(); - assert!(attributes.has(Attribute::Bold)); - attributes.set(Attribute::Italic); - assert!(attributes.has(Attribute::Italic)); - attributes.unset(Attribute::Italic); - assert!(!attributes.has(Attribute::Italic)); - attributes.toggle(Attribute::Bold); - assert!(attributes.is_empty()); - } -} +use std::ops::{BitAnd, BitOr, BitXor}; + +use crate::style::Attribute; + +/// a bitset for all possible attributes +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct Attributes(u32); + +impl From for Attributes { + fn from(attribute: Attribute) -> Self { + Self(attribute.bytes()) + } +} + +impl From<&[Attribute]> for Attributes { + fn from(arr: &[Attribute]) -> Self { + let mut attributes = Attributes::default(); + for &attr in arr { + attributes.set(attr); + } + attributes + } +} + +impl BitAnd for Attributes { + type Output = Self; + fn bitand(self, rhs: Attribute) -> Self { + Self(self.0 & rhs.bytes()) + } +} +impl BitAnd for Attributes { + type Output = Self; + fn bitand(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } +} + +impl BitOr for Attributes { + type Output = Self; + fn bitor(self, rhs: Attribute) -> Self { + Self(self.0 | rhs.bytes()) + } +} +impl BitOr for Attributes { + type Output = Self; + fn bitor(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } +} + +impl BitXor for Attributes { + type Output = Self; + fn bitxor(self, rhs: Attribute) -> Self { + Self(self.0 ^ rhs.bytes()) + } +} +impl BitXor for Attributes { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self { + Self(self.0 ^ rhs.0) + } +} + +impl Attributes { + /// Sets the attribute. + /// If it's already set, this does nothing. + #[inline(always)] + pub fn set(&mut self, attribute: Attribute) { + self.0 |= attribute.bytes(); + } + + /// Unsets the attribute. + /// If it's not set, this changes nothing. + #[inline(always)] + pub fn unset(&mut self, attribute: Attribute) { + self.0 &= !attribute.bytes(); + } + + /// Sets the attribute if it's unset, unset it + /// if it is set. + #[inline(always)] + pub fn toggle(&mut self, attribute: Attribute) { + self.0 ^= attribute.bytes(); + } + + /// Returns whether the attribute is set. + #[inline(always)] + pub const fn has(self, attribute: Attribute) -> bool { + self.0 & attribute.bytes() != 0 + } + + /// Sets all the passed attributes. Removes none. + #[inline(always)] + pub fn extend(&mut self, attributes: Attributes) { + self.0 |= attributes.0; + } + + /// Returns whether there is no attribute set. + #[inline(always)] + pub const fn is_empty(self) -> bool { + self.0 == 0 + } +} + +#[cfg(test)] +mod tests { + use super::{Attribute, Attributes}; + + #[test] + fn test_attributes() { + let mut attributes: Attributes = Attribute::Bold.into(); + assert!(attributes.has(Attribute::Bold)); + attributes.set(Attribute::Italic); + assert!(attributes.has(Attribute::Italic)); + attributes.unset(Attribute::Italic); + assert!(!attributes.has(Attribute::Italic)); + attributes.toggle(Attribute::Bold); + assert!(attributes.is_empty()); + } +} diff --git a/vendor/crossterm/src/style/content_style.rs b/vendor/crossterm/src/style/content_style.rs index 6e99bb6..4eeb498 100644 --- a/vendor/crossterm/src/style/content_style.rs +++ b/vendor/crossterm/src/style/content_style.rs @@ -1,43 +1,43 @@ -//! This module contains the `content style` that can be applied to an `styled content`. - -use std::fmt::Display; - -use crate::style::{Attributes, Color, StyledContent}; - -/// The style that can be put on content. -#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] -pub struct ContentStyle { - /// The foreground color. - pub foreground_color: Option, - /// The background color. - pub background_color: Option, - /// The underline color. - pub underline_color: Option, - /// List of attributes. - pub attributes: Attributes, -} - -impl ContentStyle { - /// Creates a `StyledContent` by applying the style to the given `val`. - #[inline] - pub fn apply(self, val: D) -> StyledContent { - StyledContent::new(self, val) - } - - /// Creates a new `ContentStyle`. - #[inline] - pub fn new() -> ContentStyle { - ContentStyle::default() - } -} - -impl AsRef for ContentStyle { - fn as_ref(&self) -> &Self { - self - } -} -impl AsMut for ContentStyle { - fn as_mut(&mut self) -> &mut Self { - self - } -} +//! This module contains the `content style` that can be applied to an `styled content`. + +use std::fmt::Display; + +use crate::style::{Attributes, Color, StyledContent}; + +/// The style that can be put on content. +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +pub struct ContentStyle { + /// The foreground color. + pub foreground_color: Option, + /// The background color. + pub background_color: Option, + /// The underline color. + pub underline_color: Option, + /// List of attributes. + pub attributes: Attributes, +} + +impl ContentStyle { + /// Creates a `StyledContent` by applying the style to the given `val`. + #[inline] + pub fn apply(self, val: D) -> StyledContent { + StyledContent::new(self, val) + } + + /// Creates a new `ContentStyle`. + #[inline] + pub fn new() -> ContentStyle { + ContentStyle::default() + } +} + +impl AsRef for ContentStyle { + fn as_ref(&self) -> &Self { + self + } +} +impl AsMut for ContentStyle { + fn as_mut(&mut self) -> &mut Self { + self + } +} diff --git a/vendor/crossterm/src/style/styled_content.rs b/vendor/crossterm/src/style/styled_content.rs index 39ebe0d..3d0ade9 100644 --- a/vendor/crossterm/src/style/styled_content.rs +++ b/vendor/crossterm/src/style/styled_content.rs @@ -1,77 +1,77 @@ -//! This module contains the logic to style some content. - -use std::fmt::{self, Display, Formatter}; - -use super::{ContentStyle, PrintStyledContent}; - -/// The style with the content to be styled. -/// -/// # Examples -/// -/// ```rust -/// use crossterm::style::{style, Color, Attribute, Stylize}; -/// -/// let styled = "Hello there" -/// .with(Color::Yellow) -/// .on(Color::Blue) -/// .attribute(Attribute::Bold); -/// -/// println!("{}", styled); -/// ``` -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct StyledContent { - /// The style (colors, content attributes). - style: ContentStyle, - /// A content to apply the style on. - content: D, -} - -impl StyledContent { - /// Creates a new `StyledContent`. - #[inline] - pub fn new(style: ContentStyle, content: D) -> StyledContent { - StyledContent { style, content } - } - - /// Returns the content. - #[inline] - pub fn content(&self) -> &D { - &self.content - } - - /// Returns the style. - #[inline] - pub fn style(&self) -> &ContentStyle { - &self.style - } - - /// Returns a mutable reference to the style, so that it can be further - /// manipulated - #[inline] - pub fn style_mut(&mut self) -> &mut ContentStyle { - &mut self.style - } -} - -impl AsRef for StyledContent { - fn as_ref(&self) -> &ContentStyle { - &self.style - } -} -impl AsMut for StyledContent { - fn as_mut(&mut self) -> &mut ContentStyle { - &mut self.style - } -} - -impl Display for StyledContent { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - crate::command::execute_fmt( - f, - PrintStyledContent(StyledContent { - style: self.style, - content: &self.content, - }), - ) - } -} +//! This module contains the logic to style some content. + +use std::fmt::{self, Display, Formatter}; + +use super::{ContentStyle, PrintStyledContent}; + +/// The style with the content to be styled. +/// +/// # Examples +/// +/// ```rust +/// use crossterm::style::{style, Color, Attribute, Stylize}; +/// +/// let styled = "Hello there" +/// .with(Color::Yellow) +/// .on(Color::Blue) +/// .attribute(Attribute::Bold); +/// +/// println!("{}", styled); +/// ``` +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct StyledContent { + /// The style (colors, content attributes). + style: ContentStyle, + /// A content to apply the style on. + content: D, +} + +impl StyledContent { + /// Creates a new `StyledContent`. + #[inline] + pub fn new(style: ContentStyle, content: D) -> StyledContent { + StyledContent { style, content } + } + + /// Returns the content. + #[inline] + pub fn content(&self) -> &D { + &self.content + } + + /// Returns the style. + #[inline] + pub fn style(&self) -> &ContentStyle { + &self.style + } + + /// Returns a mutable reference to the style, so that it can be further + /// manipulated + #[inline] + pub fn style_mut(&mut self) -> &mut ContentStyle { + &mut self.style + } +} + +impl AsRef for StyledContent { + fn as_ref(&self) -> &ContentStyle { + &self.style + } +} +impl AsMut for StyledContent { + fn as_mut(&mut self) -> &mut ContentStyle { + &mut self.style + } +} + +impl Display for StyledContent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + crate::command::execute_fmt( + f, + PrintStyledContent(StyledContent { + style: self.style, + content: &self.content, + }), + ) + } +} diff --git a/vendor/crossterm/src/style/stylize.rs b/vendor/crossterm/src/style/stylize.rs index fcb12da..f13999e 100644 --- a/vendor/crossterm/src/style/stylize.rs +++ b/vendor/crossterm/src/style/stylize.rs @@ -1,200 +1,200 @@ -use std::fmt::Display; - -use super::{style, Attribute, Color, ContentStyle, StyledContent}; - -macro_rules! stylize_method { - ($method_name:ident Attribute::$attribute:ident) => { - calculated_docs! { - #[doc = concat!( - "Applies the [`", - stringify!($attribute), - "`](Attribute::", - stringify!($attribute), - ") attribute to the text.", - )] - fn $method_name(self) -> Self::Styled { - self.attribute(Attribute::$attribute) - } - } - }; - ($method_name_fg:ident, $method_name_bg:ident, $method_name_ul:ident Color::$color:ident) => { - calculated_docs! { - #[doc = concat!( - "Sets the foreground color to [`", - stringify!($color), - "`](Color::", - stringify!($color), - ")." - )] - fn $method_name_fg(self) -> Self::Styled { - self.with(Color::$color) - } - - #[doc = concat!( - "Sets the background color to [`", - stringify!($color), - "`](Color::", - stringify!($color), - ")." - )] - fn $method_name_bg(self) -> Self::Styled { - self.on(Color::$color) - } - - #[doc = concat!( - "Sets the underline color to [`", - stringify!($color), - "`](Color::", - stringify!($color), - ")." - )] - fn $method_name_ul(self) -> Self::Styled { - self.underline(Color::$color) - } - } - }; -} - -/// Provides a set of methods to set attributes and colors. -/// -/// # Examples -/// -/// ```no_run -/// use crossterm::style::Stylize; -/// -/// println!("{}", "Bold text".bold()); -/// println!("{}", "Underlined text".underlined()); -/// println!("{}", "Negative text".negative()); -/// println!("{}", "Red on blue".red().on_blue()); -/// ``` -pub trait Stylize: Sized { - /// This type with styles applied. - type Styled: AsRef + AsMut; - - /// Styles this type. - fn stylize(self) -> Self::Styled; - - /// Sets the foreground color. - fn with(self, color: Color) -> Self::Styled { - let mut styled = self.stylize(); - styled.as_mut().foreground_color = Some(color); - styled - } - - /// Sets the background color. - fn on(self, color: Color) -> Self::Styled { - let mut styled = self.stylize(); - styled.as_mut().background_color = Some(color); - styled - } - - /// Sets the underline color. - fn underline(self, color: Color) -> Self::Styled { - let mut styled = self.stylize(); - styled.as_mut().underline_color = Some(color); - styled - } - - /// Styles the content with the attribute. - fn attribute(self, attr: Attribute) -> Self::Styled { - let mut styled = self.stylize(); - styled.as_mut().attributes.set(attr); - styled - } - - stylize_method!(reset Attribute::Reset); - stylize_method!(bold Attribute::Bold); - stylize_method!(underlined Attribute::Underlined); - stylize_method!(reverse Attribute::Reverse); - stylize_method!(dim Attribute::Dim); - stylize_method!(italic Attribute::Italic); - stylize_method!(negative Attribute::Reverse); - stylize_method!(slow_blink Attribute::SlowBlink); - stylize_method!(rapid_blink Attribute::RapidBlink); - stylize_method!(hidden Attribute::Hidden); - stylize_method!(crossed_out Attribute::CrossedOut); - - stylize_method!(black, on_black, underline_black Color::Black); - stylize_method!(dark_grey, on_dark_grey, underline_dark_grey Color::DarkGrey); - stylize_method!(red, on_red, underline_red Color::Red); - stylize_method!(dark_red, on_dark_red, underline_dark_red Color::DarkRed); - stylize_method!(green, on_green, underline_green Color::Green); - stylize_method!(dark_green, on_dark_green, underline_dark_green Color::DarkGreen); - stylize_method!(yellow, on_yellow, underline_yellow Color::Yellow); - stylize_method!(dark_yellow, on_dark_yellow, underline_dark_yellow Color::DarkYellow); - stylize_method!(blue, on_blue, underline_blue Color::Blue); - stylize_method!(dark_blue, on_dark_blue, underline_dark_blue Color::DarkBlue); - stylize_method!(magenta, on_magenta, underline_magenta Color::Magenta); - stylize_method!(dark_magenta, on_dark_magenta, underline_dark_magenta Color::DarkMagenta); - stylize_method!(cyan, on_cyan, underline_cyan Color::Cyan); - stylize_method!(dark_cyan, on_dark_cyan, underline_dark_cyan Color::DarkCyan); - stylize_method!(white, on_white, underline_white Color::White); - stylize_method!(grey, on_grey, underline_grey Color::Grey); -} - -macro_rules! impl_stylize_for_display { - ($($t:ty),*) => { $( - impl Stylize for $t { - type Styled = StyledContent; - #[inline] - fn stylize(self) -> Self::Styled { - style(self) - } - } - )* } -} -impl_stylize_for_display!(String, char, &str); - -impl Stylize for ContentStyle { - type Styled = Self; - #[inline] - fn stylize(self) -> Self::Styled { - self - } -} -impl Stylize for StyledContent { - type Styled = StyledContent; - fn stylize(self) -> Self::Styled { - self - } -} - -// Workaround for https://github.com/rust-lang/rust/issues/78835 -macro_rules! calculated_docs { - ($(#[doc = $doc:expr] $item:item)*) => { $(#[doc = $doc] $item)* }; -} -// Remove once https://github.com/rust-lang/rust-clippy/issues/7106 stabilizes. -#[allow(clippy::single_component_path_imports)] -#[allow(clippy::useless_attribute)] -use calculated_docs; - -#[cfg(test)] -mod tests { - use super::super::{Attribute, Color, ContentStyle, Stylize}; - - #[test] - fn set_fg_bg_add_attr() { - let style = ContentStyle::new() - .with(Color::Blue) - .on(Color::Red) - .attribute(Attribute::Bold); - - assert_eq!(style.foreground_color, Some(Color::Blue)); - assert_eq!(style.background_color, Some(Color::Red)); - assert!(style.attributes.has(Attribute::Bold)); - - let mut styled_content = style.apply("test"); - - styled_content = styled_content - .with(Color::Green) - .on(Color::Magenta) - .attribute(Attribute::NoItalic); - - let style = styled_content.style(); - - assert_eq!(style.foreground_color, Some(Color::Green)); - assert_eq!(style.background_color, Some(Color::Magenta)); - assert!(style.attributes.has(Attribute::Bold)); - assert!(style.attributes.has(Attribute::NoItalic)); - } -} +use std::fmt::Display; + +use super::{style, Attribute, Color, ContentStyle, StyledContent}; + +macro_rules! stylize_method { + ($method_name:ident Attribute::$attribute:ident) => { + calculated_docs! { + #[doc = concat!( + "Applies the [`", + stringify!($attribute), + "`](Attribute::", + stringify!($attribute), + ") attribute to the text.", + )] + fn $method_name(self) -> Self::Styled { + self.attribute(Attribute::$attribute) + } + } + }; + ($method_name_fg:ident, $method_name_bg:ident, $method_name_ul:ident Color::$color:ident) => { + calculated_docs! { + #[doc = concat!( + "Sets the foreground color to [`", + stringify!($color), + "`](Color::", + stringify!($color), + ")." + )] + fn $method_name_fg(self) -> Self::Styled { + self.with(Color::$color) + } + + #[doc = concat!( + "Sets the background color to [`", + stringify!($color), + "`](Color::", + stringify!($color), + ")." + )] + fn $method_name_bg(self) -> Self::Styled { + self.on(Color::$color) + } + + #[doc = concat!( + "Sets the underline color to [`", + stringify!($color), + "`](Color::", + stringify!($color), + ")." + )] + fn $method_name_ul(self) -> Self::Styled { + self.underline(Color::$color) + } + } + }; +} + +/// Provides a set of methods to set attributes and colors. +/// +/// # Examples +/// +/// ```no_run +/// use crossterm::style::Stylize; +/// +/// println!("{}", "Bold text".bold()); +/// println!("{}", "Underlined text".underlined()); +/// println!("{}", "Negative text".negative()); +/// println!("{}", "Red on blue".red().on_blue()); +/// ``` +pub trait Stylize: Sized { + /// This type with styles applied. + type Styled: AsRef + AsMut; + + /// Styles this type. + fn stylize(self) -> Self::Styled; + + /// Sets the foreground color. + fn with(self, color: Color) -> Self::Styled { + let mut styled = self.stylize(); + styled.as_mut().foreground_color = Some(color); + styled + } + + /// Sets the background color. + fn on(self, color: Color) -> Self::Styled { + let mut styled = self.stylize(); + styled.as_mut().background_color = Some(color); + styled + } + + /// Sets the underline color. + fn underline(self, color: Color) -> Self::Styled { + let mut styled = self.stylize(); + styled.as_mut().underline_color = Some(color); + styled + } + + /// Styles the content with the attribute. + fn attribute(self, attr: Attribute) -> Self::Styled { + let mut styled = self.stylize(); + styled.as_mut().attributes.set(attr); + styled + } + + stylize_method!(reset Attribute::Reset); + stylize_method!(bold Attribute::Bold); + stylize_method!(underlined Attribute::Underlined); + stylize_method!(reverse Attribute::Reverse); + stylize_method!(dim Attribute::Dim); + stylize_method!(italic Attribute::Italic); + stylize_method!(negative Attribute::Reverse); + stylize_method!(slow_blink Attribute::SlowBlink); + stylize_method!(rapid_blink Attribute::RapidBlink); + stylize_method!(hidden Attribute::Hidden); + stylize_method!(crossed_out Attribute::CrossedOut); + + stylize_method!(black, on_black, underline_black Color::Black); + stylize_method!(dark_grey, on_dark_grey, underline_dark_grey Color::DarkGrey); + stylize_method!(red, on_red, underline_red Color::Red); + stylize_method!(dark_red, on_dark_red, underline_dark_red Color::DarkRed); + stylize_method!(green, on_green, underline_green Color::Green); + stylize_method!(dark_green, on_dark_green, underline_dark_green Color::DarkGreen); + stylize_method!(yellow, on_yellow, underline_yellow Color::Yellow); + stylize_method!(dark_yellow, on_dark_yellow, underline_dark_yellow Color::DarkYellow); + stylize_method!(blue, on_blue, underline_blue Color::Blue); + stylize_method!(dark_blue, on_dark_blue, underline_dark_blue Color::DarkBlue); + stylize_method!(magenta, on_magenta, underline_magenta Color::Magenta); + stylize_method!(dark_magenta, on_dark_magenta, underline_dark_magenta Color::DarkMagenta); + stylize_method!(cyan, on_cyan, underline_cyan Color::Cyan); + stylize_method!(dark_cyan, on_dark_cyan, underline_dark_cyan Color::DarkCyan); + stylize_method!(white, on_white, underline_white Color::White); + stylize_method!(grey, on_grey, underline_grey Color::Grey); +} + +macro_rules! impl_stylize_for_display { + ($($t:ty),*) => { $( + impl Stylize for $t { + type Styled = StyledContent; + #[inline] + fn stylize(self) -> Self::Styled { + style(self) + } + } + )* } +} +impl_stylize_for_display!(String, char, &str); + +impl Stylize for ContentStyle { + type Styled = Self; + #[inline] + fn stylize(self) -> Self::Styled { + self + } +} +impl Stylize for StyledContent { + type Styled = StyledContent; + fn stylize(self) -> Self::Styled { + self + } +} + +// Workaround for https://github.com/rust-lang/rust/issues/78835 +macro_rules! calculated_docs { + ($(#[doc = $doc:expr] $item:item)*) => { $(#[doc = $doc] $item)* }; +} +// Remove once https://github.com/rust-lang/rust-clippy/issues/7106 stabilizes. +#[allow(clippy::single_component_path_imports)] +#[allow(clippy::useless_attribute)] +use calculated_docs; + +#[cfg(test)] +mod tests { + use super::super::{Attribute, Color, ContentStyle, Stylize}; + + #[test] + fn set_fg_bg_add_attr() { + let style = ContentStyle::new() + .with(Color::Blue) + .on(Color::Red) + .attribute(Attribute::Bold); + + assert_eq!(style.foreground_color, Some(Color::Blue)); + assert_eq!(style.background_color, Some(Color::Red)); + assert!(style.attributes.has(Attribute::Bold)); + + let mut styled_content = style.apply("test"); + + styled_content = styled_content + .with(Color::Green) + .on(Color::Magenta) + .attribute(Attribute::NoItalic); + + let style = styled_content.style(); + + assert_eq!(style.foreground_color, Some(Color::Green)); + assert_eq!(style.background_color, Some(Color::Magenta)); + assert!(style.attributes.has(Attribute::Bold)); + assert!(style.attributes.has(Attribute::NoItalic)); + } +} diff --git a/vendor/crossterm/src/style/sys.rs b/vendor/crossterm/src/style/sys.rs index 5a54276..66b3695 100644 --- a/vendor/crossterm/src/style/sys.rs +++ b/vendor/crossterm/src/style/sys.rs @@ -1,2 +1,2 @@ -#[cfg(windows)] -pub(crate) mod windows; +#[cfg(windows)] +pub(crate) mod windows; diff --git a/vendor/crossterm/src/style/sys/windows.rs b/vendor/crossterm/src/style/sys/windows.rs index 48114a4..ca6261c 100644 --- a/vendor/crossterm/src/style/sys/windows.rs +++ b/vendor/crossterm/src/style/sys/windows.rs @@ -1,206 +1,206 @@ -use std::convert::TryFrom; -use std::sync::atomic::{AtomicU32, Ordering}; - -use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; -use winapi::um::wincon; - -use crate::Result; - -use super::super::{Color, Colored}; - -const FG_GREEN: u16 = wincon::FOREGROUND_GREEN; -const FG_RED: u16 = wincon::FOREGROUND_RED; -const FG_BLUE: u16 = wincon::FOREGROUND_BLUE; -const FG_INTENSITY: u16 = wincon::FOREGROUND_INTENSITY; - -const BG_GREEN: u16 = wincon::BACKGROUND_GREEN; -const BG_RED: u16 = wincon::BACKGROUND_RED; -const BG_BLUE: u16 = wincon::BACKGROUND_BLUE; -const BG_INTENSITY: u16 = wincon::BACKGROUND_INTENSITY; - -pub(crate) fn set_foreground_color(fg_color: Color) -> Result<()> { - init_console_color()?; - - let color_value: u16 = Colored::ForegroundColor(fg_color).into(); - - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - - // Notice that the color values are stored in wAttribute. - // So we need to use bitwise operators to check if the values exists or to get current console colors. - let attrs = csbi.attributes(); - let bg_color = attrs & 0x0070; - let mut color = color_value | bg_color; - - // background intensity is a separate value in attrs, - // we need to check if this was applied to the current bg color. - if (attrs & wincon::BACKGROUND_INTENSITY as u16) != 0 { - color |= wincon::BACKGROUND_INTENSITY as u16; - } - - Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?; - Ok(()) -} - -pub(crate) fn set_background_color(bg_color: Color) -> Result<()> { - init_console_color()?; - - let color_value: u16 = Colored::BackgroundColor(bg_color).into(); - - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - - // Notice that the color values are stored in wAttribute. - // So we need to use bitwise operators to check if the values exists or to get current console colors. - let attrs = csbi.attributes(); - let fg_color = attrs & 0x0007; - let mut color = fg_color | color_value; - - // Foreground intensity is a separate value in attrs, - // So we need to check if this was applied to the current fg color. - if (attrs & wincon::FOREGROUND_INTENSITY as u16) != 0 { - color |= wincon::FOREGROUND_INTENSITY as u16; - } - - Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?; - Ok(()) -} - -pub(crate) fn reset() -> Result<()> { - if let Ok(original_color) = u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed)) { - Console::from(Handle::new(HandleType::CurrentOutputHandle)?) - .set_text_attribute(original_color)?; - } - - Ok(()) -} - -/// Initializes the default console color. It will will be skipped if it has already been initialized. -pub(crate) fn init_console_color() -> Result<()> { - if ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed) == u32::MAX { - let screen_buffer = ScreenBuffer::current()?; - let attr = screen_buffer.info()?.attributes(); - ORIGINAL_CONSOLE_COLOR.store(u32::from(attr), Ordering::Relaxed); - } - - Ok(()) -} - -/// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. -pub(crate) fn original_console_color() -> u16 { - u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed)) - // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` - .expect("Initial console color not set") -} - -// This is either a valid u16 in which case it stores the original console color or it is u32::MAX -// in which case it is uninitialized. -static ORIGINAL_CONSOLE_COLOR: AtomicU32 = AtomicU32::new(u32::MAX); - -impl From for u16 { - /// Returns the WinAPI color value (u16) from the `Colored` struct. - fn from(colored: Colored) -> Self { - match colored { - Colored::ForegroundColor(color) => { - match color { - Color::Black => 0, - Color::DarkGrey => FG_INTENSITY, - Color::Red => FG_INTENSITY | FG_RED, - Color::DarkRed => FG_RED, - Color::Green => FG_INTENSITY | FG_GREEN, - Color::DarkGreen => FG_GREEN, - Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, - Color::DarkYellow => FG_GREEN | FG_RED, - Color::Blue => FG_INTENSITY | FG_BLUE, - Color::DarkBlue => FG_BLUE, - Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, - Color::DarkMagenta => FG_RED | FG_BLUE, - Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, - Color::DarkCyan => FG_GREEN | FG_BLUE, - Color::White => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, - Color::Grey => FG_RED | FG_GREEN | FG_BLUE, - - Color::Reset => { - // safe unwrap, initial console color was set with `init_console_color`. - let original_color = original_console_color(); - - const REMOVE_BG_MASK: u16 = BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE; - // remove all background values from the original color, we don't want to reset those. - - original_color & !REMOVE_BG_MASK - } - - /* WinAPI will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { .. } => 0, - Color::AnsiValue(_val) => 0, - } - } - Colored::BackgroundColor(color) => { - match color { - Color::Black => 0, - Color::DarkGrey => BG_INTENSITY, - Color::Red => BG_INTENSITY | BG_RED, - Color::DarkRed => BG_RED, - Color::Green => BG_INTENSITY | BG_GREEN, - Color::DarkGreen => BG_GREEN, - Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, - Color::DarkYellow => BG_GREEN | BG_RED, - Color::Blue => BG_INTENSITY | BG_BLUE, - Color::DarkBlue => BG_BLUE, - Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, - Color::DarkMagenta => BG_RED | BG_BLUE, - Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, - Color::DarkCyan => BG_GREEN | BG_BLUE, - Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, - Color::Grey => BG_RED | BG_GREEN | BG_BLUE, - - Color::Reset => { - let original_color = original_console_color(); - - const REMOVE_FG_MASK: u16 = FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE; - // remove all foreground values from the original color, we don't want to reset those. - - original_color & !REMOVE_FG_MASK - } - /* WinAPI will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { .. } => 0, - Color::AnsiValue(_val) => 0, - } - } - Colored::UnderlineColor(_) => 0, - } - } -} - -#[cfg(test)] -mod tests { - use std::sync::atomic::Ordering; - - use crate::style::sys::windows::set_foreground_color; - - use super::{ - Color, Colored, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED, ORIGINAL_CONSOLE_COLOR, - }; - - #[test] - fn test_parse_fg_color() { - let colored = Colored::ForegroundColor(Color::Red); - assert_eq!(Into::::into(colored), FG_INTENSITY | FG_RED); - } - - #[test] - fn test_parse_bg_color() { - let colored = Colored::BackgroundColor(Color::Red); - assert_eq!(Into::::into(colored), BG_INTENSITY | BG_RED); - } - - #[test] - fn test_original_console_color_is_set() { - assert_eq!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX); - - // will call `init_console_color` - set_foreground_color(Color::Blue).unwrap(); - - assert_ne!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX); - } -} +use std::convert::TryFrom; +use std::sync::atomic::{AtomicU32, Ordering}; + +use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; +use winapi::um::wincon; + +use crate::Result; + +use super::super::{Color, Colored}; + +const FG_GREEN: u16 = wincon::FOREGROUND_GREEN; +const FG_RED: u16 = wincon::FOREGROUND_RED; +const FG_BLUE: u16 = wincon::FOREGROUND_BLUE; +const FG_INTENSITY: u16 = wincon::FOREGROUND_INTENSITY; + +const BG_GREEN: u16 = wincon::BACKGROUND_GREEN; +const BG_RED: u16 = wincon::BACKGROUND_RED; +const BG_BLUE: u16 = wincon::BACKGROUND_BLUE; +const BG_INTENSITY: u16 = wincon::BACKGROUND_INTENSITY; + +pub(crate) fn set_foreground_color(fg_color: Color) -> Result<()> { + init_console_color()?; + + let color_value: u16 = Colored::ForegroundColor(fg_color).into(); + + let screen_buffer = ScreenBuffer::current()?; + let csbi = screen_buffer.info()?; + + // Notice that the color values are stored in wAttribute. + // So we need to use bitwise operators to check if the values exists or to get current console colors. + let attrs = csbi.attributes(); + let bg_color = attrs & 0x0070; + let mut color = color_value | bg_color; + + // background intensity is a separate value in attrs, + // we need to check if this was applied to the current bg color. + if (attrs & wincon::BACKGROUND_INTENSITY as u16) != 0 { + color |= wincon::BACKGROUND_INTENSITY as u16; + } + + Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?; + Ok(()) +} + +pub(crate) fn set_background_color(bg_color: Color) -> Result<()> { + init_console_color()?; + + let color_value: u16 = Colored::BackgroundColor(bg_color).into(); + + let screen_buffer = ScreenBuffer::current()?; + let csbi = screen_buffer.info()?; + + // Notice that the color values are stored in wAttribute. + // So we need to use bitwise operators to check if the values exists or to get current console colors. + let attrs = csbi.attributes(); + let fg_color = attrs & 0x0007; + let mut color = fg_color | color_value; + + // Foreground intensity is a separate value in attrs, + // So we need to check if this was applied to the current fg color. + if (attrs & wincon::FOREGROUND_INTENSITY as u16) != 0 { + color |= wincon::FOREGROUND_INTENSITY as u16; + } + + Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?; + Ok(()) +} + +pub(crate) fn reset() -> Result<()> { + if let Ok(original_color) = u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed)) { + Console::from(Handle::new(HandleType::CurrentOutputHandle)?) + .set_text_attribute(original_color)?; + } + + Ok(()) +} + +/// Initializes the default console color. It will will be skipped if it has already been initialized. +pub(crate) fn init_console_color() -> Result<()> { + if ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed) == u32::MAX { + let screen_buffer = ScreenBuffer::current()?; + let attr = screen_buffer.info()?.attributes(); + ORIGINAL_CONSOLE_COLOR.store(u32::from(attr), Ordering::Relaxed); + } + + Ok(()) +} + +/// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. +pub(crate) fn original_console_color() -> u16 { + u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed)) + // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` + .expect("Initial console color not set") +} + +// This is either a valid u16 in which case it stores the original console color or it is u32::MAX +// in which case it is uninitialized. +static ORIGINAL_CONSOLE_COLOR: AtomicU32 = AtomicU32::new(u32::MAX); + +impl From for u16 { + /// Returns the WinAPI color value (u16) from the `Colored` struct. + fn from(colored: Colored) -> Self { + match colored { + Colored::ForegroundColor(color) => { + match color { + Color::Black => 0, + Color::DarkGrey => FG_INTENSITY, + Color::Red => FG_INTENSITY | FG_RED, + Color::DarkRed => FG_RED, + Color::Green => FG_INTENSITY | FG_GREEN, + Color::DarkGreen => FG_GREEN, + Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, + Color::DarkYellow => FG_GREEN | FG_RED, + Color::Blue => FG_INTENSITY | FG_BLUE, + Color::DarkBlue => FG_BLUE, + Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, + Color::DarkMagenta => FG_RED | FG_BLUE, + Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, + Color::DarkCyan => FG_GREEN | FG_BLUE, + Color::White => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, + Color::Grey => FG_RED | FG_GREEN | FG_BLUE, + + Color::Reset => { + // safe unwrap, initial console color was set with `init_console_color`. + let original_color = original_console_color(); + + const REMOVE_BG_MASK: u16 = BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE; + // remove all background values from the original color, we don't want to reset those. + + original_color & !REMOVE_BG_MASK + } + + /* WinAPI will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ + Color::Rgb { .. } => 0, + Color::AnsiValue(_val) => 0, + } + } + Colored::BackgroundColor(color) => { + match color { + Color::Black => 0, + Color::DarkGrey => BG_INTENSITY, + Color::Red => BG_INTENSITY | BG_RED, + Color::DarkRed => BG_RED, + Color::Green => BG_INTENSITY | BG_GREEN, + Color::DarkGreen => BG_GREEN, + Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, + Color::DarkYellow => BG_GREEN | BG_RED, + Color::Blue => BG_INTENSITY | BG_BLUE, + Color::DarkBlue => BG_BLUE, + Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, + Color::DarkMagenta => BG_RED | BG_BLUE, + Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, + Color::DarkCyan => BG_GREEN | BG_BLUE, + Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, + Color::Grey => BG_RED | BG_GREEN | BG_BLUE, + + Color::Reset => { + let original_color = original_console_color(); + + const REMOVE_FG_MASK: u16 = FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE; + // remove all foreground values from the original color, we don't want to reset those. + + original_color & !REMOVE_FG_MASK + } + /* WinAPI will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ + Color::Rgb { .. } => 0, + Color::AnsiValue(_val) => 0, + } + } + Colored::UnderlineColor(_) => 0, + } + } +} + +#[cfg(test)] +mod tests { + use std::sync::atomic::Ordering; + + use crate::style::sys::windows::set_foreground_color; + + use super::{ + Color, Colored, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED, ORIGINAL_CONSOLE_COLOR, + }; + + #[test] + fn test_parse_fg_color() { + let colored = Colored::ForegroundColor(Color::Red); + assert_eq!(Into::::into(colored), FG_INTENSITY | FG_RED); + } + + #[test] + fn test_parse_bg_color() { + let colored = Colored::BackgroundColor(Color::Red); + assert_eq!(Into::::into(colored), BG_INTENSITY | BG_RED); + } + + #[test] + fn test_original_console_color_is_set() { + assert_eq!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX); + + // will call `init_console_color` + set_foreground_color(Color::Blue).unwrap(); + + assert_ne!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX); + } +} diff --git a/vendor/crossterm/src/style/types.rs b/vendor/crossterm/src/style/types.rs index 7cd7d6e..74ed652 100644 --- a/vendor/crossterm/src/style/types.rs +++ b/vendor/crossterm/src/style/types.rs @@ -1,6 +1,6 @@ -pub use self::{attribute::Attribute, color::Color, colored::Colored, colors::Colors}; - -mod attribute; -mod color; -mod colored; -mod colors; +pub use self::{attribute::Attribute, color::Color, colored::Colored, colors::Colors}; + +mod attribute; +mod color; +mod colored; +mod colors; diff --git a/vendor/crossterm/src/style/types/attribute.rs b/vendor/crossterm/src/style/types/attribute.rs index d3e19ef..6cf29b4 100644 --- a/vendor/crossterm/src/style/types/attribute.rs +++ b/vendor/crossterm/src/style/types/attribute.rs @@ -1,183 +1,183 @@ -use std::fmt::Display; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use super::super::SetAttribute; - -// This macro generates the Attribute enum, its iterator -// function, and the static array containing the sgr code -// of each attribute -macro_rules! Attribute { - ( - $( - $(#[$inner:ident $($args:tt)*])* - $name:ident = $sgr:expr, - )* - ) => { - /// Represents an attribute. - /// - /// # Platform-specific Notes - /// - /// * Only UNIX and Windows 10 terminals do support text attributes. - /// * Keep in mind that not all terminals support all attributes. - /// * Crossterm implements almost all attributes listed in the - /// [SGR parameters](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters). - /// - /// | Attribute | Windows | UNIX | Notes | - /// | :-- | :--: | :--: | :-- | - /// | `Reset` | ✓ | ✓ | | - /// | `Bold` | ✓ | ✓ | | - /// | `Dim` | ✓ | ✓ | | - /// | `Italic` | ? | ? | Not widely supported, sometimes treated as inverse. | - /// | `Underlined` | ✓ | ✓ | | - /// | `SlowBlink` | ? | ? | Not widely supported, sometimes treated as inverse. | - /// | `RapidBlink` | ? | ? | Not widely supported. MS-DOS ANSI.SYS; 150+ per minute. | - /// | `Reverse` | ✓ | ✓ | | - /// | `Hidden` | ✓ | ✓ | Also known as Conceal. | - /// | `Fraktur` | ✗ | ✓ | Legible characters, but marked for deletion. | - /// | `DefaultForegroundColor` | ? | ? | Implementation specific (according to standard). | - /// | `DefaultBackgroundColor` | ? | ? | Implementation specific (according to standard). | - /// | `Framed` | ? | ? | Not widely supported. | - /// | `Encircled` | ? | ? | This should turn on the encircled attribute. | - /// | `OverLined` | ? | ? | This should draw a line at the top of the text. | - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use crossterm::style::Attribute; - /// - /// println!( - /// "{} Underlined {} No Underline", - /// Attribute::Underlined, - /// Attribute::NoUnderline - /// ); - /// ``` - /// - /// Style existing text: - /// - /// ```no_run - /// use crossterm::style::Stylize; - /// - /// println!("{}", "Bold text".bold()); - /// println!("{}", "Underlined text".underlined()); - /// println!("{}", "Negative text".negative()); - /// ``` - #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] - #[non_exhaustive] - pub enum Attribute { - $( - $(#[$inner $($args)*])* - $name, - )* - } - - pub static SGR: &'static[i16] = &[ - $($sgr,)* - ]; - - impl Attribute { - /// Iterates over all the variants of the Attribute enum. - pub fn iterator() -> impl Iterator { - use self::Attribute::*; - [ $($name,)* ].iter().copied() - } - } - } -} - -Attribute! { - /// Resets all the attributes. - Reset = 0, - /// Increases the text intensity. - Bold = 1, - /// Decreases the text intensity. - Dim = 2, - /// Emphasises the text. - Italic = 3, - /// Underlines the text. - Underlined = 4, - - // Other types of underlining - /// Double underlines the text. - DoubleUnderlined = 2, - /// Undercurls the text. - Undercurled = 3, - /// Underdots the text. - Underdotted = 4, - /// Underdashes the text. - Underdashed = 5, - - /// Makes the text blinking (< 150 per minute). - SlowBlink = 5, - /// Makes the text blinking (>= 150 per minute). - RapidBlink = 6, - /// Swaps foreground and background colors. - Reverse = 7, - /// Hides the text (also known as Conceal). - Hidden = 8, - /// Crosses the text. - CrossedOut = 9, - /// Sets the [Fraktur](https://en.wikipedia.org/wiki/Fraktur) typeface. - /// - /// Mostly used for [mathematical alphanumeric symbols](https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols). - Fraktur = 20, - /// Turns off the `Bold` attribute. - Inconsistent - Prefer to use NormalIntensity - NoBold = 21, - /// Switches the text back to normal intensity (no bold, italic). - NormalIntensity = 22, - /// Turns off the `Italic` attribute. - NoItalic = 23, - /// Turns off the `Underlined` attribute. - NoUnderline = 24, - /// Turns off the text blinking (`SlowBlink` or `RapidBlink`). - NoBlink = 25, - /// Turns off the `Reverse` attribute. - NoReverse = 27, - /// Turns off the `Hidden` attribute. - NoHidden = 28, - /// Turns off the `CrossedOut` attribute. - NotCrossedOut = 29, - /// Makes the text framed. - Framed = 51, - /// Makes the text encircled. - Encircled = 52, - /// Draws a line at the top of the text. - OverLined = 53, - /// Turns off the `Frame` and `Encircled` attributes. - NotFramedOrEncircled = 54, - /// Turns off the `OverLined` attribute. - NotOverLined = 55, -} - -impl Display for Attribute { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - write!(f, "{}", SetAttribute(*self))?; - Ok(()) - } -} - -impl Attribute { - /// Returns a u32 with one bit set, which is the - /// signature of this attribute in the Attributes - /// bitset. - /// - /// The +1 enables storing Reset (whose index is 0) - /// in the bitset Attributes. - #[inline(always)] - pub const fn bytes(self) -> u32 { - 1 << ((self as u32) + 1) - } - /// Returns the SGR attribute value. - /// - /// See - pub fn sgr(self) -> String { - if (self as usize) > 4 && (self as usize) < 9 { - return "4:".to_string() + SGR[self as usize].to_string().as_str(); - } - SGR[self as usize].to_string() - } -} +use std::fmt::Display; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use super::super::SetAttribute; + +// This macro generates the Attribute enum, its iterator +// function, and the static array containing the sgr code +// of each attribute +macro_rules! Attribute { + ( + $( + $(#[$inner:ident $($args:tt)*])* + $name:ident = $sgr:expr, + )* + ) => { + /// Represents an attribute. + /// + /// # Platform-specific Notes + /// + /// * Only UNIX and Windows 10 terminals do support text attributes. + /// * Keep in mind that not all terminals support all attributes. + /// * Crossterm implements almost all attributes listed in the + /// [SGR parameters](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters). + /// + /// | Attribute | Windows | UNIX | Notes | + /// | :-- | :--: | :--: | :-- | + /// | `Reset` | ✓ | ✓ | | + /// | `Bold` | ✓ | ✓ | | + /// | `Dim` | ✓ | ✓ | | + /// | `Italic` | ? | ? | Not widely supported, sometimes treated as inverse. | + /// | `Underlined` | ✓ | ✓ | | + /// | `SlowBlink` | ? | ? | Not widely supported, sometimes treated as inverse. | + /// | `RapidBlink` | ? | ? | Not widely supported. MS-DOS ANSI.SYS; 150+ per minute. | + /// | `Reverse` | ✓ | ✓ | | + /// | `Hidden` | ✓ | ✓ | Also known as Conceal. | + /// | `Fraktur` | ✗ | ✓ | Legible characters, but marked for deletion. | + /// | `DefaultForegroundColor` | ? | ? | Implementation specific (according to standard). | + /// | `DefaultBackgroundColor` | ? | ? | Implementation specific (according to standard). | + /// | `Framed` | ? | ? | Not widely supported. | + /// | `Encircled` | ? | ? | This should turn on the encircled attribute. | + /// | `OverLined` | ? | ? | This should draw a line at the top of the text. | + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use crossterm::style::Attribute; + /// + /// println!( + /// "{} Underlined {} No Underline", + /// Attribute::Underlined, + /// Attribute::NoUnderline + /// ); + /// ``` + /// + /// Style existing text: + /// + /// ```no_run + /// use crossterm::style::Stylize; + /// + /// println!("{}", "Bold text".bold()); + /// println!("{}", "Underlined text".underlined()); + /// println!("{}", "Negative text".negative()); + /// ``` + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] + #[non_exhaustive] + pub enum Attribute { + $( + $(#[$inner $($args)*])* + $name, + )* + } + + pub static SGR: &'static[i16] = &[ + $($sgr,)* + ]; + + impl Attribute { + /// Iterates over all the variants of the Attribute enum. + pub fn iterator() -> impl Iterator { + use self::Attribute::*; + [ $($name,)* ].iter().copied() + } + } + } +} + +Attribute! { + /// Resets all the attributes. + Reset = 0, + /// Increases the text intensity. + Bold = 1, + /// Decreases the text intensity. + Dim = 2, + /// Emphasises the text. + Italic = 3, + /// Underlines the text. + Underlined = 4, + + // Other types of underlining + /// Double underlines the text. + DoubleUnderlined = 2, + /// Undercurls the text. + Undercurled = 3, + /// Underdots the text. + Underdotted = 4, + /// Underdashes the text. + Underdashed = 5, + + /// Makes the text blinking (< 150 per minute). + SlowBlink = 5, + /// Makes the text blinking (>= 150 per minute). + RapidBlink = 6, + /// Swaps foreground and background colors. + Reverse = 7, + /// Hides the text (also known as Conceal). + Hidden = 8, + /// Crosses the text. + CrossedOut = 9, + /// Sets the [Fraktur](https://en.wikipedia.org/wiki/Fraktur) typeface. + /// + /// Mostly used for [mathematical alphanumeric symbols](https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols). + Fraktur = 20, + /// Turns off the `Bold` attribute. - Inconsistent - Prefer to use NormalIntensity + NoBold = 21, + /// Switches the text back to normal intensity (no bold, italic). + NormalIntensity = 22, + /// Turns off the `Italic` attribute. + NoItalic = 23, + /// Turns off the `Underlined` attribute. + NoUnderline = 24, + /// Turns off the text blinking (`SlowBlink` or `RapidBlink`). + NoBlink = 25, + /// Turns off the `Reverse` attribute. + NoReverse = 27, + /// Turns off the `Hidden` attribute. + NoHidden = 28, + /// Turns off the `CrossedOut` attribute. + NotCrossedOut = 29, + /// Makes the text framed. + Framed = 51, + /// Makes the text encircled. + Encircled = 52, + /// Draws a line at the top of the text. + OverLined = 53, + /// Turns off the `Frame` and `Encircled` attributes. + NotFramedOrEncircled = 54, + /// Turns off the `OverLined` attribute. + NotOverLined = 55, +} + +impl Display for Attribute { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + write!(f, "{}", SetAttribute(*self))?; + Ok(()) + } +} + +impl Attribute { + /// Returns a u32 with one bit set, which is the + /// signature of this attribute in the Attributes + /// bitset. + /// + /// The +1 enables storing Reset (whose index is 0) + /// in the bitset Attributes. + #[inline(always)] + pub const fn bytes(self) -> u32 { + 1 << ((self as u32) + 1) + } + /// Returns the SGR attribute value. + /// + /// See + pub fn sgr(self) -> String { + if (self as usize) > 4 && (self as usize) < 9 { + return "4:".to_string() + SGR[self as usize].to_string().as_str(); + } + SGR[self as usize].to_string() + } +} diff --git a/vendor/crossterm/src/style/types/color.rs b/vendor/crossterm/src/style/types/color.rs index b29af71..38cee97 100644 --- a/vendor/crossterm/src/style/types/color.rs +++ b/vendor/crossterm/src/style/types/color.rs @@ -1,476 +1,476 @@ -use std::{convert::AsRef, convert::TryFrom, result::Result, str::FromStr}; - -#[cfg(feature = "serde")] -use std::fmt; - -use crate::style::parse_next_u8; - -/// Represents a color. -/// -/// # Platform-specific Notes -/// -/// The following list of 16 base colors are available for almost all terminals (Windows 7 and 8 included). -/// -/// | Light | Dark | -/// | :--| :-- | -/// | `DarkGrey` | `Black` | -/// | `Red` | `DarkRed` | -/// | `Green` | `DarkGreen` | -/// | `Yellow` | `DarkYellow` | -/// | `Blue` | `DarkBlue` | -/// | `Magenta` | `DarkMagenta` | -/// | `Cyan` | `DarkCyan` | -/// | `White` | `Grey` | -/// -/// Most UNIX terminals and Windows 10 consoles support additional colors. -/// See [`Color::Rgb`] or [`Color::AnsiValue`] for more info. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum Color { - /// Resets the terminal color. - Reset, - - /// Black color. - Black, - - /// Dark grey color. - DarkGrey, - - /// Light red color. - Red, - - /// Dark red color. - DarkRed, - - /// Light green color. - Green, - - /// Dark green color. - DarkGreen, - - /// Light yellow color. - Yellow, - - /// Dark yellow color. - DarkYellow, - - /// Light blue color. - Blue, - - /// Dark blue color. - DarkBlue, - - /// Light magenta color. - Magenta, - - /// Dark magenta color. - DarkMagenta, - - /// Light cyan color. - Cyan, - - /// Dark cyan color. - DarkCyan, - - /// White color. - White, - - /// Grey color. - Grey, - - /// An RGB color. See [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model) for more info. - /// - /// Most UNIX terminals and Windows 10 supported only. - /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info. - Rgb { r: u8, g: u8, b: u8 }, - - /// An ANSI color. See [256 colors - cheat sheet](https://jonasjacek.github.io/colors/) for more info. - /// - /// Most UNIX terminals and Windows 10 supported only. - /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info. - AnsiValue(u8), -} - -impl Color { - /// Parses an ANSI color sequence. - /// - /// # Examples - /// - /// ``` - /// use crossterm::style::Color; - /// - /// assert_eq!(Color::parse_ansi("5;0"), Some(Color::Black)); - /// assert_eq!(Color::parse_ansi("5;26"), Some(Color::AnsiValue(26))); - /// assert_eq!(Color::parse_ansi("2;50;60;70"), Some(Color::Rgb { r: 50, g: 60, b: 70 })); - /// assert_eq!(Color::parse_ansi("invalid color"), None); - /// ``` - /// - /// Currently, 3/4 bit color values aren't supported so return `None`. - /// - /// See also: [`Colored::parse_ansi`](crate::style::Colored::parse_ansi). - pub fn parse_ansi(ansi: &str) -> Option { - Self::parse_ansi_iter(&mut ansi.split(';')) - } - - /// The logic for parse_ansi, takes an iterator of the sequences terms (the numbers between the - /// ';'). It's a separate function so it can be used by both Color::parse_ansi and - /// colored::parse_ansi. - /// Tested in Colored tests. - pub(crate) fn parse_ansi_iter<'a>(values: &mut impl Iterator) -> Option { - let color = match parse_next_u8(values)? { - // 8 bit colors: `5;` - 5 => { - let n = parse_next_u8(values)?; - - use Color::*; - [ - Black, // 0 - DarkRed, // 1 - DarkGreen, // 2 - DarkYellow, // 3 - DarkBlue, // 4 - DarkMagenta, // 5 - DarkCyan, // 6 - Grey, // 7 - DarkGrey, // 8 - Red, // 9 - Green, // 10 - Yellow, // 11 - Blue, // 12 - Magenta, // 13 - Cyan, // 14 - White, // 15 - ] - .get(n as usize) - .copied() - .unwrap_or(Color::AnsiValue(n)) - } - - // 24 bit colors: `2;;;` - 2 => Color::Rgb { - r: parse_next_u8(values)?, - g: parse_next_u8(values)?, - b: parse_next_u8(values)?, - }, - - _ => return None, - }; - // If there's another value, it's unexpected so return None. - if values.next().is_some() { - return None; - } - Some(color) - } -} - -impl TryFrom<&str> for Color { - type Error = (); - - /// Try to create a `Color` from the string representation. This returns an error if the string does not match. - fn try_from(src: &str) -> Result { - let src = src.to_lowercase(); - - match src.as_ref() { - "black" => Ok(Color::Black), - "dark_grey" => Ok(Color::DarkGrey), - "red" => Ok(Color::Red), - "dark_red" => Ok(Color::DarkRed), - "green" => Ok(Color::Green), - "dark_green" => Ok(Color::DarkGreen), - "yellow" => Ok(Color::Yellow), - "dark_yellow" => Ok(Color::DarkYellow), - "blue" => Ok(Color::Blue), - "dark_blue" => Ok(Color::DarkBlue), - "magenta" => Ok(Color::Magenta), - "dark_magenta" => Ok(Color::DarkMagenta), - "cyan" => Ok(Color::Cyan), - "dark_cyan" => Ok(Color::DarkCyan), - "white" => Ok(Color::White), - "grey" => Ok(Color::Grey), - _ => Err(()), - } - } -} - -impl FromStr for Color { - type Err = (); - - /// Creates a `Color` from the string representation. - /// - /// # Notes - /// - /// * Returns `Color::White` in case of an unknown color. - /// * Does not return `Err` and you can safely unwrap. - fn from_str(src: &str) -> Result { - Ok(Color::try_from(src).unwrap_or(Color::White)) - } -} - -impl From<(u8, u8, u8)> for Color { - /// Creates a 'Color' from the tuple representation. - fn from(val: (u8, u8, u8)) -> Self { - let (r, g, b) = val; - Self::Rgb { r, g, b } - } -} - -#[cfg(feature = "serde")] -impl serde::ser::Serialize for Color { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - let str = match *self { - Color::Black => "black", - Color::DarkGrey => "dark_grey", - Color::Red => "red", - Color::DarkRed => "dark_red", - Color::Green => "green", - Color::DarkGreen => "dark_green", - Color::Yellow => "yellow", - Color::DarkYellow => "dark_yellow", - Color::Blue => "blue", - Color::DarkBlue => "dark_blue", - Color::Magenta => "magenta", - Color::DarkMagenta => "dark_magenta", - Color::Cyan => "cyan", - Color::DarkCyan => "dark_cyan", - Color::White => "white", - Color::Grey => "grey", - _ => "", - }; - - if str == "" { - match *self { - Color::AnsiValue(value) => { - return serializer.serialize_str(&format!("ansi_({})", value)); - } - Color::Rgb { r, g, b } => { - return serializer.serialize_str(&format!("rgb_({},{},{})", r, g, b)); - } - _ => { - return Err(serde::ser::Error::custom("Could not serialize enum type")); - } - } - } else { - return serializer.serialize_str(str); - } - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::de::Deserialize<'de> for Color { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - struct ColorVisitor; - impl<'de> serde::de::Visitor<'de> for ColorVisitor { - type Value = Color; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str( - "`black`, `blue`, `dark_blue`, `cyan`, `dark_cyan`, `green`, `dark_green`, `grey`, `dark_grey`, `magenta`, `dark_magenta`, `red`, `dark_red`, `white`, `yellow`, `dark_yellow`, `ansi_(value)`, or `rgb_(r,g,b)`", - ) - } - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - if let Ok(c) = Color::try_from(value) { - Ok(c) - } else { - if value.contains("ansi") { - // strip away `ansi_(..)' and get the inner value between parenthesis. - let results = value.replace("ansi_(", "").replace(")", ""); - - let ansi_val = results.parse::(); - - if let Ok(ansi) = ansi_val { - return Ok(Color::AnsiValue(ansi)); - } - } else if value.contains("rgb") { - // strip away `rgb_(..)' and get the inner values between parenthesis. - let results = value - .replace("rgb_(", "") - .replace(")", "") - .split(',') - .map(|x| x.to_string()) - .collect::>(); - - if results.len() == 3 { - let r = results[0].parse::(); - let g = results[1].parse::(); - let b = results[2].parse::(); - - if r.is_ok() && g.is_ok() && b.is_ok() { - return Ok(Color::Rgb { - r: r.unwrap(), - g: g.unwrap(), - b: b.unwrap(), - }); - } - } - } - - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - } - - deserializer.deserialize_str(ColorVisitor) - } -} - -#[cfg(test)] -mod tests { - use super::Color; - - #[test] - fn test_known_color_conversion() { - assert_eq!("grey".parse(), Ok(Color::Grey)); - assert_eq!("dark_grey".parse(), Ok(Color::DarkGrey)); - assert_eq!("red".parse(), Ok(Color::Red)); - assert_eq!("dark_red".parse(), Ok(Color::DarkRed)); - assert_eq!("green".parse(), Ok(Color::Green)); - assert_eq!("dark_green".parse(), Ok(Color::DarkGreen)); - assert_eq!("yellow".parse(), Ok(Color::Yellow)); - assert_eq!("dark_yellow".parse(), Ok(Color::DarkYellow)); - assert_eq!("blue".parse(), Ok(Color::Blue)); - assert_eq!("dark_blue".parse(), Ok(Color::DarkBlue)); - assert_eq!("magenta".parse(), Ok(Color::Magenta)); - assert_eq!("dark_magenta".parse(), Ok(Color::DarkMagenta)); - assert_eq!("cyan".parse(), Ok(Color::Cyan)); - assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan)); - assert_eq!("white".parse(), Ok(Color::White)); - assert_eq!("black".parse(), Ok(Color::Black)); - } - - #[test] - fn test_unknown_color_conversion_yields_white() { - assert_eq!("foo".parse(), Ok(Color::White)); - } - - #[test] - fn test_know_rgb_color_conversion() { - assert_eq!(Color::from((0, 0, 0)), Color::Rgb { r: 0, g: 0, b: 0 }); - assert_eq!( - Color::from((255, 255, 255)), - Color::Rgb { - r: 255, - g: 255, - b: 255 - } - ); - } -} - -#[cfg(test)] -#[cfg(feature = "serde")] -mod serde_tests { - use super::Color; - use serde_json; - - #[test] - fn test_deserial_known_color_conversion() { - assert_eq!( - serde_json::from_str::("\"Red\"").unwrap(), - Color::Red - ); - assert_eq!( - serde_json::from_str::("\"red\"").unwrap(), - Color::Red - ); - assert_eq!( - serde_json::from_str::("\"dark_red\"").unwrap(), - Color::DarkRed - ); - assert_eq!( - serde_json::from_str::("\"grey\"").unwrap(), - Color::Grey - ); - assert_eq!( - serde_json::from_str::("\"dark_grey\"").unwrap(), - Color::DarkGrey - ); - assert_eq!( - serde_json::from_str::("\"green\"").unwrap(), - Color::Green - ); - assert_eq!( - serde_json::from_str::("\"dark_green\"").unwrap(), - Color::DarkGreen - ); - assert_eq!( - serde_json::from_str::("\"yellow\"").unwrap(), - Color::Yellow - ); - assert_eq!( - serde_json::from_str::("\"dark_yellow\"").unwrap(), - Color::DarkYellow - ); - assert_eq!( - serde_json::from_str::("\"blue\"").unwrap(), - Color::Blue - ); - assert_eq!( - serde_json::from_str::("\"dark_blue\"").unwrap(), - Color::DarkBlue - ); - assert_eq!( - serde_json::from_str::("\"magenta\"").unwrap(), - Color::Magenta - ); - assert_eq!( - serde_json::from_str::("\"dark_magenta\"").unwrap(), - Color::DarkMagenta - ); - assert_eq!( - serde_json::from_str::("\"cyan\"").unwrap(), - Color::Cyan - ); - assert_eq!( - serde_json::from_str::("\"dark_cyan\"").unwrap(), - Color::DarkCyan - ); - assert_eq!( - serde_json::from_str::("\"white\"").unwrap(), - Color::White - ); - assert_eq!( - serde_json::from_str::("\"black\"").unwrap(), - Color::Black - ); - } - - #[test] - fn test_deserial_unknown_color_conversion() { - assert!(serde_json::from_str::("\"unknown\"").is_err()); - } - - #[test] - fn test_deserial_ansi_value() { - assert_eq!( - serde_json::from_str::("\"ansi_(255)\"").unwrap(), - Color::AnsiValue(255) - ); - } - - #[test] - fn test_deserial_unvalid_ansi_value() { - assert!(serde_json::from_str::("\"ansi_(256)\"").is_err()); - assert!(serde_json::from_str::("\"ansi_(-1)\"").is_err()); - } - - #[test] - fn test_deserial_rgb() { - assert_eq!( - serde_json::from_str::("\"rgb_(255,255,255)\"").unwrap(), - Color::from((255, 255, 255)) - ); - } - - #[test] - fn test_deserial_unvalid_rgb() { - assert!(serde_json::from_str::("\"rgb_(255,255,255,255)\"").is_err()); - assert!(serde_json::from_str::("\"rgb_(256,255,255)\"").is_err()); - } -} +use std::{convert::AsRef, convert::TryFrom, result::Result, str::FromStr}; + +#[cfg(feature = "serde")] +use std::fmt; + +use crate::style::parse_next_u8; + +/// Represents a color. +/// +/// # Platform-specific Notes +/// +/// The following list of 16 base colors are available for almost all terminals (Windows 7 and 8 included). +/// +/// | Light | Dark | +/// | :--| :-- | +/// | `DarkGrey` | `Black` | +/// | `Red` | `DarkRed` | +/// | `Green` | `DarkGreen` | +/// | `Yellow` | `DarkYellow` | +/// | `Blue` | `DarkBlue` | +/// | `Magenta` | `DarkMagenta` | +/// | `Cyan` | `DarkCyan` | +/// | `White` | `Grey` | +/// +/// Most UNIX terminals and Windows 10 consoles support additional colors. +/// See [`Color::Rgb`] or [`Color::AnsiValue`] for more info. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum Color { + /// Resets the terminal color. + Reset, + + /// Black color. + Black, + + /// Dark grey color. + DarkGrey, + + /// Light red color. + Red, + + /// Dark red color. + DarkRed, + + /// Light green color. + Green, + + /// Dark green color. + DarkGreen, + + /// Light yellow color. + Yellow, + + /// Dark yellow color. + DarkYellow, + + /// Light blue color. + Blue, + + /// Dark blue color. + DarkBlue, + + /// Light magenta color. + Magenta, + + /// Dark magenta color. + DarkMagenta, + + /// Light cyan color. + Cyan, + + /// Dark cyan color. + DarkCyan, + + /// White color. + White, + + /// Grey color. + Grey, + + /// An RGB color. See [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model) for more info. + /// + /// Most UNIX terminals and Windows 10 supported only. + /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info. + Rgb { r: u8, g: u8, b: u8 }, + + /// An ANSI color. See [256 colors - cheat sheet](https://jonasjacek.github.io/colors/) for more info. + /// + /// Most UNIX terminals and Windows 10 supported only. + /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info. + AnsiValue(u8), +} + +impl Color { + /// Parses an ANSI color sequence. + /// + /// # Examples + /// + /// ``` + /// use crossterm::style::Color; + /// + /// assert_eq!(Color::parse_ansi("5;0"), Some(Color::Black)); + /// assert_eq!(Color::parse_ansi("5;26"), Some(Color::AnsiValue(26))); + /// assert_eq!(Color::parse_ansi("2;50;60;70"), Some(Color::Rgb { r: 50, g: 60, b: 70 })); + /// assert_eq!(Color::parse_ansi("invalid color"), None); + /// ``` + /// + /// Currently, 3/4 bit color values aren't supported so return `None`. + /// + /// See also: [`Colored::parse_ansi`](crate::style::Colored::parse_ansi). + pub fn parse_ansi(ansi: &str) -> Option { + Self::parse_ansi_iter(&mut ansi.split(';')) + } + + /// The logic for parse_ansi, takes an iterator of the sequences terms (the numbers between the + /// ';'). It's a separate function so it can be used by both Color::parse_ansi and + /// colored::parse_ansi. + /// Tested in Colored tests. + pub(crate) fn parse_ansi_iter<'a>(values: &mut impl Iterator) -> Option { + let color = match parse_next_u8(values)? { + // 8 bit colors: `5;` + 5 => { + let n = parse_next_u8(values)?; + + use Color::*; + [ + Black, // 0 + DarkRed, // 1 + DarkGreen, // 2 + DarkYellow, // 3 + DarkBlue, // 4 + DarkMagenta, // 5 + DarkCyan, // 6 + Grey, // 7 + DarkGrey, // 8 + Red, // 9 + Green, // 10 + Yellow, // 11 + Blue, // 12 + Magenta, // 13 + Cyan, // 14 + White, // 15 + ] + .get(n as usize) + .copied() + .unwrap_or(Color::AnsiValue(n)) + } + + // 24 bit colors: `2;;;` + 2 => Color::Rgb { + r: parse_next_u8(values)?, + g: parse_next_u8(values)?, + b: parse_next_u8(values)?, + }, + + _ => return None, + }; + // If there's another value, it's unexpected so return None. + if values.next().is_some() { + return None; + } + Some(color) + } +} + +impl TryFrom<&str> for Color { + type Error = (); + + /// Try to create a `Color` from the string representation. This returns an error if the string does not match. + fn try_from(src: &str) -> Result { + let src = src.to_lowercase(); + + match src.as_ref() { + "black" => Ok(Color::Black), + "dark_grey" => Ok(Color::DarkGrey), + "red" => Ok(Color::Red), + "dark_red" => Ok(Color::DarkRed), + "green" => Ok(Color::Green), + "dark_green" => Ok(Color::DarkGreen), + "yellow" => Ok(Color::Yellow), + "dark_yellow" => Ok(Color::DarkYellow), + "blue" => Ok(Color::Blue), + "dark_blue" => Ok(Color::DarkBlue), + "magenta" => Ok(Color::Magenta), + "dark_magenta" => Ok(Color::DarkMagenta), + "cyan" => Ok(Color::Cyan), + "dark_cyan" => Ok(Color::DarkCyan), + "white" => Ok(Color::White), + "grey" => Ok(Color::Grey), + _ => Err(()), + } + } +} + +impl FromStr for Color { + type Err = (); + + /// Creates a `Color` from the string representation. + /// + /// # Notes + /// + /// * Returns `Color::White` in case of an unknown color. + /// * Does not return `Err` and you can safely unwrap. + fn from_str(src: &str) -> Result { + Ok(Color::try_from(src).unwrap_or(Color::White)) + } +} + +impl From<(u8, u8, u8)> for Color { + /// Creates a 'Color' from the tuple representation. + fn from(val: (u8, u8, u8)) -> Self { + let (r, g, b) = val; + Self::Rgb { r, g, b } + } +} + +#[cfg(feature = "serde")] +impl serde::ser::Serialize for Color { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let str = match *self { + Color::Black => "black", + Color::DarkGrey => "dark_grey", + Color::Red => "red", + Color::DarkRed => "dark_red", + Color::Green => "green", + Color::DarkGreen => "dark_green", + Color::Yellow => "yellow", + Color::DarkYellow => "dark_yellow", + Color::Blue => "blue", + Color::DarkBlue => "dark_blue", + Color::Magenta => "magenta", + Color::DarkMagenta => "dark_magenta", + Color::Cyan => "cyan", + Color::DarkCyan => "dark_cyan", + Color::White => "white", + Color::Grey => "grey", + _ => "", + }; + + if str == "" { + match *self { + Color::AnsiValue(value) => { + return serializer.serialize_str(&format!("ansi_({})", value)); + } + Color::Rgb { r, g, b } => { + return serializer.serialize_str(&format!("rgb_({},{},{})", r, g, b)); + } + _ => { + return Err(serde::ser::Error::custom("Could not serialize enum type")); + } + } + } else { + return serializer.serialize_str(str); + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::de::Deserialize<'de> for Color { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + struct ColorVisitor; + impl<'de> serde::de::Visitor<'de> for ColorVisitor { + type Value = Color; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str( + "`black`, `blue`, `dark_blue`, `cyan`, `dark_cyan`, `green`, `dark_green`, `grey`, `dark_grey`, `magenta`, `dark_magenta`, `red`, `dark_red`, `white`, `yellow`, `dark_yellow`, `ansi_(value)`, or `rgb_(r,g,b)`", + ) + } + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + if let Ok(c) = Color::try_from(value) { + Ok(c) + } else { + if value.contains("ansi") { + // strip away `ansi_(..)' and get the inner value between parenthesis. + let results = value.replace("ansi_(", "").replace(")", ""); + + let ansi_val = results.parse::(); + + if let Ok(ansi) = ansi_val { + return Ok(Color::AnsiValue(ansi)); + } + } else if value.contains("rgb") { + // strip away `rgb_(..)' and get the inner values between parenthesis. + let results = value + .replace("rgb_(", "") + .replace(")", "") + .split(',') + .map(|x| x.to_string()) + .collect::>(); + + if results.len() == 3 { + let r = results[0].parse::(); + let g = results[1].parse::(); + let b = results[2].parse::(); + + if r.is_ok() && g.is_ok() && b.is_ok() { + return Ok(Color::Rgb { + r: r.unwrap(), + g: g.unwrap(), + b: b.unwrap(), + }); + } + } + } + + Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } + } + } + + deserializer.deserialize_str(ColorVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::Color; + + #[test] + fn test_known_color_conversion() { + assert_eq!("grey".parse(), Ok(Color::Grey)); + assert_eq!("dark_grey".parse(), Ok(Color::DarkGrey)); + assert_eq!("red".parse(), Ok(Color::Red)); + assert_eq!("dark_red".parse(), Ok(Color::DarkRed)); + assert_eq!("green".parse(), Ok(Color::Green)); + assert_eq!("dark_green".parse(), Ok(Color::DarkGreen)); + assert_eq!("yellow".parse(), Ok(Color::Yellow)); + assert_eq!("dark_yellow".parse(), Ok(Color::DarkYellow)); + assert_eq!("blue".parse(), Ok(Color::Blue)); + assert_eq!("dark_blue".parse(), Ok(Color::DarkBlue)); + assert_eq!("magenta".parse(), Ok(Color::Magenta)); + assert_eq!("dark_magenta".parse(), Ok(Color::DarkMagenta)); + assert_eq!("cyan".parse(), Ok(Color::Cyan)); + assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan)); + assert_eq!("white".parse(), Ok(Color::White)); + assert_eq!("black".parse(), Ok(Color::Black)); + } + + #[test] + fn test_unknown_color_conversion_yields_white() { + assert_eq!("foo".parse(), Ok(Color::White)); + } + + #[test] + fn test_know_rgb_color_conversion() { + assert_eq!(Color::from((0, 0, 0)), Color::Rgb { r: 0, g: 0, b: 0 }); + assert_eq!( + Color::from((255, 255, 255)), + Color::Rgb { + r: 255, + g: 255, + b: 255 + } + ); + } +} + +#[cfg(test)] +#[cfg(feature = "serde")] +mod serde_tests { + use super::Color; + use serde_json; + + #[test] + fn test_deserial_known_color_conversion() { + assert_eq!( + serde_json::from_str::("\"Red\"").unwrap(), + Color::Red + ); + assert_eq!( + serde_json::from_str::("\"red\"").unwrap(), + Color::Red + ); + assert_eq!( + serde_json::from_str::("\"dark_red\"").unwrap(), + Color::DarkRed + ); + assert_eq!( + serde_json::from_str::("\"grey\"").unwrap(), + Color::Grey + ); + assert_eq!( + serde_json::from_str::("\"dark_grey\"").unwrap(), + Color::DarkGrey + ); + assert_eq!( + serde_json::from_str::("\"green\"").unwrap(), + Color::Green + ); + assert_eq!( + serde_json::from_str::("\"dark_green\"").unwrap(), + Color::DarkGreen + ); + assert_eq!( + serde_json::from_str::("\"yellow\"").unwrap(), + Color::Yellow + ); + assert_eq!( + serde_json::from_str::("\"dark_yellow\"").unwrap(), + Color::DarkYellow + ); + assert_eq!( + serde_json::from_str::("\"blue\"").unwrap(), + Color::Blue + ); + assert_eq!( + serde_json::from_str::("\"dark_blue\"").unwrap(), + Color::DarkBlue + ); + assert_eq!( + serde_json::from_str::("\"magenta\"").unwrap(), + Color::Magenta + ); + assert_eq!( + serde_json::from_str::("\"dark_magenta\"").unwrap(), + Color::DarkMagenta + ); + assert_eq!( + serde_json::from_str::("\"cyan\"").unwrap(), + Color::Cyan + ); + assert_eq!( + serde_json::from_str::("\"dark_cyan\"").unwrap(), + Color::DarkCyan + ); + assert_eq!( + serde_json::from_str::("\"white\"").unwrap(), + Color::White + ); + assert_eq!( + serde_json::from_str::("\"black\"").unwrap(), + Color::Black + ); + } + + #[test] + fn test_deserial_unknown_color_conversion() { + assert!(serde_json::from_str::("\"unknown\"").is_err()); + } + + #[test] + fn test_deserial_ansi_value() { + assert_eq!( + serde_json::from_str::("\"ansi_(255)\"").unwrap(), + Color::AnsiValue(255) + ); + } + + #[test] + fn test_deserial_unvalid_ansi_value() { + assert!(serde_json::from_str::("\"ansi_(256)\"").is_err()); + assert!(serde_json::from_str::("\"ansi_(-1)\"").is_err()); + } + + #[test] + fn test_deserial_rgb() { + assert_eq!( + serde_json::from_str::("\"rgb_(255,255,255)\"").unwrap(), + Color::from((255, 255, 255)) + ); + } + + #[test] + fn test_deserial_unvalid_rgb() { + assert!(serde_json::from_str::("\"rgb_(255,255,255,255)\"").is_err()); + assert!(serde_json::from_str::("\"rgb_(256,255,255)\"").is_err()); + } +} diff --git a/vendor/crossterm/src/style/types/colored.rs b/vendor/crossterm/src/style/types/colored.rs index 34c962e..d0b8fd5 100644 --- a/vendor/crossterm/src/style/types/colored.rs +++ b/vendor/crossterm/src/style/types/colored.rs @@ -1,270 +1,270 @@ -use std::fmt::{self, Formatter}; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use crate::style::{parse_next_u8, Color}; - -/// Represents a foreground or background color. -/// -/// This can be converted to a [Colors](struct.Colors.html) by calling `into()` and applied -/// using the [SetColors](struct.SetColors.html) command. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum Colored { - /// A foreground color. - ForegroundColor(Color), - /// A background color. - BackgroundColor(Color), - /// An underline color. - /// Imporant: doesnt work on windows 10 or lower. - UnderlineColor(Color), -} - -impl Colored { - /// Parse an ANSI foreground or background color. - /// This is the string that would appear within an `ESC [ m` escape sequence, as found in - /// various configuration files. - /// - /// # Examples - /// - /// ``` - /// use crossterm::style::{Colored::{self, ForegroundColor, BackgroundColor}, Color}; - /// - /// assert_eq!(Colored::parse_ansi("38;5;0"), Some(ForegroundColor(Color::Black))); - /// assert_eq!(Colored::parse_ansi("38;5;26"), Some(ForegroundColor(Color::AnsiValue(26)))); - /// assert_eq!(Colored::parse_ansi("48;2;50;60;70"), Some(BackgroundColor(Color::Rgb { r: 50, g: 60, b: 70 }))); - /// assert_eq!(Colored::parse_ansi("49"), Some(BackgroundColor(Color::Reset))); - /// assert_eq!(Colored::parse_ansi("invalid color"), None); - /// ``` - /// - /// Currently, 3/4 bit color values aren't supported so return `None`. - /// - /// See also: [`Color::parse_ansi`]. - pub fn parse_ansi(ansi: &str) -> Option { - use Colored::{BackgroundColor, ForegroundColor, UnderlineColor}; - - let values = &mut ansi.split(';'); - - let output = match parse_next_u8(values)? { - 38 => return Color::parse_ansi_iter(values).map(ForegroundColor), - 48 => return Color::parse_ansi_iter(values).map(BackgroundColor), - 58 => return Color::parse_ansi_iter(values).map(UnderlineColor), - - 39 => ForegroundColor(Color::Reset), - 49 => BackgroundColor(Color::Reset), - 59 => UnderlineColor(Color::Reset), - - _ => return None, - }; - - if values.next().is_some() { - return None; - } - - Some(output) - } -} - -impl fmt::Display for Colored { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let color; - - match *self { - Colored::ForegroundColor(new_color) => { - if new_color == Color::Reset { - return f.write_str("39"); - } else { - f.write_str("38;")?; - color = new_color; - } - } - Colored::BackgroundColor(new_color) => { - if new_color == Color::Reset { - return f.write_str("49"); - } else { - f.write_str("48;")?; - color = new_color; - } - } - Colored::UnderlineColor(new_color) => { - if new_color == Color::Reset { - return f.write_str("59"); - } else { - f.write_str("58;")?; - color = new_color; - } - } - } - - match color { - Color::Black => f.write_str("5;0"), - Color::DarkGrey => f.write_str("5;8"), - Color::Red => f.write_str("5;9"), - Color::DarkRed => f.write_str("5;1"), - Color::Green => f.write_str("5;10"), - Color::DarkGreen => f.write_str("5;2"), - Color::Yellow => f.write_str("5;11"), - Color::DarkYellow => f.write_str("5;3"), - Color::Blue => f.write_str("5;12"), - Color::DarkBlue => f.write_str("5;4"), - Color::Magenta => f.write_str("5;13"), - Color::DarkMagenta => f.write_str("5;5"), - Color::Cyan => f.write_str("5;14"), - Color::DarkCyan => f.write_str("5;6"), - Color::White => f.write_str("5;15"), - Color::Grey => f.write_str("5;7"), - Color::Rgb { r, g, b } => write!(f, "2;{};{};{}", r, g, b), - Color::AnsiValue(val) => write!(f, "5;{}", val), - _ => Ok(()), - } - } -} - -#[cfg(test)] -mod tests { - use crate::style::{Color, Colored}; - - #[test] - fn test_format_fg_color() { - let colored = Colored::ForegroundColor(Color::Red); - assert_eq!(colored.to_string(), "38;5;9"); - } - - #[test] - fn test_format_bg_color() { - let colored = Colored::BackgroundColor(Color::Red); - assert_eq!(colored.to_string(), "48;5;9"); - } - - #[test] - fn test_format_reset_fg_color() { - let colored = Colored::ForegroundColor(Color::Reset); - assert_eq!(colored.to_string(), "39"); - } - - #[test] - fn test_format_reset_bg_color() { - let colored = Colored::BackgroundColor(Color::Reset); - assert_eq!(colored.to_string(), "49"); - } - - #[test] - fn test_format_fg_rgb_color() { - let colored = Colored::BackgroundColor(Color::Rgb { r: 1, g: 2, b: 3 }); - assert_eq!(colored.to_string(), "48;2;1;2;3"); - } - - #[test] - fn test_format_fg_ansi_color() { - let colored = Colored::ForegroundColor(Color::AnsiValue(255)); - assert_eq!(colored.to_string(), "38;5;255"); - } - - #[test] - fn test_parse_ansi_fg() { - test_parse_ansi(Colored::ForegroundColor) - } - - #[test] - fn test_parse_ansi_bg() { - test_parse_ansi(Colored::ForegroundColor) - } - - /// Used for test_parse_ansi_fg and test_parse_ansi_bg - fn test_parse_ansi(bg_or_fg: impl Fn(Color) -> Colored) { - /// Formats a re-parses `color` to check the result. - macro_rules! test { - ($color:expr) => { - let colored = bg_or_fg($color); - assert_eq!(Colored::parse_ansi(&format!("{}", colored)), Some(colored)); - }; - } - - use Color::*; - - test!(Reset); - test!(Black); - test!(DarkGrey); - test!(Red); - test!(DarkRed); - test!(Green); - test!(DarkGreen); - test!(Yellow); - test!(DarkYellow); - test!(Blue); - test!(DarkBlue); - test!(Magenta); - test!(DarkMagenta); - test!(Cyan); - test!(DarkCyan); - test!(White); - test!(Grey); - - // n in 0..=15 will give us the color values above back. - for n in 16..=255 { - test!(AnsiValue(n)); - } - - for r in 0..=255 { - for g in [0, 2, 18, 19, 60, 100, 200, 250, 254, 255].iter().copied() { - for b in [0, 12, 16, 99, 100, 161, 200, 255].iter().copied() { - test!(Rgb { r, g, b }); - } - } - } - } - - #[test] - fn test_parse_invalid_ansi_color() { - /// Checks that trying to parse `s` yields None. - fn test(s: &str) { - assert_eq!(Colored::parse_ansi(s), None); - } - test(""); - test(";"); - test(";;"); - test(";;"); - test("0"); - test("1"); - test("12"); - test("100"); - test("100048949345"); - test("39;"); - test("49;"); - test("39;2"); - test("49;2"); - test("38"); - test("38;"); - test("38;0"); - test("38;5"); - test("38;5;0;"); - test("38;5;0;2"); - test("38;5;80;"); - test("38;5;80;2"); - test("38;5;257"); - test("38;2"); - test("38;2;"); - test("38;2;0"); - test("38;2;0;2"); - test("38;2;0;2;257"); - test("38;2;0;2;25;"); - test("38;2;0;2;25;3"); - test("48"); - test("48;"); - test("48;0"); - test("48;5"); - test("48;5;0;"); - test("48;5;0;2"); - test("48;5;80;"); - test("48;5;80;2"); - test("48;5;257"); - test("48;2"); - test("48;2;"); - test("48;2;0"); - test("48;2;0;2"); - test("48;2;0;2;257"); - test("48;2;0;2;25;"); - test("48;2;0;2;25;3"); - } -} +use std::fmt::{self, Formatter}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use crate::style::{parse_next_u8, Color}; + +/// Represents a foreground or background color. +/// +/// This can be converted to a [Colors](struct.Colors.html) by calling `into()` and applied +/// using the [SetColors](struct.SetColors.html) command. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum Colored { + /// A foreground color. + ForegroundColor(Color), + /// A background color. + BackgroundColor(Color), + /// An underline color. + /// Imporant: doesnt work on windows 10 or lower. + UnderlineColor(Color), +} + +impl Colored { + /// Parse an ANSI foreground or background color. + /// This is the string that would appear within an `ESC [ m` escape sequence, as found in + /// various configuration files. + /// + /// # Examples + /// + /// ``` + /// use crossterm::style::{Colored::{self, ForegroundColor, BackgroundColor}, Color}; + /// + /// assert_eq!(Colored::parse_ansi("38;5;0"), Some(ForegroundColor(Color::Black))); + /// assert_eq!(Colored::parse_ansi("38;5;26"), Some(ForegroundColor(Color::AnsiValue(26)))); + /// assert_eq!(Colored::parse_ansi("48;2;50;60;70"), Some(BackgroundColor(Color::Rgb { r: 50, g: 60, b: 70 }))); + /// assert_eq!(Colored::parse_ansi("49"), Some(BackgroundColor(Color::Reset))); + /// assert_eq!(Colored::parse_ansi("invalid color"), None); + /// ``` + /// + /// Currently, 3/4 bit color values aren't supported so return `None`. + /// + /// See also: [`Color::parse_ansi`]. + pub fn parse_ansi(ansi: &str) -> Option { + use Colored::{BackgroundColor, ForegroundColor, UnderlineColor}; + + let values = &mut ansi.split(';'); + + let output = match parse_next_u8(values)? { + 38 => return Color::parse_ansi_iter(values).map(ForegroundColor), + 48 => return Color::parse_ansi_iter(values).map(BackgroundColor), + 58 => return Color::parse_ansi_iter(values).map(UnderlineColor), + + 39 => ForegroundColor(Color::Reset), + 49 => BackgroundColor(Color::Reset), + 59 => UnderlineColor(Color::Reset), + + _ => return None, + }; + + if values.next().is_some() { + return None; + } + + Some(output) + } +} + +impl fmt::Display for Colored { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let color; + + match *self { + Colored::ForegroundColor(new_color) => { + if new_color == Color::Reset { + return f.write_str("39"); + } else { + f.write_str("38;")?; + color = new_color; + } + } + Colored::BackgroundColor(new_color) => { + if new_color == Color::Reset { + return f.write_str("49"); + } else { + f.write_str("48;")?; + color = new_color; + } + } + Colored::UnderlineColor(new_color) => { + if new_color == Color::Reset { + return f.write_str("59"); + } else { + f.write_str("58;")?; + color = new_color; + } + } + } + + match color { + Color::Black => f.write_str("5;0"), + Color::DarkGrey => f.write_str("5;8"), + Color::Red => f.write_str("5;9"), + Color::DarkRed => f.write_str("5;1"), + Color::Green => f.write_str("5;10"), + Color::DarkGreen => f.write_str("5;2"), + Color::Yellow => f.write_str("5;11"), + Color::DarkYellow => f.write_str("5;3"), + Color::Blue => f.write_str("5;12"), + Color::DarkBlue => f.write_str("5;4"), + Color::Magenta => f.write_str("5;13"), + Color::DarkMagenta => f.write_str("5;5"), + Color::Cyan => f.write_str("5;14"), + Color::DarkCyan => f.write_str("5;6"), + Color::White => f.write_str("5;15"), + Color::Grey => f.write_str("5;7"), + Color::Rgb { r, g, b } => write!(f, "2;{};{};{}", r, g, b), + Color::AnsiValue(val) => write!(f, "5;{}", val), + _ => Ok(()), + } + } +} + +#[cfg(test)] +mod tests { + use crate::style::{Color, Colored}; + + #[test] + fn test_format_fg_color() { + let colored = Colored::ForegroundColor(Color::Red); + assert_eq!(colored.to_string(), "38;5;9"); + } + + #[test] + fn test_format_bg_color() { + let colored = Colored::BackgroundColor(Color::Red); + assert_eq!(colored.to_string(), "48;5;9"); + } + + #[test] + fn test_format_reset_fg_color() { + let colored = Colored::ForegroundColor(Color::Reset); + assert_eq!(colored.to_string(), "39"); + } + + #[test] + fn test_format_reset_bg_color() { + let colored = Colored::BackgroundColor(Color::Reset); + assert_eq!(colored.to_string(), "49"); + } + + #[test] + fn test_format_fg_rgb_color() { + let colored = Colored::BackgroundColor(Color::Rgb { r: 1, g: 2, b: 3 }); + assert_eq!(colored.to_string(), "48;2;1;2;3"); + } + + #[test] + fn test_format_fg_ansi_color() { + let colored = Colored::ForegroundColor(Color::AnsiValue(255)); + assert_eq!(colored.to_string(), "38;5;255"); + } + + #[test] + fn test_parse_ansi_fg() { + test_parse_ansi(Colored::ForegroundColor) + } + + #[test] + fn test_parse_ansi_bg() { + test_parse_ansi(Colored::ForegroundColor) + } + + /// Used for test_parse_ansi_fg and test_parse_ansi_bg + fn test_parse_ansi(bg_or_fg: impl Fn(Color) -> Colored) { + /// Formats a re-parses `color` to check the result. + macro_rules! test { + ($color:expr) => { + let colored = bg_or_fg($color); + assert_eq!(Colored::parse_ansi(&format!("{}", colored)), Some(colored)); + }; + } + + use Color::*; + + test!(Reset); + test!(Black); + test!(DarkGrey); + test!(Red); + test!(DarkRed); + test!(Green); + test!(DarkGreen); + test!(Yellow); + test!(DarkYellow); + test!(Blue); + test!(DarkBlue); + test!(Magenta); + test!(DarkMagenta); + test!(Cyan); + test!(DarkCyan); + test!(White); + test!(Grey); + + // n in 0..=15 will give us the color values above back. + for n in 16..=255 { + test!(AnsiValue(n)); + } + + for r in 0..=255 { + for g in [0, 2, 18, 19, 60, 100, 200, 250, 254, 255].iter().copied() { + for b in [0, 12, 16, 99, 100, 161, 200, 255].iter().copied() { + test!(Rgb { r, g, b }); + } + } + } + } + + #[test] + fn test_parse_invalid_ansi_color() { + /// Checks that trying to parse `s` yields None. + fn test(s: &str) { + assert_eq!(Colored::parse_ansi(s), None); + } + test(""); + test(";"); + test(";;"); + test(";;"); + test("0"); + test("1"); + test("12"); + test("100"); + test("100048949345"); + test("39;"); + test("49;"); + test("39;2"); + test("49;2"); + test("38"); + test("38;"); + test("38;0"); + test("38;5"); + test("38;5;0;"); + test("38;5;0;2"); + test("38;5;80;"); + test("38;5;80;2"); + test("38;5;257"); + test("38;2"); + test("38;2;"); + test("38;2;0"); + test("38;2;0;2"); + test("38;2;0;2;257"); + test("38;2;0;2;25;"); + test("38;2;0;2;25;3"); + test("48"); + test("48;"); + test("48;0"); + test("48;5"); + test("48;5;0;"); + test("48;5;0;2"); + test("48;5;80;"); + test("48;5;80;2"); + test("48;5;257"); + test("48;2"); + test("48;2;"); + test("48;2;0"); + test("48;2;0;2"); + test("48;2;0;2;257"); + test("48;2;0;2;25;"); + test("48;2;0;2;25;3"); + } +} diff --git a/vendor/crossterm/src/style/types/colors.rs b/vendor/crossterm/src/style/types/colors.rs index 0b4afbf..1cf8cff 100644 --- a/vendor/crossterm/src/style/types/colors.rs +++ b/vendor/crossterm/src/style/types/colors.rs @@ -1,234 +1,234 @@ -use crate::style::{Color, Colored}; - -/// Represents, optionally, a foreground and/or a background color. -/// -/// It can be applied using the `SetColors` command. -/// -/// It can also be created from a [Colored](enum.Colored.html) value or a tuple of -/// `(Color, Color)` in the order `(foreground, background)`. -/// -/// The [then](#method.then) method can be used to combine `Colors` values. -/// -/// For example: -/// ```no_run -/// use crossterm::style::{Color, Colors, Colored}; -/// -/// // An example color, loaded from a config, file in ANSI format. -/// let config_color = "38;2;23;147;209"; -/// -/// // Default to green text on a black background. -/// let default_colors = Colors::new(Color::Green, Color::Black); -/// // Load a colored value from a config and override the default colors -/// let colors = match Colored::parse_ansi(config_color) { -/// Some(colored) => default_colors.then(&colored.into()), -/// None => default_colors, -/// }; -/// ``` -/// -/// See [Color](enum.Color.html). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Colors { - pub foreground: Option, - pub background: Option, -} - -impl Colors { - /// Returns a new `Color` which, when applied, has the same effect as applying `self` and *then* - /// `other`. - pub fn then(&self, other: &Colors) -> Colors { - Colors { - foreground: other.foreground.or(self.foreground), - background: other.background.or(self.background), - } - } -} - -impl Colors { - pub fn new(foreground: Color, background: Color) -> Colors { - Colors { - foreground: Some(foreground), - background: Some(background), - } - } -} - -impl From for Colors { - fn from(colored: Colored) -> Colors { - match colored { - Colored::ForegroundColor(color) => Colors { - foreground: Some(color), - background: None, - }, - Colored::BackgroundColor(color) => Colors { - foreground: None, - background: Some(color), - }, - Colored::UnderlineColor(color) => Colors { - foreground: None, - background: Some(color), - }, - } - } -} - -#[cfg(test)] -mod tests { - use crate::style::{Color, Colors}; - - #[test] - fn test_colors_then() { - use Color::*; - - assert_eq!( - Colors { - foreground: None, - background: None, - } - .then(&Colors { - foreground: None, - background: None, - }), - Colors { - foreground: None, - background: None, - } - ); - - assert_eq!( - Colors { - foreground: None, - background: None, - } - .then(&Colors { - foreground: Some(Black), - background: None, - }), - Colors { - foreground: Some(Black), - background: None, - } - ); - - assert_eq!( - Colors { - foreground: None, - background: None, - } - .then(&Colors { - foreground: None, - background: Some(Grey), - }), - Colors { - foreground: None, - background: Some(Grey), - } - ); - - assert_eq!( - Colors { - foreground: None, - background: None, - } - .then(&Colors::new(White, Grey)), - Colors::new(White, Grey), - ); - - assert_eq!( - Colors { - foreground: None, - background: Some(Blue), - } - .then(&Colors::new(White, Grey)), - Colors::new(White, Grey), - ); - - assert_eq!( - Colors { - foreground: Some(Blue), - background: None, - } - .then(&Colors::new(White, Grey)), - Colors::new(White, Grey), - ); - - assert_eq!( - Colors::new(Blue, Green).then(&Colors::new(White, Grey)), - Colors::new(White, Grey), - ); - - assert_eq!( - Colors { - foreground: Some(Blue), - background: Some(Green), - } - .then(&Colors { - foreground: None, - background: Some(Grey), - }), - Colors { - foreground: Some(Blue), - background: Some(Grey), - } - ); - - assert_eq!( - Colors { - foreground: Some(Blue), - background: Some(Green), - } - .then(&Colors { - foreground: Some(White), - background: None, - }), - Colors { - foreground: Some(White), - background: Some(Green), - } - ); - - assert_eq!( - Colors { - foreground: Some(Blue), - background: Some(Green), - } - .then(&Colors { - foreground: None, - background: None, - }), - Colors { - foreground: Some(Blue), - background: Some(Green), - } - ); - - assert_eq!( - Colors { - foreground: None, - background: Some(Green), - } - .then(&Colors { - foreground: None, - background: None, - }), - Colors { - foreground: None, - background: Some(Green), - } - ); - - assert_eq!( - Colors { - foreground: Some(Blue), - background: None, - } - .then(&Colors { - foreground: None, - background: None, - }), - Colors { - foreground: Some(Blue), - background: None, - } - ); - } -} +use crate::style::{Color, Colored}; + +/// Represents, optionally, a foreground and/or a background color. +/// +/// It can be applied using the `SetColors` command. +/// +/// It can also be created from a [Colored](enum.Colored.html) value or a tuple of +/// `(Color, Color)` in the order `(foreground, background)`. +/// +/// The [then](#method.then) method can be used to combine `Colors` values. +/// +/// For example: +/// ```no_run +/// use crossterm::style::{Color, Colors, Colored}; +/// +/// // An example color, loaded from a config, file in ANSI format. +/// let config_color = "38;2;23;147;209"; +/// +/// // Default to green text on a black background. +/// let default_colors = Colors::new(Color::Green, Color::Black); +/// // Load a colored value from a config and override the default colors +/// let colors = match Colored::parse_ansi(config_color) { +/// Some(colored) => default_colors.then(&colored.into()), +/// None => default_colors, +/// }; +/// ``` +/// +/// See [Color](enum.Color.html). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Colors { + pub foreground: Option, + pub background: Option, +} + +impl Colors { + /// Returns a new `Color` which, when applied, has the same effect as applying `self` and *then* + /// `other`. + pub fn then(&self, other: &Colors) -> Colors { + Colors { + foreground: other.foreground.or(self.foreground), + background: other.background.or(self.background), + } + } +} + +impl Colors { + pub fn new(foreground: Color, background: Color) -> Colors { + Colors { + foreground: Some(foreground), + background: Some(background), + } + } +} + +impl From for Colors { + fn from(colored: Colored) -> Colors { + match colored { + Colored::ForegroundColor(color) => Colors { + foreground: Some(color), + background: None, + }, + Colored::BackgroundColor(color) => Colors { + foreground: None, + background: Some(color), + }, + Colored::UnderlineColor(color) => Colors { + foreground: None, + background: Some(color), + }, + } + } +} + +#[cfg(test)] +mod tests { + use crate::style::{Color, Colors}; + + #[test] + fn test_colors_then() { + use Color::*; + + assert_eq!( + Colors { + foreground: None, + background: None, + } + .then(&Colors { + foreground: None, + background: None, + }), + Colors { + foreground: None, + background: None, + } + ); + + assert_eq!( + Colors { + foreground: None, + background: None, + } + .then(&Colors { + foreground: Some(Black), + background: None, + }), + Colors { + foreground: Some(Black), + background: None, + } + ); + + assert_eq!( + Colors { + foreground: None, + background: None, + } + .then(&Colors { + foreground: None, + background: Some(Grey), + }), + Colors { + foreground: None, + background: Some(Grey), + } + ); + + assert_eq!( + Colors { + foreground: None, + background: None, + } + .then(&Colors::new(White, Grey)), + Colors::new(White, Grey), + ); + + assert_eq!( + Colors { + foreground: None, + background: Some(Blue), + } + .then(&Colors::new(White, Grey)), + Colors::new(White, Grey), + ); + + assert_eq!( + Colors { + foreground: Some(Blue), + background: None, + } + .then(&Colors::new(White, Grey)), + Colors::new(White, Grey), + ); + + assert_eq!( + Colors::new(Blue, Green).then(&Colors::new(White, Grey)), + Colors::new(White, Grey), + ); + + assert_eq!( + Colors { + foreground: Some(Blue), + background: Some(Green), + } + .then(&Colors { + foreground: None, + background: Some(Grey), + }), + Colors { + foreground: Some(Blue), + background: Some(Grey), + } + ); + + assert_eq!( + Colors { + foreground: Some(Blue), + background: Some(Green), + } + .then(&Colors { + foreground: Some(White), + background: None, + }), + Colors { + foreground: Some(White), + background: Some(Green), + } + ); + + assert_eq!( + Colors { + foreground: Some(Blue), + background: Some(Green), + } + .then(&Colors { + foreground: None, + background: None, + }), + Colors { + foreground: Some(Blue), + background: Some(Green), + } + ); + + assert_eq!( + Colors { + foreground: None, + background: Some(Green), + } + .then(&Colors { + foreground: None, + background: None, + }), + Colors { + foreground: None, + background: Some(Green), + } + ); + + assert_eq!( + Colors { + foreground: Some(Blue), + background: None, + } + .then(&Colors { + foreground: None, + background: None, + }), + Colors { + foreground: Some(Blue), + background: None, + } + ); + } +} diff --git a/vendor/crossterm/src/terminal.rs b/vendor/crossterm/src/terminal.rs index b2d4449..6acaeaf 100644 --- a/vendor/crossterm/src/terminal.rs +++ b/vendor/crossterm/src/terminal.rs @@ -1,442 +1,442 @@ -//! # Terminal -//! -//! The `terminal` module provides functionality to work with the terminal. -//! -//! This documentation does not contain a lot of examples. The reason is that it's fairly -//! obvious how to use this crate. Although, we do provide -//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository -//! to demonstrate the capabilities. -//! -//! Most terminal actions can be performed with commands. -//! Please have a look at [command documentation](../index.html#command-api) for a more detailed documentation. -//! -//! ## Screen Buffer -//! -//! A screen buffer is a two-dimensional array of character -//! and color data which is displayed in a terminal screen. -//! -//! The terminal has several of those buffers and is able to switch between them. -//! The default screen in which you work is called the 'main screen'. -//! The other screens are called the 'alternative screen'. -//! -//! It is important to understand that crossterm does not yet support creating screens, -//! or switch between more than two buffers, and only offers the ability to change -//! between the 'alternate' and 'main screen'. -//! -//! ### Alternate Screen -//! -//! By default, you will be working on the main screen. -//! There is also another screen called the 'alternative' screen. -//! This screen is slightly different from the main screen. -//! For example, it has the exact dimensions of the terminal window, -//! without any scroll-back area. -//! -//! Crossterm offers the possibility to switch to the 'alternative' screen, -//! make some modifications, and move back to the 'main' screen again. -//! The main screen will stay intact and will have the original data as we performed all -//! operations on the alternative screen. -//! -//! An good example of this is Vim. -//! When it is launched from bash, a whole new buffer is used to modify a file. -//! Then, when the modification is finished, it closes again and continues on the main screen. -//! -//! ### Raw Mode -//! -//! By default, the terminal functions in a certain way. -//! For example, it will move the cursor to the beginning of the next line when the input hits the end of a line. -//! Or that the backspace is interpreted for character removal. -//! -//! Sometimes these default modes are irrelevant, -//! and in this case, we can turn them off. -//! This is what happens when you enable raw modes. -//! -//! Those modes will be set when enabling raw modes: -//! -//! - Input will not be forwarded to screen -//! - Input will not be processed on enter press -//! - Input will not be line buffered (input sent byte-by-byte to input buffer) -//! - Special keys like backspace and CTRL+C will not be processed by terminal driver -//! - New line character will not be processed therefore `println!` can't be used, use `write!` instead -//! -//! Raw mode can be enabled/disabled with the [enable_raw_mode](terminal::enable_raw_mode) and [disable_raw_mode](terminal::disable_raw_mode) functions. -//! -//! ## Examples -//! -//! ```no_run -//! use std::io::{stdout, Write}; -//! use crossterm::{execute, Result, terminal::{ScrollUp, SetSize, size}}; -//! -//! fn main() -> Result<()> { -//! let (cols, rows) = size()?; -//! // Resize terminal and scroll up. -//! execute!( -//! stdout(), -//! SetSize(10, 10), -//! ScrollUp(5) -//! )?; -//! -//! // Be a good citizen, cleanup -//! execute!(stdout(), SetSize(cols, rows))?; -//! Ok(()) -//! } -//! ``` -//! -//! For manual execution control check out [crossterm::queue](../macro.queue.html). - -use std::fmt; - -#[cfg(windows)] -use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(windows)] -use winapi::um::wincon::ENABLE_WRAP_AT_EOL_OUTPUT; - -#[doc(no_inline)] -use crate::Command; -use crate::{csi, impl_display, Result}; - -pub(crate) mod sys; - -/// Tells whether the raw mode is enabled. -/// -/// Please have a look at the [raw mode](./index.html#raw-mode) section. -pub fn is_raw_mode_enabled() -> Result { - #[cfg(unix)] - { - Ok(sys::is_raw_mode_enabled()) - } - - #[cfg(windows)] - { - sys::is_raw_mode_enabled() - } -} - -/// Enables raw mode. -/// -/// Please have a look at the [raw mode](./index.html#raw-mode) section. -pub fn enable_raw_mode() -> Result<()> { - sys::enable_raw_mode() -} - -/// Disables raw mode. -/// -/// Please have a look at the [raw mode](./index.html#raw-mode) section. -pub fn disable_raw_mode() -> Result<()> { - sys::disable_raw_mode() -} - -/// Returns the terminal size `(columns, rows)`. -/// -/// The top left cell is represented `(1, 1)`. -pub fn size() -> Result<(u16, u16)> { - sys::size() -} - -/// Disables line wrapping. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DisableLineWrap; - -impl Command for DisableLineWrap { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?7l")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - let console_mode = ConsoleMode::from(screen_buffer.handle().clone()); - let new_mode = console_mode.mode()? & !ENABLE_WRAP_AT_EOL_OUTPUT; - console_mode.set_mode(new_mode)?; - Ok(()) - } -} - -/// Enable line wrapping. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EnableLineWrap; - -impl Command for EnableLineWrap { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?7h")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - let console_mode = ConsoleMode::from(screen_buffer.handle().clone()); - let new_mode = console_mode.mode()? | ENABLE_WRAP_AT_EOL_OUTPUT; - console_mode.set_mode(new_mode)?; - Ok(()) - } -} - -/// A command that switches to alternate screen. -/// -/// # Notes -/// -/// * Commands must be executed/queued for execution otherwise they do nothing. -/// * Use [LeaveAlternateScreen](./struct.LeaveAlternateScreen.html) command to leave the entered alternate screen. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::{stdout, Write}; -/// use crossterm::{execute, Result, terminal::{EnterAlternateScreen, LeaveAlternateScreen}}; -/// -/// fn main() -> Result<()> { -/// execute!(stdout(), EnterAlternateScreen)?; -/// -/// // Do anything on the alternate screen -/// -/// execute!(stdout(), LeaveAlternateScreen) -/// } -/// ``` -/// -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EnterAlternateScreen; - -impl Command for EnterAlternateScreen { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?1049h")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - let alternate_screen = ScreenBuffer::create()?; - alternate_screen.show()?; - Ok(()) - } -} - -/// A command that switches back to the main screen. -/// -/// # Notes -/// -/// * Commands must be executed/queued for execution otherwise they do nothing. -/// * Use [EnterAlternateScreen](./struct.EnterAlternateScreen.html) to enter the alternate screen. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::{stdout, Write}; -/// use crossterm::{execute, Result, terminal::{EnterAlternateScreen, LeaveAlternateScreen}}; -/// -/// fn main() -> Result<()> { -/// execute!(stdout(), EnterAlternateScreen)?; -/// -/// // Do anything on the alternate screen -/// -/// execute!(stdout(), LeaveAlternateScreen) -/// } -/// ``` -/// -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct LeaveAlternateScreen; - -impl Command for LeaveAlternateScreen { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(csi!("?1049l")) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - let screen_buffer = ScreenBuffer::from(Handle::current_out_handle()?); - screen_buffer.show()?; - Ok(()) - } -} - -/// Different ways to clear the terminal buffer. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum ClearType { - /// All cells. - All, - /// All plus history - Purge, - /// All cells from the cursor position downwards. - FromCursorDown, - /// All cells from the cursor position upwards. - FromCursorUp, - /// All cells at the cursor row. - CurrentLine, - /// All cells from the cursor position until the new line. - UntilNewLine, -} - -/// A command that scrolls the terminal screen a given number of rows up. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ScrollUp(pub u16); - -impl Command for ScrollUp { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - if self.0 != 0 { - write!(f, csi!("{}S"), self.0)?; - } - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::scroll_up(self.0) - } -} - -/// A command that scrolls the terminal screen a given number of rows down. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ScrollDown(pub u16); - -impl Command for ScrollDown { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - if self.0 != 0 { - write!(f, csi!("{}T"), self.0)?; - } - Ok(()) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::scroll_down(self.0) - } -} - -/// A command that clears the terminal screen buffer. -/// -/// See the [`ClearType`](enum.ClearType.html) enum. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Clear(pub ClearType); - -impl Command for Clear { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - f.write_str(match self.0 { - ClearType::All => csi!("2J"), - ClearType::Purge => csi!("3J"), - ClearType::FromCursorDown => csi!("J"), - ClearType::FromCursorUp => csi!("1J"), - ClearType::CurrentLine => csi!("2K"), - ClearType::UntilNewLine => csi!("K"), - }) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::clear(self.0) - } -} - -/// A command that sets the terminal buffer size `(columns, rows)`. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetSize(pub u16, pub u16); - -impl Command for SetSize { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, csi!("8;{};{}t"), self.1, self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::set_size(self.0, self.1) - } -} - -/// A command that sets the terminal title -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SetTitle(pub T); - -impl Command for SetTitle { - fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { - write!(f, "\x1B]0;{}\x07", &self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::set_window_title(&self.0) - } -} - -impl_display!(for ScrollUp); -impl_display!(for ScrollDown); -impl_display!(for SetSize); -impl_display!(for Clear); - -#[cfg(test)] -mod tests { - use std::{io::stdout, thread, time}; - - use crate::execute; - - use super::*; - - // Test is disabled, because it's failing on Travis CI - #[test] - #[ignore] - fn test_resize_ansi() { - let (width, height) = size().unwrap(); - - execute!(stdout(), SetSize(35, 35)).unwrap(); - - // see issue: https://github.com/eminence/terminal-size/issues/11 - thread::sleep(time::Duration::from_millis(30)); - - assert_eq!((35, 35), size().unwrap()); - - // reset to previous size - execute!(stdout(), SetSize(width, height)).unwrap(); - - // see issue: https://github.com/eminence/terminal-size/issues/11 - thread::sleep(time::Duration::from_millis(30)); - - assert_eq!((width, height), size().unwrap()); - } - - #[test] - fn test_raw_mode() { - // check we start from normal mode (may fail on some test harnesses) - assert!(!is_raw_mode_enabled().unwrap()); - - // enable the raw mode - if enable_raw_mode().is_err() { - // Enabling raw mode doesn't work on the ci - // So we just ignore it - return; - } - - // check it worked (on unix it doesn't really check the underlying - // tty but rather check that the code is consistent) - assert!(is_raw_mode_enabled().unwrap()); - - // enable it again, this should not change anything - enable_raw_mode().unwrap(); - - // check we're still in raw mode - assert!(is_raw_mode_enabled().unwrap()); - - // now let's disable it - disable_raw_mode().unwrap(); - - // check we're back to normal mode - assert!(!is_raw_mode_enabled().unwrap()); - } -} +//! # Terminal +//! +//! The `terminal` module provides functionality to work with the terminal. +//! +//! This documentation does not contain a lot of examples. The reason is that it's fairly +//! obvious how to use this crate. Although, we do provide +//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository +//! to demonstrate the capabilities. +//! +//! Most terminal actions can be performed with commands. +//! Please have a look at [command documentation](../index.html#command-api) for a more detailed documentation. +//! +//! ## Screen Buffer +//! +//! A screen buffer is a two-dimensional array of character +//! and color data which is displayed in a terminal screen. +//! +//! The terminal has several of those buffers and is able to switch between them. +//! The default screen in which you work is called the 'main screen'. +//! The other screens are called the 'alternative screen'. +//! +//! It is important to understand that crossterm does not yet support creating screens, +//! or switch between more than two buffers, and only offers the ability to change +//! between the 'alternate' and 'main screen'. +//! +//! ### Alternate Screen +//! +//! By default, you will be working on the main screen. +//! There is also another screen called the 'alternative' screen. +//! This screen is slightly different from the main screen. +//! For example, it has the exact dimensions of the terminal window, +//! without any scroll-back area. +//! +//! Crossterm offers the possibility to switch to the 'alternative' screen, +//! make some modifications, and move back to the 'main' screen again. +//! The main screen will stay intact and will have the original data as we performed all +//! operations on the alternative screen. +//! +//! An good example of this is Vim. +//! When it is launched from bash, a whole new buffer is used to modify a file. +//! Then, when the modification is finished, it closes again and continues on the main screen. +//! +//! ### Raw Mode +//! +//! By default, the terminal functions in a certain way. +//! For example, it will move the cursor to the beginning of the next line when the input hits the end of a line. +//! Or that the backspace is interpreted for character removal. +//! +//! Sometimes these default modes are irrelevant, +//! and in this case, we can turn them off. +//! This is what happens when you enable raw modes. +//! +//! Those modes will be set when enabling raw modes: +//! +//! - Input will not be forwarded to screen +//! - Input will not be processed on enter press +//! - Input will not be line buffered (input sent byte-by-byte to input buffer) +//! - Special keys like backspace and CTRL+C will not be processed by terminal driver +//! - New line character will not be processed therefore `println!` can't be used, use `write!` instead +//! +//! Raw mode can be enabled/disabled with the [enable_raw_mode](terminal::enable_raw_mode) and [disable_raw_mode](terminal::disable_raw_mode) functions. +//! +//! ## Examples +//! +//! ```no_run +//! use std::io::{stdout, Write}; +//! use crossterm::{execute, Result, terminal::{ScrollUp, SetSize, size}}; +//! +//! fn main() -> Result<()> { +//! let (cols, rows) = size()?; +//! // Resize terminal and scroll up. +//! execute!( +//! stdout(), +//! SetSize(10, 10), +//! ScrollUp(5) +//! )?; +//! +//! // Be a good citizen, cleanup +//! execute!(stdout(), SetSize(cols, rows))?; +//! Ok(()) +//! } +//! ``` +//! +//! For manual execution control check out [crossterm::queue](../macro.queue.html). + +use std::fmt; + +#[cfg(windows)] +use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(windows)] +use winapi::um::wincon::ENABLE_WRAP_AT_EOL_OUTPUT; + +#[doc(no_inline)] +use crate::Command; +use crate::{csi, impl_display, Result}; + +pub(crate) mod sys; + +/// Tells whether the raw mode is enabled. +/// +/// Please have a look at the [raw mode](./index.html#raw-mode) section. +pub fn is_raw_mode_enabled() -> Result { + #[cfg(unix)] + { + Ok(sys::is_raw_mode_enabled()) + } + + #[cfg(windows)] + { + sys::is_raw_mode_enabled() + } +} + +/// Enables raw mode. +/// +/// Please have a look at the [raw mode](./index.html#raw-mode) section. +pub fn enable_raw_mode() -> Result<()> { + sys::enable_raw_mode() +} + +/// Disables raw mode. +/// +/// Please have a look at the [raw mode](./index.html#raw-mode) section. +pub fn disable_raw_mode() -> Result<()> { + sys::disable_raw_mode() +} + +/// Returns the terminal size `(columns, rows)`. +/// +/// The top left cell is represented `(1, 1)`. +pub fn size() -> Result<(u16, u16)> { + sys::size() +} + +/// Disables line wrapping. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DisableLineWrap; + +impl Command for DisableLineWrap { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?7l")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + let screen_buffer = ScreenBuffer::current()?; + let console_mode = ConsoleMode::from(screen_buffer.handle().clone()); + let new_mode = console_mode.mode()? & !ENABLE_WRAP_AT_EOL_OUTPUT; + console_mode.set_mode(new_mode)?; + Ok(()) + } +} + +/// Enable line wrapping. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EnableLineWrap; + +impl Command for EnableLineWrap { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?7h")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + let screen_buffer = ScreenBuffer::current()?; + let console_mode = ConsoleMode::from(screen_buffer.handle().clone()); + let new_mode = console_mode.mode()? | ENABLE_WRAP_AT_EOL_OUTPUT; + console_mode.set_mode(new_mode)?; + Ok(()) + } +} + +/// A command that switches to alternate screen. +/// +/// # Notes +/// +/// * Commands must be executed/queued for execution otherwise they do nothing. +/// * Use [LeaveAlternateScreen](./struct.LeaveAlternateScreen.html) command to leave the entered alternate screen. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{stdout, Write}; +/// use crossterm::{execute, Result, terminal::{EnterAlternateScreen, LeaveAlternateScreen}}; +/// +/// fn main() -> Result<()> { +/// execute!(stdout(), EnterAlternateScreen)?; +/// +/// // Do anything on the alternate screen +/// +/// execute!(stdout(), LeaveAlternateScreen) +/// } +/// ``` +/// +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EnterAlternateScreen; + +impl Command for EnterAlternateScreen { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?1049h")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + let alternate_screen = ScreenBuffer::create()?; + alternate_screen.show()?; + Ok(()) + } +} + +/// A command that switches back to the main screen. +/// +/// # Notes +/// +/// * Commands must be executed/queued for execution otherwise they do nothing. +/// * Use [EnterAlternateScreen](./struct.EnterAlternateScreen.html) to enter the alternate screen. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{stdout, Write}; +/// use crossterm::{execute, Result, terminal::{EnterAlternateScreen, LeaveAlternateScreen}}; +/// +/// fn main() -> Result<()> { +/// execute!(stdout(), EnterAlternateScreen)?; +/// +/// // Do anything on the alternate screen +/// +/// execute!(stdout(), LeaveAlternateScreen) +/// } +/// ``` +/// +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct LeaveAlternateScreen; + +impl Command for LeaveAlternateScreen { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(csi!("?1049l")) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + let screen_buffer = ScreenBuffer::from(Handle::current_out_handle()?); + screen_buffer.show()?; + Ok(()) + } +} + +/// Different ways to clear the terminal buffer. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum ClearType { + /// All cells. + All, + /// All plus history + Purge, + /// All cells from the cursor position downwards. + FromCursorDown, + /// All cells from the cursor position upwards. + FromCursorUp, + /// All cells at the cursor row. + CurrentLine, + /// All cells from the cursor position until the new line. + UntilNewLine, +} + +/// A command that scrolls the terminal screen a given number of rows up. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ScrollUp(pub u16); + +impl Command for ScrollUp { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + if self.0 != 0 { + write!(f, csi!("{}S"), self.0)?; + } + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::scroll_up(self.0) + } +} + +/// A command that scrolls the terminal screen a given number of rows down. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ScrollDown(pub u16); + +impl Command for ScrollDown { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + if self.0 != 0 { + write!(f, csi!("{}T"), self.0)?; + } + Ok(()) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::scroll_down(self.0) + } +} + +/// A command that clears the terminal screen buffer. +/// +/// See the [`ClearType`](enum.ClearType.html) enum. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Clear(pub ClearType); + +impl Command for Clear { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + f.write_str(match self.0 { + ClearType::All => csi!("2J"), + ClearType::Purge => csi!("3J"), + ClearType::FromCursorDown => csi!("J"), + ClearType::FromCursorUp => csi!("1J"), + ClearType::CurrentLine => csi!("2K"), + ClearType::UntilNewLine => csi!("K"), + }) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::clear(self.0) + } +} + +/// A command that sets the terminal buffer size `(columns, rows)`. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetSize(pub u16, pub u16); + +impl Command for SetSize { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, csi!("8;{};{}t"), self.1, self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::set_size(self.0, self.1) + } +} + +/// A command that sets the terminal title +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SetTitle(pub T); + +impl Command for SetTitle { + fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "\x1B]0;{}\x07", &self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::set_window_title(&self.0) + } +} + +impl_display!(for ScrollUp); +impl_display!(for ScrollDown); +impl_display!(for SetSize); +impl_display!(for Clear); + +#[cfg(test)] +mod tests { + use std::{io::stdout, thread, time}; + + use crate::execute; + + use super::*; + + // Test is disabled, because it's failing on Travis CI + #[test] + #[ignore] + fn test_resize_ansi() { + let (width, height) = size().unwrap(); + + execute!(stdout(), SetSize(35, 35)).unwrap(); + + // see issue: https://github.com/eminence/terminal-size/issues/11 + thread::sleep(time::Duration::from_millis(30)); + + assert_eq!((35, 35), size().unwrap()); + + // reset to previous size + execute!(stdout(), SetSize(width, height)).unwrap(); + + // see issue: https://github.com/eminence/terminal-size/issues/11 + thread::sleep(time::Duration::from_millis(30)); + + assert_eq!((width, height), size().unwrap()); + } + + #[test] + fn test_raw_mode() { + // check we start from normal mode (may fail on some test harnesses) + assert!(!is_raw_mode_enabled().unwrap()); + + // enable the raw mode + if enable_raw_mode().is_err() { + // Enabling raw mode doesn't work on the ci + // So we just ignore it + return; + } + + // check it worked (on unix it doesn't really check the underlying + // tty but rather check that the code is consistent) + assert!(is_raw_mode_enabled().unwrap()); + + // enable it again, this should not change anything + enable_raw_mode().unwrap(); + + // check we're still in raw mode + assert!(is_raw_mode_enabled().unwrap()); + + // now let's disable it + disable_raw_mode().unwrap(); + + // check we're back to normal mode + assert!(!is_raw_mode_enabled().unwrap()); + } +} diff --git a/vendor/crossterm/src/terminal/sys.rs b/vendor/crossterm/src/terminal/sys.rs index 0c09fb4..8828b35 100644 --- a/vendor/crossterm/src/terminal/sys.rs +++ b/vendor/crossterm/src/terminal/sys.rs @@ -1,15 +1,15 @@ -//! This module provides platform related functions. - -#[cfg(unix)] -pub(crate) use self::unix::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled, size}; -#[cfg(windows)] -pub(crate) use self::windows::{ - clear, disable_raw_mode, enable_raw_mode, is_raw_mode_enabled, scroll_down, scroll_up, - set_size, set_window_title, size, -}; - -#[cfg(windows)] -mod windows; - -#[cfg(unix)] -mod unix; +//! This module provides platform related functions. + +#[cfg(unix)] +pub(crate) use self::unix::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled, size}; +#[cfg(windows)] +pub(crate) use self::windows::{ + clear, disable_raw_mode, enable_raw_mode, is_raw_mode_enabled, scroll_down, scroll_up, + set_size, set_window_title, size, +}; + +#[cfg(windows)] +mod windows; + +#[cfg(unix)] +mod unix; diff --git a/vendor/crossterm/src/terminal/sys/unix.rs b/vendor/crossterm/src/terminal/sys/unix.rs index e8291dd..48d7b62 100644 --- a/vendor/crossterm/src/terminal/sys/unix.rs +++ b/vendor/crossterm/src/terminal/sys/unix.rs @@ -1,144 +1,144 @@ -//! UNIX related logic for terminal manipulation. - -use std::fs::File; -use std::os::unix::io::{IntoRawFd, RawFd}; -use std::{io, mem, process}; - -use libc::{ - cfmakeraw, ioctl, tcgetattr, tcsetattr, termios as Termios, winsize, STDOUT_FILENO, TCSANOW, - TIOCGWINSZ, -}; -use parking_lot::Mutex; - -use crate::error::Result; -use crate::event::sys::unix::file_descriptor::{tty_fd, FileDesc}; - -// Some(Termios) -> we're in the raw mode and this is the previous mode -// None -> we're not in the raw mode -static TERMINAL_MODE_PRIOR_RAW_MODE: Mutex> = parking_lot::const_mutex(None); - -pub(crate) fn is_raw_mode_enabled() -> bool { - TERMINAL_MODE_PRIOR_RAW_MODE.lock().is_some() -} - -#[allow(clippy::useless_conversion)] -pub(crate) fn size() -> Result<(u16, u16)> { - // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc - let mut size = winsize { - ws_row: 0, - ws_col: 0, - ws_xpixel: 0, - ws_ypixel: 0, - }; - - let file = File::open("/dev/tty").map(|file| (FileDesc::new(file.into_raw_fd(), true))); - let fd = if let Ok(file) = &file { - file.raw_fd() - } else { - // Fallback to libc::STDOUT_FILENO if /dev/tty is missing - STDOUT_FILENO - }; - - if wrap_with_result(unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) }).is_ok() - && size.ws_col != 0 - && size.ws_row != 0 - { - return Ok((size.ws_col, size.ws_row)); - } - - tput_size().ok_or_else(|| std::io::Error::last_os_error().into()) -} - -pub(crate) fn enable_raw_mode() -> Result<()> { - let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock(); - - if original_mode.is_some() { - return Ok(()); - } - - let tty = tty_fd()?; - let fd = tty.raw_fd(); - let mut ios = get_terminal_attr(fd)?; - let original_mode_ios = ios; - - raw_terminal_attr(&mut ios); - set_terminal_attr(fd, &ios)?; - - // Keep it last - set the original mode only if we were able to switch to the raw mode - *original_mode = Some(original_mode_ios); - - Ok(()) -} - -/// Reset the raw mode. -/// -/// More precisely, reset the whole termios mode to what it was before the first call -/// to [enable_raw_mode]. If you don't mess with termios outside of crossterm, it's -/// effectively disabling the raw mode and doing nothing else. -pub(crate) fn disable_raw_mode() -> Result<()> { - let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock(); - - if let Some(original_mode_ios) = original_mode.as_ref() { - let tty = tty_fd()?; - set_terminal_attr(tty.raw_fd(), original_mode_ios)?; - // Keep it last - remove the original mode only if we were able to switch back - *original_mode = None; - } - - Ok(()) -} - -/// execute tput with the given argument and parse -/// the output as a u16. -/// -/// The arg should be "cols" or "lines" -fn tput_value(arg: &str) -> Option { - let output = process::Command::new("tput").arg(arg).output().ok()?; - let value = output - .stdout - .into_iter() - .filter_map(|b| char::from(b).to_digit(10)) - .fold(0, |v, n| v * 10 + n as u16); - - if value > 0 { - Some(value) - } else { - None - } -} - -/// Returns the size of the screen as determined by tput. -/// -/// This alternate way of computing the size is useful -/// when in a subshell. -fn tput_size() -> Option<(u16, u16)> { - match (tput_value("cols"), tput_value("lines")) { - (Some(w), Some(h)) => Some((w, h)), - _ => None, - } -} - -// Transform the given mode into an raw mode (non-canonical) mode. -fn raw_terminal_attr(termios: &mut Termios) { - unsafe { cfmakeraw(termios) } -} - -fn get_terminal_attr(fd: RawFd) -> Result { - unsafe { - let mut termios = mem::zeroed(); - wrap_with_result(tcgetattr(fd, &mut termios))?; - Ok(termios) - } -} - -fn set_terminal_attr(fd: RawFd, termios: &Termios) -> Result<()> { - wrap_with_result(unsafe { tcsetattr(fd, TCSANOW, termios) }) -} - -fn wrap_with_result(result: i32) -> Result<()> { - if result == -1 { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } -} +//! UNIX related logic for terminal manipulation. + +use std::fs::File; +use std::os::unix::io::{IntoRawFd, RawFd}; +use std::{io, mem, process}; + +use libc::{ + cfmakeraw, ioctl, tcgetattr, tcsetattr, termios as Termios, winsize, STDOUT_FILENO, TCSANOW, + TIOCGWINSZ, +}; +use parking_lot::Mutex; + +use crate::error::Result; +use crate::event::sys::unix::file_descriptor::{tty_fd, FileDesc}; + +// Some(Termios) -> we're in the raw mode and this is the previous mode +// None -> we're not in the raw mode +static TERMINAL_MODE_PRIOR_RAW_MODE: Mutex> = parking_lot::const_mutex(None); + +pub(crate) fn is_raw_mode_enabled() -> bool { + TERMINAL_MODE_PRIOR_RAW_MODE.lock().is_some() +} + +#[allow(clippy::useless_conversion)] +pub(crate) fn size() -> Result<(u16, u16)> { + // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc + let mut size = winsize { + ws_row: 0, + ws_col: 0, + ws_xpixel: 0, + ws_ypixel: 0, + }; + + let file = File::open("/dev/tty").map(|file| (FileDesc::new(file.into_raw_fd(), true))); + let fd = if let Ok(file) = &file { + file.raw_fd() + } else { + // Fallback to libc::STDOUT_FILENO if /dev/tty is missing + STDOUT_FILENO + }; + + if wrap_with_result(unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) }).is_ok() + && size.ws_col != 0 + && size.ws_row != 0 + { + return Ok((size.ws_col, size.ws_row)); + } + + tput_size().ok_or_else(|| std::io::Error::last_os_error().into()) +} + +pub(crate) fn enable_raw_mode() -> Result<()> { + let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock(); + + if original_mode.is_some() { + return Ok(()); + } + + let tty = tty_fd()?; + let fd = tty.raw_fd(); + let mut ios = get_terminal_attr(fd)?; + let original_mode_ios = ios; + + raw_terminal_attr(&mut ios); + set_terminal_attr(fd, &ios)?; + + // Keep it last - set the original mode only if we were able to switch to the raw mode + *original_mode = Some(original_mode_ios); + + Ok(()) +} + +/// Reset the raw mode. +/// +/// More precisely, reset the whole termios mode to what it was before the first call +/// to [enable_raw_mode]. If you don't mess with termios outside of crossterm, it's +/// effectively disabling the raw mode and doing nothing else. +pub(crate) fn disable_raw_mode() -> Result<()> { + let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock(); + + if let Some(original_mode_ios) = original_mode.as_ref() { + let tty = tty_fd()?; + set_terminal_attr(tty.raw_fd(), original_mode_ios)?; + // Keep it last - remove the original mode only if we were able to switch back + *original_mode = None; + } + + Ok(()) +} + +/// execute tput with the given argument and parse +/// the output as a u16. +/// +/// The arg should be "cols" or "lines" +fn tput_value(arg: &str) -> Option { + let output = process::Command::new("tput").arg(arg).output().ok()?; + let value = output + .stdout + .into_iter() + .filter_map(|b| char::from(b).to_digit(10)) + .fold(0, |v, n| v * 10 + n as u16); + + if value > 0 { + Some(value) + } else { + None + } +} + +/// Returns the size of the screen as determined by tput. +/// +/// This alternate way of computing the size is useful +/// when in a subshell. +fn tput_size() -> Option<(u16, u16)> { + match (tput_value("cols"), tput_value("lines")) { + (Some(w), Some(h)) => Some((w, h)), + _ => None, + } +} + +// Transform the given mode into an raw mode (non-canonical) mode. +fn raw_terminal_attr(termios: &mut Termios) { + unsafe { cfmakeraw(termios) } +} + +fn get_terminal_attr(fd: RawFd) -> Result { + unsafe { + let mut termios = mem::zeroed(); + wrap_with_result(tcgetattr(fd, &mut termios))?; + Ok(termios) + } +} + +fn set_terminal_attr(fd: RawFd, termios: &Termios) -> Result<()> { + wrap_with_result(unsafe { tcsetattr(fd, TCSANOW, termios) }) +} + +fn wrap_with_result(result: i32) -> Result<()> { + if result == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } +} diff --git a/vendor/crossterm/src/terminal/sys/windows.rs b/vendor/crossterm/src/terminal/sys/windows.rs index 0e57227..e90ab5d 100644 --- a/vendor/crossterm/src/terminal/sys/windows.rs +++ b/vendor/crossterm/src/terminal/sys/windows.rs @@ -1,396 +1,396 @@ -//! WinAPI related logic for terminal manipulation. - -use std::fmt::{self, Write}; -use std::io; - -use crossterm_winapi::{Console, ConsoleMode, Coord, Handle, ScreenBuffer, Size}; -use winapi::{ - shared::minwindef::DWORD, - um::wincon::{SetConsoleTitleW, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT}, -}; - -use crate::{cursor, terminal::ClearType, ErrorKind, Result}; - -/// bits which can't be set in raw mode -const NOT_RAW_MODE_MASK: DWORD = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT; - -pub(crate) fn is_raw_mode_enabled() -> Result { - let console_mode = ConsoleMode::from(Handle::current_in_handle()?); - - let dw_mode = console_mode.mode()?; - - Ok( - // check none of the "not raw" bits is set - dw_mode & NOT_RAW_MODE_MASK == 0, - ) -} - -pub(crate) fn enable_raw_mode() -> Result<()> { - let console_mode = ConsoleMode::from(Handle::current_in_handle()?); - - let dw_mode = console_mode.mode()?; - - let new_mode = dw_mode & !NOT_RAW_MODE_MASK; - - console_mode.set_mode(new_mode)?; - - Ok(()) -} - -pub(crate) fn disable_raw_mode() -> Result<()> { - let console_mode = ConsoleMode::from(Handle::current_in_handle()?); - - let dw_mode = console_mode.mode()?; - - let new_mode = dw_mode | NOT_RAW_MODE_MASK; - - console_mode.set_mode(new_mode)?; - - Ok(()) -} - -pub(crate) fn size() -> Result<(u16, u16)> { - let terminal_size = ScreenBuffer::current()?.info()?.terminal_size(); - // windows starts counting at 0, unix at 1, add one to replicated unix behaviour. - Ok(( - (terminal_size.width + 1) as u16, - (terminal_size.height + 1) as u16, - )) -} - -pub(crate) fn clear(clear_type: ClearType) -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - - let pos = csbi.cursor_pos(); - let buffer_size = csbi.buffer_size(); - let current_attribute = csbi.attributes(); - - match clear_type { - ClearType::All => { - clear_entire_screen(buffer_size, current_attribute)?; - } - ClearType::FromCursorDown => clear_after_cursor(pos, buffer_size, current_attribute)?, - ClearType::FromCursorUp => clear_before_cursor(pos, buffer_size, current_attribute)?, - ClearType::CurrentLine => clear_current_line(pos, buffer_size, current_attribute)?, - ClearType::UntilNewLine => clear_until_line(pos, buffer_size, current_attribute)?, - _ => { - clear_entire_screen(buffer_size, current_attribute)?; - } //TODO: make purge flush the entire screen buffer not just the visible window. - }; - Ok(()) -} - -pub(crate) fn scroll_up(row_count: u16) -> Result<()> { - let csbi = ScreenBuffer::current()?; - let mut window = csbi.info()?.terminal_window(); - - // check whether the window is too close to the screen buffer top - let count = row_count as i16; - if window.top >= count { - window.top -= count; // move top down - window.bottom -= count; // move bottom down - - Console::output()?.set_console_info(true, window)?; - } - Ok(()) -} - -pub(crate) fn scroll_down(row_count: u16) -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - let mut window = csbi.terminal_window(); - let buffer_size = csbi.buffer_size(); - - // check whether the window is too close to the screen buffer top - let count = row_count as i16; - if window.bottom < buffer_size.height - count { - window.top += count; // move top down - window.bottom += count; // move bottom down - - Console::output()?.set_console_info(true, window)?; - } - Ok(()) -} - -pub(crate) fn set_size(width: u16, height: u16) -> Result<()> { - if width <= 1 { - return Err(ErrorKind::new( - io::ErrorKind::InvalidInput, - "terminal width must be at least 1", - )); - } - - if height <= 1 { - return Err(ErrorKind::new( - io::ErrorKind::InvalidInput, - "terminal height must be at least 1", - )); - } - - // get the position of the current console window - let screen_buffer = ScreenBuffer::current()?; - let console = Console::from(screen_buffer.handle().clone()); - let csbi = screen_buffer.info()?; - - let current_size = csbi.buffer_size(); - let window = csbi.terminal_window(); - - let mut new_size = Size::new(current_size.width, current_size.height); - - // If the buffer is smaller than this new window size, resize the - // buffer to be large enough. Include window position. - let mut resize_buffer = false; - - let width = width as i16; - if current_size.width < window.left + width { - if window.left >= i16::max_value() - width { - return Err(ErrorKind::new( - io::ErrorKind::InvalidInput, - "terminal width too large", - )); - } - - new_size.width = window.left + width; - resize_buffer = true; - } - let height = height as i16; - if current_size.height < window.top + height { - if window.top >= i16::max_value() - height { - return Err(ErrorKind::new( - io::ErrorKind::InvalidInput, - "terminal height too large", - )); - } - - new_size.height = window.top + height; - resize_buffer = true; - } - - if resize_buffer { - screen_buffer.set_size(new_size.width - 1, new_size.height - 1)?; - } - - let mut window = window; - - // preserve the position, but change the size. - window.bottom = window.top + height - 1; - window.right = window.left + width - 1; - console.set_console_info(true, window)?; - - // if we resized the buffer, un-resize it. - if resize_buffer { - screen_buffer.set_size(current_size.width - 1, current_size.height - 1)?; - } - - let bounds = console.largest_window_size()?; - - if width > bounds.x { - return Err(ErrorKind::new( - io::ErrorKind::InvalidInput, - format!("terminal width {} too large", width), - )); - } - if height > bounds.y { - return Err(ErrorKind::new( - io::ErrorKind::InvalidInput, - format!("terminal height {} too large", height), - )); - } - - Ok(()) -} - -pub(crate) fn set_window_title(title: impl fmt::Display) -> Result<()> { - struct Utf16Encoder(Vec); - impl Write for Utf16Encoder { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.0.extend(s.encode_utf16()); - Ok(()) - } - } - - let mut title_utf16 = Utf16Encoder(Vec::new()); - write!(title_utf16, "{}", title).expect("formatting failed"); - title_utf16.0.push(0); - let title = title_utf16.0; - - let result = unsafe { SetConsoleTitleW(title.as_ptr()) }; - if result != 0 { - Ok(()) - } else { - Err(ErrorKind::last_os_error()) - } -} - -fn clear_after_cursor(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { - let (mut x, mut y) = (location.x, location.y); - - // if cursor position is at the outer right position - if x as i16 > buffer_size.width { - y += 1; - x = 0; - } - - // location where to start clearing - let start_location = Coord::new(x, y); - - // get sum cells before cursor - let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32; - - clear_winapi(start_location, cells_to_write, current_attribute) -} - -fn clear_before_cursor(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { - let (xpos, ypos) = (location.x, location.y); - - // one cell after cursor position - let x = 0; - // one at row of cursor position - let y = 0; - - // location where to start clearing - let start_location = Coord::new(x, y); - - // get sum cells before cursor - let cells_to_write = (buffer_size.width as u32 * ypos as u32) + (xpos as u32 + 1); - - // clear everything before cursor position - clear_winapi(start_location, cells_to_write, current_attribute) -} - -fn clear_entire_screen(buffer_size: Size, current_attribute: u16) -> Result<()> { - // get sum cells before cursor - let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32; - - // location where to start clearing - let start_location = Coord::new(0, 0); - - // clear the entire screen - clear_winapi(start_location, cells_to_write, current_attribute)?; - - // put the cursor back at cell 0,0 - cursor::sys::move_to(0, 0)?; - Ok(()) -} - -fn clear_current_line(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { - // location where to start clearing - let start_location = Coord::new(0, location.y); - - // get sum cells before cursor - let cells_to_write = buffer_size.width as u32; - - // clear the whole current line - clear_winapi(start_location, cells_to_write, current_attribute)?; - - // put the cursor back at cell 1 on current row - cursor::sys::move_to(0, location.y as u16)?; - Ok(()) -} - -fn clear_until_line(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { - let (x, y) = (location.x, location.y); - - // location where to start clearing - let start_location = Coord::new(x, y); - - // get sum cells before cursor - let cells_to_write = (buffer_size.width - x as i16) as u32; - - // clear until the current line - clear_winapi(start_location, cells_to_write, current_attribute)?; - - // put the cursor back at original cursor position before we did the clearing - cursor::sys::move_to(x as u16, y as u16)?; - Ok(()) -} - -fn clear_winapi(start_location: Coord, cells_to_write: u32, current_attribute: u16) -> Result<()> { - let console = Console::from(Handle::current_out_handle()?); - console.fill_whit_character(start_location, cells_to_write, ' ')?; - console.fill_whit_attribute(start_location, cells_to_write, current_attribute)?; - Ok(()) -} - -#[cfg(test)] -mod tests { - use std::{ffi::OsString, os::windows::ffi::OsStringExt}; - - use crossterm_winapi::ScreenBuffer; - use winapi::um::wincon::GetConsoleTitleW; - - use super::{scroll_down, scroll_up, set_size, set_window_title, size}; - - #[test] - fn test_resize_winapi() { - let (width, height) = size().unwrap(); - - set_size(30, 30).unwrap(); - assert_eq!((30, 30), size().unwrap()); - - // reset to previous size - set_size(width, height).unwrap(); - assert_eq!((width, height), size().unwrap()); - } - - // Test is disabled, because it's failing on Travis CI - #[test] - #[ignore] - fn test_scroll_down_winapi() { - let current_window = ScreenBuffer::current() - .unwrap() - .info() - .unwrap() - .terminal_window(); - - scroll_down(2).unwrap(); - - let new_window = ScreenBuffer::current() - .unwrap() - .info() - .unwrap() - .terminal_window(); - - assert_eq!(new_window.top, current_window.top + 2); - assert_eq!(new_window.bottom, current_window.bottom + 2); - } - - // Test is disabled, because it's failing on Travis CI - #[test] - #[ignore] - fn test_scroll_up_winapi() { - // move the terminal buffer down before moving it up - test_scroll_down_winapi(); - - let current_window = ScreenBuffer::current() - .unwrap() - .info() - .unwrap() - .terminal_window(); - - scroll_up(2).unwrap(); - - let new_window = ScreenBuffer::current() - .unwrap() - .info() - .unwrap() - .terminal_window(); - - assert_eq!(new_window.top, current_window.top - 2); - assert_eq!(new_window.bottom, current_window.bottom - 2); - } - - #[test] - fn test_set_title_winapi() { - let test_title = "this is a crossterm test title"; - set_window_title(test_title).unwrap(); - - let mut raw = [0_u16; 128]; - let length = unsafe { GetConsoleTitleW(raw.as_mut_ptr(), raw.len() as u32) } as usize; - assert_ne!(0, length); - - let console_title = OsString::from_wide(&raw[..length]).into_string().unwrap(); - assert_eq!(test_title, &console_title[..]); - } -} +//! WinAPI related logic for terminal manipulation. + +use std::fmt::{self, Write}; +use std::io; + +use crossterm_winapi::{Console, ConsoleMode, Coord, Handle, ScreenBuffer, Size}; +use winapi::{ + shared::minwindef::DWORD, + um::wincon::{SetConsoleTitleW, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT}, +}; + +use crate::{cursor, terminal::ClearType, ErrorKind, Result}; + +/// bits which can't be set in raw mode +const NOT_RAW_MODE_MASK: DWORD = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT; + +pub(crate) fn is_raw_mode_enabled() -> Result { + let console_mode = ConsoleMode::from(Handle::current_in_handle()?); + + let dw_mode = console_mode.mode()?; + + Ok( + // check none of the "not raw" bits is set + dw_mode & NOT_RAW_MODE_MASK == 0, + ) +} + +pub(crate) fn enable_raw_mode() -> Result<()> { + let console_mode = ConsoleMode::from(Handle::current_in_handle()?); + + let dw_mode = console_mode.mode()?; + + let new_mode = dw_mode & !NOT_RAW_MODE_MASK; + + console_mode.set_mode(new_mode)?; + + Ok(()) +} + +pub(crate) fn disable_raw_mode() -> Result<()> { + let console_mode = ConsoleMode::from(Handle::current_in_handle()?); + + let dw_mode = console_mode.mode()?; + + let new_mode = dw_mode | NOT_RAW_MODE_MASK; + + console_mode.set_mode(new_mode)?; + + Ok(()) +} + +pub(crate) fn size() -> Result<(u16, u16)> { + let terminal_size = ScreenBuffer::current()?.info()?.terminal_size(); + // windows starts counting at 0, unix at 1, add one to replicated unix behaviour. + Ok(( + (terminal_size.width + 1) as u16, + (terminal_size.height + 1) as u16, + )) +} + +pub(crate) fn clear(clear_type: ClearType) -> Result<()> { + let screen_buffer = ScreenBuffer::current()?; + let csbi = screen_buffer.info()?; + + let pos = csbi.cursor_pos(); + let buffer_size = csbi.buffer_size(); + let current_attribute = csbi.attributes(); + + match clear_type { + ClearType::All => { + clear_entire_screen(buffer_size, current_attribute)?; + } + ClearType::FromCursorDown => clear_after_cursor(pos, buffer_size, current_attribute)?, + ClearType::FromCursorUp => clear_before_cursor(pos, buffer_size, current_attribute)?, + ClearType::CurrentLine => clear_current_line(pos, buffer_size, current_attribute)?, + ClearType::UntilNewLine => clear_until_line(pos, buffer_size, current_attribute)?, + _ => { + clear_entire_screen(buffer_size, current_attribute)?; + } //TODO: make purge flush the entire screen buffer not just the visible window. + }; + Ok(()) +} + +pub(crate) fn scroll_up(row_count: u16) -> Result<()> { + let csbi = ScreenBuffer::current()?; + let mut window = csbi.info()?.terminal_window(); + + // check whether the window is too close to the screen buffer top + let count = row_count as i16; + if window.top >= count { + window.top -= count; // move top down + window.bottom -= count; // move bottom down + + Console::output()?.set_console_info(true, window)?; + } + Ok(()) +} + +pub(crate) fn scroll_down(row_count: u16) -> Result<()> { + let screen_buffer = ScreenBuffer::current()?; + let csbi = screen_buffer.info()?; + let mut window = csbi.terminal_window(); + let buffer_size = csbi.buffer_size(); + + // check whether the window is too close to the screen buffer top + let count = row_count as i16; + if window.bottom < buffer_size.height - count { + window.top += count; // move top down + window.bottom += count; // move bottom down + + Console::output()?.set_console_info(true, window)?; + } + Ok(()) +} + +pub(crate) fn set_size(width: u16, height: u16) -> Result<()> { + if width <= 1 { + return Err(ErrorKind::new( + io::ErrorKind::InvalidInput, + "terminal width must be at least 1", + )); + } + + if height <= 1 { + return Err(ErrorKind::new( + io::ErrorKind::InvalidInput, + "terminal height must be at least 1", + )); + } + + // get the position of the current console window + let screen_buffer = ScreenBuffer::current()?; + let console = Console::from(screen_buffer.handle().clone()); + let csbi = screen_buffer.info()?; + + let current_size = csbi.buffer_size(); + let window = csbi.terminal_window(); + + let mut new_size = Size::new(current_size.width, current_size.height); + + // If the buffer is smaller than this new window size, resize the + // buffer to be large enough. Include window position. + let mut resize_buffer = false; + + let width = width as i16; + if current_size.width < window.left + width { + if window.left >= i16::max_value() - width { + return Err(ErrorKind::new( + io::ErrorKind::InvalidInput, + "terminal width too large", + )); + } + + new_size.width = window.left + width; + resize_buffer = true; + } + let height = height as i16; + if current_size.height < window.top + height { + if window.top >= i16::max_value() - height { + return Err(ErrorKind::new( + io::ErrorKind::InvalidInput, + "terminal height too large", + )); + } + + new_size.height = window.top + height; + resize_buffer = true; + } + + if resize_buffer { + screen_buffer.set_size(new_size.width - 1, new_size.height - 1)?; + } + + let mut window = window; + + // preserve the position, but change the size. + window.bottom = window.top + height - 1; + window.right = window.left + width - 1; + console.set_console_info(true, window)?; + + // if we resized the buffer, un-resize it. + if resize_buffer { + screen_buffer.set_size(current_size.width - 1, current_size.height - 1)?; + } + + let bounds = console.largest_window_size()?; + + if width > bounds.x { + return Err(ErrorKind::new( + io::ErrorKind::InvalidInput, + format!("terminal width {} too large", width), + )); + } + if height > bounds.y { + return Err(ErrorKind::new( + io::ErrorKind::InvalidInput, + format!("terminal height {} too large", height), + )); + } + + Ok(()) +} + +pub(crate) fn set_window_title(title: impl fmt::Display) -> Result<()> { + struct Utf16Encoder(Vec); + impl Write for Utf16Encoder { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0.extend(s.encode_utf16()); + Ok(()) + } + } + + let mut title_utf16 = Utf16Encoder(Vec::new()); + write!(title_utf16, "{}", title).expect("formatting failed"); + title_utf16.0.push(0); + let title = title_utf16.0; + + let result = unsafe { SetConsoleTitleW(title.as_ptr()) }; + if result != 0 { + Ok(()) + } else { + Err(ErrorKind::last_os_error()) + } +} + +fn clear_after_cursor(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { + let (mut x, mut y) = (location.x, location.y); + + // if cursor position is at the outer right position + if x as i16 > buffer_size.width { + y += 1; + x = 0; + } + + // location where to start clearing + let start_location = Coord::new(x, y); + + // get sum cells before cursor + let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32; + + clear_winapi(start_location, cells_to_write, current_attribute) +} + +fn clear_before_cursor(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { + let (xpos, ypos) = (location.x, location.y); + + // one cell after cursor position + let x = 0; + // one at row of cursor position + let y = 0; + + // location where to start clearing + let start_location = Coord::new(x, y); + + // get sum cells before cursor + let cells_to_write = (buffer_size.width as u32 * ypos as u32) + (xpos as u32 + 1); + + // clear everything before cursor position + clear_winapi(start_location, cells_to_write, current_attribute) +} + +fn clear_entire_screen(buffer_size: Size, current_attribute: u16) -> Result<()> { + // get sum cells before cursor + let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32; + + // location where to start clearing + let start_location = Coord::new(0, 0); + + // clear the entire screen + clear_winapi(start_location, cells_to_write, current_attribute)?; + + // put the cursor back at cell 0,0 + cursor::sys::move_to(0, 0)?; + Ok(()) +} + +fn clear_current_line(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { + // location where to start clearing + let start_location = Coord::new(0, location.y); + + // get sum cells before cursor + let cells_to_write = buffer_size.width as u32; + + // clear the whole current line + clear_winapi(start_location, cells_to_write, current_attribute)?; + + // put the cursor back at cell 1 on current row + cursor::sys::move_to(0, location.y as u16)?; + Ok(()) +} + +fn clear_until_line(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { + let (x, y) = (location.x, location.y); + + // location where to start clearing + let start_location = Coord::new(x, y); + + // get sum cells before cursor + let cells_to_write = (buffer_size.width - x as i16) as u32; + + // clear until the current line + clear_winapi(start_location, cells_to_write, current_attribute)?; + + // put the cursor back at original cursor position before we did the clearing + cursor::sys::move_to(x as u16, y as u16)?; + Ok(()) +} + +fn clear_winapi(start_location: Coord, cells_to_write: u32, current_attribute: u16) -> Result<()> { + let console = Console::from(Handle::current_out_handle()?); + console.fill_whit_character(start_location, cells_to_write, ' ')?; + console.fill_whit_attribute(start_location, cells_to_write, current_attribute)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::{ffi::OsString, os::windows::ffi::OsStringExt}; + + use crossterm_winapi::ScreenBuffer; + use winapi::um::wincon::GetConsoleTitleW; + + use super::{scroll_down, scroll_up, set_size, set_window_title, size}; + + #[test] + fn test_resize_winapi() { + let (width, height) = size().unwrap(); + + set_size(30, 30).unwrap(); + assert_eq!((30, 30), size().unwrap()); + + // reset to previous size + set_size(width, height).unwrap(); + assert_eq!((width, height), size().unwrap()); + } + + // Test is disabled, because it's failing on Travis CI + #[test] + #[ignore] + fn test_scroll_down_winapi() { + let current_window = ScreenBuffer::current() + .unwrap() + .info() + .unwrap() + .terminal_window(); + + scroll_down(2).unwrap(); + + let new_window = ScreenBuffer::current() + .unwrap() + .info() + .unwrap() + .terminal_window(); + + assert_eq!(new_window.top, current_window.top + 2); + assert_eq!(new_window.bottom, current_window.bottom + 2); + } + + // Test is disabled, because it's failing on Travis CI + #[test] + #[ignore] + fn test_scroll_up_winapi() { + // move the terminal buffer down before moving it up + test_scroll_down_winapi(); + + let current_window = ScreenBuffer::current() + .unwrap() + .info() + .unwrap() + .terminal_window(); + + scroll_up(2).unwrap(); + + let new_window = ScreenBuffer::current() + .unwrap() + .info() + .unwrap() + .terminal_window(); + + assert_eq!(new_window.top, current_window.top - 2); + assert_eq!(new_window.bottom, current_window.bottom - 2); + } + + #[test] + fn test_set_title_winapi() { + let test_title = "this is a crossterm test title"; + set_window_title(test_title).unwrap(); + + let mut raw = [0_u16; 128]; + let length = unsafe { GetConsoleTitleW(raw.as_mut_ptr(), raw.len() as u32) } as usize; + assert_ne!(0, length); + + let console_title = OsString::from_wide(&raw[..length]).into_string().unwrap(); + assert_eq!(test_title, &console_title[..]); + } +} diff --git a/vendor/crossterm/src/tty.rs b/vendor/crossterm/src/tty.rs index 78e32aa..eb63129 100644 --- a/vendor/crossterm/src/tty.rs +++ b/vendor/crossterm/src/tty.rs @@ -1,46 +1,46 @@ -//! Making it a little more convenient and safe to query whether -//! something is a terminal teletype or not. -//! This module defines the IsTty trait and the is_tty method to -//! return true if the item represents a terminal. - -#[cfg(unix)] -use std::os::unix::io::AsRawFd; -#[cfg(windows)] -use std::os::windows::io::AsRawHandle; - -#[cfg(windows)] -use winapi::um::consoleapi::GetConsoleMode; - -/// Adds the `is_tty` method to types that might represent a terminal -/// -/// ```rust -/// use std::io::stdout; -/// use crossterm::tty::IsTty; -/// -/// let is_tty: bool = stdout().is_tty(); -/// ``` -pub trait IsTty { - /// Returns true when an instance is a terminal teletype, otherwise false. - fn is_tty(&self) -> bool; -} - -/// On UNIX, the `isatty()` function returns true if a file -/// descriptor is a terminal. -#[cfg(unix)] -impl IsTty for S { - fn is_tty(&self) -> bool { - let fd = self.as_raw_fd(); - unsafe { libc::isatty(fd) == 1 } - } -} - -/// On windows, `GetConsoleMode` will return true if we are in a terminal. -/// Otherwise false. -#[cfg(windows)] -impl IsTty for S { - fn is_tty(&self) -> bool { - let mut mode = 0; - let ok = unsafe { GetConsoleMode(self.as_raw_handle() as *mut _, &mut mode) }; - ok == 1 - } -} +//! Making it a little more convenient and safe to query whether +//! something is a terminal teletype or not. +//! This module defines the IsTty trait and the is_tty method to +//! return true if the item represents a terminal. + +#[cfg(unix)] +use std::os::unix::io::AsRawFd; +#[cfg(windows)] +use std::os::windows::io::AsRawHandle; + +#[cfg(windows)] +use winapi::um::consoleapi::GetConsoleMode; + +/// Adds the `is_tty` method to types that might represent a terminal +/// +/// ```rust +/// use std::io::stdout; +/// use crossterm::tty::IsTty; +/// +/// let is_tty: bool = stdout().is_tty(); +/// ``` +pub trait IsTty { + /// Returns true when an instance is a terminal teletype, otherwise false. + fn is_tty(&self) -> bool; +} + +/// On UNIX, the `isatty()` function returns true if a file +/// descriptor is a terminal. +#[cfg(unix)] +impl IsTty for S { + fn is_tty(&self) -> bool { + let fd = self.as_raw_fd(); + unsafe { libc::isatty(fd) == 1 } + } +} + +/// On windows, `GetConsoleMode` will return true if we are in a terminal. +/// Otherwise false. +#[cfg(windows)] +impl IsTty for S { + fn is_tty(&self) -> bool { + let mut mode = 0; + let ok = unsafe { GetConsoleMode(self.as_raw_handle() as *mut _, &mut mode) }; + ok == 1 + } +} diff --git a/vendor/crossterm_winapi/CHANGELOG.md b/vendor/crossterm_winapi/CHANGELOG.md index 44d7a29..dd0f912 100644 --- a/vendor/crossterm_winapi/CHANGELOG.md +++ b/vendor/crossterm_winapi/CHANGELOG.md @@ -1,45 +1,45 @@ -# Version 0.9.0 -- Fix panic on certain event flags. - -# Version 0.8.0 -- Changed some return types. -- Improved some internal error handling. - -# Version 0.7.0 -- Make resize event return correct screen dimensions instead of buffers size. - -# Version 0.6.1 -- Make semaphore `Send` and `Sync` again. -- Make `Inner` `Send` and `Sync` again. - -# Version 0.6.0 -- Added Common traits (`Debug`, `Clone`, etc) to many public facing types, -especially data struct. -- Significantly updated the `input` structs, so that winapi native types are no longer exposed to the library by crossterm_winapi structs. -- Removed PartialOrd from types where it didn't really make sense -- Reimplemented `Console::read_single_input_event` and `Console::read_console_input` to be more efficient, safe, and correct -- Make `Console::read_console_input` not return a `u32`; the numbr of events is the length of the returned vector. - - -# Version 0.5.1 -- Make `Semaphore` implement `Clone`. - -# Version 0.5.0 -- Add `Semaphore` object handling -- Make `ButtonState` more flexible. - -# Version 0.4.0 -- The `Handle` API has been reworked to make it `Send` + `Sync` and close the underlying `HANDLE` when dropped. - -# Version 0.3.0 - -- Make read sync block for windows systems ([PR #2](https://github.com/crossterm-rs/crossterm-winapi/pull/2)) - -# Version 0.2.1 - -- Maintenance release only -- Moved to a [separate repository](https://github.com/crossterm-rs/crossterm-winapi) - -# Version 0.2.0 - -- `Console::get_handle` to `Console::handle` +# Version 0.9.0 +- Fix panic on certain event flags. + +# Version 0.8.0 +- Changed some return types. +- Improved some internal error handling. + +# Version 0.7.0 +- Make resize event return correct screen dimensions instead of buffers size. + +# Version 0.6.1 +- Make semaphore `Send` and `Sync` again. +- Make `Inner` `Send` and `Sync` again. + +# Version 0.6.0 +- Added Common traits (`Debug`, `Clone`, etc) to many public facing types, +especially data struct. +- Significantly updated the `input` structs, so that winapi native types are no longer exposed to the library by crossterm_winapi structs. +- Removed PartialOrd from types where it didn't really make sense +- Reimplemented `Console::read_single_input_event` and `Console::read_console_input` to be more efficient, safe, and correct +- Make `Console::read_console_input` not return a `u32`; the numbr of events is the length of the returned vector. + + +# Version 0.5.1 +- Make `Semaphore` implement `Clone`. + +# Version 0.5.0 +- Add `Semaphore` object handling +- Make `ButtonState` more flexible. + +# Version 0.4.0 +- The `Handle` API has been reworked to make it `Send` + `Sync` and close the underlying `HANDLE` when dropped. + +# Version 0.3.0 + +- Make read sync block for windows systems ([PR #2](https://github.com/crossterm-rs/crossterm-winapi/pull/2)) + +# Version 0.2.1 + +- Maintenance release only +- Moved to a [separate repository](https://github.com/crossterm-rs/crossterm-winapi) + +# Version 0.2.0 + +- `Console::get_handle` to `Console::handle` diff --git a/vendor/crossterm_winapi/LICENSE b/vendor/crossterm_winapi/LICENSE index 8b02a7f..5618e2a 100644 --- a/vendor/crossterm_winapi/LICENSE +++ b/vendor/crossterm_winapi/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2019 Timon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2019 Timon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/crossterm_winapi/README.md b/vendor/crossterm_winapi/README.md index 10b054c..584eb00 100644 --- a/vendor/crossterm_winapi/README.md +++ b/vendor/crossterm_winapi/README.md @@ -1,72 +1,72 @@ -![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] - -# Crossterm Windows API Abstractions - -This crate provides some wrappers aground common used WinAPI functions. - -The purpose of this library is originally meant for the [crossterm](https://github.com/crossterm-rs/crossterm), -but could be used apart from it. Although, notice that it unstable right because some changes to the -API could be expected. - -# Features - -This crate provides some abstractions over reading input, console screen buffer, and handle. - -The following WinAPI calls: - -- CONSOLE_SCREEN_BUFFER_INFO (used to extract information like cursor pos, terminal size etc.) -- HANDLE (the handle needed to run functions from WinAPI) -- SetConsoleActiveScreenBuffer (activate an other screen buffer) -- Set/GetConsoleMode (e.g. console modes like disabling output) -- SetConsoleTextAttribute (eg. coloring) -- SetConsoleWindowInfo (changing the buffer location e.g. scrolling) -- FillConsoleOutputAttribute, FillConsoleOutputCharacter (used to replace some block of cells with a color or character.) -- SetConsoleInfo -- ReadConsoleW -- Semaphore object handling - -# Example - -The [examples](https://github.com/crossterm-rs/examples) repository has more complete and verbose examples. - -## Screen buffer information - -```rust -use crossterm_winapi::{ScreenBuffer, Handle}; - -fn print_screen_buffer_information() { - let screen_buffer = ScreenBuffer::current().unwrap(); - - // get console screen buffer information - let csbi = screen_buffer.info().unwrap(); - - println!("cursor post: {:?}", csbi.cursor_pos()); - println!("attributes: {:?}", csbi.attributes()); - println!("terminal window dimentions {:?}", csbi.terminal_window()); - println!("terminal size {:?}", csbi.terminal_size()); -} -``` - -## Handle - -```rust -use crossterm_winapi::{HandleType, Handle}; - -fn get_different_handle_types() { - let out_put_handle = Handle::new(HandleType::OutputHandle).unwrap(); - let out_put_handle = Handle::new(HandleType::InputHandle).unwrap(); - let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle).unwrap(); - let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle).unwrap(); -} -``` - -[s1]: https://img.shields.io/crates/v/crossterm_winapi.svg -[l1]: https://crates.io/crates/crossterm_winapi - -[s2]: https://img.shields.io/badge/license-MIT-blue.svg -[l2]: LICENSE - -[s3]: https://docs.rs/crossterm_winapi/badge.svg -[l3]: https://docs.rs/crossterm_winapi/ - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master +![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] + +# Crossterm Windows API Abstractions + +This crate provides some wrappers aground common used WinAPI functions. + +The purpose of this library is originally meant for the [crossterm](https://github.com/crossterm-rs/crossterm), +but could be used apart from it. Although, notice that it unstable right because some changes to the +API could be expected. + +# Features + +This crate provides some abstractions over reading input, console screen buffer, and handle. + +The following WinAPI calls: + +- CONSOLE_SCREEN_BUFFER_INFO (used to extract information like cursor pos, terminal size etc.) +- HANDLE (the handle needed to run functions from WinAPI) +- SetConsoleActiveScreenBuffer (activate an other screen buffer) +- Set/GetConsoleMode (e.g. console modes like disabling output) +- SetConsoleTextAttribute (eg. coloring) +- SetConsoleWindowInfo (changing the buffer location e.g. scrolling) +- FillConsoleOutputAttribute, FillConsoleOutputCharacter (used to replace some block of cells with a color or character.) +- SetConsoleInfo +- ReadConsoleW +- Semaphore object handling + +# Example + +The [examples](https://github.com/crossterm-rs/examples) repository has more complete and verbose examples. + +## Screen buffer information + +```rust +use crossterm_winapi::{ScreenBuffer, Handle}; + +fn print_screen_buffer_information() { + let screen_buffer = ScreenBuffer::current().unwrap(); + + // get console screen buffer information + let csbi = screen_buffer.info().unwrap(); + + println!("cursor post: {:?}", csbi.cursor_pos()); + println!("attributes: {:?}", csbi.attributes()); + println!("terminal window dimentions {:?}", csbi.terminal_window()); + println!("terminal size {:?}", csbi.terminal_size()); +} +``` + +## Handle + +```rust +use crossterm_winapi::{HandleType, Handle}; + +fn get_different_handle_types() { + let out_put_handle = Handle::new(HandleType::OutputHandle).unwrap(); + let out_put_handle = Handle::new(HandleType::InputHandle).unwrap(); + let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle).unwrap(); + let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle).unwrap(); +} +``` + +[s1]: https://img.shields.io/crates/v/crossterm_winapi.svg +[l1]: https://crates.io/crates/crossterm_winapi + +[s2]: https://img.shields.io/badge/license-MIT-blue.svg +[l2]: LICENSE + +[s3]: https://docs.rs/crossterm_winapi/badge.svg +[l3]: https://docs.rs/crossterm_winapi/ + +[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master diff --git a/vendor/crossterm_winapi/docs/CONTRIBUTING.md b/vendor/crossterm_winapi/docs/CONTRIBUTING.md index b2dd2cf..77289f2 100644 --- a/vendor/crossterm_winapi/docs/CONTRIBUTING.md +++ b/vendor/crossterm_winapi/docs/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing - -The `crossterm` crate -[contributing guidelines](https://github.com/crossterm-rs/crossterm/blob/master/docs/CONTRIBUTING.md) applies. +# Contributing + +The `crossterm` crate +[contributing guidelines](https://github.com/crossterm-rs/crossterm/blob/master/docs/CONTRIBUTING.md) applies. diff --git a/vendor/crossterm_winapi/docs/known-problems.md b/vendor/crossterm_winapi/docs/known-problems.md index f68b7fe..a099cc2 100644 --- a/vendor/crossterm_winapi/docs/known-problems.md +++ b/vendor/crossterm_winapi/docs/known-problems.md @@ -1,10 +1,10 @@ -# Known problems - -There are some problems I discovered during development. I don't think it has to do anything with -the crossterm, but it has to do with how terminals handle ANSI or WinAPI. - -## WinAPI - -- PowerShell does not interpret 'DarkYellow' and uses gray instead, cmd is working perfectly fine. -- PowerShell inserts an '\n' (enter) when the program starts, this enter is the one you pressed when running the command. -- After the program ran, PowerShell will reset the background and foreground colors. +# Known problems + +There are some problems I discovered during development. I don't think it has to do anything with +the crossterm, but it has to do with how terminals handle ANSI or WinAPI. + +## WinAPI + +- PowerShell does not interpret 'DarkYellow' and uses gray instead, cmd is working perfectly fine. +- PowerShell inserts an '\n' (enter) when the program starts, this enter is the one you pressed when running the command. +- After the program ran, PowerShell will reset the background and foreground colors. diff --git a/vendor/crossterm_winapi/examples/coloring_example.rs b/vendor/crossterm_winapi/examples/coloring_example.rs index 87f5dd8..da72de3 100644 --- a/vendor/crossterm_winapi/examples/coloring_example.rs +++ b/vendor/crossterm_winapi/examples/coloring_example.rs @@ -1,64 +1,64 @@ -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::{Console, ScreenBuffer}; - -#[cfg(windows)] -fn set_background_color() -> Result<()> { - // background value - const BLUE_BACKGROUND: u16 = 0x0010; - - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - - // Notice that the color values are stored in wAttribute. - // So wee need to use bitwise operators to check if the values exists or to get current console colors. - let attrs = csbi.attributes(); - let fg_color = attrs & 0x0007; - - // apply the blue background flag to the current attributes - let new_color = fg_color | BLUE_BACKGROUND; - - // set the console text attribute to the new color value. - Console::from(screen_buffer.handle().clone()).set_text_attribute(new_color)?; - - Ok(()) -} - -#[cfg(windows)] -fn set_foreground_color() -> Result<()> { - // background value - const BLUE_FOREGROUND: u16 = 0x0001; - - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - - // Notice that the color values are stored in wAttribute. - // So we need to use bitwise operators to check if the values exists or to get current console colors. - let attrs = csbi.attributes(); - let bg_color = attrs & 0x0070; - let mut color = BLUE_FOREGROUND | bg_color; - - // background intensity is a separate value in attrs, - // wee need to check if this was applied to the current bg color. - if (attrs & 0x0080 as u16) != 0 { - color = color | 0x0080 as u16; - } - - // set the console text attribute to the new color value. - Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?; - - Ok(()) -} - -#[cfg(windows)] -fn main() -> Result<()> { - set_background_color()?; - set_foreground_color() -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} +#[cfg(windows)] +use std::io::Result; + +#[cfg(windows)] +use crossterm_winapi::{Console, ScreenBuffer}; + +#[cfg(windows)] +fn set_background_color() -> Result<()> { + // background value + const BLUE_BACKGROUND: u16 = 0x0010; + + let screen_buffer = ScreenBuffer::current()?; + let csbi = screen_buffer.info()?; + + // Notice that the color values are stored in wAttribute. + // So wee need to use bitwise operators to check if the values exists or to get current console colors. + let attrs = csbi.attributes(); + let fg_color = attrs & 0x0007; + + // apply the blue background flag to the current attributes + let new_color = fg_color | BLUE_BACKGROUND; + + // set the console text attribute to the new color value. + Console::from(screen_buffer.handle().clone()).set_text_attribute(new_color)?; + + Ok(()) +} + +#[cfg(windows)] +fn set_foreground_color() -> Result<()> { + // background value + const BLUE_FOREGROUND: u16 = 0x0001; + + let screen_buffer = ScreenBuffer::current()?; + let csbi = screen_buffer.info()?; + + // Notice that the color values are stored in wAttribute. + // So we need to use bitwise operators to check if the values exists or to get current console colors. + let attrs = csbi.attributes(); + let bg_color = attrs & 0x0070; + let mut color = BLUE_FOREGROUND | bg_color; + + // background intensity is a separate value in attrs, + // wee need to check if this was applied to the current bg color. + if (attrs & 0x0080 as u16) != 0 { + color = color | 0x0080 as u16; + } + + // set the console text attribute to the new color value. + Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?; + + Ok(()) +} + +#[cfg(windows)] +fn main() -> Result<()> { + set_background_color()?; + set_foreground_color() +} + +#[cfg(not(windows))] +fn main() { + println!("This example is for the Windows platform only."); +} diff --git a/vendor/crossterm_winapi/examples/console.rs b/vendor/crossterm_winapi/examples/console.rs index ba3e595..e8da5e3 100644 --- a/vendor/crossterm_winapi/examples/console.rs +++ b/vendor/crossterm_winapi/examples/console.rs @@ -1,26 +1,26 @@ -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::ConsoleMode; - -#[cfg(windows)] -fn change_console_mode() -> Result<()> { - let console_mode = ConsoleMode::new()?; - - // get the current console mode: - let _mode: u32 = console_mode.mode()?; - - // set the console mode (not sure if this is an actual value xp) - console_mode.set_mode(10) -} - -#[cfg(windows)] -fn main() -> Result<()> { - change_console_mode() -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} +#[cfg(windows)] +use std::io::Result; + +#[cfg(windows)] +use crossterm_winapi::ConsoleMode; + +#[cfg(windows)] +fn change_console_mode() -> Result<()> { + let console_mode = ConsoleMode::new()?; + + // get the current console mode: + let _mode: u32 = console_mode.mode()?; + + // set the console mode (not sure if this is an actual value xp) + console_mode.set_mode(10) +} + +#[cfg(windows)] +fn main() -> Result<()> { + change_console_mode() +} + +#[cfg(not(windows))] +fn main() { + println!("This example is for the Windows platform only."); +} diff --git a/vendor/crossterm_winapi/examples/handle.rs b/vendor/crossterm_winapi/examples/handle.rs index 1ca1a17..27fbb3c 100644 --- a/vendor/crossterm_winapi/examples/handle.rs +++ b/vendor/crossterm_winapi/examples/handle.rs @@ -1,30 +1,30 @@ -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::{Handle, HandleType}; - -#[cfg(windows)] -#[allow(unused_variables)] -fn main() -> Result<()> { - // see the description of the types to see what they do. - let out_put_handle = Handle::new(HandleType::OutputHandle)?; - let out_put_handle = Handle::new(HandleType::InputHandle)?; - let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle)?; - let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle)?; - - // now you have this handle you might want to get the WinAPI `HANDLE` it is wrapping. - // you can do this by defencing. - - let handle /*:HANDLE*/ = *out_put_handle; - - // you can also pass you own `HANDLE` to create an instance of `Handle` - let handle = unsafe { Handle::from_raw(handle) }; /* winapi::um::winnt::HANDLE */ - - Ok(()) -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} +#[cfg(windows)] +use std::io::Result; + +#[cfg(windows)] +use crossterm_winapi::{Handle, HandleType}; + +#[cfg(windows)] +#[allow(unused_variables)] +fn main() -> Result<()> { + // see the description of the types to see what they do. + let out_put_handle = Handle::new(HandleType::OutputHandle)?; + let out_put_handle = Handle::new(HandleType::InputHandle)?; + let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle)?; + let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle)?; + + // now you have this handle you might want to get the WinAPI `HANDLE` it is wrapping. + // you can do this by defencing. + + let handle /*:HANDLE*/ = *out_put_handle; + + // you can also pass you own `HANDLE` to create an instance of `Handle` + let handle = unsafe { Handle::from_raw(handle) }; /* winapi::um::winnt::HANDLE */ + + Ok(()) +} + +#[cfg(not(windows))] +fn main() { + println!("This example is for the Windows platform only."); +} diff --git a/vendor/crossterm_winapi/examples/screen_buffer.rs b/vendor/crossterm_winapi/examples/screen_buffer.rs index 24148ca..086815c 100644 --- a/vendor/crossterm_winapi/examples/screen_buffer.rs +++ b/vendor/crossterm_winapi/examples/screen_buffer.rs @@ -1,41 +1,41 @@ -#![allow(dead_code)] - -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::ScreenBuffer; - -#[cfg(windows)] -fn print_screen_buffer_information() -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - - // get console screen buffer information - let csbi = screen_buffer.info()?; - - println!("cursor post: {:?}", csbi.cursor_pos()); - println!("attributes: {:?}", csbi.attributes()); - println!("terminal window dimentions {:?}", csbi.terminal_window()); - println!("terminal size {:?}", csbi.terminal_size()); - - Ok(()) -} - -#[cfg(windows)] -fn multiple_screen_buffers() -> Result<()> { - // create new screen buffer - let screen_buffer = ScreenBuffer::create()?; - - // which to this screen buffer - screen_buffer.show() -} - -#[cfg(windows)] -fn main() -> Result<()> { - print_screen_buffer_information() -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} +#![allow(dead_code)] + +#[cfg(windows)] +use std::io::Result; + +#[cfg(windows)] +use crossterm_winapi::ScreenBuffer; + +#[cfg(windows)] +fn print_screen_buffer_information() -> Result<()> { + let screen_buffer = ScreenBuffer::current()?; + + // get console screen buffer information + let csbi = screen_buffer.info()?; + + println!("cursor post: {:?}", csbi.cursor_pos()); + println!("attributes: {:?}", csbi.attributes()); + println!("terminal window dimentions {:?}", csbi.terminal_window()); + println!("terminal size {:?}", csbi.terminal_size()); + + Ok(()) +} + +#[cfg(windows)] +fn multiple_screen_buffers() -> Result<()> { + // create new screen buffer + let screen_buffer = ScreenBuffer::create()?; + + // which to this screen buffer + screen_buffer.show() +} + +#[cfg(windows)] +fn main() -> Result<()> { + print_screen_buffer_information() +} + +#[cfg(not(windows))] +fn main() { + println!("This example is for the Windows platform only."); +} diff --git a/vendor/crossterm_winapi/src/console.rs b/vendor/crossterm_winapi/src/console.rs index 41499d8..7c80f47 100644 --- a/vendor/crossterm_winapi/src/console.rs +++ b/vendor/crossterm_winapi/src/console.rs @@ -1,236 +1,236 @@ -use std::io::{self, Result}; -use std::iter; -use std::slice; -use std::str; - -use winapi::ctypes::c_void; -use winapi::shared::minwindef::DWORD; -use winapi::shared::ntdef::NULL; -use winapi::um::consoleapi::{GetNumberOfConsoleInputEvents, ReadConsoleInputW, WriteConsoleW}; -use winapi::um::wincon::{ - FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetLargestConsoleWindowSize, - SetConsoleTextAttribute, SetConsoleWindowInfo, COORD, INPUT_RECORD, SMALL_RECT, -}; - -use super::{result, Coord, Handle, HandleType, InputRecord, WindowPositions}; - -/// A wrapper around a screen buffer. -#[derive(Debug, Clone)] -pub struct Console { - handle: Handle, -} - -impl Console { - /// Create new instance of `Console`. - /// - /// This created instance will use the default output handle (STD_OUTPUT_HANDLE) as handle for the function call it wraps. - pub fn output() -> Result { - Ok(Console { - handle: Handle::new(HandleType::OutputHandle)?, - }) - } - - /// Sets the attributes of characters written to the console screen buffer by the `WriteFile` or `WriteConsole` functions, or echoed by the `ReadFile` or `ReadConsole` functions. - /// This function affects text written after the function call. - /// - /// The attributes is a bitmask of possible [character - /// attributes](https://docs.microsoft.com/en-us/windows/console/console-screen-buffers#character-attributes). - /// - /// This wraps - /// [`SetConsoleTextAttribute`](https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute). - pub fn set_text_attribute(&self, value: u16) -> Result<()> { - result(unsafe { SetConsoleTextAttribute(*self.handle, value) })?; - Ok(()) - } - - /// Sets the current size and position of a console screen buffer's window. - /// - /// This wraps - /// [`SetConsoleWindowInfo`](https://docs.microsoft.com/en-us/windows/console/setconsolewindowinfo). - pub fn set_console_info(&self, absolute: bool, rect: WindowPositions) -> Result<()> { - let absolute = match absolute { - true => 1, - false => 0, - }; - let a = SMALL_RECT::from(rect); - - result(unsafe { SetConsoleWindowInfo(*self.handle, absolute, &a) })?; - - Ok(()) - } - - /// Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates. - /// Returns the number of characters that have been written. - /// - /// This wraps - /// [`FillConsoleOutputCharacterA`](https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter). - pub fn fill_whit_character( - &self, - start_location: Coord, - cells_to_write: u32, - filling_char: char, - ) -> Result { - let mut chars_written = 0; - result(unsafe { - // fill the cells in console with blanks - FillConsoleOutputCharacterA( - *self.handle, - filling_char as i8, - cells_to_write, - COORD::from(start_location), - &mut chars_written, - ) - })?; - - Ok(chars_written) - } - - /// Sets the character attributes for a specified number of character cells, beginning at the specified coordinates in a screen buffer. - /// Returns the number of cells that have been modified. - /// - /// This wraps - /// [`FillConsoleOutputAttribute`](https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputattribute). - pub fn fill_whit_attribute( - &self, - start_location: Coord, - cells_to_write: u32, - dw_attribute: u16, - ) -> Result { - let mut cells_written = 0; - // Get the position of the current console window - result(unsafe { - FillConsoleOutputAttribute( - *self.handle, - dw_attribute, - cells_to_write, - COORD::from(start_location), - &mut cells_written, - ) - })?; - - Ok(cells_written) - } - - /// Retrieves the size of the largest possible console window, based on the current text and the size of the display. - /// - /// This wraps [`GetLargestConsoleWindowSize`](https://docs.microsoft.com/en-us/windows/console/getlargestconsolewindowsize) - pub fn largest_window_size(&self) -> Result { - crate::coord_result(unsafe { GetLargestConsoleWindowSize(*self.handle) }) - } - - /// Writes a character string to a console screen buffer beginning at the current cursor location. - /// - /// This wraps - /// [`WriteConsoleW`](https://docs.microsoft.com/en-us/windows/console/writeconsole). - pub fn write_char_buffer(&self, buf: &[u8]) -> Result { - // get string from u8[] and parse it to an c_str - let utf8 = match str::from_utf8(buf) { - Ok(string) => string, - Err(_) => { - return Err(io::Error::new( - io::ErrorKind::Other, - "Could not parse to utf8 string", - )); - } - }; - - let utf16: Vec = utf8.encode_utf16().collect(); - let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void; - - let mut cells_written: u32 = 0; - - result(unsafe { - WriteConsoleW( - *self.handle, - utf16_ptr, - utf16.len() as u32, - &mut cells_written, - NULL, - ) - })?; - - Ok(utf8.as_bytes().len()) - } - - /// Read one input event. - /// - /// This wraps - /// [`ReadConsoleInputW`](https://docs.microsoft.com/en-us/windows/console/readconsoleinput). - pub fn read_single_input_event(&self) -> Result { - let mut record: INPUT_RECORD = INPUT_RECORD::default(); - - { - // Convert an INPUT_RECORD to an &mut [INPUT_RECORD] of length 1 - let buf = slice::from_mut(&mut record); - let num_read = self.read_input(buf)?; - - // The windows API promises that ReadConsoleInput returns at least - // 1 element - debug_assert!(num_read == 1); - } - - Ok(record.into()) - } - - /// Read all available input events without blocking. - /// - /// This wraps - /// [`ReadConsoleInputW`](https://docs.microsoft.com/en-us/windows/console/readconsoleinput). - pub fn read_console_input(&self) -> Result> { - let buf_len = self.number_of_console_input_events()?; - - // Fast-skipping all the code below if there is nothing to read at all - if buf_len == 0 { - return Ok(vec![]); - } - - let mut buf: Vec = iter::repeat_with(INPUT_RECORD::default) - .take(buf_len as usize) - .collect(); - - let num_read = self.read_input(buf.as_mut_slice())?; - - Ok(buf - .into_iter() - .take(num_read) - .map(InputRecord::from) - .collect()) - } - - /// Get the number of available input events that can be read without blocking. - /// - /// This wraps - /// [`GetNumberOfConsoleInputEvents`](https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents). - pub fn number_of_console_input_events(&self) -> Result { - let mut buf_len: DWORD = 0; - result(unsafe { GetNumberOfConsoleInputEvents(*self.handle, &mut buf_len) })?; - Ok(buf_len) - } - - /// Read input (via ReadConsoleInputW) into buf and return the number - /// of events read. ReadConsoleInputW guarantees that at least one event - /// is read, even if it means blocking the thread. buf.len() must fit in - /// a u32. - fn read_input(&self, buf: &mut [INPUT_RECORD]) -> Result { - let mut num_records = 0; - debug_assert!(buf.len() < std::u32::MAX as usize); - - result(unsafe { - ReadConsoleInputW( - *self.handle, - buf.as_mut_ptr(), - buf.len() as u32, - &mut num_records, - ) - })?; - - Ok(num_records as usize) - } -} - -impl From for Console { - /// Create a `Console` instance who's functions will be executed on the the given `Handle` - fn from(handle: Handle) -> Self { - Console { handle } - } -} +use std::io::{self, Result}; +use std::iter; +use std::slice; +use std::str; + +use winapi::ctypes::c_void; +use winapi::shared::minwindef::DWORD; +use winapi::shared::ntdef::NULL; +use winapi::um::consoleapi::{GetNumberOfConsoleInputEvents, ReadConsoleInputW, WriteConsoleW}; +use winapi::um::wincon::{ + FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetLargestConsoleWindowSize, + SetConsoleTextAttribute, SetConsoleWindowInfo, COORD, INPUT_RECORD, SMALL_RECT, +}; + +use super::{result, Coord, Handle, HandleType, InputRecord, WindowPositions}; + +/// A wrapper around a screen buffer. +#[derive(Debug, Clone)] +pub struct Console { + handle: Handle, +} + +impl Console { + /// Create new instance of `Console`. + /// + /// This created instance will use the default output handle (STD_OUTPUT_HANDLE) as handle for the function call it wraps. + pub fn output() -> Result { + Ok(Console { + handle: Handle::new(HandleType::OutputHandle)?, + }) + } + + /// Sets the attributes of characters written to the console screen buffer by the `WriteFile` or `WriteConsole` functions, or echoed by the `ReadFile` or `ReadConsole` functions. + /// This function affects text written after the function call. + /// + /// The attributes is a bitmask of possible [character + /// attributes](https://docs.microsoft.com/en-us/windows/console/console-screen-buffers#character-attributes). + /// + /// This wraps + /// [`SetConsoleTextAttribute`](https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute). + pub fn set_text_attribute(&self, value: u16) -> Result<()> { + result(unsafe { SetConsoleTextAttribute(*self.handle, value) })?; + Ok(()) + } + + /// Sets the current size and position of a console screen buffer's window. + /// + /// This wraps + /// [`SetConsoleWindowInfo`](https://docs.microsoft.com/en-us/windows/console/setconsolewindowinfo). + pub fn set_console_info(&self, absolute: bool, rect: WindowPositions) -> Result<()> { + let absolute = match absolute { + true => 1, + false => 0, + }; + let a = SMALL_RECT::from(rect); + + result(unsafe { SetConsoleWindowInfo(*self.handle, absolute, &a) })?; + + Ok(()) + } + + /// Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates. + /// Returns the number of characters that have been written. + /// + /// This wraps + /// [`FillConsoleOutputCharacterA`](https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter). + pub fn fill_whit_character( + &self, + start_location: Coord, + cells_to_write: u32, + filling_char: char, + ) -> Result { + let mut chars_written = 0; + result(unsafe { + // fill the cells in console with blanks + FillConsoleOutputCharacterA( + *self.handle, + filling_char as i8, + cells_to_write, + COORD::from(start_location), + &mut chars_written, + ) + })?; + + Ok(chars_written) + } + + /// Sets the character attributes for a specified number of character cells, beginning at the specified coordinates in a screen buffer. + /// Returns the number of cells that have been modified. + /// + /// This wraps + /// [`FillConsoleOutputAttribute`](https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputattribute). + pub fn fill_whit_attribute( + &self, + start_location: Coord, + cells_to_write: u32, + dw_attribute: u16, + ) -> Result { + let mut cells_written = 0; + // Get the position of the current console window + result(unsafe { + FillConsoleOutputAttribute( + *self.handle, + dw_attribute, + cells_to_write, + COORD::from(start_location), + &mut cells_written, + ) + })?; + + Ok(cells_written) + } + + /// Retrieves the size of the largest possible console window, based on the current text and the size of the display. + /// + /// This wraps [`GetLargestConsoleWindowSize`](https://docs.microsoft.com/en-us/windows/console/getlargestconsolewindowsize) + pub fn largest_window_size(&self) -> Result { + crate::coord_result(unsafe { GetLargestConsoleWindowSize(*self.handle) }) + } + + /// Writes a character string to a console screen buffer beginning at the current cursor location. + /// + /// This wraps + /// [`WriteConsoleW`](https://docs.microsoft.com/en-us/windows/console/writeconsole). + pub fn write_char_buffer(&self, buf: &[u8]) -> Result { + // get string from u8[] and parse it to an c_str + let utf8 = match str::from_utf8(buf) { + Ok(string) => string, + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::Other, + "Could not parse to utf8 string", + )); + } + }; + + let utf16: Vec = utf8.encode_utf16().collect(); + let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void; + + let mut cells_written: u32 = 0; + + result(unsafe { + WriteConsoleW( + *self.handle, + utf16_ptr, + utf16.len() as u32, + &mut cells_written, + NULL, + ) + })?; + + Ok(utf8.as_bytes().len()) + } + + /// Read one input event. + /// + /// This wraps + /// [`ReadConsoleInputW`](https://docs.microsoft.com/en-us/windows/console/readconsoleinput). + pub fn read_single_input_event(&self) -> Result { + let mut record: INPUT_RECORD = INPUT_RECORD::default(); + + { + // Convert an INPUT_RECORD to an &mut [INPUT_RECORD] of length 1 + let buf = slice::from_mut(&mut record); + let num_read = self.read_input(buf)?; + + // The windows API promises that ReadConsoleInput returns at least + // 1 element + debug_assert!(num_read == 1); + } + + Ok(record.into()) + } + + /// Read all available input events without blocking. + /// + /// This wraps + /// [`ReadConsoleInputW`](https://docs.microsoft.com/en-us/windows/console/readconsoleinput). + pub fn read_console_input(&self) -> Result> { + let buf_len = self.number_of_console_input_events()?; + + // Fast-skipping all the code below if there is nothing to read at all + if buf_len == 0 { + return Ok(vec![]); + } + + let mut buf: Vec = iter::repeat_with(INPUT_RECORD::default) + .take(buf_len as usize) + .collect(); + + let num_read = self.read_input(buf.as_mut_slice())?; + + Ok(buf + .into_iter() + .take(num_read) + .map(InputRecord::from) + .collect()) + } + + /// Get the number of available input events that can be read without blocking. + /// + /// This wraps + /// [`GetNumberOfConsoleInputEvents`](https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents). + pub fn number_of_console_input_events(&self) -> Result { + let mut buf_len: DWORD = 0; + result(unsafe { GetNumberOfConsoleInputEvents(*self.handle, &mut buf_len) })?; + Ok(buf_len) + } + + /// Read input (via ReadConsoleInputW) into buf and return the number + /// of events read. ReadConsoleInputW guarantees that at least one event + /// is read, even if it means blocking the thread. buf.len() must fit in + /// a u32. + fn read_input(&self, buf: &mut [INPUT_RECORD]) -> Result { + let mut num_records = 0; + debug_assert!(buf.len() < std::u32::MAX as usize); + + result(unsafe { + ReadConsoleInputW( + *self.handle, + buf.as_mut_ptr(), + buf.len() as u32, + &mut num_records, + ) + })?; + + Ok(num_records as usize) + } +} + +impl From for Console { + /// Create a `Console` instance who's functions will be executed on the the given `Handle` + fn from(handle: Handle) -> Self { + Console { handle } + } +} diff --git a/vendor/crossterm_winapi/src/console_mode.rs b/vendor/crossterm_winapi/src/console_mode.rs index 1bef06b..802dc6d 100644 --- a/vendor/crossterm_winapi/src/console_mode.rs +++ b/vendor/crossterm_winapi/src/console_mode.rs @@ -1,75 +1,75 @@ -use std::io::Result; - -use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode}; - -use super::{result, Handle, HandleType}; - -/// A wrapper around a screen buffer, focusing on calls to get and set the console mode. -/// -/// This wraps [`SetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/setconsolemode) -/// and [`GetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/getconsolemode). -#[derive(Debug, Clone)] -pub struct ConsoleMode { - // the handle used for the functions of this type. - handle: Handle, -} - -impl ConsoleMode { - /// Create a new `ConsoleMode` instance. - /// - /// This will use the standard output as its handle. - /// When you explicitly want to specify the handle used for the function calls use `ConsoleMode::from(handle)` instead. - pub fn new() -> Result { - Ok(ConsoleMode { - handle: Handle::new(HandleType::OutputHandle)?, - }) - } - - /// Set the console mode to the given console mode. - /// - /// This function sets the `dwMode`. - /// - /// This wraps - /// [`SetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/setconsolemode). - pub fn set_mode(&self, console_mode: u32) -> Result<()> { - result(unsafe { SetConsoleMode(*self.handle, console_mode) }) - } - - /// Get the console mode. - /// - /// This function returns the `lpMode`. - /// - /// This wraps - /// [`GetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/getconsolemode). - pub fn mode(&self) -> Result { - let mut console_mode = 0; - result(unsafe { GetConsoleMode(*self.handle, &mut console_mode) })?; - Ok(console_mode) - } -} - -impl From for ConsoleMode { - fn from(handle: Handle) -> Self { - ConsoleMode { handle } - } -} - -#[cfg(test)] -mod tests { - use super::ConsoleMode; - - // TODO - Test is ignored, because it's failing on Travis CI - #[test] - #[ignore] - fn test_set_get_mode() { - let mode = ConsoleMode::new().unwrap(); - - let original_mode = mode.mode().unwrap(); - - mode.set_mode(0x0004).unwrap(); - let console_mode = mode.mode().unwrap(); - assert_eq!(console_mode & 0x0004, mode.mode().unwrap()); - - mode.set_mode(original_mode).unwrap(); - } -} +use std::io::Result; + +use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode}; + +use super::{result, Handle, HandleType}; + +/// A wrapper around a screen buffer, focusing on calls to get and set the console mode. +/// +/// This wraps [`SetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/setconsolemode) +/// and [`GetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/getconsolemode). +#[derive(Debug, Clone)] +pub struct ConsoleMode { + // the handle used for the functions of this type. + handle: Handle, +} + +impl ConsoleMode { + /// Create a new `ConsoleMode` instance. + /// + /// This will use the standard output as its handle. + /// When you explicitly want to specify the handle used for the function calls use `ConsoleMode::from(handle)` instead. + pub fn new() -> Result { + Ok(ConsoleMode { + handle: Handle::new(HandleType::OutputHandle)?, + }) + } + + /// Set the console mode to the given console mode. + /// + /// This function sets the `dwMode`. + /// + /// This wraps + /// [`SetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/setconsolemode). + pub fn set_mode(&self, console_mode: u32) -> Result<()> { + result(unsafe { SetConsoleMode(*self.handle, console_mode) }) + } + + /// Get the console mode. + /// + /// This function returns the `lpMode`. + /// + /// This wraps + /// [`GetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/getconsolemode). + pub fn mode(&self) -> Result { + let mut console_mode = 0; + result(unsafe { GetConsoleMode(*self.handle, &mut console_mode) })?; + Ok(console_mode) + } +} + +impl From for ConsoleMode { + fn from(handle: Handle) -> Self { + ConsoleMode { handle } + } +} + +#[cfg(test)] +mod tests { + use super::ConsoleMode; + + // TODO - Test is ignored, because it's failing on Travis CI + #[test] + #[ignore] + fn test_set_get_mode() { + let mode = ConsoleMode::new().unwrap(); + + let original_mode = mode.mode().unwrap(); + + mode.set_mode(0x0004).unwrap(); + let console_mode = mode.mode().unwrap(); + assert_eq!(console_mode & 0x0004, mode.mode().unwrap()); + + mode.set_mode(original_mode).unwrap(); + } +} diff --git a/vendor/crossterm_winapi/src/csbi.rs b/vendor/crossterm_winapi/src/csbi.rs index ead6c1f..17f04fe 100644 --- a/vendor/crossterm_winapi/src/csbi.rs +++ b/vendor/crossterm_winapi/src/csbi.rs @@ -1,82 +1,82 @@ -use std::fmt; -use std::mem::zeroed; - -use winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO; - -use super::{Coord, Size, WindowPositions}; - -/// Information about a console screen buffer. -/// -/// This wraps -/// [`CONSOLE_SCREEN_BUFFER_INFO`](https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str). -// TODO: replace the innards of this type with our own, more friendly types, like Coord. -// This will obviously be a breaking change. -#[derive(Clone)] -pub struct ScreenBufferInfo(pub CONSOLE_SCREEN_BUFFER_INFO); - -// TODO: replace this with a derive ASAP -impl fmt::Debug for ScreenBufferInfo { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ScreenBufferInfo") - .field("dwSize", &self.buffer_size()) - .field("dwCursorPosition", &self.cursor_pos()) - .field("wAttributes", &self.attributes()) // TODO: hex print this - .field("srWindow", &self.terminal_window()) - .field( - "dwMaximumWindowSize", - &Size::from(self.0.dwMaximumWindowSize), - ) - .finish() - } -} - -impl ScreenBufferInfo { - /// Create a new console screen buffer without all zeroed properties. - pub fn new() -> ScreenBufferInfo { - ScreenBufferInfo(unsafe { zeroed() }) - } - - /// Get the size of the screen buffer. - /// - /// Will take `dwSize` from the current screen buffer and convert it into a [`Size`]. - pub fn buffer_size(&self) -> Size { - Size::from(self.0.dwSize) - } - - /// Get the size of the terminal display window. - /// - /// Will calculate the width and height from `srWindow` and convert it into a [`Size`]. - pub fn terminal_size(&self) -> Size { - Size::new( - self.0.srWindow.Right - self.0.srWindow.Left, - self.0.srWindow.Bottom - self.0.srWindow.Top, - ) - } - - /// Get the position and size of the terminal display window. - /// - /// Will take `srWindow` and convert it into the `WindowPositions` type. - pub fn terminal_window(&self) -> WindowPositions { - WindowPositions::from(self.0) - } - - /// Get the current attributes of the characters that are being written to the console. - /// - /// Will take `wAttributes` from the current screen buffer. - pub fn attributes(&self) -> u16 { - self.0.wAttributes - } - - /// Get the current column and row of the terminal cursor in the screen buffer. - /// - /// Will take `dwCursorPosition` from the current screen buffer. - pub fn cursor_pos(&self) -> Coord { - Coord::from(self.0.dwCursorPosition) - } -} - -impl From for ScreenBufferInfo { - fn from(csbi: CONSOLE_SCREEN_BUFFER_INFO) -> Self { - ScreenBufferInfo(csbi) - } -} +use std::fmt; +use std::mem::zeroed; + +use winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO; + +use super::{Coord, Size, WindowPositions}; + +/// Information about a console screen buffer. +/// +/// This wraps +/// [`CONSOLE_SCREEN_BUFFER_INFO`](https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str). +// TODO: replace the innards of this type with our own, more friendly types, like Coord. +// This will obviously be a breaking change. +#[derive(Clone)] +pub struct ScreenBufferInfo(pub CONSOLE_SCREEN_BUFFER_INFO); + +// TODO: replace this with a derive ASAP +impl fmt::Debug for ScreenBufferInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("ScreenBufferInfo") + .field("dwSize", &self.buffer_size()) + .field("dwCursorPosition", &self.cursor_pos()) + .field("wAttributes", &self.attributes()) // TODO: hex print this + .field("srWindow", &self.terminal_window()) + .field( + "dwMaximumWindowSize", + &Size::from(self.0.dwMaximumWindowSize), + ) + .finish() + } +} + +impl ScreenBufferInfo { + /// Create a new console screen buffer without all zeroed properties. + pub fn new() -> ScreenBufferInfo { + ScreenBufferInfo(unsafe { zeroed() }) + } + + /// Get the size of the screen buffer. + /// + /// Will take `dwSize` from the current screen buffer and convert it into a [`Size`]. + pub fn buffer_size(&self) -> Size { + Size::from(self.0.dwSize) + } + + /// Get the size of the terminal display window. + /// + /// Will calculate the width and height from `srWindow` and convert it into a [`Size`]. + pub fn terminal_size(&self) -> Size { + Size::new( + self.0.srWindow.Right - self.0.srWindow.Left, + self.0.srWindow.Bottom - self.0.srWindow.Top, + ) + } + + /// Get the position and size of the terminal display window. + /// + /// Will take `srWindow` and convert it into the `WindowPositions` type. + pub fn terminal_window(&self) -> WindowPositions { + WindowPositions::from(self.0) + } + + /// Get the current attributes of the characters that are being written to the console. + /// + /// Will take `wAttributes` from the current screen buffer. + pub fn attributes(&self) -> u16 { + self.0.wAttributes + } + + /// Get the current column and row of the terminal cursor in the screen buffer. + /// + /// Will take `dwCursorPosition` from the current screen buffer. + pub fn cursor_pos(&self) -> Coord { + Coord::from(self.0.dwCursorPosition) + } +} + +impl From for ScreenBufferInfo { + fn from(csbi: CONSOLE_SCREEN_BUFFER_INFO) -> Self { + ScreenBufferInfo(csbi) + } +} diff --git a/vendor/crossterm_winapi/src/handle.rs b/vendor/crossterm_winapi/src/handle.rs index 504f1bd..fd11d8d 100644 --- a/vendor/crossterm_winapi/src/handle.rs +++ b/vendor/crossterm_winapi/src/handle.rs @@ -1,219 +1,219 @@ -//! This module contains some logic for working with the console handle. - -use std::io::Result; -use std::ops::Deref; -use std::ptr::null_mut; -use std::sync::Arc; - -use winapi::shared::minwindef::DWORD; -use winapi::um::{ - fileapi::{CreateFileW, OPEN_EXISTING}, - handleapi::{CloseHandle, INVALID_HANDLE_VALUE}, - processenv::GetStdHandle, - winbase::{STD_INPUT_HANDLE, STD_OUTPUT_HANDLE}, - winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE}, -}; - -use super::handle_result; - -/// The standard handles of a process. -/// -/// See [the Windows documentation on console -/// handles](https://docs.microsoft.com/en-us/windows/console/console-handles) for more info. -#[derive(Debug, Clone, Copy)] -pub enum HandleType { - /// The process' standard output. - OutputHandle, - /// The process' standard input. - InputHandle, - /// The process' active console screen buffer, `CONOUT$`. - CurrentOutputHandle, - /// The process' console input buffer, `CONIN$`. - CurrentInputHandle, -} - -/// Inner structure for closing a handle on Drop. -/// -/// The second parameter indicates if the HANDLE is exclusively owned or not. -/// A non-exclusive handle can be created using for example -/// `Handle::input_handle` or `Handle::output_handle`, which corresponds to -/// stdin and stdout respectively. -#[derive(Debug)] -struct Inner { - handle: HANDLE, - is_exclusive: bool, -} - -impl Inner { - fn new_exclusive(handle: HANDLE) -> Self { - Inner { - handle, - is_exclusive: true, - } - } - - fn new_shared(handle: HANDLE) -> Self { - Inner { - handle, - is_exclusive: false, - } - } -} - -impl Drop for Inner { - fn drop(&mut self) { - if self.is_exclusive { - assert!( - unsafe { CloseHandle(self.handle) != 0 }, - "failed to close handle" - ) - } - } -} - -unsafe impl Send for Inner {} - -unsafe impl Sync for Inner {} - -/// This abstracts away some WinAPI calls to set and get some console handles. -/// -/// It wraps WinAPI's [`HANDLE`] type. -#[derive(Debug, Clone)] -pub struct Handle { - handle: Arc, -} - -impl Handle { - /// Create a new handle of a certaint type. - pub fn new(handle: HandleType) -> Result { - match handle { - HandleType::OutputHandle => Handle::output_handle(), - HandleType::InputHandle => Handle::input_handle(), - HandleType::CurrentOutputHandle => Handle::current_out_handle(), - HandleType::CurrentInputHandle => Handle::current_in_handle(), - } - } - - /// Construct a handle from a raw handle. - /// - /// # Safety - /// - /// This is unsafe since there is not guarantee that the underlying HANDLE is thread-safe to implement `Send` and `Sync`. - /// Most HANDLE's however, are thread safe. - pub unsafe fn from_raw(handle: HANDLE) -> Self { - Self { - handle: Arc::new(Inner::new_exclusive(handle)), - } - } - - /// Get the handle of the active screen buffer. - /// When using multiple screen buffers this will always point to the to the current screen output buffer. - /// - /// This function uses `CONOUT$` to create a file handle to the current output buffer. - /// - /// This wraps - /// [`CreateFileW`](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew). - pub fn current_out_handle() -> Result { - let utf16: Vec = "CONOUT$\0".encode_utf16().collect(); - let utf16_ptr: *const u16 = utf16.as_ptr(); - - let handle = handle_result(unsafe { - CreateFileW( - utf16_ptr, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - null_mut(), - OPEN_EXISTING, - 0, - null_mut(), - ) - })?; - - Ok(Handle { - handle: Arc::new(Inner::new_exclusive(handle)), - }) - } - - /// Get the handle of the console input buffer. - /// - /// This function uses `CONIN$` to create a file handle to the current input buffer. - /// - /// This wraps - /// [`CreateFileW`](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew). - pub fn current_in_handle() -> Result { - let utf16: Vec = "CONIN$\0".encode_utf16().collect(); - let utf16_ptr: *const u16 = utf16.as_ptr(); - - let handle = handle_result(unsafe { - CreateFileW( - utf16_ptr, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - null_mut(), - OPEN_EXISTING, - 0, - null_mut(), - ) - })?; - - Ok(Handle { - handle: Arc::new(Inner::new_exclusive(handle)), - }) - } - - /// Get the handle of the standard output. - /// - /// On success this function returns the `HANDLE` to `STD_OUTPUT_HANDLE`. - /// - /// This wraps [`GetStdHandle`](https://docs.microsoft.com/en-us/windows/console/getstdhandle) - /// called with `STD_OUTPUT_HANDLE`. - pub fn output_handle() -> Result { - Self::std_handle(STD_OUTPUT_HANDLE) - } - - /// Get the handle of the input screen buffer. - /// - /// On success this function returns the `HANDLE` to `STD_INPUT_HANDLE`. - /// - /// This wraps [`GetStdHandle`](https://docs.microsoft.com/en-us/windows/console/getstdhandle) - /// called with `STD_INPUT_HANDLE`. - pub fn input_handle() -> Result { - Self::std_handle(STD_INPUT_HANDLE) - } - - fn std_handle(which_std: DWORD) -> Result { - let handle = handle_result(unsafe { GetStdHandle(which_std) })?; - - Ok(Handle { - handle: Arc::new(Inner::new_shared(handle)), - }) - } - - /// Checks if the console handle is an invalid handle value. - /// - /// This is done by checking if the passed `HANDLE` is equal to `INVALID_HANDLE_VALUE`. - pub fn is_valid_handle(handle: &HANDLE) -> bool { - *handle != INVALID_HANDLE_VALUE - } -} - -impl Deref for Handle { - type Target = HANDLE; - - fn deref(&self) -> &HANDLE { - &self.handle.handle - } -} - -#[cfg(test)] -mod tests { - use super::{Handle, HandleType}; - - #[test] - fn test_get_handle() { - assert!(Handle::new(HandleType::OutputHandle).is_ok()); - assert!(Handle::new(HandleType::InputHandle).is_ok()); - assert!(Handle::new(HandleType::CurrentOutputHandle).is_ok()); - assert!(Handle::new(HandleType::CurrentInputHandle).is_ok()); - } -} +//! This module contains some logic for working with the console handle. + +use std::io::Result; +use std::ops::Deref; +use std::ptr::null_mut; +use std::sync::Arc; + +use winapi::shared::minwindef::DWORD; +use winapi::um::{ + fileapi::{CreateFileW, OPEN_EXISTING}, + handleapi::{CloseHandle, INVALID_HANDLE_VALUE}, + processenv::GetStdHandle, + winbase::{STD_INPUT_HANDLE, STD_OUTPUT_HANDLE}, + winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE}, +}; + +use super::handle_result; + +/// The standard handles of a process. +/// +/// See [the Windows documentation on console +/// handles](https://docs.microsoft.com/en-us/windows/console/console-handles) for more info. +#[derive(Debug, Clone, Copy)] +pub enum HandleType { + /// The process' standard output. + OutputHandle, + /// The process' standard input. + InputHandle, + /// The process' active console screen buffer, `CONOUT$`. + CurrentOutputHandle, + /// The process' console input buffer, `CONIN$`. + CurrentInputHandle, +} + +/// Inner structure for closing a handle on Drop. +/// +/// The second parameter indicates if the HANDLE is exclusively owned or not. +/// A non-exclusive handle can be created using for example +/// `Handle::input_handle` or `Handle::output_handle`, which corresponds to +/// stdin and stdout respectively. +#[derive(Debug)] +struct Inner { + handle: HANDLE, + is_exclusive: bool, +} + +impl Inner { + fn new_exclusive(handle: HANDLE) -> Self { + Inner { + handle, + is_exclusive: true, + } + } + + fn new_shared(handle: HANDLE) -> Self { + Inner { + handle, + is_exclusive: false, + } + } +} + +impl Drop for Inner { + fn drop(&mut self) { + if self.is_exclusive { + assert!( + unsafe { CloseHandle(self.handle) != 0 }, + "failed to close handle" + ) + } + } +} + +unsafe impl Send for Inner {} + +unsafe impl Sync for Inner {} + +/// This abstracts away some WinAPI calls to set and get some console handles. +/// +/// It wraps WinAPI's [`HANDLE`] type. +#[derive(Debug, Clone)] +pub struct Handle { + handle: Arc, +} + +impl Handle { + /// Create a new handle of a certaint type. + pub fn new(handle: HandleType) -> Result { + match handle { + HandleType::OutputHandle => Handle::output_handle(), + HandleType::InputHandle => Handle::input_handle(), + HandleType::CurrentOutputHandle => Handle::current_out_handle(), + HandleType::CurrentInputHandle => Handle::current_in_handle(), + } + } + + /// Construct a handle from a raw handle. + /// + /// # Safety + /// + /// This is unsafe since there is not guarantee that the underlying HANDLE is thread-safe to implement `Send` and `Sync`. + /// Most HANDLE's however, are thread safe. + pub unsafe fn from_raw(handle: HANDLE) -> Self { + Self { + handle: Arc::new(Inner::new_exclusive(handle)), + } + } + + /// Get the handle of the active screen buffer. + /// When using multiple screen buffers this will always point to the to the current screen output buffer. + /// + /// This function uses `CONOUT$` to create a file handle to the current output buffer. + /// + /// This wraps + /// [`CreateFileW`](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew). + pub fn current_out_handle() -> Result { + let utf16: Vec = "CONOUT$\0".encode_utf16().collect(); + let utf16_ptr: *const u16 = utf16.as_ptr(); + + let handle = handle_result(unsafe { + CreateFileW( + utf16_ptr, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + null_mut(), + OPEN_EXISTING, + 0, + null_mut(), + ) + })?; + + Ok(Handle { + handle: Arc::new(Inner::new_exclusive(handle)), + }) + } + + /// Get the handle of the console input buffer. + /// + /// This function uses `CONIN$` to create a file handle to the current input buffer. + /// + /// This wraps + /// [`CreateFileW`](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew). + pub fn current_in_handle() -> Result { + let utf16: Vec = "CONIN$\0".encode_utf16().collect(); + let utf16_ptr: *const u16 = utf16.as_ptr(); + + let handle = handle_result(unsafe { + CreateFileW( + utf16_ptr, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + null_mut(), + OPEN_EXISTING, + 0, + null_mut(), + ) + })?; + + Ok(Handle { + handle: Arc::new(Inner::new_exclusive(handle)), + }) + } + + /// Get the handle of the standard output. + /// + /// On success this function returns the `HANDLE` to `STD_OUTPUT_HANDLE`. + /// + /// This wraps [`GetStdHandle`](https://docs.microsoft.com/en-us/windows/console/getstdhandle) + /// called with `STD_OUTPUT_HANDLE`. + pub fn output_handle() -> Result { + Self::std_handle(STD_OUTPUT_HANDLE) + } + + /// Get the handle of the input screen buffer. + /// + /// On success this function returns the `HANDLE` to `STD_INPUT_HANDLE`. + /// + /// This wraps [`GetStdHandle`](https://docs.microsoft.com/en-us/windows/console/getstdhandle) + /// called with `STD_INPUT_HANDLE`. + pub fn input_handle() -> Result { + Self::std_handle(STD_INPUT_HANDLE) + } + + fn std_handle(which_std: DWORD) -> Result { + let handle = handle_result(unsafe { GetStdHandle(which_std) })?; + + Ok(Handle { + handle: Arc::new(Inner::new_shared(handle)), + }) + } + + /// Checks if the console handle is an invalid handle value. + /// + /// This is done by checking if the passed `HANDLE` is equal to `INVALID_HANDLE_VALUE`. + pub fn is_valid_handle(handle: &HANDLE) -> bool { + *handle != INVALID_HANDLE_VALUE + } +} + +impl Deref for Handle { + type Target = HANDLE; + + fn deref(&self) -> &HANDLE { + &self.handle.handle + } +} + +#[cfg(test)] +mod tests { + use super::{Handle, HandleType}; + + #[test] + fn test_get_handle() { + assert!(Handle::new(HandleType::OutputHandle).is_ok()); + assert!(Handle::new(HandleType::InputHandle).is_ok()); + assert!(Handle::new(HandleType::CurrentOutputHandle).is_ok()); + assert!(Handle::new(HandleType::CurrentInputHandle).is_ok()); + } +} diff --git a/vendor/crossterm_winapi/src/lib.rs b/vendor/crossterm_winapi/src/lib.rs index fd92034..14626eb 100644 --- a/vendor/crossterm_winapi/src/lib.rs +++ b/vendor/crossterm_winapi/src/lib.rs @@ -1,71 +1,71 @@ -#![cfg(windows)] -#![deny(unused_imports)] - -use std::io; - -use winapi::shared::minwindef::BOOL; -use winapi::um::handleapi::INVALID_HANDLE_VALUE; -use winapi::um::wincontypes::COORD; -use winapi::um::winnt::HANDLE; - -pub use self::{ - console::Console, - console_mode::ConsoleMode, - csbi::ScreenBufferInfo, - handle::{Handle, HandleType}, - screen_buffer::ScreenBuffer, - semaphore::Semaphore, - structs::{ - ButtonState, ControlKeyState, Coord, EventFlags, InputRecord, KeyEventRecord, MouseEvent, - Size, WindowPositions, - }, -}; - -mod console; -mod console_mode; -mod csbi; -mod handle; -mod screen_buffer; -mod semaphore; -mod structs; - -/// Get the result of a call to WinAPI as an [`io::Result`]. -#[inline] -pub fn result(return_value: BOOL) -> io::Result<()> { - if return_value != 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } -} - -/// Get the result of a call to WinAPI that returns a -/// [`COORD`](https://docs.microsoft.com/en-us/windows/console/coord-str) as an [`io::Result`]. -#[inline] -pub fn coord_result(return_value: COORD) -> io::Result { - if return_value.X != 0 && return_value.Y != 0 { - Ok(Coord::from(return_value)) - } else { - Err(io::Error::last_os_error()) - } -} - -/// Get the result of a call to WinAPI that returns a handle or `INVALID_HANDLE_VALUE`. -#[inline] -pub fn handle_result(return_value: HANDLE) -> io::Result { - if return_value != INVALID_HANDLE_VALUE { - Ok(return_value) - } else { - Err(io::Error::last_os_error()) - } -} - -/// Get the result of a call to WinAPI that returns a handle or `NULL`. -#[inline] -pub fn nonnull_handle_result(return_value: HANDLE) -> io::Result { - if return_value.is_null() { - Err(io::Error::last_os_error()) - } else { - Ok(return_value) - } -} +#![cfg(windows)] +#![deny(unused_imports)] + +use std::io; + +use winapi::shared::minwindef::BOOL; +use winapi::um::handleapi::INVALID_HANDLE_VALUE; +use winapi::um::wincontypes::COORD; +use winapi::um::winnt::HANDLE; + +pub use self::{ + console::Console, + console_mode::ConsoleMode, + csbi::ScreenBufferInfo, + handle::{Handle, HandleType}, + screen_buffer::ScreenBuffer, + semaphore::Semaphore, + structs::{ + ButtonState, ControlKeyState, Coord, EventFlags, InputRecord, KeyEventRecord, MouseEvent, + Size, WindowPositions, + }, +}; + +mod console; +mod console_mode; +mod csbi; +mod handle; +mod screen_buffer; +mod semaphore; +mod structs; + +/// Get the result of a call to WinAPI as an [`io::Result`]. +#[inline] +pub fn result(return_value: BOOL) -> io::Result<()> { + if return_value != 0 { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } +} + +/// Get the result of a call to WinAPI that returns a +/// [`COORD`](https://docs.microsoft.com/en-us/windows/console/coord-str) as an [`io::Result`]. +#[inline] +pub fn coord_result(return_value: COORD) -> io::Result { + if return_value.X != 0 && return_value.Y != 0 { + Ok(Coord::from(return_value)) + } else { + Err(io::Error::last_os_error()) + } +} + +/// Get the result of a call to WinAPI that returns a handle or `INVALID_HANDLE_VALUE`. +#[inline] +pub fn handle_result(return_value: HANDLE) -> io::Result { + if return_value != INVALID_HANDLE_VALUE { + Ok(return_value) + } else { + Err(io::Error::last_os_error()) + } +} + +/// Get the result of a call to WinAPI that returns a handle or `NULL`. +#[inline] +pub fn nonnull_handle_result(return_value: HANDLE) -> io::Result { + if return_value.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(return_value) + } +} diff --git a/vendor/crossterm_winapi/src/screen_buffer.rs b/vendor/crossterm_winapi/src/screen_buffer.rs index cd56b04..0b0af98 100644 --- a/vendor/crossterm_winapi/src/screen_buffer.rs +++ b/vendor/crossterm_winapi/src/screen_buffer.rs @@ -1,117 +1,117 @@ -//! This contains the logic for working with the console buffer. - -use std::io::Result; -use std::mem::size_of; - -use winapi::{ - shared::minwindef::TRUE, - shared::ntdef::NULL, - um::{ - minwinbase::SECURITY_ATTRIBUTES, - wincon::{ - CreateConsoleScreenBuffer, GetConsoleScreenBufferInfo, SetConsoleActiveScreenBuffer, - SetConsoleScreenBufferSize, CONSOLE_TEXTMODE_BUFFER, COORD, - }, - winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE}, - }, -}; - -use super::{handle_result, result, Handle, HandleType, ScreenBufferInfo}; - -/// A wrapper around a screen buffer. -#[derive(Clone, Debug)] -pub struct ScreenBuffer { - handle: Handle, -} - -impl ScreenBuffer { - /// Create a wrapper around a screen buffer from its handle. - pub fn new(handle: Handle) -> Self { - Self { handle } - } - - /// Get the current console screen buffer - pub fn current() -> Result { - Ok(ScreenBuffer { - handle: Handle::new(HandleType::CurrentOutputHandle)?, - }) - } - - /// Create new console screen buffer. - /// - /// This wraps - /// [`CreateConsoleScreenBuffer`](https://docs.microsoft.com/en-us/windows/console/createconsolescreenbuffer) - pub fn create() -> Result { - let security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES { - nLength: size_of::() as u32, - lpSecurityDescriptor: NULL, - bInheritHandle: TRUE, - }; - - let new_screen_buffer = handle_result(unsafe { - CreateConsoleScreenBuffer( - GENERIC_READ | // read/write access - GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, // shared - &security_attr, // default security attributes - CONSOLE_TEXTMODE_BUFFER, // must be TEXTMODE - NULL, - ) - })?; - Ok(ScreenBuffer { - handle: unsafe { Handle::from_raw(new_screen_buffer) }, - }) - } - - /// Set this screen buffer to the current one. - /// - /// This wraps - /// [`SetConsoleActiveScreenBuffer`](https://docs.microsoft.com/en-us/windows/console/setconsoleactivescreenbuffer). - pub fn show(&self) -> Result<()> { - result(unsafe { SetConsoleActiveScreenBuffer(*self.handle) }) - } - - /// Get the screen buffer information like terminal size, cursor position, buffer size. - /// - /// This wraps - /// [`GetConsoleScreenBufferInfo`](https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo). - pub fn info(&self) -> Result { - let mut csbi = ScreenBufferInfo::new(); - result(unsafe { GetConsoleScreenBufferInfo(*self.handle, &mut csbi.0) })?; - Ok(csbi) - } - - /// Set the console screen buffer size to the given size. - /// - /// This wraps - /// [`SetConsoleScreenBufferSize`](https://docs.microsoft.com/en-us/windows/console/setconsolescreenbuffersize). - pub fn set_size(&self, x: i16, y: i16) -> Result<()> { - result(unsafe { SetConsoleScreenBufferSize(*self.handle, COORD { X: x, Y: y }) }) - } - - /// Get the underlying raw `HANDLE` used by this type to execute with. - pub fn handle(&self) -> &Handle { - &self.handle - } -} - -impl From for ScreenBuffer { - fn from(handle: Handle) -> Self { - ScreenBuffer { handle } - } -} - -#[cfg(test)] -mod tests { - use super::ScreenBuffer; - - #[test] - fn test_screen_buffer_info() { - let buffer = ScreenBuffer::current().unwrap(); - let info = buffer.info().unwrap(); - info.terminal_size(); - info.terminal_window(); - info.attributes(); - info.cursor_pos(); - } -} +//! This contains the logic for working with the console buffer. + +use std::io::Result; +use std::mem::size_of; + +use winapi::{ + shared::minwindef::TRUE, + shared::ntdef::NULL, + um::{ + minwinbase::SECURITY_ATTRIBUTES, + wincon::{ + CreateConsoleScreenBuffer, GetConsoleScreenBufferInfo, SetConsoleActiveScreenBuffer, + SetConsoleScreenBufferSize, CONSOLE_TEXTMODE_BUFFER, COORD, + }, + winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE}, + }, +}; + +use super::{handle_result, result, Handle, HandleType, ScreenBufferInfo}; + +/// A wrapper around a screen buffer. +#[derive(Clone, Debug)] +pub struct ScreenBuffer { + handle: Handle, +} + +impl ScreenBuffer { + /// Create a wrapper around a screen buffer from its handle. + pub fn new(handle: Handle) -> Self { + Self { handle } + } + + /// Get the current console screen buffer + pub fn current() -> Result { + Ok(ScreenBuffer { + handle: Handle::new(HandleType::CurrentOutputHandle)?, + }) + } + + /// Create new console screen buffer. + /// + /// This wraps + /// [`CreateConsoleScreenBuffer`](https://docs.microsoft.com/en-us/windows/console/createconsolescreenbuffer) + pub fn create() -> Result { + let security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES { + nLength: size_of::() as u32, + lpSecurityDescriptor: NULL, + bInheritHandle: TRUE, + }; + + let new_screen_buffer = handle_result(unsafe { + CreateConsoleScreenBuffer( + GENERIC_READ | // read/write access + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, // shared + &security_attr, // default security attributes + CONSOLE_TEXTMODE_BUFFER, // must be TEXTMODE + NULL, + ) + })?; + Ok(ScreenBuffer { + handle: unsafe { Handle::from_raw(new_screen_buffer) }, + }) + } + + /// Set this screen buffer to the current one. + /// + /// This wraps + /// [`SetConsoleActiveScreenBuffer`](https://docs.microsoft.com/en-us/windows/console/setconsoleactivescreenbuffer). + pub fn show(&self) -> Result<()> { + result(unsafe { SetConsoleActiveScreenBuffer(*self.handle) }) + } + + /// Get the screen buffer information like terminal size, cursor position, buffer size. + /// + /// This wraps + /// [`GetConsoleScreenBufferInfo`](https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo). + pub fn info(&self) -> Result { + let mut csbi = ScreenBufferInfo::new(); + result(unsafe { GetConsoleScreenBufferInfo(*self.handle, &mut csbi.0) })?; + Ok(csbi) + } + + /// Set the console screen buffer size to the given size. + /// + /// This wraps + /// [`SetConsoleScreenBufferSize`](https://docs.microsoft.com/en-us/windows/console/setconsolescreenbuffersize). + pub fn set_size(&self, x: i16, y: i16) -> Result<()> { + result(unsafe { SetConsoleScreenBufferSize(*self.handle, COORD { X: x, Y: y }) }) + } + + /// Get the underlying raw `HANDLE` used by this type to execute with. + pub fn handle(&self) -> &Handle { + &self.handle + } +} + +impl From for ScreenBuffer { + fn from(handle: Handle) -> Self { + ScreenBuffer { handle } + } +} + +#[cfg(test)] +mod tests { + use super::ScreenBuffer; + + #[test] + fn test_screen_buffer_info() { + let buffer = ScreenBuffer::current().unwrap(); + let info = buffer.info().unwrap(); + info.terminal_size(); + info.terminal_window(); + info.attributes(); + info.cursor_pos(); + } +} diff --git a/vendor/crossterm_winapi/src/semaphore.rs b/vendor/crossterm_winapi/src/semaphore.rs index 494a166..f3e4526 100644 --- a/vendor/crossterm_winapi/src/semaphore.rs +++ b/vendor/crossterm_winapi/src/semaphore.rs @@ -1,41 +1,41 @@ -use std::{io, ptr}; - -use winapi::um::synchapi::{CreateSemaphoreW, ReleaseSemaphore}; - -use crate::{nonnull_handle_result, result, Handle}; - -/// A [Windows semaphore](https://docs.microsoft.com/en-us/windows/win32/sync/semaphore-objects). -#[derive(Clone, Debug)] -pub struct Semaphore(Handle); - -impl Semaphore { - /// Construct a new semaphore. - /// - /// This wraps - /// [`CreateSemaphoreW`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createsemaphorew). - pub fn new() -> io::Result { - let handle = nonnull_handle_result(unsafe { - CreateSemaphoreW(ptr::null_mut(), 0, 1, ptr::null_mut()) - })?; - - let handle = unsafe { Handle::from_raw(handle) }; - Ok(Self(handle)) - } - - /// Release a permit on the semaphore. - /// - /// This wraps - /// [`ReleaseSemaphore`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-releasesemaphore). - pub fn release(&self) -> io::Result<()> { - result(unsafe { ReleaseSemaphore(*self.0, 1, ptr::null_mut()) }) - } - - /// Access the underlying handle to the semaphore. - pub fn handle(&self) -> &Handle { - &self.0 - } -} - -unsafe impl Send for Semaphore {} - -unsafe impl Sync for Semaphore {} +use std::{io, ptr}; + +use winapi::um::synchapi::{CreateSemaphoreW, ReleaseSemaphore}; + +use crate::{nonnull_handle_result, result, Handle}; + +/// A [Windows semaphore](https://docs.microsoft.com/en-us/windows/win32/sync/semaphore-objects). +#[derive(Clone, Debug)] +pub struct Semaphore(Handle); + +impl Semaphore { + /// Construct a new semaphore. + /// + /// This wraps + /// [`CreateSemaphoreW`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createsemaphorew). + pub fn new() -> io::Result { + let handle = nonnull_handle_result(unsafe { + CreateSemaphoreW(ptr::null_mut(), 0, 1, ptr::null_mut()) + })?; + + let handle = unsafe { Handle::from_raw(handle) }; + Ok(Self(handle)) + } + + /// Release a permit on the semaphore. + /// + /// This wraps + /// [`ReleaseSemaphore`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-releasesemaphore). + pub fn release(&self) -> io::Result<()> { + result(unsafe { ReleaseSemaphore(*self.0, 1, ptr::null_mut()) }) + } + + /// Access the underlying handle to the semaphore. + pub fn handle(&self) -> &Handle { + &self.0 + } +} + +unsafe impl Send for Semaphore {} + +unsafe impl Sync for Semaphore {} diff --git a/vendor/crossterm_winapi/src/structs.rs b/vendor/crossterm_winapi/src/structs.rs index be1e65d..5a3344f 100644 --- a/vendor/crossterm_winapi/src/structs.rs +++ b/vendor/crossterm_winapi/src/structs.rs @@ -1,11 +1,11 @@ -pub use self::coord::Coord; -pub use self::input::{ - ButtonState, ControlKeyState, EventFlags, InputRecord, KeyEventRecord, MouseEvent, -}; -pub use self::size::Size; -pub use self::window_coords::WindowPositions; - -mod coord; -mod input; -mod size; -mod window_coords; +pub use self::coord::Coord; +pub use self::input::{ + ButtonState, ControlKeyState, EventFlags, InputRecord, KeyEventRecord, MouseEvent, +}; +pub use self::size::Size; +pub use self::window_coords::WindowPositions; + +mod coord; +mod input; +mod size; +mod window_coords; diff --git a/vendor/crossterm_winapi/src/structs/coord.rs b/vendor/crossterm_winapi/src/structs/coord.rs index a96dbe9..a44a4e1 100644 --- a/vendor/crossterm_winapi/src/structs/coord.rs +++ b/vendor/crossterm_winapi/src/structs/coord.rs @@ -1,42 +1,42 @@ -//! This module provides a type that represents some location/coordination. -//! For example, in WinAPI we have `COORD` which looks and feels inconvenient. -//! This module provides also some trait implementations who will make parsing and working with `COORD` easier. - -use winapi::um::wincon::COORD; - -/// This is type represents the position of something on a certain 'x' and 'y'. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd)] -pub struct Coord { - /// the position on the x axis - pub x: i16, - /// the position on the y axis - pub y: i16, -} - -impl Coord { - /// Create a new coordinate from its x and y position. - pub fn new(x: i16, y: i16) -> Coord { - Coord { x, y } - } -} - -impl From for Coord { - fn from(coord: COORD) -> Self { - Coord::new(coord.X, coord.Y) - } -} - -impl From for COORD { - fn from(location: Coord) -> Self { - COORD { - X: location.x, - Y: location.y, - } - } -} - -impl Into<(u16, u16)> for Coord { - fn into(self) -> (u16, u16) { - (self.x as u16, self.y as u16) - } -} +//! This module provides a type that represents some location/coordination. +//! For example, in WinAPI we have `COORD` which looks and feels inconvenient. +//! This module provides also some trait implementations who will make parsing and working with `COORD` easier. + +use winapi::um::wincon::COORD; + +/// This is type represents the position of something on a certain 'x' and 'y'. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd)] +pub struct Coord { + /// the position on the x axis + pub x: i16, + /// the position on the y axis + pub y: i16, +} + +impl Coord { + /// Create a new coordinate from its x and y position. + pub fn new(x: i16, y: i16) -> Coord { + Coord { x, y } + } +} + +impl From for Coord { + fn from(coord: COORD) -> Self { + Coord::new(coord.X, coord.Y) + } +} + +impl From for COORD { + fn from(location: Coord) -> Self { + COORD { + X: location.x, + Y: location.y, + } + } +} + +impl Into<(u16, u16)> for Coord { + fn into(self) -> (u16, u16) { + (self.x as u16, self.y as u16) + } +} diff --git a/vendor/crossterm_winapi/src/structs/input.rs b/vendor/crossterm_winapi/src/structs/input.rs index 6e9e98f..bc0e122 100644 --- a/vendor/crossterm_winapi/src/structs/input.rs +++ b/vendor/crossterm_winapi/src/structs/input.rs @@ -1,327 +1,327 @@ -//! This module provides a few structs to wrap common input struts to a rusty interface -//! -//! Types like: -//! - `KEY_EVENT_RECORD` -//! - `MOUSE_EVENT_RECORD` -//! - `ControlKeyState` -//! - `ButtonState` -//! - `EventFlags` -//! - `InputEventType` -//! - `INPUT_RECORD` - -use winapi::shared::minwindef::DWORD; -use winapi::um::wincon::{ - FOCUS_EVENT, FOCUS_EVENT_RECORD, FROM_LEFT_1ST_BUTTON_PRESSED, FROM_LEFT_2ND_BUTTON_PRESSED, - FROM_LEFT_3RD_BUTTON_PRESSED, FROM_LEFT_4TH_BUTTON_PRESSED, INPUT_RECORD, KEY_EVENT, - KEY_EVENT_RECORD, MENU_EVENT, MENU_EVENT_RECORD, MOUSE_EVENT, MOUSE_EVENT_RECORD, - RIGHTMOST_BUTTON_PRESSED, WINDOW_BUFFER_SIZE_EVENT, WINDOW_BUFFER_SIZE_RECORD, -}; - -use super::Coord; -use crate::ScreenBuffer; - -/// A [keyboard input event](https://docs.microsoft.com/en-us/windows/console/key-event-record-str). -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct KeyEventRecord { - /// If the key is pressed, this member is true. Otherwise, this member is - /// false (the key is released). - pub key_down: bool, - /// The repeat count, which indicates that a key is being held down. - /// For example, when a key is held down, you might get five events with - /// this member equal to 1, one event with this member equal to 5, or - /// multiple events with this member greater than or equal to 1. - pub repeat_count: u16, - /// A virtual-key code that identifies the given key in a - /// device-independent manner. - pub virtual_key_code: u16, - /// The virtual scan code of the given key that represents the - /// device-dependent value generated by the keyboard hardware. - pub virtual_scan_code: u16, - /// The translated Unicode character (as a WCHAR, or utf-16 value) - pub u_char: u16, - /// The state of the control keys. - pub control_key_state: ControlKeyState, -} - -impl KeyEventRecord { - /// Convert a `KEY_EVENT_RECORD` to KeyEventRecord. This function is private - /// because the `KEY_EVENT_RECORD` has several union fields for characters - /// (u8 vs u16) that we always interpret as u16. We always use the wide - /// versions of windows API calls to support this. - #[inline] - fn from_winapi(record: &KEY_EVENT_RECORD) -> Self { - KeyEventRecord { - key_down: record.bKeyDown != 0, - repeat_count: record.wRepeatCount, - virtual_key_code: record.wVirtualKeyCode, - virtual_scan_code: record.wVirtualScanCode, - u_char: unsafe { *record.uChar.UnicodeChar() }, - control_key_state: ControlKeyState(record.dwControlKeyState), - } - } -} - -/// A [mouse input event](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str). -#[derive(PartialEq, Debug, Copy, Clone, Eq)] -pub struct MouseEvent { - /// The position of the mouse when the event occurred in cell coordinates. - pub mouse_position: Coord, - /// The state of the mouse's buttons. - pub button_state: ButtonState, - /// The state of the control keys. - pub control_key_state: ControlKeyState, - /// What type of mouse event it is. - pub event_flags: EventFlags, -} - -impl From for MouseEvent { - #[inline] - fn from(event: MOUSE_EVENT_RECORD) -> Self { - MouseEvent { - mouse_position: event.dwMousePosition.into(), - button_state: event.dwButtonState.into(), - control_key_state: ControlKeyState(event.dwControlKeyState), - event_flags: event.dwEventFlags.into(), - } - } -} - -/// The status of the mouse buttons. -/// The least significant bit corresponds to the leftmost mouse button. -/// The next least significant bit corresponds to the rightmost mouse button. -/// The next bit indicates the next-to-leftmost mouse button. -/// The bits then correspond left to right to the mouse buttons. -/// A bit is 1 if the button was pressed. -/// -/// The state can be one of the following: -/// -/// ``` -/// # enum __ { -/// Release = 0x0000, -/// /// The leftmost mouse button. -/// FromLeft1stButtonPressed = 0x0001, -/// /// The second button from the left. -/// FromLeft2ndButtonPressed = 0x0004, -/// /// The third button from the left. -/// FromLeft3rdButtonPressed = 0x0008, -/// /// The fourth button from the left. -/// FromLeft4thButtonPressed = 0x0010, -/// /// The rightmost mouse button. -/// RightmostButtonPressed = 0x0002, -/// /// This button state is not recognized. -/// Unknown = 0x0021, -/// /// The wheel was rotated backward, toward the user; this will only be activated for `MOUSE_WHEELED ` from `dwEventFlags` -/// Negative = 0x0020, -/// # } -/// ``` -/// -/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members) -#[derive(PartialEq, Debug, Copy, Clone, Eq)] -pub struct ButtonState { - state: i32, -} - -impl From for ButtonState { - #[inline] - fn from(event: DWORD) -> Self { - let state = event as i32; - ButtonState { state } - } -} - -impl ButtonState { - /// Get whether no buttons are being pressed. - pub fn release_button(&self) -> bool { - self.state == 0 - } - - /// Returns whether the left button was pressed. - pub fn left_button(&self) -> bool { - self.state as u32 & FROM_LEFT_1ST_BUTTON_PRESSED != 0 - } - - /// Returns whether the right button was pressed. - pub fn right_button(&self) -> bool { - self.state as u32 - & (RIGHTMOST_BUTTON_PRESSED - | FROM_LEFT_3RD_BUTTON_PRESSED - | FROM_LEFT_4TH_BUTTON_PRESSED) - != 0 - } - - /// Returns whether the right button was pressed. - pub fn middle_button(&self) -> bool { - self.state as u32 & FROM_LEFT_2ND_BUTTON_PRESSED != 0 - } - - /// Returns whether there is a down scroll. - pub fn scroll_down(&self) -> bool { - self.state < 0 - } - - /// Returns whether there is a up scroll. - pub fn scroll_up(&self) -> bool { - self.state > 0 - } - - /// Returns the raw state. - pub fn state(&self) -> i32 { - self.state - } -} - -/// The state of the control keys. -/// -/// This is a bitmask of the following values. -/// -/// | Description | Value | -/// | --- | --- | -/// | The right alt key is pressed | `0x0001` | -/// | The left alt key is pressed | `x0002` | -/// | The right control key is pressed | `0x0004` | -/// | The left control key is pressed | `x0008` | -/// | The shift key is pressed | `0x0010` | -/// | The num lock light is on | `0x0020` | -/// | The scroll lock light is on | `0x0040` | -/// | The caps lock light is on | `0x0080` | -/// | The key is [enhanced](https://docs.microsoft.com/en-us/windows/console/key-event-record-str#remarks) | `0x0100` | -#[derive(PartialEq, Debug, Copy, Clone, Eq)] -pub struct ControlKeyState(u32); - -impl ControlKeyState { - /// Whether the control key has a state. - pub fn has_state(&self, state: u32) -> bool { - (state & self.0) != 0 - } -} - -/// The type of mouse event. -/// If this value is zero, it indicates a mouse button being pressed or released. -/// Otherwise, this member is one of the following values. -/// -/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members) -#[derive(PartialEq, Debug, Copy, Clone, Eq)] -pub enum EventFlags { - PressOrRelease = 0x0000, - /// The second click (button press) of a double-click occurred. The first click is returned as a regular button-press event. - DoubleClick = 0x0002, - /// The horizontal mouse wheel was moved. - MouseHwheeled = 0x0008, - /// If the high word of the dwButtonState member contains a positive value, the wheel was rotated to the right. Otherwise, the wheel was rotated to the left. - MouseMoved = 0x0001, - /// A change in mouse position occurred. - /// The vertical mouse wheel was moved, if the high word of the dwButtonState member contains a positive value, the wheel was rotated forward, away from the user. - /// Otherwise, the wheel was rotated backward, toward the user. - MouseWheeled = 0x0004, - // This button state is not recognized. - Unknown = 0x0021, -} - -// TODO: Replace with TryFrom. -impl From for EventFlags { - fn from(event: DWORD) -> Self { - match event { - 0x0000 => EventFlags::PressOrRelease, - 0x0002 => EventFlags::DoubleClick, - 0x0008 => EventFlags::MouseHwheeled, - 0x0001 => EventFlags::MouseMoved, - 0x0004 => EventFlags::MouseWheeled, - _ => EventFlags::Unknown, - } - } -} - -/// The [size of console screen -/// buffer](https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct WindowBufferSizeRecord { - pub size: Coord, -} - -impl From for WindowBufferSizeRecord { - #[inline] - fn from(record: WINDOW_BUFFER_SIZE_RECORD) -> Self { - WindowBufferSizeRecord { - size: record.dwSize.into(), - } - } -} - -/// A [focus event](https://docs.microsoft.com/en-us/windows/console/focus-event-record-str). This -/// is used only internally by Windows and should be ignored. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct FocusEventRecord { - /// Reserved; do not use. - pub set_focus: bool, -} - -impl From for FocusEventRecord { - #[inline] - fn from(record: FOCUS_EVENT_RECORD) -> Self { - FocusEventRecord { - set_focus: record.bSetFocus != 0, - } - } -} - -/// A [menu event](https://docs.microsoft.com/en-us/windows/console/menu-event-record-str). This is -/// used only internally by Windows and should be ignored. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MenuEventRecord { - /// Reserved; do not use. - pub command_id: u32, -} - -impl From for MenuEventRecord { - #[inline] - fn from(record: MENU_EVENT_RECORD) -> Self { - MenuEventRecord { - command_id: record.dwCommandId, - } - } -} - -/// An [input event](https://docs.microsoft.com/en-us/windows/console/input-record-str). -/// -/// These records can be read from the input buffer by using the `ReadConsoleInput` -/// or `PeekConsoleInput` function, or written to the input buffer by using the -/// `WriteConsoleInput` function. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum InputRecord { - /// A keyboard event occurred. - KeyEvent(KeyEventRecord), - /// The mouse was moved or a mouse button was pressed. - MouseEvent(MouseEvent), - /// A console screen buffer was resized. - WindowBufferSizeEvent(WindowBufferSizeRecord), - /// A focus event occured. This is used only internally by Windows and should be ignored. - FocusEvent(FocusEventRecord), - /// A menu event occurred. This is used only internally by Windows and should be ignored. - MenuEvent(MenuEventRecord), -} - -impl From for InputRecord { - #[inline] - fn from(record: INPUT_RECORD) -> Self { - match record.EventType { - KEY_EVENT => InputRecord::KeyEvent(KeyEventRecord::from_winapi(unsafe { - record.Event.KeyEvent() - })), - MOUSE_EVENT => InputRecord::MouseEvent(unsafe { *record.Event.MouseEvent() }.into()), - WINDOW_BUFFER_SIZE_EVENT => InputRecord::WindowBufferSizeEvent({ - let mut buffer = - unsafe { WindowBufferSizeRecord::from(*record.Event.WindowBufferSizeEvent()) }; - let window = ScreenBuffer::current().unwrap().info().unwrap(); - let screen_size = window.terminal_size(); - - buffer.size.y = screen_size.height; - buffer.size.x = screen_size.width; - - buffer - }), - FOCUS_EVENT => InputRecord::FocusEvent(unsafe { *record.Event.FocusEvent() }.into()), - MENU_EVENT => InputRecord::MenuEvent(unsafe { *record.Event.MenuEvent() }.into()), - code => panic!("Unexpected INPUT_RECORD EventType: {}", code), - } - } -} +//! This module provides a few structs to wrap common input struts to a rusty interface +//! +//! Types like: +//! - `KEY_EVENT_RECORD` +//! - `MOUSE_EVENT_RECORD` +//! - `ControlKeyState` +//! - `ButtonState` +//! - `EventFlags` +//! - `InputEventType` +//! - `INPUT_RECORD` + +use winapi::shared::minwindef::DWORD; +use winapi::um::wincon::{ + FOCUS_EVENT, FOCUS_EVENT_RECORD, FROM_LEFT_1ST_BUTTON_PRESSED, FROM_LEFT_2ND_BUTTON_PRESSED, + FROM_LEFT_3RD_BUTTON_PRESSED, FROM_LEFT_4TH_BUTTON_PRESSED, INPUT_RECORD, KEY_EVENT, + KEY_EVENT_RECORD, MENU_EVENT, MENU_EVENT_RECORD, MOUSE_EVENT, MOUSE_EVENT_RECORD, + RIGHTMOST_BUTTON_PRESSED, WINDOW_BUFFER_SIZE_EVENT, WINDOW_BUFFER_SIZE_RECORD, +}; + +use super::Coord; +use crate::ScreenBuffer; + +/// A [keyboard input event](https://docs.microsoft.com/en-us/windows/console/key-event-record-str). +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct KeyEventRecord { + /// If the key is pressed, this member is true. Otherwise, this member is + /// false (the key is released). + pub key_down: bool, + /// The repeat count, which indicates that a key is being held down. + /// For example, when a key is held down, you might get five events with + /// this member equal to 1, one event with this member equal to 5, or + /// multiple events with this member greater than or equal to 1. + pub repeat_count: u16, + /// A virtual-key code that identifies the given key in a + /// device-independent manner. + pub virtual_key_code: u16, + /// The virtual scan code of the given key that represents the + /// device-dependent value generated by the keyboard hardware. + pub virtual_scan_code: u16, + /// The translated Unicode character (as a WCHAR, or utf-16 value) + pub u_char: u16, + /// The state of the control keys. + pub control_key_state: ControlKeyState, +} + +impl KeyEventRecord { + /// Convert a `KEY_EVENT_RECORD` to KeyEventRecord. This function is private + /// because the `KEY_EVENT_RECORD` has several union fields for characters + /// (u8 vs u16) that we always interpret as u16. We always use the wide + /// versions of windows API calls to support this. + #[inline] + fn from_winapi(record: &KEY_EVENT_RECORD) -> Self { + KeyEventRecord { + key_down: record.bKeyDown != 0, + repeat_count: record.wRepeatCount, + virtual_key_code: record.wVirtualKeyCode, + virtual_scan_code: record.wVirtualScanCode, + u_char: unsafe { *record.uChar.UnicodeChar() }, + control_key_state: ControlKeyState(record.dwControlKeyState), + } + } +} + +/// A [mouse input event](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str). +#[derive(PartialEq, Debug, Copy, Clone, Eq)] +pub struct MouseEvent { + /// The position of the mouse when the event occurred in cell coordinates. + pub mouse_position: Coord, + /// The state of the mouse's buttons. + pub button_state: ButtonState, + /// The state of the control keys. + pub control_key_state: ControlKeyState, + /// What type of mouse event it is. + pub event_flags: EventFlags, +} + +impl From for MouseEvent { + #[inline] + fn from(event: MOUSE_EVENT_RECORD) -> Self { + MouseEvent { + mouse_position: event.dwMousePosition.into(), + button_state: event.dwButtonState.into(), + control_key_state: ControlKeyState(event.dwControlKeyState), + event_flags: event.dwEventFlags.into(), + } + } +} + +/// The status of the mouse buttons. +/// The least significant bit corresponds to the leftmost mouse button. +/// The next least significant bit corresponds to the rightmost mouse button. +/// The next bit indicates the next-to-leftmost mouse button. +/// The bits then correspond left to right to the mouse buttons. +/// A bit is 1 if the button was pressed. +/// +/// The state can be one of the following: +/// +/// ``` +/// # enum __ { +/// Release = 0x0000, +/// /// The leftmost mouse button. +/// FromLeft1stButtonPressed = 0x0001, +/// /// The second button from the left. +/// FromLeft2ndButtonPressed = 0x0004, +/// /// The third button from the left. +/// FromLeft3rdButtonPressed = 0x0008, +/// /// The fourth button from the left. +/// FromLeft4thButtonPressed = 0x0010, +/// /// The rightmost mouse button. +/// RightmostButtonPressed = 0x0002, +/// /// This button state is not recognized. +/// Unknown = 0x0021, +/// /// The wheel was rotated backward, toward the user; this will only be activated for `MOUSE_WHEELED ` from `dwEventFlags` +/// Negative = 0x0020, +/// # } +/// ``` +/// +/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members) +#[derive(PartialEq, Debug, Copy, Clone, Eq)] +pub struct ButtonState { + state: i32, +} + +impl From for ButtonState { + #[inline] + fn from(event: DWORD) -> Self { + let state = event as i32; + ButtonState { state } + } +} + +impl ButtonState { + /// Get whether no buttons are being pressed. + pub fn release_button(&self) -> bool { + self.state == 0 + } + + /// Returns whether the left button was pressed. + pub fn left_button(&self) -> bool { + self.state as u32 & FROM_LEFT_1ST_BUTTON_PRESSED != 0 + } + + /// Returns whether the right button was pressed. + pub fn right_button(&self) -> bool { + self.state as u32 + & (RIGHTMOST_BUTTON_PRESSED + | FROM_LEFT_3RD_BUTTON_PRESSED + | FROM_LEFT_4TH_BUTTON_PRESSED) + != 0 + } + + /// Returns whether the right button was pressed. + pub fn middle_button(&self) -> bool { + self.state as u32 & FROM_LEFT_2ND_BUTTON_PRESSED != 0 + } + + /// Returns whether there is a down scroll. + pub fn scroll_down(&self) -> bool { + self.state < 0 + } + + /// Returns whether there is a up scroll. + pub fn scroll_up(&self) -> bool { + self.state > 0 + } + + /// Returns the raw state. + pub fn state(&self) -> i32 { + self.state + } +} + +/// The state of the control keys. +/// +/// This is a bitmask of the following values. +/// +/// | Description | Value | +/// | --- | --- | +/// | The right alt key is pressed | `0x0001` | +/// | The left alt key is pressed | `x0002` | +/// | The right control key is pressed | `0x0004` | +/// | The left control key is pressed | `x0008` | +/// | The shift key is pressed | `0x0010` | +/// | The num lock light is on | `0x0020` | +/// | The scroll lock light is on | `0x0040` | +/// | The caps lock light is on | `0x0080` | +/// | The key is [enhanced](https://docs.microsoft.com/en-us/windows/console/key-event-record-str#remarks) | `0x0100` | +#[derive(PartialEq, Debug, Copy, Clone, Eq)] +pub struct ControlKeyState(u32); + +impl ControlKeyState { + /// Whether the control key has a state. + pub fn has_state(&self, state: u32) -> bool { + (state & self.0) != 0 + } +} + +/// The type of mouse event. +/// If this value is zero, it indicates a mouse button being pressed or released. +/// Otherwise, this member is one of the following values. +/// +/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members) +#[derive(PartialEq, Debug, Copy, Clone, Eq)] +pub enum EventFlags { + PressOrRelease = 0x0000, + /// The second click (button press) of a double-click occurred. The first click is returned as a regular button-press event. + DoubleClick = 0x0002, + /// The horizontal mouse wheel was moved. + MouseHwheeled = 0x0008, + /// If the high word of the dwButtonState member contains a positive value, the wheel was rotated to the right. Otherwise, the wheel was rotated to the left. + MouseMoved = 0x0001, + /// A change in mouse position occurred. + /// The vertical mouse wheel was moved, if the high word of the dwButtonState member contains a positive value, the wheel was rotated forward, away from the user. + /// Otherwise, the wheel was rotated backward, toward the user. + MouseWheeled = 0x0004, + // This button state is not recognized. + Unknown = 0x0021, +} + +// TODO: Replace with TryFrom. +impl From for EventFlags { + fn from(event: DWORD) -> Self { + match event { + 0x0000 => EventFlags::PressOrRelease, + 0x0002 => EventFlags::DoubleClick, + 0x0008 => EventFlags::MouseHwheeled, + 0x0001 => EventFlags::MouseMoved, + 0x0004 => EventFlags::MouseWheeled, + _ => EventFlags::Unknown, + } + } +} + +/// The [size of console screen +/// buffer](https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct WindowBufferSizeRecord { + pub size: Coord, +} + +impl From for WindowBufferSizeRecord { + #[inline] + fn from(record: WINDOW_BUFFER_SIZE_RECORD) -> Self { + WindowBufferSizeRecord { + size: record.dwSize.into(), + } + } +} + +/// A [focus event](https://docs.microsoft.com/en-us/windows/console/focus-event-record-str). This +/// is used only internally by Windows and should be ignored. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FocusEventRecord { + /// Reserved; do not use. + pub set_focus: bool, +} + +impl From for FocusEventRecord { + #[inline] + fn from(record: FOCUS_EVENT_RECORD) -> Self { + FocusEventRecord { + set_focus: record.bSetFocus != 0, + } + } +} + +/// A [menu event](https://docs.microsoft.com/en-us/windows/console/menu-event-record-str). This is +/// used only internally by Windows and should be ignored. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MenuEventRecord { + /// Reserved; do not use. + pub command_id: u32, +} + +impl From for MenuEventRecord { + #[inline] + fn from(record: MENU_EVENT_RECORD) -> Self { + MenuEventRecord { + command_id: record.dwCommandId, + } + } +} + +/// An [input event](https://docs.microsoft.com/en-us/windows/console/input-record-str). +/// +/// These records can be read from the input buffer by using the `ReadConsoleInput` +/// or `PeekConsoleInput` function, or written to the input buffer by using the +/// `WriteConsoleInput` function. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum InputRecord { + /// A keyboard event occurred. + KeyEvent(KeyEventRecord), + /// The mouse was moved or a mouse button was pressed. + MouseEvent(MouseEvent), + /// A console screen buffer was resized. + WindowBufferSizeEvent(WindowBufferSizeRecord), + /// A focus event occured. This is used only internally by Windows and should be ignored. + FocusEvent(FocusEventRecord), + /// A menu event occurred. This is used only internally by Windows and should be ignored. + MenuEvent(MenuEventRecord), +} + +impl From for InputRecord { + #[inline] + fn from(record: INPUT_RECORD) -> Self { + match record.EventType { + KEY_EVENT => InputRecord::KeyEvent(KeyEventRecord::from_winapi(unsafe { + record.Event.KeyEvent() + })), + MOUSE_EVENT => InputRecord::MouseEvent(unsafe { *record.Event.MouseEvent() }.into()), + WINDOW_BUFFER_SIZE_EVENT => InputRecord::WindowBufferSizeEvent({ + let mut buffer = + unsafe { WindowBufferSizeRecord::from(*record.Event.WindowBufferSizeEvent()) }; + let window = ScreenBuffer::current().unwrap().info().unwrap(); + let screen_size = window.terminal_size(); + + buffer.size.y = screen_size.height; + buffer.size.x = screen_size.width; + + buffer + }), + FOCUS_EVENT => InputRecord::FocusEvent(unsafe { *record.Event.FocusEvent() }.into()), + MENU_EVENT => InputRecord::MenuEvent(unsafe { *record.Event.MenuEvent() }.into()), + code => panic!("Unexpected INPUT_RECORD EventType: {}", code), + } + } +} diff --git a/vendor/crossterm_winapi/src/structs/size.rs b/vendor/crossterm_winapi/src/structs/size.rs index 4cba7a6..aa60728 100644 --- a/vendor/crossterm_winapi/src/structs/size.rs +++ b/vendor/crossterm_winapi/src/structs/size.rs @@ -1,31 +1,31 @@ -//! This module provides a type that represents some size. -//! For example, in WinAPI we have `COORD` to represent screen/buffer size but this is a little inconvenient. -//! This module provides some trait implementations who will make parsing and working with `COORD` easier. - -use winapi::um::wincon::COORD; - -/// This is type represents the size of something in width and height. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct Size { - pub width: i16, - pub height: i16, -} - -impl Size { - /// Create a new size instance by passing in the width and height. - pub fn new(width: i16, height: i16) -> Size { - Size { width, height } - } -} - -impl From for Size { - fn from(coord: COORD) -> Self { - Size::new(coord.X, coord.Y) - } -} - -impl Into<(u16, u16)> for Size { - fn into(self) -> (u16, u16) { - (self.width as u16, self.height as u16) - } -} +//! This module provides a type that represents some size. +//! For example, in WinAPI we have `COORD` to represent screen/buffer size but this is a little inconvenient. +//! This module provides some trait implementations who will make parsing and working with `COORD` easier. + +use winapi::um::wincon::COORD; + +/// This is type represents the size of something in width and height. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub struct Size { + pub width: i16, + pub height: i16, +} + +impl Size { + /// Create a new size instance by passing in the width and height. + pub fn new(width: i16, height: i16) -> Size { + Size { width, height } + } +} + +impl From for Size { + fn from(coord: COORD) -> Self { + Size::new(coord.X, coord.Y) + } +} + +impl Into<(u16, u16)> for Size { + fn into(self) -> (u16, u16) { + (self.width as u16, self.height as u16) + } +} diff --git a/vendor/crossterm_winapi/src/structs/window_coords.rs b/vendor/crossterm_winapi/src/structs/window_coords.rs index ef2017a..0ac804f 100644 --- a/vendor/crossterm_winapi/src/structs/window_coords.rs +++ b/vendor/crossterm_winapi/src/structs/window_coords.rs @@ -1,46 +1,46 @@ -//! This module provides a type that represents some rectangle. -//! For example, in WinAPI we have `SMALL_RECT` to represent a window size but this is a little inconvenient. -//! This module provides some trait implementations who will make parsing and working with `SMALL_RECT` easier. - -use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT}; - -/// This is a wrapper for the locations of a rectangle. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct WindowPositions { - /// The rectangle's offset from the left. - pub left: i16, - /// The rectangle's offset from the right. - pub right: i16, - /// The rectangle's offset from the bottom. - pub bottom: i16, - /// The rectangle's offset from the top. - pub top: i16, -} - -impl From for WindowPositions { - fn from(csbi: CONSOLE_SCREEN_BUFFER_INFO) -> Self { - csbi.srWindow.into() - } -} - -impl From for SMALL_RECT { - fn from(positions: WindowPositions) -> Self { - SMALL_RECT { - Top: positions.top, - Right: positions.right, - Bottom: positions.bottom, - Left: positions.left, - } - } -} - -impl From for WindowPositions { - fn from(rect: SMALL_RECT) -> Self { - WindowPositions { - left: rect.Left, - right: rect.Right, - bottom: rect.Bottom, - top: rect.Top, - } - } -} +//! This module provides a type that represents some rectangle. +//! For example, in WinAPI we have `SMALL_RECT` to represent a window size but this is a little inconvenient. +//! This module provides some trait implementations who will make parsing and working with `SMALL_RECT` easier. + +use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT}; + +/// This is a wrapper for the locations of a rectangle. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub struct WindowPositions { + /// The rectangle's offset from the left. + pub left: i16, + /// The rectangle's offset from the right. + pub right: i16, + /// The rectangle's offset from the bottom. + pub bottom: i16, + /// The rectangle's offset from the top. + pub top: i16, +} + +impl From for WindowPositions { + fn from(csbi: CONSOLE_SCREEN_BUFFER_INFO) -> Self { + csbi.srWindow.into() + } +} + +impl From for SMALL_RECT { + fn from(positions: WindowPositions) -> Self { + SMALL_RECT { + Top: positions.top, + Right: positions.right, + Bottom: positions.bottom, + Left: positions.left, + } + } +} + +impl From for WindowPositions { + fn from(rect: SMALL_RECT) -> Self { + WindowPositions { + left: rect.Left, + right: rect.Right, + bottom: rect.Bottom, + top: rect.Top, + } + } +} diff --git a/vendor/cxx/tools/buck/prelude/python_bootstrap/tools/win_python_wrapper.bat b/vendor/cxx/tools/buck/prelude/python_bootstrap/tools/win_python_wrapper.bat index ba23c78..f4e2036 100644 --- a/vendor/cxx/tools/buck/prelude/python_bootstrap/tools/win_python_wrapper.bat +++ b/vendor/cxx/tools/buck/prelude/python_bootstrap/tools/win_python_wrapper.bat @@ -1,22 +1,22 @@ -@REM Copyright (c) Meta Platforms, Inc. and affiliates. -@REM -@REM This source code is licensed under both the MIT license found in the -@REM LICENSE-MIT file in the root directory of this source tree and the Apache -@REM License, Version 2.0 found in the LICENSE-APACHE file in the root directory -@REM of this source tree. - -:: A wrapper to set PYTHONPATH and run a python command with the specified interpreter -:: First arg: paths to library that should be made available -:: Second arg: path to python interpreter -:: Third arg: path to python file that should be run -:: Fourth and onwards: any other arg that should be passed -@echo off - -:: See https://stackoverflow.com/questions/382587/how-to-get-batch-file-parameters-from-nth-position-on -setlocal enabledelayedexpansion -set args=;;;;;;%* -set args=!args:;;;;;;%1 =! - -set PYTHONPATH=%1; -%args% -endlocal +@REM Copyright (c) Meta Platforms, Inc. and affiliates. +@REM +@REM This source code is licensed under both the MIT license found in the +@REM LICENSE-MIT file in the root directory of this source tree and the Apache +@REM License, Version 2.0 found in the LICENSE-APACHE file in the root directory +@REM of this source tree. + +:: A wrapper to set PYTHONPATH and run a python command with the specified interpreter +:: First arg: paths to library that should be made available +:: Second arg: path to python interpreter +:: Third arg: path to python file that should be run +:: Fourth and onwards: any other arg that should be passed +@echo off + +:: See https://stackoverflow.com/questions/382587/how-to-get-batch-file-parameters-from-nth-position-on +setlocal enabledelayedexpansion +set args=;;;;;;%* +set args=!args:;;;;;;%1 =! + +set PYTHONPATH=%1; +%args% +endlocal diff --git a/vendor/cxx/tools/buck/prelude/tools/find_and_replace.bat b/vendor/cxx/tools/buck/prelude/tools/find_and_replace.bat index ed49620..0e05293 100644 --- a/vendor/cxx/tools/buck/prelude/tools/find_and_replace.bat +++ b/vendor/cxx/tools/buck/prelude/tools/find_and_replace.bat @@ -1,25 +1,25 @@ -@REM Copyright (c) Meta Platforms, Inc. and affiliates. -@REM -@REM This source code is licensed under both the MIT license found in the -@REM LICENSE-MIT file in the root directory of this source tree and the Apache -@REM License, Version 2.0 found in the LICENSE-APACHE file in the root directory -@REM of this source tree. - -@echo off &setlocal -:: arg1 = string to replace -:: arg2 = replacement -:: arg3 = input file -:: arg4 = output file -:: Take all instances of arg1 in arg3 and replace it with arg2 -:: The modified string is outputted into arg4, arg3 will not be modified -set BEFORE=%1 -set AFTER=%2 -set IN=%3 -set OUT=%4 -(for /f "delims=" %%i in (%IN%) do ( - set "line=%%i" - setlocal enabledelayedexpansion - set "line=!line:%BEFORE%=%AFTER%!" - echo(!line! - endlocal -))>"%OUT%" +@REM Copyright (c) Meta Platforms, Inc. and affiliates. +@REM +@REM This source code is licensed under both the MIT license found in the +@REM LICENSE-MIT file in the root directory of this source tree and the Apache +@REM License, Version 2.0 found in the LICENSE-APACHE file in the root directory +@REM of this source tree. + +@echo off &setlocal +:: arg1 = string to replace +:: arg2 = replacement +:: arg3 = input file +:: arg4 = output file +:: Take all instances of arg1 in arg3 and replace it with arg2 +:: The modified string is outputted into arg4, arg3 will not be modified +set BEFORE=%1 +set AFTER=%2 +set IN=%3 +set OUT=%4 +(for /f "delims=" %%i in (%IN%) do ( + set "line=%%i" + setlocal enabledelayedexpansion + set "line=!line:%BEFORE%=%AFTER%!" + echo(!line! + endlocal +))>"%OUT%" diff --git a/vendor/libc/src/unix/linux_like/android/b64/aarch64/int128.rs b/vendor/libc/src/unix/linux_like/android/b64/aarch64/int128.rs index 41ae1fc..4535e73 100644 --- a/vendor/libc/src/unix/linux_like/android/b64/aarch64/int128.rs +++ b/vendor/libc/src/unix/linux_like/android/b64/aarch64/int128.rs @@ -1,7 +1,7 @@ -s! { - pub struct user_fpsimd_struct { - pub vregs: [::__uint128_t; 32], - pub fpsr: u32, - pub fpcr: u32, - } -} +s! { + pub struct user_fpsimd_struct { + pub vregs: [::__uint128_t; 32], + pub fpsr: u32, + pub fpcr: u32, + } +} diff --git a/vendor/libc/src/unix/linux_like/linux/gnu/b64/aarch64/int128.rs b/vendor/libc/src/unix/linux_like/linux/gnu/b64/aarch64/int128.rs index 41ae1fc..4535e73 100644 --- a/vendor/libc/src/unix/linux_like/linux/gnu/b64/aarch64/int128.rs +++ b/vendor/libc/src/unix/linux_like/linux/gnu/b64/aarch64/int128.rs @@ -1,7 +1,7 @@ -s! { - pub struct user_fpsimd_struct { - pub vregs: [::__uint128_t; 32], - pub fpsr: u32, - pub fpcr: u32, - } -} +s! { + pub struct user_fpsimd_struct { + pub vregs: [::__uint128_t; 32], + pub fpsr: u32, + pub fpcr: u32, + } +} diff --git a/vendor/libc/src/unix/linux_like/linux/musl/b64/aarch64/int128.rs b/vendor/libc/src/unix/linux_like/linux/musl/b64/aarch64/int128.rs index 41ae1fc..4535e73 100644 --- a/vendor/libc/src/unix/linux_like/linux/musl/b64/aarch64/int128.rs +++ b/vendor/libc/src/unix/linux_like/linux/musl/b64/aarch64/int128.rs @@ -1,7 +1,7 @@ -s! { - pub struct user_fpsimd_struct { - pub vregs: [::__uint128_t; 32], - pub fpsr: u32, - pub fpcr: u32, - } -} +s! { + pub struct user_fpsimd_struct { + pub vregs: [::__uint128_t; 32], + pub fpsr: u32, + pub fpcr: u32, + } +} diff --git a/vendor/log/CHANGELOG.md b/vendor/log/CHANGELOG.md index 6c697fa..7d68f89 100644 --- a/vendor/log/CHANGELOG.md +++ b/vendor/log/CHANGELOG.md @@ -1,241 +1,241 @@ -# Change Log - -## [Unreleased] - -## [0.4.17] - 2022-04-29 - -* Update `kv_unstable` internal dependencies. - -## [0.4.16] - 2022-03-22 - -* Fix a conflict with unqualified `Option` use in macros. - -## [0.4.15] - 2022-02-23 - -* Silence a warning about the deprecated `spin_loop_hint`. -* Relax ordering in the atomic `set_max_level` call. -* Add thumbv4t-none-eabi to targets that don't support atomics -* Allow levels to be iterated over. -* Implement `Log` on some common wrapper types. -* Improvements to test coverage. -* Improvements to documentation. -* Add key-value support to the `log!` macros. -* Tighten `kv_unstable` internal dependencies so they don't bump past their current alpha. -* Add a simple visit API to `kv_unstable`. -* Support `NonZero*` integers as values in structured logging -* Support static strings as keys in structured logging - -## [0.4.14] - 2021-01-27 - -* Remove the `__private_api_log_lit` special case. -* Fixed incorrect combination of `kv_unstable` and `std` features causing compile failures. -* Remove unstable `Value::to_*` conversions that were incorrectly using `as`. -* Rename unstable `Value::to_error` to `Value::to_borrowed_error`. - -## [0.4.13] - 2021-01-11 - -* This is the same as `0.4.11`, except with a `kv_unstable_std` feature added to aid migrating current dependents to `0.4.14` (which was originally going to be `0.4.13` until it was decided to create a patch from `0.4.11` to minimize disruption). - -## [0.4.12] - 2020-12-24 - -### New - -* Support platforms without atomics by racing instead of failing to compile -* Implement `Log` for `Box` -* Update `cfg-if` to `1.0` -* Internal reworks of the structured logging API. Removed the `Fill` API -and added `source::as_map` and `source::as_list` to easily serialize a `Source` -as either a map of `{key: value, ..}` or as a list of `[(key, value), ..]`. - -### Fixed - -* Fixed deserialization of `LevelFilter` to use their `u64` index variants - -## [0.4.11] - 2020-07-09 - -### New - -* Support coercing structured values into concrete types. -* Reference the `win_dbg_logger` in the readme. - -### Fixed - -* Updates a few deprecated items used internally. -* Fixed issues in docs and expands sections. -* Show the correct build badge in the readme. -* Fix up a possible inference breakage with structured value errors. -* Respect formatting flags in structured value formatting. - -## [0.4.10] - 2019-12-16 (yanked) - -### Fixed - -* Fixed the `log!` macros so they work in expression context (this regressed in `0.4.9`, which has been yanked). - -## [0.4.9] - 2019-12-12 (yanked) - -### Minimum Supported Rust Version - -This release bumps the minimum compiler version to `1.31.0`. This was mainly needed for `cfg-if`, -but between `1.16.0` and `1.31.0` there are a lot of language and library improvements we now -take advantage of. - -### New - -* Unstable support for capturing key-value pairs in a record using the `log!` macros - -### Improved - -* Better documentation for max level filters. -* Internal updates to line up with bumped MSRV - -## [0.4.8] - 2019-07-28 - -### New - -* Support attempting to get `Record` fields as static strings. - -## [0.4.7] - 2019-07-06 - -### New - -* Support for embedded environments with thread-unsafe initialization. -* Initial unstable support for capturing structured data under the `kv_unstable` -feature gate. This new API doesn't affect existing users and may change in future -patches (so those changes may not appear in the changelog until it stabilizes). - -### Improved - -* Docs for using `log` with the 2018 edition. -* Error messages for macros missing arguments. - -## [0.4.6] - 2018-10-27 - -### Improved - -* Support 2018-style macro import for the `log_enabled!` macro. - -## [0.4.5] - 2018-09-03 - -### Improved - -* Make `log`'s internal helper macros less likely to conflict with user-defined - macros. - -## [0.4.4] - 2018-08-17 - -### Improved - -* Support 2018-style imports of the log macros. - -## [0.4.3] - 2018-06-29 - -### Improved - -* More code generation improvements. - -## [0.4.2] - 2018-06-05 - -### Improved - -* Log invocations now generate less code. - -### Fixed - -* Example Logger implementations now properly set the max log level. - -## [0.4.1] - 2017-12-30 - -### Fixed - -* Some doc links were fixed. - -## [0.4.0] - 2017-12-24 - -The changes in this release include cleanup of some obscure functionality and a more robust public -API designed to support bridges to other logging systems, and provide more flexibility to new -features in the future. - -### Compatibility - -Vast portions of the Rust ecosystem use the 0.3.x release series of log, and we don't want to force -the community to go through the pain of upgrading every crate to 0.4.x at the exact same time. Along -with 0.4.0, we've published a new 0.3.9 release which acts as a "shim" over 0.4.0. This will allow -crates using either version to coexist without losing messages from one side or the other. - -There is one caveat - a log message generated by a crate using 0.4.x but consumed by a logging -implementation using 0.3.x will not have a file name or module path. Applications affected by this -can upgrade their logging implementations to one using 0.4.x to avoid losing this information. The -other direction does not lose any information, fortunately! - -**TL;DR** Libraries should feel comfortable upgrading to 0.4.0 without treating that as a breaking -change. Applications may need to update their logging implementation (e.g. env-logger) to a newer -version using log 0.4.x to avoid losing module and file information. - -### New - -* The crate is now `no_std` by default. -* `Level` and `LevelFilter` now implement `Serialize` and `Deserialize` when the `serde` feature is - enabled. -* The `Record` and `Metadata` types can now be constructed by third-party code via a builder API. -* The `logger` free function returns a reference to the logger implementation. This, along with the - ability to construct `Record`s, makes it possible to bridge from another logging framework to - this one without digging into the private internals of the crate. The standard `error!` `warn!`, - etc, macros now exclusively use the public API of the crate rather than "secret" internal APIs. -* `Log::flush` has been added to allow crates to tell the logging implementation to ensure that all - "in flight" log events have been persisted. This can be used, for example, just before an - application exits to ensure that asynchronous log sinks finish their work. - -### Removed - -* The `shutdown` and `shutdown_raw` functions have been removed. Supporting shutdown significantly - complicated the implementation and imposed a performance cost on each logging operation. -* The `log_panics` function and its associated `nightly` Cargo feature have been removed. Use the - [log-panics](https://crates.io/crates/log-panics) instead. - -### Changed - -* The `Log` prefix has been removed from type names. For example, `LogLevelFilter` is now - `LevelFilter`, and `LogRecord` is now `Record`. -* The `MaxLogLevelFilter` object has been removed in favor of a `set_max_level` free function. -* The `set_logger` free functions have been restructured. The logger is now directly passed to the - functions rather than a closure which returns the logger. `set_logger` now takes a `&'static - Log` and is usable in `no_std` contexts in place of the old `set_logger_raw`. `set_boxed_logger` - is a convenience function which takes a `Box` but otherwise acts like `set_logger`. It - requires the `std` feature. -* The `file` and `module_path` values in `Record` no longer have the `'static` lifetime to support - integration with other logging frameworks that don't provide a `'static` lifetime for the - equivalent values. -* The `file`, `line`, and `module_path` values in `Record` are now `Option`s to support integration - with other logging frameworks that don't provide those values. - -### In the Future - -* We're looking to add support for *structured* logging - the inclusion of extra key-value pairs of - information in a log event in addition to the normal string message. This should be able to be - added in a backwards compatible manner to the 0.4.x series when the design is worked out. - -## Older - -Look at the [release tags] for information about older releases. - -[Unreleased]: https://github.com/rust-lang-nursery/log/compare/0.4.17...HEAD -[0.4.17]: https://github.com/rust-lang-nursery/log/compare/0.4.16...0.4.17 -[0.4.16]: https://github.com/rust-lang-nursery/log/compare/0.4.15...0.4.16 -[0.4.15]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.15 -[0.4.14]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.14 -[0.4.13]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.13 -[0.4.12]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.12 -[0.4.11]: https://github.com/rust-lang-nursery/log/compare/0.4.10...0.4.11 -[0.4.10]: https://github.com/rust-lang-nursery/log/compare/0.4.9...0.4.10 -[0.4.9]: https://github.com/rust-lang-nursery/log/compare/0.4.8...0.4.9 -[0.4.8]: https://github.com/rust-lang-nursery/log/compare/0.4.7...0.4.8 -[0.4.7]: https://github.com/rust-lang-nursery/log/compare/0.4.6...0.4.7 -[0.4.6]: https://github.com/rust-lang-nursery/log/compare/0.4.5...0.4.6 -[0.4.5]: https://github.com/rust-lang-nursery/log/compare/0.4.4...0.4.5 -[0.4.4]: https://github.com/rust-lang-nursery/log/compare/0.4.3...0.4.4 -[0.4.3]: https://github.com/rust-lang-nursery/log/compare/0.4.2...0.4.3 -[0.4.2]: https://github.com/rust-lang-nursery/log/compare/0.4.1...0.4.2 -[0.4.1]: https://github.com/rust-lang-nursery/log/compare/0.4.0...0.4.1 -[0.4.0]: https://github.com/rust-lang-nursery/log/compare/0.3.8...0.4.0 -[release tags]: https://github.com/rust-lang-nursery/log/releases +# Change Log + +## [Unreleased] + +## [0.4.17] - 2022-04-29 + +* Update `kv_unstable` internal dependencies. + +## [0.4.16] - 2022-03-22 + +* Fix a conflict with unqualified `Option` use in macros. + +## [0.4.15] - 2022-02-23 + +* Silence a warning about the deprecated `spin_loop_hint`. +* Relax ordering in the atomic `set_max_level` call. +* Add thumbv4t-none-eabi to targets that don't support atomics +* Allow levels to be iterated over. +* Implement `Log` on some common wrapper types. +* Improvements to test coverage. +* Improvements to documentation. +* Add key-value support to the `log!` macros. +* Tighten `kv_unstable` internal dependencies so they don't bump past their current alpha. +* Add a simple visit API to `kv_unstable`. +* Support `NonZero*` integers as values in structured logging +* Support static strings as keys in structured logging + +## [0.4.14] - 2021-01-27 + +* Remove the `__private_api_log_lit` special case. +* Fixed incorrect combination of `kv_unstable` and `std` features causing compile failures. +* Remove unstable `Value::to_*` conversions that were incorrectly using `as`. +* Rename unstable `Value::to_error` to `Value::to_borrowed_error`. + +## [0.4.13] - 2021-01-11 + +* This is the same as `0.4.11`, except with a `kv_unstable_std` feature added to aid migrating current dependents to `0.4.14` (which was originally going to be `0.4.13` until it was decided to create a patch from `0.4.11` to minimize disruption). + +## [0.4.12] - 2020-12-24 + +### New + +* Support platforms without atomics by racing instead of failing to compile +* Implement `Log` for `Box` +* Update `cfg-if` to `1.0` +* Internal reworks of the structured logging API. Removed the `Fill` API +and added `source::as_map` and `source::as_list` to easily serialize a `Source` +as either a map of `{key: value, ..}` or as a list of `[(key, value), ..]`. + +### Fixed + +* Fixed deserialization of `LevelFilter` to use their `u64` index variants + +## [0.4.11] - 2020-07-09 + +### New + +* Support coercing structured values into concrete types. +* Reference the `win_dbg_logger` in the readme. + +### Fixed + +* Updates a few deprecated items used internally. +* Fixed issues in docs and expands sections. +* Show the correct build badge in the readme. +* Fix up a possible inference breakage with structured value errors. +* Respect formatting flags in structured value formatting. + +## [0.4.10] - 2019-12-16 (yanked) + +### Fixed + +* Fixed the `log!` macros so they work in expression context (this regressed in `0.4.9`, which has been yanked). + +## [0.4.9] - 2019-12-12 (yanked) + +### Minimum Supported Rust Version + +This release bumps the minimum compiler version to `1.31.0`. This was mainly needed for `cfg-if`, +but between `1.16.0` and `1.31.0` there are a lot of language and library improvements we now +take advantage of. + +### New + +* Unstable support for capturing key-value pairs in a record using the `log!` macros + +### Improved + +* Better documentation for max level filters. +* Internal updates to line up with bumped MSRV + +## [0.4.8] - 2019-07-28 + +### New + +* Support attempting to get `Record` fields as static strings. + +## [0.4.7] - 2019-07-06 + +### New + +* Support for embedded environments with thread-unsafe initialization. +* Initial unstable support for capturing structured data under the `kv_unstable` +feature gate. This new API doesn't affect existing users and may change in future +patches (so those changes may not appear in the changelog until it stabilizes). + +### Improved + +* Docs for using `log` with the 2018 edition. +* Error messages for macros missing arguments. + +## [0.4.6] - 2018-10-27 + +### Improved + +* Support 2018-style macro import for the `log_enabled!` macro. + +## [0.4.5] - 2018-09-03 + +### Improved + +* Make `log`'s internal helper macros less likely to conflict with user-defined + macros. + +## [0.4.4] - 2018-08-17 + +### Improved + +* Support 2018-style imports of the log macros. + +## [0.4.3] - 2018-06-29 + +### Improved + +* More code generation improvements. + +## [0.4.2] - 2018-06-05 + +### Improved + +* Log invocations now generate less code. + +### Fixed + +* Example Logger implementations now properly set the max log level. + +## [0.4.1] - 2017-12-30 + +### Fixed + +* Some doc links were fixed. + +## [0.4.0] - 2017-12-24 + +The changes in this release include cleanup of some obscure functionality and a more robust public +API designed to support bridges to other logging systems, and provide more flexibility to new +features in the future. + +### Compatibility + +Vast portions of the Rust ecosystem use the 0.3.x release series of log, and we don't want to force +the community to go through the pain of upgrading every crate to 0.4.x at the exact same time. Along +with 0.4.0, we've published a new 0.3.9 release which acts as a "shim" over 0.4.0. This will allow +crates using either version to coexist without losing messages from one side or the other. + +There is one caveat - a log message generated by a crate using 0.4.x but consumed by a logging +implementation using 0.3.x will not have a file name or module path. Applications affected by this +can upgrade their logging implementations to one using 0.4.x to avoid losing this information. The +other direction does not lose any information, fortunately! + +**TL;DR** Libraries should feel comfortable upgrading to 0.4.0 without treating that as a breaking +change. Applications may need to update their logging implementation (e.g. env-logger) to a newer +version using log 0.4.x to avoid losing module and file information. + +### New + +* The crate is now `no_std` by default. +* `Level` and `LevelFilter` now implement `Serialize` and `Deserialize` when the `serde` feature is + enabled. +* The `Record` and `Metadata` types can now be constructed by third-party code via a builder API. +* The `logger` free function returns a reference to the logger implementation. This, along with the + ability to construct `Record`s, makes it possible to bridge from another logging framework to + this one without digging into the private internals of the crate. The standard `error!` `warn!`, + etc, macros now exclusively use the public API of the crate rather than "secret" internal APIs. +* `Log::flush` has been added to allow crates to tell the logging implementation to ensure that all + "in flight" log events have been persisted. This can be used, for example, just before an + application exits to ensure that asynchronous log sinks finish their work. + +### Removed + +* The `shutdown` and `shutdown_raw` functions have been removed. Supporting shutdown significantly + complicated the implementation and imposed a performance cost on each logging operation. +* The `log_panics` function and its associated `nightly` Cargo feature have been removed. Use the + [log-panics](https://crates.io/crates/log-panics) instead. + +### Changed + +* The `Log` prefix has been removed from type names. For example, `LogLevelFilter` is now + `LevelFilter`, and `LogRecord` is now `Record`. +* The `MaxLogLevelFilter` object has been removed in favor of a `set_max_level` free function. +* The `set_logger` free functions have been restructured. The logger is now directly passed to the + functions rather than a closure which returns the logger. `set_logger` now takes a `&'static + Log` and is usable in `no_std` contexts in place of the old `set_logger_raw`. `set_boxed_logger` + is a convenience function which takes a `Box` but otherwise acts like `set_logger`. It + requires the `std` feature. +* The `file` and `module_path` values in `Record` no longer have the `'static` lifetime to support + integration with other logging frameworks that don't provide a `'static` lifetime for the + equivalent values. +* The `file`, `line`, and `module_path` values in `Record` are now `Option`s to support integration + with other logging frameworks that don't provide those values. + +### In the Future + +* We're looking to add support for *structured* logging - the inclusion of extra key-value pairs of + information in a log event in addition to the normal string message. This should be able to be + added in a backwards compatible manner to the 0.4.x series when the design is worked out. + +## Older + +Look at the [release tags] for information about older releases. + +[Unreleased]: https://github.com/rust-lang-nursery/log/compare/0.4.17...HEAD +[0.4.17]: https://github.com/rust-lang-nursery/log/compare/0.4.16...0.4.17 +[0.4.16]: https://github.com/rust-lang-nursery/log/compare/0.4.15...0.4.16 +[0.4.15]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.15 +[0.4.14]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.14 +[0.4.13]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.13 +[0.4.12]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.12 +[0.4.11]: https://github.com/rust-lang-nursery/log/compare/0.4.10...0.4.11 +[0.4.10]: https://github.com/rust-lang-nursery/log/compare/0.4.9...0.4.10 +[0.4.9]: https://github.com/rust-lang-nursery/log/compare/0.4.8...0.4.9 +[0.4.8]: https://github.com/rust-lang-nursery/log/compare/0.4.7...0.4.8 +[0.4.7]: https://github.com/rust-lang-nursery/log/compare/0.4.6...0.4.7 +[0.4.6]: https://github.com/rust-lang-nursery/log/compare/0.4.5...0.4.6 +[0.4.5]: https://github.com/rust-lang-nursery/log/compare/0.4.4...0.4.5 +[0.4.4]: https://github.com/rust-lang-nursery/log/compare/0.4.3...0.4.4 +[0.4.3]: https://github.com/rust-lang-nursery/log/compare/0.4.2...0.4.3 +[0.4.2]: https://github.com/rust-lang-nursery/log/compare/0.4.1...0.4.2 +[0.4.1]: https://github.com/rust-lang-nursery/log/compare/0.4.0...0.4.1 +[0.4.0]: https://github.com/rust-lang-nursery/log/compare/0.3.8...0.4.0 +[release tags]: https://github.com/rust-lang-nursery/log/releases diff --git a/vendor/log/LICENSE-APACHE b/vendor/log/LICENSE-APACHE index 16fe87b..f47c941 100644 --- a/vendor/log/LICENSE-APACHE +++ b/vendor/log/LICENSE-APACHE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/log/LICENSE-MIT b/vendor/log/LICENSE-MIT index 39d4bdb..c0428e2 100644 --- a/vendor/log/LICENSE-MIT +++ b/vendor/log/LICENSE-MIT @@ -1,25 +1,25 @@ -Copyright (c) 2014 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/log/README.md b/vendor/log/README.md index f16da73..7fa4353 100644 --- a/vendor/log/README.md +++ b/vendor/log/README.md @@ -1,117 +1,117 @@ -log -=== - -A Rust library providing a lightweight logging *facade*. - -[![Build status](https://img.shields.io/github/workflow/status/rust-lang/log/CI/master)](https://github.com/rust-lang/log/actions) -[![Latest version](https://img.shields.io/crates/v/log.svg)](https://crates.io/crates/log) -[![Documentation](https://docs.rs/log/badge.svg)](https://docs.rs/log) -![License](https://img.shields.io/crates/l/log.svg) - -* [`log` documentation](https://docs.rs/log) - -A logging facade provides a single logging API that abstracts over the actual -logging implementation. Libraries can use the logging API provided by this -crate, and the consumer of those libraries can choose the logging -implementation that is most suitable for its use case. - - -## Minimum supported `rustc` - -`1.31.0+` - -This version is explicitly tested in CI and may be bumped in any release as needed. Maintaining compatibility with older compilers is a priority though, so the bar for bumping the minimum supported version is set very high. Any changes to the supported minimum version will be called out in the release notes. - -## Usage - -### In libraries - -Libraries should link only to the `log` crate, and use the provided macros to -log whatever information will be useful to downstream consumers: - -```toml -[dependencies] -log = "0.4" -``` - -```rust -use log::{info, trace, warn}; - -pub fn shave_the_yak(yak: &mut Yak) { - trace!("Commencing yak shaving"); - - loop { - match find_a_razor() { - Ok(razor) => { - info!("Razor located: {}", razor); - yak.shave(razor); - break; - } - Err(err) => { - warn!("Unable to locate a razor: {}, retrying", err); - } - } - } -} -``` - -### In executables - -In order to produce log output, executables have to use a logger implementation compatible with the facade. -There are many available implementations to choose from, here are some of the most popular ones: - -* Simple minimal loggers: - * [`env_logger`](https://docs.rs/env_logger/*/env_logger/) - * [`simple_logger`](https://docs.rs/simple_logger/*/simple_logger/) - * [`simplelog`](https://docs.rs/simplelog/*/simplelog/) - * [`pretty_env_logger`](https://docs.rs/pretty_env_logger/*/pretty_env_logger/) - * [`stderrlog`](https://docs.rs/stderrlog/*/stderrlog/) - * [`flexi_logger`](https://docs.rs/flexi_logger/*/flexi_logger/) -* Complex configurable frameworks: - * [`log4rs`](https://docs.rs/log4rs/*/log4rs/) - * [`fern`](https://docs.rs/fern/*/fern/) -* Adaptors for other facilities: - * [`syslog`](https://docs.rs/syslog/*/syslog/) - * [`systemd-journal-logger`](https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/) - * [`slog-stdlog`](https://docs.rs/slog-stdlog/*/slog_stdlog/) - * [`android_log`](https://docs.rs/android_log/*/android_log/) - * [`win_dbg_logger`](https://docs.rs/win_dbg_logger/*/win_dbg_logger/) - * [`db_logger`](https://docs.rs/db_logger/*/db_logger/) -* For WebAssembly binaries: - * [`console_log`](https://docs.rs/console_log/*/console_log/) -* For dynamic libraries: - * You may need to construct [an FFI-safe wrapper over `log`](https://github.com/rust-lang/log/issues/421) to initialize in your libraries. -* Utilities: - * [`log_err`](https://docs.rs/log_err/*/log_err/) - -Executables should choose a logger implementation and initialize it early in the -runtime of the program. Logger implementations will typically include a -function to do this. Any log messages generated before the logger is -initialized will be ignored. - -The executable itself may use the `log` crate to log as well. - -## Structured logging - -If you enable the `kv_unstable` feature, you can associate structured data with your log records: - -```rust -use log::{info, trace, warn, as_serde, as_error}; - -pub fn shave_the_yak(yak: &mut Yak) { - trace!(target = "yak_events", yak = as_serde!(yak); "Commencing yak shaving"); - - loop { - match find_a_razor() { - Ok(razor) => { - info!(razor = razor; "Razor located"); - yak.shave(razor); - break; - } - Err(err) => { - warn!(err = as_error!(err); "Unable to locate a razor, retrying"); - } - } - } -} -``` +log +=== + +A Rust library providing a lightweight logging *facade*. + +[![Build status](https://img.shields.io/github/workflow/status/rust-lang/log/CI/master)](https://github.com/rust-lang/log/actions) +[![Latest version](https://img.shields.io/crates/v/log.svg)](https://crates.io/crates/log) +[![Documentation](https://docs.rs/log/badge.svg)](https://docs.rs/log) +![License](https://img.shields.io/crates/l/log.svg) + +* [`log` documentation](https://docs.rs/log) + +A logging facade provides a single logging API that abstracts over the actual +logging implementation. Libraries can use the logging API provided by this +crate, and the consumer of those libraries can choose the logging +implementation that is most suitable for its use case. + + +## Minimum supported `rustc` + +`1.31.0+` + +This version is explicitly tested in CI and may be bumped in any release as needed. Maintaining compatibility with older compilers is a priority though, so the bar for bumping the minimum supported version is set very high. Any changes to the supported minimum version will be called out in the release notes. + +## Usage + +### In libraries + +Libraries should link only to the `log` crate, and use the provided macros to +log whatever information will be useful to downstream consumers: + +```toml +[dependencies] +log = "0.4" +``` + +```rust +use log::{info, trace, warn}; + +pub fn shave_the_yak(yak: &mut Yak) { + trace!("Commencing yak shaving"); + + loop { + match find_a_razor() { + Ok(razor) => { + info!("Razor located: {}", razor); + yak.shave(razor); + break; + } + Err(err) => { + warn!("Unable to locate a razor: {}, retrying", err); + } + } + } +} +``` + +### In executables + +In order to produce log output, executables have to use a logger implementation compatible with the facade. +There are many available implementations to choose from, here are some of the most popular ones: + +* Simple minimal loggers: + * [`env_logger`](https://docs.rs/env_logger/*/env_logger/) + * [`simple_logger`](https://docs.rs/simple_logger/*/simple_logger/) + * [`simplelog`](https://docs.rs/simplelog/*/simplelog/) + * [`pretty_env_logger`](https://docs.rs/pretty_env_logger/*/pretty_env_logger/) + * [`stderrlog`](https://docs.rs/stderrlog/*/stderrlog/) + * [`flexi_logger`](https://docs.rs/flexi_logger/*/flexi_logger/) +* Complex configurable frameworks: + * [`log4rs`](https://docs.rs/log4rs/*/log4rs/) + * [`fern`](https://docs.rs/fern/*/fern/) +* Adaptors for other facilities: + * [`syslog`](https://docs.rs/syslog/*/syslog/) + * [`systemd-journal-logger`](https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/) + * [`slog-stdlog`](https://docs.rs/slog-stdlog/*/slog_stdlog/) + * [`android_log`](https://docs.rs/android_log/*/android_log/) + * [`win_dbg_logger`](https://docs.rs/win_dbg_logger/*/win_dbg_logger/) + * [`db_logger`](https://docs.rs/db_logger/*/db_logger/) +* For WebAssembly binaries: + * [`console_log`](https://docs.rs/console_log/*/console_log/) +* For dynamic libraries: + * You may need to construct [an FFI-safe wrapper over `log`](https://github.com/rust-lang/log/issues/421) to initialize in your libraries. +* Utilities: + * [`log_err`](https://docs.rs/log_err/*/log_err/) + +Executables should choose a logger implementation and initialize it early in the +runtime of the program. Logger implementations will typically include a +function to do this. Any log messages generated before the logger is +initialized will be ignored. + +The executable itself may use the `log` crate to log as well. + +## Structured logging + +If you enable the `kv_unstable` feature, you can associate structured data with your log records: + +```rust +use log::{info, trace, warn, as_serde, as_error}; + +pub fn shave_the_yak(yak: &mut Yak) { + trace!(target = "yak_events", yak = as_serde!(yak); "Commencing yak shaving"); + + loop { + match find_a_razor() { + Ok(razor) => { + info!(razor = razor; "Razor located"); + yak.shave(razor); + break; + } + Err(err) => { + warn!(err = as_error!(err); "Unable to locate a razor, retrying"); + } + } + } +} +``` diff --git a/vendor/log/benches/value.rs b/vendor/log/benches/value.rs index 4e6960e..3dab3bf 100644 --- a/vendor/log/benches/value.rs +++ b/vendor/log/benches/value.rs @@ -1,30 +1,30 @@ -#![cfg(feature = "kv_unstable")] -#![feature(test)] - -extern crate log; -extern crate test; - -use log::kv::Value; - -#[bench] -fn u8_to_value(b: &mut test::Bencher) { - b.iter(|| Value::from(1u8)) -} - -#[bench] -fn u8_to_value_debug(b: &mut test::Bencher) { - b.iter(|| Value::from_debug(&1u8)) -} - -#[bench] -fn str_to_value_debug(b: &mut test::Bencher) { - b.iter(|| Value::from_debug(&"a string")) -} - -#[bench] -fn custom_to_value_debug(b: &mut test::Bencher) { - #[derive(Debug)] - struct A; - - b.iter(|| Value::from_debug(&A)) -} +#![cfg(feature = "kv_unstable")] +#![feature(test)] + +extern crate log; +extern crate test; + +use log::kv::Value; + +#[bench] +fn u8_to_value(b: &mut test::Bencher) { + b.iter(|| Value::from(1u8)) +} + +#[bench] +fn u8_to_value_debug(b: &mut test::Bencher) { + b.iter(|| Value::from_debug(&1u8)) +} + +#[bench] +fn str_to_value_debug(b: &mut test::Bencher) { + b.iter(|| Value::from_debug(&"a string")) +} + +#[bench] +fn custom_to_value_debug(b: &mut test::Bencher) { + #[derive(Debug)] + struct A; + + b.iter(|| Value::from_debug(&A)) +} diff --git a/vendor/log/build.rs b/vendor/log/build.rs index dd8d4e0..30c7edb 100644 --- a/vendor/log/build.rs +++ b/vendor/log/build.rs @@ -1,46 +1,46 @@ -//! This build script detects target platforms that lack proper support for -//! atomics and sets `cfg` flags accordingly. - -use std::env; -use std::str; - -fn main() { - let target = match rustc_target() { - Some(target) => target, - None => return, - }; - - if target_has_atomic_cas(&target) { - println!("cargo:rustc-cfg=atomic_cas"); - } - - if target_has_atomics(&target) { - println!("cargo:rustc-cfg=has_atomics"); - } - - println!("cargo:rerun-if-changed=build.rs"); -} - -fn target_has_atomic_cas(target: &str) -> bool { - match &target[..] { - "thumbv6m-none-eabi" - | "msp430-none-elf" - | "riscv32i-unknown-none-elf" - | "riscv32imc-unknown-none-elf" => false, - _ => true, - } -} - -fn target_has_atomics(target: &str) -> bool { - match &target[..] { - "thumbv4t-none-eabi" - | "msp430-none-elf" - | "riscv32i-unknown-none-elf" - | "riscv32imc-unknown-none-elf" => false, - _ => true, - } -} - -fn rustc_target() -> Option { - env::var("TARGET").ok() -} +//! This build script detects target platforms that lack proper support for +//! atomics and sets `cfg` flags accordingly. + +use std::env; +use std::str; + +fn main() { + let target = match rustc_target() { + Some(target) => target, + None => return, + }; + + if target_has_atomic_cas(&target) { + println!("cargo:rustc-cfg=atomic_cas"); + } + + if target_has_atomics(&target) { + println!("cargo:rustc-cfg=has_atomics"); + } + + println!("cargo:rerun-if-changed=build.rs"); +} + +fn target_has_atomic_cas(target: &str) -> bool { + match &target[..] { + "thumbv6m-none-eabi" + | "msp430-none-elf" + | "riscv32i-unknown-none-elf" + | "riscv32imc-unknown-none-elf" => false, + _ => true, + } +} + +fn target_has_atomics(target: &str) -> bool { + match &target[..] { + "thumbv4t-none-eabi" + | "msp430-none-elf" + | "riscv32i-unknown-none-elf" + | "riscv32imc-unknown-none-elf" => false, + _ => true, + } +} + +fn rustc_target() -> Option { + env::var("TARGET").ok() +} diff --git a/vendor/log/src/kv/error.rs b/vendor/log/src/kv/error.rs index c72d323..1adad23 100644 --- a/vendor/log/src/kv/error.rs +++ b/vendor/log/src/kv/error.rs @@ -1,90 +1,90 @@ -use std::fmt; - -/// An error encountered while working with structured data. -#[derive(Debug)] -pub struct Error { - inner: Inner, -} - -#[derive(Debug)] -enum Inner { - #[cfg(feature = "std")] - Boxed(std_support::BoxedError), - Msg(&'static str), - Value(value_bag::Error), - Fmt, -} - -impl Error { - /// Create an error from a message. - pub fn msg(msg: &'static str) -> Self { - Error { - inner: Inner::Msg(msg), - } - } - - // Not public so we don't leak the `value_bag` API - pub(super) fn from_value(err: value_bag::Error) -> Self { - Error { - inner: Inner::Value(err), - } - } - - // Not public so we don't leak the `value_bag` API - pub(super) fn into_value(self) -> value_bag::Error { - match self.inner { - Inner::Value(err) => err, - #[cfg(feature = "kv_unstable_std")] - _ => value_bag::Error::boxed(self), - #[cfg(not(feature = "kv_unstable_std"))] - _ => value_bag::Error::msg("error inspecting a value"), - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Inner::*; - match &self.inner { - #[cfg(feature = "std")] - &Boxed(ref err) => err.fmt(f), - &Value(ref err) => err.fmt(f), - &Msg(ref msg) => msg.fmt(f), - &Fmt => fmt::Error.fmt(f), - } - } -} - -impl From for Error { - fn from(_: fmt::Error) -> Self { - Error { inner: Inner::Fmt } - } -} - -#[cfg(feature = "std")] -mod std_support { - use super::*; - use std::{error, io}; - - pub(super) type BoxedError = Box; - - impl Error { - /// Create an error from a standard error type. - pub fn boxed(err: E) -> Self - where - E: Into, - { - Error { - inner: Inner::Boxed(err.into()), - } - } - } - - impl error::Error for Error {} - - impl From for Error { - fn from(err: io::Error) -> Self { - Error::boxed(err) - } - } -} +use std::fmt; + +/// An error encountered while working with structured data. +#[derive(Debug)] +pub struct Error { + inner: Inner, +} + +#[derive(Debug)] +enum Inner { + #[cfg(feature = "std")] + Boxed(std_support::BoxedError), + Msg(&'static str), + Value(value_bag::Error), + Fmt, +} + +impl Error { + /// Create an error from a message. + pub fn msg(msg: &'static str) -> Self { + Error { + inner: Inner::Msg(msg), + } + } + + // Not public so we don't leak the `value_bag` API + pub(super) fn from_value(err: value_bag::Error) -> Self { + Error { + inner: Inner::Value(err), + } + } + + // Not public so we don't leak the `value_bag` API + pub(super) fn into_value(self) -> value_bag::Error { + match self.inner { + Inner::Value(err) => err, + #[cfg(feature = "kv_unstable_std")] + _ => value_bag::Error::boxed(self), + #[cfg(not(feature = "kv_unstable_std"))] + _ => value_bag::Error::msg("error inspecting a value"), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Inner::*; + match &self.inner { + #[cfg(feature = "std")] + &Boxed(ref err) => err.fmt(f), + &Value(ref err) => err.fmt(f), + &Msg(ref msg) => msg.fmt(f), + &Fmt => fmt::Error.fmt(f), + } + } +} + +impl From for Error { + fn from(_: fmt::Error) -> Self { + Error { inner: Inner::Fmt } + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + use std::{error, io}; + + pub(super) type BoxedError = Box; + + impl Error { + /// Create an error from a standard error type. + pub fn boxed(err: E) -> Self + where + E: Into, + { + Error { + inner: Inner::Boxed(err.into()), + } + } + } + + impl error::Error for Error {} + + impl From for Error { + fn from(err: io::Error) -> Self { + Error::boxed(err) + } + } +} diff --git a/vendor/log/src/kv/key.rs b/vendor/log/src/kv/key.rs index 762e08d..0e688c1 100644 --- a/vendor/log/src/kv/key.rs +++ b/vendor/log/src/kv/key.rs @@ -1,172 +1,172 @@ -//! Structured keys. - -use std::borrow::Borrow; -use std::cmp; -use std::fmt; -use std::hash; - -/// A type that can be converted into a [`Key`](struct.Key.html). -pub trait ToKey { - /// Perform the conversion. - fn to_key(&self) -> Key; -} - -impl<'a, T> ToKey for &'a T -where - T: ToKey + ?Sized, -{ - fn to_key(&self) -> Key { - (**self).to_key() - } -} - -impl<'k> ToKey for Key<'k> { - fn to_key(&self) -> Key { - Key { key: self.key } - } -} - -impl ToKey for str { - fn to_key(&self) -> Key { - Key::from_str(self) - } -} - -/// A key in a structured key-value pair. -#[derive(Clone)] -pub struct Key<'k> { - key: &'k str, -} - -impl<'k> Key<'k> { - /// Get a key from a borrowed string. - pub fn from_str(key: &'k str) -> Self { - Key { key: key } - } - - /// Get a borrowed string from this key. - pub fn as_str(&self) -> &str { - self.key - } -} - -impl<'k> fmt::Debug for Key<'k> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.key.fmt(f) - } -} - -impl<'k> fmt::Display for Key<'k> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.key.fmt(f) - } -} - -impl<'k> hash::Hash for Key<'k> { - fn hash(&self, state: &mut H) - where - H: hash::Hasher, - { - self.as_str().hash(state) - } -} - -impl<'k, 'ko> PartialEq> for Key<'k> { - fn eq(&self, other: &Key<'ko>) -> bool { - self.as_str().eq(other.as_str()) - } -} - -impl<'k> Eq for Key<'k> {} - -impl<'k, 'ko> PartialOrd> for Key<'k> { - fn partial_cmp(&self, other: &Key<'ko>) -> Option { - self.as_str().partial_cmp(other.as_str()) - } -} - -impl<'k> Ord for Key<'k> { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.as_str().cmp(other.as_str()) - } -} - -impl<'k> AsRef for Key<'k> { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl<'k> Borrow for Key<'k> { - fn borrow(&self) -> &str { - self.as_str() - } -} - -impl<'k> From<&'k str> for Key<'k> { - fn from(s: &'k str) -> Self { - Key::from_str(s) - } -} - -#[cfg(feature = "std")] -mod std_support { - use super::*; - - use std::borrow::Cow; - - impl ToKey for String { - fn to_key(&self) -> Key { - Key::from_str(self) - } - } - - impl<'a> ToKey for Cow<'a, str> { - fn to_key(&self) -> Key { - Key::from_str(self) - } - } -} - -#[cfg(feature = "kv_unstable_sval")] -mod sval_support { - use super::*; - - extern crate sval; - - use self::sval::value::{self, Value}; - - impl<'a> Value for Key<'a> { - fn stream(&self, stream: &mut value::Stream) -> value::Result { - self.key.stream(stream) - } - } -} - -#[cfg(feature = "kv_unstable_serde")] -mod serde_support { - use super::*; - - extern crate serde; - - use self::serde::{Serialize, Serializer}; - - impl<'a> Serialize for Key<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.key.serialize(serializer) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn key_from_string() { - assert_eq!("a key", Key::from_str("a key").as_str()); - } -} +//! Structured keys. + +use std::borrow::Borrow; +use std::cmp; +use std::fmt; +use std::hash; + +/// A type that can be converted into a [`Key`](struct.Key.html). +pub trait ToKey { + /// Perform the conversion. + fn to_key(&self) -> Key; +} + +impl<'a, T> ToKey for &'a T +where + T: ToKey + ?Sized, +{ + fn to_key(&self) -> Key { + (**self).to_key() + } +} + +impl<'k> ToKey for Key<'k> { + fn to_key(&self) -> Key { + Key { key: self.key } + } +} + +impl ToKey for str { + fn to_key(&self) -> Key { + Key::from_str(self) + } +} + +/// A key in a structured key-value pair. +#[derive(Clone)] +pub struct Key<'k> { + key: &'k str, +} + +impl<'k> Key<'k> { + /// Get a key from a borrowed string. + pub fn from_str(key: &'k str) -> Self { + Key { key: key } + } + + /// Get a borrowed string from this key. + pub fn as_str(&self) -> &str { + self.key + } +} + +impl<'k> fmt::Debug for Key<'k> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.key.fmt(f) + } +} + +impl<'k> fmt::Display for Key<'k> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.key.fmt(f) + } +} + +impl<'k> hash::Hash for Key<'k> { + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { + self.as_str().hash(state) + } +} + +impl<'k, 'ko> PartialEq> for Key<'k> { + fn eq(&self, other: &Key<'ko>) -> bool { + self.as_str().eq(other.as_str()) + } +} + +impl<'k> Eq for Key<'k> {} + +impl<'k, 'ko> PartialOrd> for Key<'k> { + fn partial_cmp(&self, other: &Key<'ko>) -> Option { + self.as_str().partial_cmp(other.as_str()) + } +} + +impl<'k> Ord for Key<'k> { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_str().cmp(other.as_str()) + } +} + +impl<'k> AsRef for Key<'k> { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl<'k> Borrow for Key<'k> { + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl<'k> From<&'k str> for Key<'k> { + fn from(s: &'k str) -> Self { + Key::from_str(s) + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + + use std::borrow::Cow; + + impl ToKey for String { + fn to_key(&self) -> Key { + Key::from_str(self) + } + } + + impl<'a> ToKey for Cow<'a, str> { + fn to_key(&self) -> Key { + Key::from_str(self) + } + } +} + +#[cfg(feature = "kv_unstable_sval")] +mod sval_support { + use super::*; + + extern crate sval; + + use self::sval::value::{self, Value}; + + impl<'a> Value for Key<'a> { + fn stream(&self, stream: &mut value::Stream) -> value::Result { + self.key.stream(stream) + } + } +} + +#[cfg(feature = "kv_unstable_serde")] +mod serde_support { + use super::*; + + extern crate serde; + + use self::serde::{Serialize, Serializer}; + + impl<'a> Serialize for Key<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.key.serialize(serializer) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn key_from_string() { + assert_eq!("a key", Key::from_str("a key").as_str()); + } +} diff --git a/vendor/log/src/kv/mod.rs b/vendor/log/src/kv/mod.rs index 5dc6933..f96b821 100644 --- a/vendor/log/src/kv/mod.rs +++ b/vendor/log/src/kv/mod.rs @@ -1,26 +1,26 @@ -//! **UNSTABLE:** Structured key-value pairs. -//! -//! This module is unstable and breaking changes may be made -//! at any time. See [the tracking issue](https://github.com/rust-lang-nursery/log/issues/328) -//! for more details. -//! -//! Add the `kv_unstable` feature to your `Cargo.toml` to enable -//! this module: -//! -//! ```toml -//! [dependencies.log] -//! features = ["kv_unstable"] -//! ``` - -mod error; -mod key; -pub mod source; - -pub mod value; - -pub use self::error::Error; -pub use self::key::{Key, ToKey}; -pub use self::source::{Source, Visitor}; - -#[doc(inline)] -pub use self::value::{ToValue, Value}; +//! **UNSTABLE:** Structured key-value pairs. +//! +//! This module is unstable and breaking changes may be made +//! at any time. See [the tracking issue](https://github.com/rust-lang-nursery/log/issues/328) +//! for more details. +//! +//! Add the `kv_unstable` feature to your `Cargo.toml` to enable +//! this module: +//! +//! ```toml +//! [dependencies.log] +//! features = ["kv_unstable"] +//! ``` + +mod error; +mod key; +pub mod source; + +pub mod value; + +pub use self::error::Error; +pub use self::key::{Key, ToKey}; +pub use self::source::{Source, Visitor}; + +#[doc(inline)] +pub use self::value::{ToValue, Value}; diff --git a/vendor/log/src/kv/source.rs b/vendor/log/src/kv/source.rs index 8762c62..e3ecde9 100644 --- a/vendor/log/src/kv/source.rs +++ b/vendor/log/src/kv/source.rs @@ -1,754 +1,754 @@ -//! Sources for key-value pairs. - -#[cfg(feature = "kv_unstable_sval")] -extern crate sval; - -#[cfg(feature = "kv_unstable_serde")] -extern crate serde; - -use kv::{Error, Key, ToKey, ToValue, Value}; -use std::fmt; - -/// A source of key-value pairs. -/// -/// The source may be a single pair, a set of pairs, or a filter over a set of pairs. -/// Use the [`Visitor`](trait.Visitor.html) trait to inspect the structured data -/// in a source. -pub trait Source { - /// Visit key-value pairs. - /// - /// A source doesn't have to guarantee any ordering or uniqueness of key-value pairs. - /// If the given visitor returns an error then the source may early-return with it, - /// even if there are more key-value pairs. - /// - /// # Implementation notes - /// - /// A source should yield the same key-value pairs to a subsequent visitor unless - /// that visitor itself fails. - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; - - /// Get the value for a given key. - /// - /// If the key appears multiple times in the source then which key is returned - /// is implementation specific. - /// - /// # Implementation notes - /// - /// A source that can provide a more efficient implementation of this method - /// should override it. - #[cfg(not(test))] - fn get<'v>(&'v self, key: Key) -> Option> { - get_default(self, key) - } - - #[cfg(test)] - fn get<'v>(&'v self, key: Key) -> Option>; - - /// Count the number of key-value pairs that can be visited. - /// - /// # Implementation notes - /// - /// A source that knows the number of key-value pairs upfront may provide a more - /// efficient implementation. - /// - /// A subsequent call to `visit` should yield the same number of key-value pairs - /// to the visitor, unless that visitor fails part way through. - #[cfg(not(test))] - fn count(&self) -> usize { - count_default(self) - } - - #[cfg(test)] - fn count(&self) -> usize; -} - -/// The default implemention of `Source::get` -pub(crate) fn get_default<'v>(source: &'v (impl Source + ?Sized), key: Key) -> Option> { - struct Get<'k, 'v> { - key: Key<'k>, - found: Option>, - } - - impl<'k, 'kvs> Visitor<'kvs> for Get<'k, 'kvs> { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - if self.key == key { - self.found = Some(value); - } - - Ok(()) - } - } - - let mut get = Get { key, found: None }; - - let _ = source.visit(&mut get); - get.found -} - -/// The default implementation of `Source::count`. -pub(crate) fn count_default(source: impl Source) -> usize { - struct Count(usize); - - impl<'kvs> Visitor<'kvs> for Count { - fn visit_pair(&mut self, _: Key<'kvs>, _: Value<'kvs>) -> Result<(), Error> { - self.0 += 1; - - Ok(()) - } - } - - let mut count = Count(0); - let _ = source.visit(&mut count); - count.0 -} - -impl<'a, T> Source for &'a T -where - T: Source + ?Sized, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - Source::visit(&**self, visitor) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - Source::get(&**self, key) - } - - fn count(&self) -> usize { - Source::count(&**self) - } -} - -impl Source for (K, V) -where - K: ToKey, - V: ToValue, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - visitor.visit_pair(self.0.to_key(), self.1.to_value()) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - if self.0.to_key() == key { - Some(self.1.to_value()) - } else { - None - } - } - - fn count(&self) -> usize { - 1 - } -} - -impl Source for [S] -where - S: Source, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - for source in self { - source.visit(visitor)?; - } - - Ok(()) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - for source in self { - if let Some(found) = source.get(key.clone()) { - return Some(found); - } - } - - None - } - - fn count(&self) -> usize { - self.len() - } -} - -impl Source for Option -where - S: Source, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - if let Some(ref source) = *self { - source.visit(visitor)?; - } - - Ok(()) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - self.as_ref().and_then(|s| s.get(key)) - } - - fn count(&self) -> usize { - self.as_ref().map(Source::count).unwrap_or(0) - } -} - -/// A visitor for the key-value pairs in a [`Source`](trait.Source.html). -pub trait Visitor<'kvs> { - /// Visit a key-value pair. - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error>; -} - -impl<'a, 'kvs, T> Visitor<'kvs> for &'a mut T -where - T: Visitor<'kvs> + ?Sized, -{ - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - (**self).visit_pair(key, value) - } -} - -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugMap<'a, 'b> { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.entry(&key, &value); - Ok(()) - } -} - -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugList<'a, 'b> { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.entry(&(key, value)); - Ok(()) - } -} - -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugSet<'a, 'b> { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.entry(&(key, value)); - Ok(()) - } -} - -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugTuple<'a, 'b> { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.field(&key); - self.field(&value); - Ok(()) - } -} - -#[cfg(feature = "std")] -mod std_support { - use super::*; - use std::borrow::Borrow; - use std::collections::{BTreeMap, HashMap}; - use std::hash::{BuildHasher, Hash}; - - impl Source for Box - where - S: Source + ?Sized, - { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - Source::visit(&**self, visitor) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - Source::get(&**self, key) - } - - fn count(&self) -> usize { - Source::count(&**self) - } - } - - impl Source for Vec - where - S: Source, - { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - Source::visit(&**self, visitor) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - Source::get(&**self, key) - } - - fn count(&self) -> usize { - Source::count(&**self) - } - } - - impl<'kvs, V> Visitor<'kvs> for Box - where - V: Visitor<'kvs> + ?Sized, - { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - (**self).visit_pair(key, value) - } - } - - impl Source for HashMap - where - K: ToKey + Borrow + Eq + Hash, - V: ToValue, - S: BuildHasher, - { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - for (key, value) in self { - visitor.visit_pair(key.to_key(), value.to_value())?; - } - Ok(()) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - HashMap::get(self, key.as_str()).map(|v| v.to_value()) - } - - fn count(&self) -> usize { - self.len() - } - } - - impl Source for BTreeMap - where - K: ToKey + Borrow + Ord, - V: ToValue, - { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - for (key, value) in self { - visitor.visit_pair(key.to_key(), value.to_value())?; - } - Ok(()) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - BTreeMap::get(self, key.as_str()).map(|v| v.to_value()) - } - - fn count(&self) -> usize { - self.len() - } - } - - #[cfg(test)] - mod tests { - use super::*; - use kv::value::tests::Token; - use std::collections::{BTreeMap, HashMap}; - - #[test] - fn count() { - assert_eq!(1, Source::count(&Box::new(("a", 1)))); - assert_eq!(2, Source::count(&vec![("a", 1), ("b", 2)])); - } - - #[test] - fn get() { - let source = vec![("a", 1), ("b", 2), ("a", 1)]; - assert_eq!( - Token::I64(1), - Source::get(&source, Key::from_str("a")).unwrap().to_token() - ); - - let source = Box::new(Option::None::<(&str, i32)>); - assert!(Source::get(&source, Key::from_str("a")).is_none()); - } - - #[test] - fn hash_map() { - let mut map = HashMap::new(); - map.insert("a", 1); - map.insert("b", 2); - - assert_eq!(2, Source::count(&map)); - assert_eq!( - Token::I64(1), - Source::get(&map, Key::from_str("a")).unwrap().to_token() - ); - } - - #[test] - fn btree_map() { - let mut map = BTreeMap::new(); - map.insert("a", 1); - map.insert("b", 2); - - assert_eq!(2, Source::count(&map)); - assert_eq!( - Token::I64(1), - Source::get(&map, Key::from_str("a")).unwrap().to_token() - ); - } - } -} - -/// The result of calling `Source::as_map`. -pub struct AsMap(S); - -/// Visit this source as a map. -pub fn as_map(source: S) -> AsMap -where - S: Source, -{ - AsMap(source) -} - -impl Source for AsMap -where - S: Source, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - self.0.visit(visitor) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - self.0.get(key) - } - - fn count(&self) -> usize { - self.0.count() - } -} - -impl fmt::Debug for AsMap -where - S: Source, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut f = f.debug_map(); - self.0.visit(&mut f).map_err(|_| fmt::Error)?; - f.finish() - } -} - -/// The result of calling `Source::as_list` -pub struct AsList(S); - -/// Visit this source as a list. -pub fn as_list(source: S) -> AsList -where - S: Source, -{ - AsList(source) -} - -impl Source for AsList -where - S: Source, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - self.0.visit(visitor) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - self.0.get(key) - } - - fn count(&self) -> usize { - self.0.count() - } -} - -impl fmt::Debug for AsList -where - S: Source, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut f = f.debug_list(); - self.0.visit(&mut f).map_err(|_| fmt::Error)?; - f.finish() - } -} - -#[cfg(feature = "kv_unstable_sval")] -mod sval_support { - use super::*; - - use self::sval::value; - - impl value::Value for AsMap - where - S: Source, - { - fn stream(&self, stream: &mut value::Stream) -> value::Result { - struct StreamVisitor<'a, 'b>(&'a mut value::Stream<'b>); - - impl<'a, 'b, 'kvs> Visitor<'kvs> for StreamVisitor<'a, 'b> { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.0 - .map_key(key) - .map_err(|_| Error::msg("failed to stream map key"))?; - self.0 - .map_value(value) - .map_err(|_| Error::msg("failed to stream map value"))?; - Ok(()) - } - } - - stream - .map_begin(Some(self.count())) - .map_err(|_| self::sval::Error::msg("failed to begin map"))?; - - self.visit(&mut StreamVisitor(stream)) - .map_err(|_| self::sval::Error::msg("failed to visit key-values"))?; - - stream - .map_end() - .map_err(|_| self::sval::Error::msg("failed to end map")) - } - } - - impl value::Value for AsList - where - S: Source, - { - fn stream(&self, stream: &mut value::Stream) -> value::Result { - struct StreamVisitor<'a, 'b>(&'a mut value::Stream<'b>); - - impl<'a, 'b, 'kvs> Visitor<'kvs> for StreamVisitor<'a, 'b> { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.0 - .seq_elem((key, value)) - .map_err(|_| Error::msg("failed to stream seq entry"))?; - Ok(()) - } - } - - stream - .seq_begin(Some(self.count())) - .map_err(|_| self::sval::Error::msg("failed to begin seq"))?; - - self.visit(&mut StreamVisitor(stream)) - .map_err(|_| self::sval::Error::msg("failed to visit key-values"))?; - - stream - .seq_end() - .map_err(|_| self::sval::Error::msg("failed to end seq")) - } - } - - #[cfg(test)] - mod tests { - use super::*; - - use self::sval::Value; - - use crate::kv::source; - - #[test] - fn derive_stream() { - #[derive(Value)] - pub struct MyRecordAsMap<'a> { - msg: &'a str, - kvs: source::AsMap<&'a dyn Source>, - } - - #[derive(Value)] - pub struct MyRecordAsList<'a> { - msg: &'a str, - kvs: source::AsList<&'a dyn Source>, - } - } - } -} - -#[cfg(feature = "kv_unstable_serde")] -pub mod as_map { - //! `serde` adapters for serializing a `Source` as a map. - - use super::*; - - use self::serde::{Serialize, Serializer}; - - /// Serialize a `Source` as a map. - pub fn serialize(source: &T, serializer: S) -> Result - where - T: Source, - S: Serializer, - { - as_map(source).serialize(serializer) - } -} - -#[cfg(feature = "kv_unstable_serde")] -pub mod as_list { - //! `serde` adapters for serializing a `Source` as a list. - - use super::*; - - use self::serde::{Serialize, Serializer}; - - /// Serialize a `Source` as a list. - pub fn serialize(source: &T, serializer: S) -> Result - where - T: Source, - S: Serializer, - { - as_list(source).serialize(serializer) - } -} - -#[cfg(feature = "kv_unstable_serde")] -mod serde_support { - use super::*; - - use self::serde::ser::{Error as SerError, Serialize, SerializeMap, SerializeSeq, Serializer}; - - impl Serialize for AsMap - where - T: Source, - { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - struct SerializerVisitor<'a, S>(&'a mut S); - - impl<'a, 'kvs, S> Visitor<'kvs> for SerializerVisitor<'a, S> - where - S: SerializeMap, - { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.0 - .serialize_entry(&key, &value) - .map_err(|_| Error::msg("failed to serialize map entry"))?; - Ok(()) - } - } - - let mut map = serializer.serialize_map(Some(self.count()))?; - - self.visit(&mut SerializerVisitor(&mut map)) - .map_err(|_| S::Error::custom("failed to visit key-values"))?; - - map.end() - } - } - - impl Serialize for AsList - where - T: Source, - { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - struct SerializerVisitor<'a, S>(&'a mut S); - - impl<'a, 'kvs, S> Visitor<'kvs> for SerializerVisitor<'a, S> - where - S: SerializeSeq, - { - fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { - self.0 - .serialize_element(&(key, value)) - .map_err(|_| Error::msg("failed to serialize seq entry"))?; - Ok(()) - } - } - - let mut seq = serializer.serialize_seq(Some(self.count()))?; - - self.visit(&mut SerializerVisitor(&mut seq)) - .map_err(|_| S::Error::custom("failed to visit seq"))?; - - seq.end() - } - } - - #[cfg(test)] - mod tests { - use super::*; - - use self::serde::Serialize; - - use crate::kv::source; - - #[test] - fn derive_serialize() { - #[derive(Serialize)] - pub struct MyRecordAsMap<'a> { - msg: &'a str, - #[serde(flatten)] - #[serde(with = "source::as_map")] - kvs: &'a dyn Source, - } - - #[derive(Serialize)] - pub struct MyRecordAsList<'a> { - msg: &'a str, - #[serde(flatten)] - #[serde(with = "source::as_list")] - kvs: &'a dyn Source, - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use kv::value::tests::Token; - - #[test] - fn source_is_object_safe() { - fn _check(_: &dyn Source) {} - } - - #[test] - fn visitor_is_object_safe() { - fn _check(_: &dyn Visitor) {} - } - - #[test] - fn count() { - struct OnePair { - key: &'static str, - value: i32, - } - - impl Source for OnePair { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - visitor.visit_pair(self.key.to_key(), self.value.to_value()) - } - - fn get<'v>(&'v self, key: Key) -> Option> { - get_default(self, key) - } - - fn count(&self) -> usize { - count_default(self) - } - } - - assert_eq!(1, Source::count(&("a", 1))); - assert_eq!(2, Source::count(&[("a", 1), ("b", 2)] as &[_])); - assert_eq!(0, Source::count(&Option::None::<(&str, i32)>)); - assert_eq!(1, Source::count(&OnePair { key: "a", value: 1 })); - } - - #[test] - fn get() { - let source = &[("a", 1), ("b", 2), ("a", 1)] as &[_]; - assert_eq!( - Token::I64(1), - Source::get(source, Key::from_str("a")).unwrap().to_token() - ); - assert_eq!( - Token::I64(2), - Source::get(source, Key::from_str("b")).unwrap().to_token() - ); - assert!(Source::get(&source, Key::from_str("c")).is_none()); - - let source = Option::None::<(&str, i32)>; - assert!(Source::get(&source, Key::from_str("a")).is_none()); - } - - #[test] - fn as_map() { - let _ = crate::kv::source::as_map(("a", 1)); - let _ = crate::kv::source::as_map(&("a", 1) as &dyn Source); - } - - #[test] - fn as_list() { - let _ = crate::kv::source::as_list(("a", 1)); - let _ = crate::kv::source::as_list(&("a", 1) as &dyn Source); - } -} +//! Sources for key-value pairs. + +#[cfg(feature = "kv_unstable_sval")] +extern crate sval; + +#[cfg(feature = "kv_unstable_serde")] +extern crate serde; + +use kv::{Error, Key, ToKey, ToValue, Value}; +use std::fmt; + +/// A source of key-value pairs. +/// +/// The source may be a single pair, a set of pairs, or a filter over a set of pairs. +/// Use the [`Visitor`](trait.Visitor.html) trait to inspect the structured data +/// in a source. +pub trait Source { + /// Visit key-value pairs. + /// + /// A source doesn't have to guarantee any ordering or uniqueness of key-value pairs. + /// If the given visitor returns an error then the source may early-return with it, + /// even if there are more key-value pairs. + /// + /// # Implementation notes + /// + /// A source should yield the same key-value pairs to a subsequent visitor unless + /// that visitor itself fails. + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; + + /// Get the value for a given key. + /// + /// If the key appears multiple times in the source then which key is returned + /// is implementation specific. + /// + /// # Implementation notes + /// + /// A source that can provide a more efficient implementation of this method + /// should override it. + #[cfg(not(test))] + fn get<'v>(&'v self, key: Key) -> Option> { + get_default(self, key) + } + + #[cfg(test)] + fn get<'v>(&'v self, key: Key) -> Option>; + + /// Count the number of key-value pairs that can be visited. + /// + /// # Implementation notes + /// + /// A source that knows the number of key-value pairs upfront may provide a more + /// efficient implementation. + /// + /// A subsequent call to `visit` should yield the same number of key-value pairs + /// to the visitor, unless that visitor fails part way through. + #[cfg(not(test))] + fn count(&self) -> usize { + count_default(self) + } + + #[cfg(test)] + fn count(&self) -> usize; +} + +/// The default implemention of `Source::get` +pub(crate) fn get_default<'v>(source: &'v (impl Source + ?Sized), key: Key) -> Option> { + struct Get<'k, 'v> { + key: Key<'k>, + found: Option>, + } + + impl<'k, 'kvs> Visitor<'kvs> for Get<'k, 'kvs> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + if self.key == key { + self.found = Some(value); + } + + Ok(()) + } + } + + let mut get = Get { key, found: None }; + + let _ = source.visit(&mut get); + get.found +} + +/// The default implementation of `Source::count`. +pub(crate) fn count_default(source: impl Source) -> usize { + struct Count(usize); + + impl<'kvs> Visitor<'kvs> for Count { + fn visit_pair(&mut self, _: Key<'kvs>, _: Value<'kvs>) -> Result<(), Error> { + self.0 += 1; + + Ok(()) + } + } + + let mut count = Count(0); + let _ = source.visit(&mut count); + count.0 +} + +impl<'a, T> Source for &'a T +where + T: Source + ?Sized, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } +} + +impl Source for (K, V) +where + K: ToKey, + V: ToValue, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + visitor.visit_pair(self.0.to_key(), self.1.to_value()) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + if self.0.to_key() == key { + Some(self.1.to_value()) + } else { + None + } + } + + fn count(&self) -> usize { + 1 + } +} + +impl Source for [S] +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + for source in self { + source.visit(visitor)?; + } + + Ok(()) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + for source in self { + if let Some(found) = source.get(key.clone()) { + return Some(found); + } + } + + None + } + + fn count(&self) -> usize { + self.len() + } +} + +impl Source for Option +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + if let Some(ref source) = *self { + source.visit(visitor)?; + } + + Ok(()) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + self.as_ref().and_then(|s| s.get(key)) + } + + fn count(&self) -> usize { + self.as_ref().map(Source::count).unwrap_or(0) + } +} + +/// A visitor for the key-value pairs in a [`Source`](trait.Source.html). +pub trait Visitor<'kvs> { + /// Visit a key-value pair. + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error>; +} + +impl<'a, 'kvs, T> Visitor<'kvs> for &'a mut T +where + T: Visitor<'kvs> + ?Sized, +{ + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + (**self).visit_pair(key, value) + } +} + +impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugMap<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&key, &value); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugList<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&(key, value)); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugSet<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&(key, value)); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugTuple<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.field(&key); + self.field(&value); + Ok(()) + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + use std::borrow::Borrow; + use std::collections::{BTreeMap, HashMap}; + use std::hash::{BuildHasher, Hash}; + + impl Source for Box + where + S: Source + ?Sized, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl Source for Vec + where + S: Source, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl<'kvs, V> Visitor<'kvs> for Box + where + V: Visitor<'kvs> + ?Sized, + { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + (**self).visit_pair(key, value) + } + } + + impl Source for HashMap + where + K: ToKey + Borrow + Eq + Hash, + V: ToValue, + S: BuildHasher, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + for (key, value) in self { + visitor.visit_pair(key.to_key(), value.to_value())?; + } + Ok(()) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + HashMap::get(self, key.as_str()).map(|v| v.to_value()) + } + + fn count(&self) -> usize { + self.len() + } + } + + impl Source for BTreeMap + where + K: ToKey + Borrow + Ord, + V: ToValue, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + for (key, value) in self { + visitor.visit_pair(key.to_key(), value.to_value())?; + } + Ok(()) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + BTreeMap::get(self, key.as_str()).map(|v| v.to_value()) + } + + fn count(&self) -> usize { + self.len() + } + } + + #[cfg(test)] + mod tests { + use super::*; + use kv::value::tests::Token; + use std::collections::{BTreeMap, HashMap}; + + #[test] + fn count() { + assert_eq!(1, Source::count(&Box::new(("a", 1)))); + assert_eq!(2, Source::count(&vec![("a", 1), ("b", 2)])); + } + + #[test] + fn get() { + let source = vec![("a", 1), ("b", 2), ("a", 1)]; + assert_eq!( + Token::I64(1), + Source::get(&source, Key::from_str("a")).unwrap().to_token() + ); + + let source = Box::new(Option::None::<(&str, i32)>); + assert!(Source::get(&source, Key::from_str("a")).is_none()); + } + + #[test] + fn hash_map() { + let mut map = HashMap::new(); + map.insert("a", 1); + map.insert("b", 2); + + assert_eq!(2, Source::count(&map)); + assert_eq!( + Token::I64(1), + Source::get(&map, Key::from_str("a")).unwrap().to_token() + ); + } + + #[test] + fn btree_map() { + let mut map = BTreeMap::new(); + map.insert("a", 1); + map.insert("b", 2); + + assert_eq!(2, Source::count(&map)); + assert_eq!( + Token::I64(1), + Source::get(&map, Key::from_str("a")).unwrap().to_token() + ); + } + } +} + +/// The result of calling `Source::as_map`. +pub struct AsMap(S); + +/// Visit this source as a map. +pub fn as_map(source: S) -> AsMap +where + S: Source, +{ + AsMap(source) +} + +impl Source for AsMap +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + self.0.visit(visitor) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + self.0.get(key) + } + + fn count(&self) -> usize { + self.0.count() + } +} + +impl fmt::Debug for AsMap +where + S: Source, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut f = f.debug_map(); + self.0.visit(&mut f).map_err(|_| fmt::Error)?; + f.finish() + } +} + +/// The result of calling `Source::as_list` +pub struct AsList(S); + +/// Visit this source as a list. +pub fn as_list(source: S) -> AsList +where + S: Source, +{ + AsList(source) +} + +impl Source for AsList +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + self.0.visit(visitor) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + self.0.get(key) + } + + fn count(&self) -> usize { + self.0.count() + } +} + +impl fmt::Debug for AsList +where + S: Source, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut f = f.debug_list(); + self.0.visit(&mut f).map_err(|_| fmt::Error)?; + f.finish() + } +} + +#[cfg(feature = "kv_unstable_sval")] +mod sval_support { + use super::*; + + use self::sval::value; + + impl value::Value for AsMap + where + S: Source, + { + fn stream(&self, stream: &mut value::Stream) -> value::Result { + struct StreamVisitor<'a, 'b>(&'a mut value::Stream<'b>); + + impl<'a, 'b, 'kvs> Visitor<'kvs> for StreamVisitor<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.0 + .map_key(key) + .map_err(|_| Error::msg("failed to stream map key"))?; + self.0 + .map_value(value) + .map_err(|_| Error::msg("failed to stream map value"))?; + Ok(()) + } + } + + stream + .map_begin(Some(self.count())) + .map_err(|_| self::sval::Error::msg("failed to begin map"))?; + + self.visit(&mut StreamVisitor(stream)) + .map_err(|_| self::sval::Error::msg("failed to visit key-values"))?; + + stream + .map_end() + .map_err(|_| self::sval::Error::msg("failed to end map")) + } + } + + impl value::Value for AsList + where + S: Source, + { + fn stream(&self, stream: &mut value::Stream) -> value::Result { + struct StreamVisitor<'a, 'b>(&'a mut value::Stream<'b>); + + impl<'a, 'b, 'kvs> Visitor<'kvs> for StreamVisitor<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.0 + .seq_elem((key, value)) + .map_err(|_| Error::msg("failed to stream seq entry"))?; + Ok(()) + } + } + + stream + .seq_begin(Some(self.count())) + .map_err(|_| self::sval::Error::msg("failed to begin seq"))?; + + self.visit(&mut StreamVisitor(stream)) + .map_err(|_| self::sval::Error::msg("failed to visit key-values"))?; + + stream + .seq_end() + .map_err(|_| self::sval::Error::msg("failed to end seq")) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + use self::sval::Value; + + use crate::kv::source; + + #[test] + fn derive_stream() { + #[derive(Value)] + pub struct MyRecordAsMap<'a> { + msg: &'a str, + kvs: source::AsMap<&'a dyn Source>, + } + + #[derive(Value)] + pub struct MyRecordAsList<'a> { + msg: &'a str, + kvs: source::AsList<&'a dyn Source>, + } + } + } +} + +#[cfg(feature = "kv_unstable_serde")] +pub mod as_map { + //! `serde` adapters for serializing a `Source` as a map. + + use super::*; + + use self::serde::{Serialize, Serializer}; + + /// Serialize a `Source` as a map. + pub fn serialize(source: &T, serializer: S) -> Result + where + T: Source, + S: Serializer, + { + as_map(source).serialize(serializer) + } +} + +#[cfg(feature = "kv_unstable_serde")] +pub mod as_list { + //! `serde` adapters for serializing a `Source` as a list. + + use super::*; + + use self::serde::{Serialize, Serializer}; + + /// Serialize a `Source` as a list. + pub fn serialize(source: &T, serializer: S) -> Result + where + T: Source, + S: Serializer, + { + as_list(source).serialize(serializer) + } +} + +#[cfg(feature = "kv_unstable_serde")] +mod serde_support { + use super::*; + + use self::serde::ser::{Error as SerError, Serialize, SerializeMap, SerializeSeq, Serializer}; + + impl Serialize for AsMap + where + T: Source, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + struct SerializerVisitor<'a, S>(&'a mut S); + + impl<'a, 'kvs, S> Visitor<'kvs> for SerializerVisitor<'a, S> + where + S: SerializeMap, + { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.0 + .serialize_entry(&key, &value) + .map_err(|_| Error::msg("failed to serialize map entry"))?; + Ok(()) + } + } + + let mut map = serializer.serialize_map(Some(self.count()))?; + + self.visit(&mut SerializerVisitor(&mut map)) + .map_err(|_| S::Error::custom("failed to visit key-values"))?; + + map.end() + } + } + + impl Serialize for AsList + where + T: Source, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + struct SerializerVisitor<'a, S>(&'a mut S); + + impl<'a, 'kvs, S> Visitor<'kvs> for SerializerVisitor<'a, S> + where + S: SerializeSeq, + { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.0 + .serialize_element(&(key, value)) + .map_err(|_| Error::msg("failed to serialize seq entry"))?; + Ok(()) + } + } + + let mut seq = serializer.serialize_seq(Some(self.count()))?; + + self.visit(&mut SerializerVisitor(&mut seq)) + .map_err(|_| S::Error::custom("failed to visit seq"))?; + + seq.end() + } + } + + #[cfg(test)] + mod tests { + use super::*; + + use self::serde::Serialize; + + use crate::kv::source; + + #[test] + fn derive_serialize() { + #[derive(Serialize)] + pub struct MyRecordAsMap<'a> { + msg: &'a str, + #[serde(flatten)] + #[serde(with = "source::as_map")] + kvs: &'a dyn Source, + } + + #[derive(Serialize)] + pub struct MyRecordAsList<'a> { + msg: &'a str, + #[serde(flatten)] + #[serde(with = "source::as_list")] + kvs: &'a dyn Source, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use kv::value::tests::Token; + + #[test] + fn source_is_object_safe() { + fn _check(_: &dyn Source) {} + } + + #[test] + fn visitor_is_object_safe() { + fn _check(_: &dyn Visitor) {} + } + + #[test] + fn count() { + struct OnePair { + key: &'static str, + value: i32, + } + + impl Source for OnePair { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + visitor.visit_pair(self.key.to_key(), self.value.to_value()) + } + + fn get<'v>(&'v self, key: Key) -> Option> { + get_default(self, key) + } + + fn count(&self) -> usize { + count_default(self) + } + } + + assert_eq!(1, Source::count(&("a", 1))); + assert_eq!(2, Source::count(&[("a", 1), ("b", 2)] as &[_])); + assert_eq!(0, Source::count(&Option::None::<(&str, i32)>)); + assert_eq!(1, Source::count(&OnePair { key: "a", value: 1 })); + } + + #[test] + fn get() { + let source = &[("a", 1), ("b", 2), ("a", 1)] as &[_]; + assert_eq!( + Token::I64(1), + Source::get(source, Key::from_str("a")).unwrap().to_token() + ); + assert_eq!( + Token::I64(2), + Source::get(source, Key::from_str("b")).unwrap().to_token() + ); + assert!(Source::get(&source, Key::from_str("c")).is_none()); + + let source = Option::None::<(&str, i32)>; + assert!(Source::get(&source, Key::from_str("a")).is_none()); + } + + #[test] + fn as_map() { + let _ = crate::kv::source::as_map(("a", 1)); + let _ = crate::kv::source::as_map(&("a", 1) as &dyn Source); + } + + #[test] + fn as_list() { + let _ = crate::kv::source::as_list(("a", 1)); + let _ = crate::kv::source::as_list(&("a", 1) as &dyn Source); + } +} diff --git a/vendor/log/src/kv/value.rs b/vendor/log/src/kv/value.rs index 607bc09..9730f47 100644 --- a/vendor/log/src/kv/value.rs +++ b/vendor/log/src/kv/value.rs @@ -1,1034 +1,1034 @@ -//! Structured values. - -use std::fmt; - -extern crate value_bag; - -#[cfg(feature = "kv_unstable_sval")] -extern crate sval; - -#[cfg(feature = "kv_unstable_serde")] -extern crate serde; - -use self::value_bag::ValueBag; - -pub use kv::Error; - -/// A type that can be converted into a [`Value`](struct.Value.html). -pub trait ToValue { - /// Perform the conversion. - fn to_value(&self) -> Value; -} - -impl<'a, T> ToValue for &'a T -where - T: ToValue + ?Sized, -{ - fn to_value(&self) -> Value { - (**self).to_value() - } -} - -impl<'v> ToValue for Value<'v> { - fn to_value(&self) -> Value { - Value { - inner: self.inner.clone(), - } - } -} - -/// Get a value from a type implementing `std::fmt::Debug`. -#[macro_export] -macro_rules! as_debug { - ($capture:expr) => { - $crate::kv::Value::from_debug(&$capture) - }; -} - -/// Get a value from a type implementing `std::fmt::Display`. -#[macro_export] -macro_rules! as_display { - ($capture:expr) => { - $crate::kv::Value::from_display(&$capture) - }; -} - -/// Get a value from an error. -#[cfg(feature = "kv_unstable_std")] -#[macro_export] -macro_rules! as_error { - ($capture:expr) => { - $crate::kv::Value::from_dyn_error(&$capture) - }; -} - -#[cfg(feature = "kv_unstable_serde")] -/// Get a value from a type implementing `serde::Serialize`. -#[macro_export] -macro_rules! as_serde { - ($capture:expr) => { - $crate::kv::Value::from_serde(&$capture) - }; -} - -/// Get a value from a type implementing `sval::value::Value`. -#[cfg(feature = "kv_unstable_sval")] -#[macro_export] -macro_rules! as_sval { - ($capture:expr) => { - $crate::kv::Value::from_sval(&$capture) - }; -} - -/// A value in a structured key-value pair. -/// -/// # Capturing values -/// -/// There are a few ways to capture a value: -/// -/// - Using the `Value::capture_*` methods. -/// - Using the `Value::from_*` methods. -/// - Using the `ToValue` trait. -/// - Using the standard `From` trait. -/// -/// ## Using the `Value::capture_*` methods -/// -/// `Value` offers a few constructor methods that capture values of different kinds. -/// These methods require a `T: 'static` to support downcasting. -/// -/// ``` -/// use log::kv::Value; -/// -/// let value = Value::capture_debug(&42i32); -/// -/// assert_eq!(Some(42), value.to_i64()); -/// ``` -/// -/// ## Using the `Value::from_*` methods -/// -/// `Value` offers a few constructor methods that capture values of different kinds. -/// These methods don't require `T: 'static`, but can't support downcasting. -/// -/// ``` -/// use log::kv::Value; -/// -/// let value = Value::from_debug(&42i32); -/// -/// assert_eq!(None, value.to_i64()); -/// ``` -/// -/// ## Using the `ToValue` trait -/// -/// The `ToValue` trait can be used to capture values generically. -/// It's the bound used by `Source`. -/// -/// ``` -/// # use log::kv::ToValue; -/// let value = 42i32.to_value(); -/// -/// assert_eq!(Some(42), value.to_i64()); -/// ``` -/// -/// ``` -/// # use std::fmt::Debug; -/// use log::kv::ToValue; -/// -/// let value = (&42i32 as &dyn Debug).to_value(); -/// -/// assert_eq!(None, value.to_i64()); -/// ``` -/// -/// ## Using the standard `From` trait -/// -/// Standard types that implement `ToValue` also implement `From`. -/// -/// ``` -/// use log::kv::Value; -/// -/// let value = Value::from(42i32); -/// -/// assert_eq!(Some(42), value.to_i64()); -/// ``` -pub struct Value<'v> { - inner: ValueBag<'v>, -} - -impl<'v> Value<'v> { - /// Get a value from a type implementing `ToValue`. - pub fn from_any(value: &'v T) -> Self - where - T: ToValue, - { - value.to_value() - } - - /// Get a value from a type implementing `std::fmt::Debug`. - pub fn capture_debug(value: &'v T) -> Self - where - T: fmt::Debug + 'static, - { - Value { - inner: ValueBag::capture_debug(value), - } - } - - /// Get a value from a type implementing `std::fmt::Display`. - pub fn capture_display(value: &'v T) -> Self - where - T: fmt::Display + 'static, - { - Value { - inner: ValueBag::capture_display(value), - } - } - - /// Get a value from an error. - #[cfg(feature = "kv_unstable_std")] - pub fn capture_error(err: &'v T) -> Self - where - T: std::error::Error + 'static, - { - Value { - inner: ValueBag::capture_error(err), - } - } - - #[cfg(feature = "kv_unstable_serde")] - /// Get a value from a type implementing `serde::Serialize`. - pub fn capture_serde(value: &'v T) -> Self - where - T: self::serde::Serialize + 'static, - { - Value { - inner: ValueBag::capture_serde1(value), - } - } - - /// Get a value from a type implementing `sval::value::Value`. - #[cfg(feature = "kv_unstable_sval")] - pub fn capture_sval(value: &'v T) -> Self - where - T: self::sval::value::Value + 'static, - { - Value { - inner: ValueBag::capture_sval1(value), - } - } - - /// Get a value from a type implementing `std::fmt::Debug`. - pub fn from_debug(value: &'v T) -> Self - where - T: fmt::Debug, - { - Value { - inner: ValueBag::from_debug(value), - } - } - - /// Get a value from a type implementing `std::fmt::Display`. - pub fn from_display(value: &'v T) -> Self - where - T: fmt::Display, - { - Value { - inner: ValueBag::from_display(value), - } - } - - /// Get a value from a type implementing `serde::Serialize`. - #[cfg(feature = "kv_unstable_serde")] - pub fn from_serde(value: &'v T) -> Self - where - T: self::serde::Serialize, - { - Value { - inner: ValueBag::from_serde1(value), - } - } - - /// Get a value from a type implementing `sval::value::Value`. - #[cfg(feature = "kv_unstable_sval")] - pub fn from_sval(value: &'v T) -> Self - where - T: self::sval::value::Value, - { - Value { - inner: ValueBag::from_sval1(value), - } - } - - /// Get a value from a dynamic `std::fmt::Debug`. - pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { - Value { - inner: ValueBag::from_dyn_debug(value), - } - } - - /// Get a value from a dynamic `std::fmt::Display`. - pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { - Value { - inner: ValueBag::from_dyn_display(value), - } - } - - /// Get a value from a dynamic error. - #[cfg(feature = "kv_unstable_std")] - pub fn from_dyn_error(err: &'v (dyn std::error::Error + 'static)) -> Self { - Value { - inner: ValueBag::from_dyn_error(err), - } - } - - /// Get a value from a type implementing `sval::value::Value`. - #[cfg(feature = "kv_unstable_sval")] - pub fn from_dyn_sval(value: &'v dyn self::sval::value::Value) -> Self { - Value { - inner: ValueBag::from_dyn_sval1(value), - } - } - - /// Get a value from an internal primitive. - fn from_value_bag(value: T) -> Self - where - T: Into>, - { - Value { - inner: value.into(), - } - } - - /// Check whether this value can be downcast to `T`. - pub fn is(&self) -> bool { - self.inner.is::() - } - - /// Try downcast this value to `T`. - pub fn downcast_ref(&self) -> Option<&T> { - self.inner.downcast_ref::() - } - - /// Inspect this value using a simple visitor. - pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> { - struct Visitor(V); - - impl<'v, V> value_bag::visit::Visit<'v> for Visitor - where - V: Visit<'v>, - { - fn visit_any(&mut self, value: ValueBag) -> Result<(), value_bag::Error> { - self.0 - .visit_any(Value { inner: value }) - .map_err(Error::into_value) - } - - fn visit_u64(&mut self, value: u64) -> Result<(), value_bag::Error> { - self.0.visit_u64(value).map_err(Error::into_value) - } - - fn visit_i64(&mut self, value: i64) -> Result<(), value_bag::Error> { - self.0.visit_i64(value).map_err(Error::into_value) - } - - fn visit_u128(&mut self, value: u128) -> Result<(), value_bag::Error> { - self.0.visit_u128(value).map_err(Error::into_value) - } - - fn visit_i128(&mut self, value: i128) -> Result<(), value_bag::Error> { - self.0.visit_i128(value).map_err(Error::into_value) - } - - fn visit_f64(&mut self, value: f64) -> Result<(), value_bag::Error> { - self.0.visit_f64(value).map_err(Error::into_value) - } - - fn visit_bool(&mut self, value: bool) -> Result<(), value_bag::Error> { - self.0.visit_bool(value).map_err(Error::into_value) - } - - fn visit_str(&mut self, value: &str) -> Result<(), value_bag::Error> { - self.0.visit_str(value).map_err(Error::into_value) - } - - fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), value_bag::Error> { - self.0.visit_borrowed_str(value).map_err(Error::into_value) - } - - fn visit_char(&mut self, value: char) -> Result<(), value_bag::Error> { - self.0.visit_char(value).map_err(Error::into_value) - } - - #[cfg(feature = "kv_unstable_std")] - fn visit_error( - &mut self, - err: &(dyn std::error::Error + 'static), - ) -> Result<(), value_bag::Error> { - self.0.visit_error(err).map_err(Error::into_value) - } - - #[cfg(feature = "kv_unstable_std")] - fn visit_borrowed_error( - &mut self, - err: &'v (dyn std::error::Error + 'static), - ) -> Result<(), value_bag::Error> { - self.0.visit_borrowed_error(err).map_err(Error::into_value) - } - } - - self.inner - .visit(&mut Visitor(visitor)) - .map_err(Error::from_value) - } -} - -impl<'v> fmt::Debug for Value<'v> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.inner, f) - } -} - -impl<'v> fmt::Display for Value<'v> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.inner, f) - } -} - -impl ToValue for dyn fmt::Debug { - fn to_value(&self) -> Value { - Value::from_dyn_debug(self) - } -} - -impl ToValue for dyn fmt::Display { - fn to_value(&self) -> Value { - Value::from_dyn_display(self) - } -} - -#[cfg(feature = "kv_unstable_std")] -impl ToValue for dyn std::error::Error + 'static { - fn to_value(&self) -> Value { - Value::from_dyn_error(self) - } -} - -#[cfg(feature = "kv_unstable_serde")] -impl<'v> self::serde::Serialize for Value<'v> { - fn serialize(&self, s: S) -> Result - where - S: self::serde::Serializer, - { - self.inner.serialize(s) - } -} - -#[cfg(feature = "kv_unstable_sval")] -impl<'v> self::sval::value::Value for Value<'v> { - fn stream(&self, stream: &mut self::sval::value::Stream) -> self::sval::value::Result { - self::sval::value::Value::stream(&self.inner, stream) - } -} - -#[cfg(feature = "kv_unstable_sval")] -impl ToValue for dyn self::sval::value::Value { - fn to_value(&self) -> Value { - Value::from_dyn_sval(self) - } -} - -impl ToValue for str { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl ToValue for u128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl ToValue for i128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl ToValue for std::num::NonZeroU128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl ToValue for std::num::NonZeroI128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl<'v> From<&'v str> for Value<'v> { - fn from(value: &'v str) -> Self { - Value::from_value_bag(value) - } -} - -impl<'v> From<&'v u128> for Value<'v> { - fn from(value: &'v u128) -> Self { - Value::from_value_bag(value) - } -} - -impl<'v> From<&'v i128> for Value<'v> { - fn from(value: &'v i128) -> Self { - Value::from_value_bag(value) - } -} - -impl<'v> From<&'v std::num::NonZeroU128> for Value<'v> { - fn from(v: &'v std::num::NonZeroU128) -> Value<'v> { - // SAFETY: `NonZeroU128` and `u128` have the same ABI - Value::from_value_bag(unsafe { std::mem::transmute::<&std::num::NonZeroU128, &u128>(v) }) - } -} - -impl<'v> From<&'v std::num::NonZeroI128> for Value<'v> { - fn from(v: &'v std::num::NonZeroI128) -> Value<'v> { - // SAFETY: `NonZeroI128` and `i128` have the same ABI - Value::from_value_bag(unsafe { std::mem::transmute::<&std::num::NonZeroI128, &i128>(v) }) - } -} - -impl ToValue for () { - fn to_value(&self) -> Value { - Value::from_value_bag(()) - } -} - -impl ToValue for Option -where - T: ToValue, -{ - fn to_value(&self) -> Value { - match *self { - Some(ref value) => value.to_value(), - None => Value::from_value_bag(()), - } - } -} - -macro_rules! impl_to_value_primitive { - ($($into_ty:ty,)*) => { - $( - impl ToValue for $into_ty { - fn to_value(&self) -> Value { - Value::from(*self) - } - } - - impl<'v> From<$into_ty> for Value<'v> { - fn from(value: $into_ty) -> Self { - Value::from_value_bag(value) - } - } - )* - }; -} - -macro_rules! impl_to_value_nonzero_primitive { - ($($into_ty:ident,)*) => { - $( - impl ToValue for std::num::$into_ty { - fn to_value(&self) -> Value { - Value::from(self.get()) - } - } - - impl<'v> From for Value<'v> { - fn from(value: std::num::$into_ty) -> Self { - Value::from(value.get()) - } - } - )* - }; -} - -macro_rules! impl_value_to_primitive { - ($(#[doc = $doc:tt] $into_name:ident -> $into_ty:ty,)*) => { - impl<'v> Value<'v> { - $( - #[doc = $doc] - pub fn $into_name(&self) -> Option<$into_ty> { - self.inner.$into_name() - } - )* - } - } -} - -impl_to_value_primitive![usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32, f64, char, bool,]; - -#[rustfmt::skip] -impl_to_value_nonzero_primitive![ - NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, - NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, -]; - -impl_value_to_primitive![ - #[doc = "Try convert this value into a `u64`."] - to_u64 -> u64, - #[doc = "Try convert this value into a `i64`."] - to_i64 -> i64, - #[doc = "Try convert this value into a `u128`."] - to_u128 -> u128, - #[doc = "Try convert this value into a `i128`."] - to_i128 -> i128, - #[doc = "Try convert this value into a `f64`."] - to_f64 -> f64, - #[doc = "Try convert this value into a `char`."] - to_char -> char, - #[doc = "Try convert this value into a `bool`."] - to_bool -> bool, -]; - -impl<'v> Value<'v> { - /// Try convert this value into an error. - #[cfg(feature = "kv_unstable_std")] - pub fn to_borrowed_error(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.inner.to_borrowed_error() - } - - /// Try convert this value into a borrowed string. - pub fn to_borrowed_str(&self) -> Option<&str> { - self.inner.to_borrowed_str() - } -} - -#[cfg(feature = "kv_unstable_std")] -mod std_support { - use super::*; - - use std::borrow::Cow; - - impl ToValue for Box - where - T: ToValue + ?Sized, - { - fn to_value(&self) -> Value { - (**self).to_value() - } - } - - impl ToValue for String { - fn to_value(&self) -> Value { - Value::from(&**self) - } - } - - impl<'v> ToValue for Cow<'v, str> { - fn to_value(&self) -> Value { - Value::from(&**self) - } - } - - impl<'v> Value<'v> { - /// Try convert this value into a string. - pub fn to_str(&self) -> Option> { - self.inner.to_str() - } - } - - impl<'v> From<&'v String> for Value<'v> { - fn from(v: &'v String) -> Self { - Value::from(&**v) - } - } -} - -/// A visitor for a `Value`. -pub trait Visit<'v> { - /// Visit a `Value`. - /// - /// This is the only required method on `Visit` and acts as a fallback for any - /// more specific methods that aren't overridden. - /// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation, - /// or serialized using its `sval::Value` or `serde::Serialize` implementation. - fn visit_any(&mut self, value: Value) -> Result<(), Error>; - - /// Visit an unsigned integer. - fn visit_u64(&mut self, value: u64) -> Result<(), Error> { - self.visit_any(value.into()) - } - - /// Visit a signed integer. - fn visit_i64(&mut self, value: i64) -> Result<(), Error> { - self.visit_any(value.into()) - } - - /// Visit a big unsigned integer. - fn visit_u128(&mut self, value: u128) -> Result<(), Error> { - self.visit_any((&value).into()) - } - - /// Visit a big signed integer. - fn visit_i128(&mut self, value: i128) -> Result<(), Error> { - self.visit_any((&value).into()) - } - - /// Visit a floating point. - fn visit_f64(&mut self, value: f64) -> Result<(), Error> { - self.visit_any(value.into()) - } - - /// Visit a boolean. - fn visit_bool(&mut self, value: bool) -> Result<(), Error> { - self.visit_any(value.into()) - } - - /// Visit a string. - fn visit_str(&mut self, value: &str) -> Result<(), Error> { - self.visit_any(value.into()) - } - - /// Visit a string. - fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { - self.visit_str(value) - } - - /// Visit a Unicode character. - fn visit_char(&mut self, value: char) -> Result<(), Error> { - let mut b = [0; 4]; - self.visit_str(&*value.encode_utf8(&mut b)) - } - - /// Visit an error. - #[cfg(feature = "kv_unstable_std")] - fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { - self.visit_any(Value::from_dyn_error(err)) - } - - /// Visit an error. - #[cfg(feature = "kv_unstable_std")] - fn visit_borrowed_error( - &mut self, - err: &'v (dyn std::error::Error + 'static), - ) -> Result<(), Error> { - self.visit_any(Value::from_dyn_error(err)) - } -} - -impl<'a, 'v, T: ?Sized> Visit<'v> for &'a mut T -where - T: Visit<'v>, -{ - fn visit_any(&mut self, value: Value) -> Result<(), Error> { - (**self).visit_any(value) - } - - fn visit_u64(&mut self, value: u64) -> Result<(), Error> { - (**self).visit_u64(value) - } - - fn visit_i64(&mut self, value: i64) -> Result<(), Error> { - (**self).visit_i64(value) - } - - fn visit_u128(&mut self, value: u128) -> Result<(), Error> { - (**self).visit_u128(value) - } - - fn visit_i128(&mut self, value: i128) -> Result<(), Error> { - (**self).visit_i128(value) - } - - fn visit_f64(&mut self, value: f64) -> Result<(), Error> { - (**self).visit_f64(value) - } - - fn visit_bool(&mut self, value: bool) -> Result<(), Error> { - (**self).visit_bool(value) - } - - fn visit_str(&mut self, value: &str) -> Result<(), Error> { - (**self).visit_str(value) - } - - fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { - (**self).visit_borrowed_str(value) - } - - fn visit_char(&mut self, value: char) -> Result<(), Error> { - (**self).visit_char(value) - } - - #[cfg(feature = "kv_unstable_std")] - fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { - (**self).visit_error(err) - } - - #[cfg(feature = "kv_unstable_std")] - fn visit_borrowed_error( - &mut self, - err: &'v (dyn std::error::Error + 'static), - ) -> Result<(), Error> { - (**self).visit_borrowed_error(err) - } -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - - pub(crate) use super::value_bag::test::Token; - - impl<'v> Value<'v> { - pub(crate) fn to_token(&self) -> Token { - self.inner.to_token() - } - } - - fn unsigned() -> impl Iterator> { - vec![ - Value::from(8u8), - Value::from(16u16), - Value::from(32u32), - Value::from(64u64), - Value::from(1usize), - Value::from(std::num::NonZeroU8::new(8).unwrap()), - Value::from(std::num::NonZeroU16::new(16).unwrap()), - Value::from(std::num::NonZeroU32::new(32).unwrap()), - Value::from(std::num::NonZeroU64::new(64).unwrap()), - Value::from(std::num::NonZeroUsize::new(1).unwrap()), - ] - .into_iter() - } - - fn signed() -> impl Iterator> { - vec![ - Value::from(-8i8), - Value::from(-16i16), - Value::from(-32i32), - Value::from(-64i64), - Value::from(-1isize), - Value::from(std::num::NonZeroI8::new(-8).unwrap()), - Value::from(std::num::NonZeroI16::new(-16).unwrap()), - Value::from(std::num::NonZeroI32::new(-32).unwrap()), - Value::from(std::num::NonZeroI64::new(-64).unwrap()), - Value::from(std::num::NonZeroIsize::new(-1).unwrap()), - ] - .into_iter() - } - - fn float() -> impl Iterator> { - vec![Value::from(32.32f32), Value::from(64.64f64)].into_iter() - } - - fn bool() -> impl Iterator> { - vec![Value::from(true), Value::from(false)].into_iter() - } - - fn str() -> impl Iterator> { - vec![Value::from("a string"), Value::from("a loong string")].into_iter() - } - - fn char() -> impl Iterator> { - vec![Value::from('a'), Value::from('⛰')].into_iter() - } - - #[test] - fn test_capture_fmt() { - assert_eq!(Some(42u64), Value::capture_display(&42).to_u64()); - assert_eq!(Some(42u64), Value::capture_debug(&42).to_u64()); - - assert!(Value::from_display(&42).to_u64().is_none()); - assert!(Value::from_debug(&42).to_u64().is_none()); - } - - #[cfg(feature = "kv_unstable_std")] - #[test] - fn test_capture_error() { - let err = std::io::Error::from(std::io::ErrorKind::Other); - - assert!(Value::capture_error(&err).to_borrowed_error().is_some()); - assert!(Value::from_dyn_error(&err).to_borrowed_error().is_some()); - } - - #[cfg(feature = "kv_unstable_serde")] - #[test] - fn test_capture_serde() { - assert_eq!(Some(42u64), Value::capture_serde(&42).to_u64()); - - assert_eq!(Some(42u64), Value::from_serde(&42).to_u64()); - } - - #[cfg(feature = "kv_unstable_sval")] - #[test] - fn test_capture_sval() { - assert_eq!(Some(42u64), Value::capture_sval(&42).to_u64()); - - assert_eq!(Some(42u64), Value::from_sval(&42).to_u64()); - } - - #[test] - fn test_to_value_display() { - assert_eq!(42u64.to_value().to_string(), "42"); - assert_eq!(42i64.to_value().to_string(), "42"); - assert_eq!(42.01f64.to_value().to_string(), "42.01"); - assert_eq!(true.to_value().to_string(), "true"); - assert_eq!('a'.to_value().to_string(), "a"); - assert_eq!("a loong string".to_value().to_string(), "a loong string"); - assert_eq!(Some(true).to_value().to_string(), "true"); - assert_eq!(().to_value().to_string(), "None"); - assert_eq!(Option::None::.to_value().to_string(), "None"); - } - - #[test] - fn test_to_value_structured() { - assert_eq!(42u64.to_value().to_token(), Token::U64(42)); - assert_eq!(42i64.to_value().to_token(), Token::I64(42)); - assert_eq!(42.01f64.to_value().to_token(), Token::F64(42.01)); - assert_eq!(true.to_value().to_token(), Token::Bool(true)); - assert_eq!('a'.to_value().to_token(), Token::Char('a')); - assert_eq!( - "a loong string".to_value().to_token(), - Token::Str("a loong string".into()) - ); - assert_eq!(Some(true).to_value().to_token(), Token::Bool(true)); - assert_eq!(().to_value().to_token(), Token::None); - assert_eq!(Option::None::.to_value().to_token(), Token::None); - } - - #[test] - fn test_to_number() { - for v in unsigned() { - assert!(v.to_u64().is_some()); - assert!(v.to_i64().is_some()); - } - - for v in signed() { - assert!(v.to_i64().is_some()); - } - - for v in unsigned().chain(signed()).chain(float()) { - assert!(v.to_f64().is_some()); - } - - for v in bool().chain(str()).chain(char()) { - assert!(v.to_u64().is_none()); - assert!(v.to_i64().is_none()); - assert!(v.to_f64().is_none()); - } - } - - #[test] - fn test_to_str() { - for v in str() { - assert!(v.to_borrowed_str().is_some()); - - #[cfg(feature = "kv_unstable_std")] - assert!(v.to_str().is_some()); - } - - let short_lived = String::from("short lived"); - let v = Value::from(&*short_lived); - - assert!(v.to_borrowed_str().is_some()); - - #[cfg(feature = "kv_unstable_std")] - assert!(v.to_str().is_some()); - - for v in unsigned().chain(signed()).chain(float()).chain(bool()) { - assert!(v.to_borrowed_str().is_none()); - - #[cfg(feature = "kv_unstable_std")] - assert!(v.to_str().is_none()); - } - } - - #[test] - fn test_to_bool() { - for v in bool() { - assert!(v.to_bool().is_some()); - } - - for v in unsigned() - .chain(signed()) - .chain(float()) - .chain(str()) - .chain(char()) - { - assert!(v.to_bool().is_none()); - } - } - - #[test] - fn test_to_char() { - for v in char() { - assert!(v.to_char().is_some()); - } - - for v in unsigned() - .chain(signed()) - .chain(float()) - .chain(str()) - .chain(bool()) - { - assert!(v.to_char().is_none()); - } - } - - #[test] - fn test_downcast_ref() { - #[derive(Debug)] - struct Foo(u64); - - let v = Value::capture_debug(&Foo(42)); - - assert!(v.is::()); - assert_eq!(42u64, v.downcast_ref::().expect("invalid downcast").0); - } - - #[test] - fn test_visit_integer() { - struct Extract(Option); - - impl<'v> Visit<'v> for Extract { - fn visit_any(&mut self, value: Value) -> Result<(), Error> { - unimplemented!("unexpected value: {:?}", value) - } - - fn visit_u64(&mut self, value: u64) -> Result<(), Error> { - self.0 = Some(value); - - Ok(()) - } - } - - let mut extract = Extract(None); - Value::from(42u64).visit(&mut extract).unwrap(); - - assert_eq!(Some(42), extract.0); - } - - #[test] - fn test_visit_borrowed_str() { - struct Extract<'v>(Option<&'v str>); - - impl<'v> Visit<'v> for Extract<'v> { - fn visit_any(&mut self, value: Value) -> Result<(), Error> { - unimplemented!("unexpected value: {:?}", value) - } - - fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { - self.0 = Some(value); - - Ok(()) - } - } - - let mut extract = Extract(None); - - let short_lived = String::from("A short-lived string"); - Value::from(&*short_lived).visit(&mut extract).unwrap(); - - assert_eq!(Some("A short-lived string"), extract.0); - } -} +//! Structured values. + +use std::fmt; + +extern crate value_bag; + +#[cfg(feature = "kv_unstable_sval")] +extern crate sval; + +#[cfg(feature = "kv_unstable_serde")] +extern crate serde; + +use self::value_bag::ValueBag; + +pub use kv::Error; + +/// A type that can be converted into a [`Value`](struct.Value.html). +pub trait ToValue { + /// Perform the conversion. + fn to_value(&self) -> Value; +} + +impl<'a, T> ToValue for &'a T +where + T: ToValue + ?Sized, +{ + fn to_value(&self) -> Value { + (**self).to_value() + } +} + +impl<'v> ToValue for Value<'v> { + fn to_value(&self) -> Value { + Value { + inner: self.inner.clone(), + } + } +} + +/// Get a value from a type implementing `std::fmt::Debug`. +#[macro_export] +macro_rules! as_debug { + ($capture:expr) => { + $crate::kv::Value::from_debug(&$capture) + }; +} + +/// Get a value from a type implementing `std::fmt::Display`. +#[macro_export] +macro_rules! as_display { + ($capture:expr) => { + $crate::kv::Value::from_display(&$capture) + }; +} + +/// Get a value from an error. +#[cfg(feature = "kv_unstable_std")] +#[macro_export] +macro_rules! as_error { + ($capture:expr) => { + $crate::kv::Value::from_dyn_error(&$capture) + }; +} + +#[cfg(feature = "kv_unstable_serde")] +/// Get a value from a type implementing `serde::Serialize`. +#[macro_export] +macro_rules! as_serde { + ($capture:expr) => { + $crate::kv::Value::from_serde(&$capture) + }; +} + +/// Get a value from a type implementing `sval::value::Value`. +#[cfg(feature = "kv_unstable_sval")] +#[macro_export] +macro_rules! as_sval { + ($capture:expr) => { + $crate::kv::Value::from_sval(&$capture) + }; +} + +/// A value in a structured key-value pair. +/// +/// # Capturing values +/// +/// There are a few ways to capture a value: +/// +/// - Using the `Value::capture_*` methods. +/// - Using the `Value::from_*` methods. +/// - Using the `ToValue` trait. +/// - Using the standard `From` trait. +/// +/// ## Using the `Value::capture_*` methods +/// +/// `Value` offers a few constructor methods that capture values of different kinds. +/// These methods require a `T: 'static` to support downcasting. +/// +/// ``` +/// use log::kv::Value; +/// +/// let value = Value::capture_debug(&42i32); +/// +/// assert_eq!(Some(42), value.to_i64()); +/// ``` +/// +/// ## Using the `Value::from_*` methods +/// +/// `Value` offers a few constructor methods that capture values of different kinds. +/// These methods don't require `T: 'static`, but can't support downcasting. +/// +/// ``` +/// use log::kv::Value; +/// +/// let value = Value::from_debug(&42i32); +/// +/// assert_eq!(None, value.to_i64()); +/// ``` +/// +/// ## Using the `ToValue` trait +/// +/// The `ToValue` trait can be used to capture values generically. +/// It's the bound used by `Source`. +/// +/// ``` +/// # use log::kv::ToValue; +/// let value = 42i32.to_value(); +/// +/// assert_eq!(Some(42), value.to_i64()); +/// ``` +/// +/// ``` +/// # use std::fmt::Debug; +/// use log::kv::ToValue; +/// +/// let value = (&42i32 as &dyn Debug).to_value(); +/// +/// assert_eq!(None, value.to_i64()); +/// ``` +/// +/// ## Using the standard `From` trait +/// +/// Standard types that implement `ToValue` also implement `From`. +/// +/// ``` +/// use log::kv::Value; +/// +/// let value = Value::from(42i32); +/// +/// assert_eq!(Some(42), value.to_i64()); +/// ``` +pub struct Value<'v> { + inner: ValueBag<'v>, +} + +impl<'v> Value<'v> { + /// Get a value from a type implementing `ToValue`. + pub fn from_any(value: &'v T) -> Self + where + T: ToValue, + { + value.to_value() + } + + /// Get a value from a type implementing `std::fmt::Debug`. + pub fn capture_debug(value: &'v T) -> Self + where + T: fmt::Debug + 'static, + { + Value { + inner: ValueBag::capture_debug(value), + } + } + + /// Get a value from a type implementing `std::fmt::Display`. + pub fn capture_display(value: &'v T) -> Self + where + T: fmt::Display + 'static, + { + Value { + inner: ValueBag::capture_display(value), + } + } + + /// Get a value from an error. + #[cfg(feature = "kv_unstable_std")] + pub fn capture_error(err: &'v T) -> Self + where + T: std::error::Error + 'static, + { + Value { + inner: ValueBag::capture_error(err), + } + } + + #[cfg(feature = "kv_unstable_serde")] + /// Get a value from a type implementing `serde::Serialize`. + pub fn capture_serde(value: &'v T) -> Self + where + T: self::serde::Serialize + 'static, + { + Value { + inner: ValueBag::capture_serde1(value), + } + } + + /// Get a value from a type implementing `sval::value::Value`. + #[cfg(feature = "kv_unstable_sval")] + pub fn capture_sval(value: &'v T) -> Self + where + T: self::sval::value::Value + 'static, + { + Value { + inner: ValueBag::capture_sval1(value), + } + } + + /// Get a value from a type implementing `std::fmt::Debug`. + pub fn from_debug(value: &'v T) -> Self + where + T: fmt::Debug, + { + Value { + inner: ValueBag::from_debug(value), + } + } + + /// Get a value from a type implementing `std::fmt::Display`. + pub fn from_display(value: &'v T) -> Self + where + T: fmt::Display, + { + Value { + inner: ValueBag::from_display(value), + } + } + + /// Get a value from a type implementing `serde::Serialize`. + #[cfg(feature = "kv_unstable_serde")] + pub fn from_serde(value: &'v T) -> Self + where + T: self::serde::Serialize, + { + Value { + inner: ValueBag::from_serde1(value), + } + } + + /// Get a value from a type implementing `sval::value::Value`. + #[cfg(feature = "kv_unstable_sval")] + pub fn from_sval(value: &'v T) -> Self + where + T: self::sval::value::Value, + { + Value { + inner: ValueBag::from_sval1(value), + } + } + + /// Get a value from a dynamic `std::fmt::Debug`. + pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { + Value { + inner: ValueBag::from_dyn_debug(value), + } + } + + /// Get a value from a dynamic `std::fmt::Display`. + pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { + Value { + inner: ValueBag::from_dyn_display(value), + } + } + + /// Get a value from a dynamic error. + #[cfg(feature = "kv_unstable_std")] + pub fn from_dyn_error(err: &'v (dyn std::error::Error + 'static)) -> Self { + Value { + inner: ValueBag::from_dyn_error(err), + } + } + + /// Get a value from a type implementing `sval::value::Value`. + #[cfg(feature = "kv_unstable_sval")] + pub fn from_dyn_sval(value: &'v dyn self::sval::value::Value) -> Self { + Value { + inner: ValueBag::from_dyn_sval1(value), + } + } + + /// Get a value from an internal primitive. + fn from_value_bag(value: T) -> Self + where + T: Into>, + { + Value { + inner: value.into(), + } + } + + /// Check whether this value can be downcast to `T`. + pub fn is(&self) -> bool { + self.inner.is::() + } + + /// Try downcast this value to `T`. + pub fn downcast_ref(&self) -> Option<&T> { + self.inner.downcast_ref::() + } + + /// Inspect this value using a simple visitor. + pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> { + struct Visitor(V); + + impl<'v, V> value_bag::visit::Visit<'v> for Visitor + where + V: Visit<'v>, + { + fn visit_any(&mut self, value: ValueBag) -> Result<(), value_bag::Error> { + self.0 + .visit_any(Value { inner: value }) + .map_err(Error::into_value) + } + + fn visit_u64(&mut self, value: u64) -> Result<(), value_bag::Error> { + self.0.visit_u64(value).map_err(Error::into_value) + } + + fn visit_i64(&mut self, value: i64) -> Result<(), value_bag::Error> { + self.0.visit_i64(value).map_err(Error::into_value) + } + + fn visit_u128(&mut self, value: u128) -> Result<(), value_bag::Error> { + self.0.visit_u128(value).map_err(Error::into_value) + } + + fn visit_i128(&mut self, value: i128) -> Result<(), value_bag::Error> { + self.0.visit_i128(value).map_err(Error::into_value) + } + + fn visit_f64(&mut self, value: f64) -> Result<(), value_bag::Error> { + self.0.visit_f64(value).map_err(Error::into_value) + } + + fn visit_bool(&mut self, value: bool) -> Result<(), value_bag::Error> { + self.0.visit_bool(value).map_err(Error::into_value) + } + + fn visit_str(&mut self, value: &str) -> Result<(), value_bag::Error> { + self.0.visit_str(value).map_err(Error::into_value) + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), value_bag::Error> { + self.0.visit_borrowed_str(value).map_err(Error::into_value) + } + + fn visit_char(&mut self, value: char) -> Result<(), value_bag::Error> { + self.0.visit_char(value).map_err(Error::into_value) + } + + #[cfg(feature = "kv_unstable_std")] + fn visit_error( + &mut self, + err: &(dyn std::error::Error + 'static), + ) -> Result<(), value_bag::Error> { + self.0.visit_error(err).map_err(Error::into_value) + } + + #[cfg(feature = "kv_unstable_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), value_bag::Error> { + self.0.visit_borrowed_error(err).map_err(Error::into_value) + } + } + + self.inner + .visit(&mut Visitor(visitor)) + .map_err(Error::from_value) + } +} + +impl<'v> fmt::Debug for Value<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} + +impl<'v> fmt::Display for Value<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +impl ToValue for dyn fmt::Debug { + fn to_value(&self) -> Value { + Value::from_dyn_debug(self) + } +} + +impl ToValue for dyn fmt::Display { + fn to_value(&self) -> Value { + Value::from_dyn_display(self) + } +} + +#[cfg(feature = "kv_unstable_std")] +impl ToValue for dyn std::error::Error + 'static { + fn to_value(&self) -> Value { + Value::from_dyn_error(self) + } +} + +#[cfg(feature = "kv_unstable_serde")] +impl<'v> self::serde::Serialize for Value<'v> { + fn serialize(&self, s: S) -> Result + where + S: self::serde::Serializer, + { + self.inner.serialize(s) + } +} + +#[cfg(feature = "kv_unstable_sval")] +impl<'v> self::sval::value::Value for Value<'v> { + fn stream(&self, stream: &mut self::sval::value::Stream) -> self::sval::value::Result { + self::sval::value::Value::stream(&self.inner, stream) + } +} + +#[cfg(feature = "kv_unstable_sval")] +impl ToValue for dyn self::sval::value::Value { + fn to_value(&self) -> Value { + Value::from_dyn_sval(self) + } +} + +impl ToValue for str { + fn to_value(&self) -> Value { + Value::from(self) + } +} + +impl ToValue for u128 { + fn to_value(&self) -> Value { + Value::from(self) + } +} + +impl ToValue for i128 { + fn to_value(&self) -> Value { + Value::from(self) + } +} + +impl ToValue for std::num::NonZeroU128 { + fn to_value(&self) -> Value { + Value::from(self) + } +} + +impl ToValue for std::num::NonZeroI128 { + fn to_value(&self) -> Value { + Value::from(self) + } +} + +impl<'v> From<&'v str> for Value<'v> { + fn from(value: &'v str) -> Self { + Value::from_value_bag(value) + } +} + +impl<'v> From<&'v u128> for Value<'v> { + fn from(value: &'v u128) -> Self { + Value::from_value_bag(value) + } +} + +impl<'v> From<&'v i128> for Value<'v> { + fn from(value: &'v i128) -> Self { + Value::from_value_bag(value) + } +} + +impl<'v> From<&'v std::num::NonZeroU128> for Value<'v> { + fn from(v: &'v std::num::NonZeroU128) -> Value<'v> { + // SAFETY: `NonZeroU128` and `u128` have the same ABI + Value::from_value_bag(unsafe { std::mem::transmute::<&std::num::NonZeroU128, &u128>(v) }) + } +} + +impl<'v> From<&'v std::num::NonZeroI128> for Value<'v> { + fn from(v: &'v std::num::NonZeroI128) -> Value<'v> { + // SAFETY: `NonZeroI128` and `i128` have the same ABI + Value::from_value_bag(unsafe { std::mem::transmute::<&std::num::NonZeroI128, &i128>(v) }) + } +} + +impl ToValue for () { + fn to_value(&self) -> Value { + Value::from_value_bag(()) + } +} + +impl ToValue for Option +where + T: ToValue, +{ + fn to_value(&self) -> Value { + match *self { + Some(ref value) => value.to_value(), + None => Value::from_value_bag(()), + } + } +} + +macro_rules! impl_to_value_primitive { + ($($into_ty:ty,)*) => { + $( + impl ToValue for $into_ty { + fn to_value(&self) -> Value { + Value::from(*self) + } + } + + impl<'v> From<$into_ty> for Value<'v> { + fn from(value: $into_ty) -> Self { + Value::from_value_bag(value) + } + } + )* + }; +} + +macro_rules! impl_to_value_nonzero_primitive { + ($($into_ty:ident,)*) => { + $( + impl ToValue for std::num::$into_ty { + fn to_value(&self) -> Value { + Value::from(self.get()) + } + } + + impl<'v> From for Value<'v> { + fn from(value: std::num::$into_ty) -> Self { + Value::from(value.get()) + } + } + )* + }; +} + +macro_rules! impl_value_to_primitive { + ($(#[doc = $doc:tt] $into_name:ident -> $into_ty:ty,)*) => { + impl<'v> Value<'v> { + $( + #[doc = $doc] + pub fn $into_name(&self) -> Option<$into_ty> { + self.inner.$into_name() + } + )* + } + } +} + +impl_to_value_primitive![usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32, f64, char, bool,]; + +#[rustfmt::skip] +impl_to_value_nonzero_primitive![ + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, +]; + +impl_value_to_primitive![ + #[doc = "Try convert this value into a `u64`."] + to_u64 -> u64, + #[doc = "Try convert this value into a `i64`."] + to_i64 -> i64, + #[doc = "Try convert this value into a `u128`."] + to_u128 -> u128, + #[doc = "Try convert this value into a `i128`."] + to_i128 -> i128, + #[doc = "Try convert this value into a `f64`."] + to_f64 -> f64, + #[doc = "Try convert this value into a `char`."] + to_char -> char, + #[doc = "Try convert this value into a `bool`."] + to_bool -> bool, +]; + +impl<'v> Value<'v> { + /// Try convert this value into an error. + #[cfg(feature = "kv_unstable_std")] + pub fn to_borrowed_error(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.to_borrowed_error() + } + + /// Try convert this value into a borrowed string. + pub fn to_borrowed_str(&self) -> Option<&str> { + self.inner.to_borrowed_str() + } +} + +#[cfg(feature = "kv_unstable_std")] +mod std_support { + use super::*; + + use std::borrow::Cow; + + impl ToValue for Box + where + T: ToValue + ?Sized, + { + fn to_value(&self) -> Value { + (**self).to_value() + } + } + + impl ToValue for String { + fn to_value(&self) -> Value { + Value::from(&**self) + } + } + + impl<'v> ToValue for Cow<'v, str> { + fn to_value(&self) -> Value { + Value::from(&**self) + } + } + + impl<'v> Value<'v> { + /// Try convert this value into a string. + pub fn to_str(&self) -> Option> { + self.inner.to_str() + } + } + + impl<'v> From<&'v String> for Value<'v> { + fn from(v: &'v String) -> Self { + Value::from(&**v) + } + } +} + +/// A visitor for a `Value`. +pub trait Visit<'v> { + /// Visit a `Value`. + /// + /// This is the only required method on `Visit` and acts as a fallback for any + /// more specific methods that aren't overridden. + /// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation, + /// or serialized using its `sval::Value` or `serde::Serialize` implementation. + fn visit_any(&mut self, value: Value) -> Result<(), Error>; + + /// Visit an unsigned integer. + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a signed integer. + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a big unsigned integer. + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + self.visit_any((&value).into()) + } + + /// Visit a big signed integer. + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + self.visit_any((&value).into()) + } + + /// Visit a floating point. + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a boolean. + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a string. + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a string. + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.visit_str(value) + } + + /// Visit a Unicode character. + fn visit_char(&mut self, value: char) -> Result<(), Error> { + let mut b = [0; 4]; + self.visit_str(&*value.encode_utf8(&mut b)) + } + + /// Visit an error. + #[cfg(feature = "kv_unstable_std")] + fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { + self.visit_any(Value::from_dyn_error(err)) + } + + /// Visit an error. + #[cfg(feature = "kv_unstable_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + self.visit_any(Value::from_dyn_error(err)) + } +} + +impl<'a, 'v, T: ?Sized> Visit<'v> for &'a mut T +where + T: Visit<'v>, +{ + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + (**self).visit_any(value) + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + (**self).visit_u64(value) + } + + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + (**self).visit_i64(value) + } + + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + (**self).visit_u128(value) + } + + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + (**self).visit_i128(value) + } + + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + (**self).visit_f64(value) + } + + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + (**self).visit_bool(value) + } + + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + (**self).visit_str(value) + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + (**self).visit_borrowed_str(value) + } + + fn visit_char(&mut self, value: char) -> Result<(), Error> { + (**self).visit_char(value) + } + + #[cfg(feature = "kv_unstable_std")] + fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { + (**self).visit_error(err) + } + + #[cfg(feature = "kv_unstable_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + (**self).visit_borrowed_error(err) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + pub(crate) use super::value_bag::test::Token; + + impl<'v> Value<'v> { + pub(crate) fn to_token(&self) -> Token { + self.inner.to_token() + } + } + + fn unsigned() -> impl Iterator> { + vec![ + Value::from(8u8), + Value::from(16u16), + Value::from(32u32), + Value::from(64u64), + Value::from(1usize), + Value::from(std::num::NonZeroU8::new(8).unwrap()), + Value::from(std::num::NonZeroU16::new(16).unwrap()), + Value::from(std::num::NonZeroU32::new(32).unwrap()), + Value::from(std::num::NonZeroU64::new(64).unwrap()), + Value::from(std::num::NonZeroUsize::new(1).unwrap()), + ] + .into_iter() + } + + fn signed() -> impl Iterator> { + vec![ + Value::from(-8i8), + Value::from(-16i16), + Value::from(-32i32), + Value::from(-64i64), + Value::from(-1isize), + Value::from(std::num::NonZeroI8::new(-8).unwrap()), + Value::from(std::num::NonZeroI16::new(-16).unwrap()), + Value::from(std::num::NonZeroI32::new(-32).unwrap()), + Value::from(std::num::NonZeroI64::new(-64).unwrap()), + Value::from(std::num::NonZeroIsize::new(-1).unwrap()), + ] + .into_iter() + } + + fn float() -> impl Iterator> { + vec![Value::from(32.32f32), Value::from(64.64f64)].into_iter() + } + + fn bool() -> impl Iterator> { + vec![Value::from(true), Value::from(false)].into_iter() + } + + fn str() -> impl Iterator> { + vec![Value::from("a string"), Value::from("a loong string")].into_iter() + } + + fn char() -> impl Iterator> { + vec![Value::from('a'), Value::from('⛰')].into_iter() + } + + #[test] + fn test_capture_fmt() { + assert_eq!(Some(42u64), Value::capture_display(&42).to_u64()); + assert_eq!(Some(42u64), Value::capture_debug(&42).to_u64()); + + assert!(Value::from_display(&42).to_u64().is_none()); + assert!(Value::from_debug(&42).to_u64().is_none()); + } + + #[cfg(feature = "kv_unstable_std")] + #[test] + fn test_capture_error() { + let err = std::io::Error::from(std::io::ErrorKind::Other); + + assert!(Value::capture_error(&err).to_borrowed_error().is_some()); + assert!(Value::from_dyn_error(&err).to_borrowed_error().is_some()); + } + + #[cfg(feature = "kv_unstable_serde")] + #[test] + fn test_capture_serde() { + assert_eq!(Some(42u64), Value::capture_serde(&42).to_u64()); + + assert_eq!(Some(42u64), Value::from_serde(&42).to_u64()); + } + + #[cfg(feature = "kv_unstable_sval")] + #[test] + fn test_capture_sval() { + assert_eq!(Some(42u64), Value::capture_sval(&42).to_u64()); + + assert_eq!(Some(42u64), Value::from_sval(&42).to_u64()); + } + + #[test] + fn test_to_value_display() { + assert_eq!(42u64.to_value().to_string(), "42"); + assert_eq!(42i64.to_value().to_string(), "42"); + assert_eq!(42.01f64.to_value().to_string(), "42.01"); + assert_eq!(true.to_value().to_string(), "true"); + assert_eq!('a'.to_value().to_string(), "a"); + assert_eq!("a loong string".to_value().to_string(), "a loong string"); + assert_eq!(Some(true).to_value().to_string(), "true"); + assert_eq!(().to_value().to_string(), "None"); + assert_eq!(Option::None::.to_value().to_string(), "None"); + } + + #[test] + fn test_to_value_structured() { + assert_eq!(42u64.to_value().to_token(), Token::U64(42)); + assert_eq!(42i64.to_value().to_token(), Token::I64(42)); + assert_eq!(42.01f64.to_value().to_token(), Token::F64(42.01)); + assert_eq!(true.to_value().to_token(), Token::Bool(true)); + assert_eq!('a'.to_value().to_token(), Token::Char('a')); + assert_eq!( + "a loong string".to_value().to_token(), + Token::Str("a loong string".into()) + ); + assert_eq!(Some(true).to_value().to_token(), Token::Bool(true)); + assert_eq!(().to_value().to_token(), Token::None); + assert_eq!(Option::None::.to_value().to_token(), Token::None); + } + + #[test] + fn test_to_number() { + for v in unsigned() { + assert!(v.to_u64().is_some()); + assert!(v.to_i64().is_some()); + } + + for v in signed() { + assert!(v.to_i64().is_some()); + } + + for v in unsigned().chain(signed()).chain(float()) { + assert!(v.to_f64().is_some()); + } + + for v in bool().chain(str()).chain(char()) { + assert!(v.to_u64().is_none()); + assert!(v.to_i64().is_none()); + assert!(v.to_f64().is_none()); + } + } + + #[test] + fn test_to_str() { + for v in str() { + assert!(v.to_borrowed_str().is_some()); + + #[cfg(feature = "kv_unstable_std")] + assert!(v.to_str().is_some()); + } + + let short_lived = String::from("short lived"); + let v = Value::from(&*short_lived); + + assert!(v.to_borrowed_str().is_some()); + + #[cfg(feature = "kv_unstable_std")] + assert!(v.to_str().is_some()); + + for v in unsigned().chain(signed()).chain(float()).chain(bool()) { + assert!(v.to_borrowed_str().is_none()); + + #[cfg(feature = "kv_unstable_std")] + assert!(v.to_str().is_none()); + } + } + + #[test] + fn test_to_bool() { + for v in bool() { + assert!(v.to_bool().is_some()); + } + + for v in unsigned() + .chain(signed()) + .chain(float()) + .chain(str()) + .chain(char()) + { + assert!(v.to_bool().is_none()); + } + } + + #[test] + fn test_to_char() { + for v in char() { + assert!(v.to_char().is_some()); + } + + for v in unsigned() + .chain(signed()) + .chain(float()) + .chain(str()) + .chain(bool()) + { + assert!(v.to_char().is_none()); + } + } + + #[test] + fn test_downcast_ref() { + #[derive(Debug)] + struct Foo(u64); + + let v = Value::capture_debug(&Foo(42)); + + assert!(v.is::()); + assert_eq!(42u64, v.downcast_ref::().expect("invalid downcast").0); + } + + #[test] + fn test_visit_integer() { + struct Extract(Option); + + impl<'v> Visit<'v> for Extract { + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + unimplemented!("unexpected value: {:?}", value) + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.0 = Some(value); + + Ok(()) + } + } + + let mut extract = Extract(None); + Value::from(42u64).visit(&mut extract).unwrap(); + + assert_eq!(Some(42), extract.0); + } + + #[test] + fn test_visit_borrowed_str() { + struct Extract<'v>(Option<&'v str>); + + impl<'v> Visit<'v> for Extract<'v> { + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + unimplemented!("unexpected value: {:?}", value) + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.0 = Some(value); + + Ok(()) + } + } + + let mut extract = Extract(None); + + let short_lived = String::from("A short-lived string"); + Value::from(&*short_lived).visit(&mut extract).unwrap(); + + assert_eq!(Some("A short-lived string"), extract.0); + } +} diff --git a/vendor/log/src/lib.rs b/vendor/log/src/lib.rs index 93830bb..4ead826 100644 --- a/vendor/log/src/lib.rs +++ b/vendor/log/src/lib.rs @@ -1,1971 +1,1971 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A lightweight logging facade. -//! -//! The `log` crate provides a single logging API that abstracts over the -//! actual logging implementation. Libraries can use the logging API provided -//! by this crate, and the consumer of those libraries can choose the logging -//! implementation that is most suitable for its use case. -//! -//! If no logging implementation is selected, the facade falls back to a "noop" -//! implementation that ignores all log messages. The overhead in this case -//! is very small - just an integer load, comparison and jump. -//! -//! A log request consists of a _target_, a _level_, and a _body_. A target is a -//! string which defaults to the module path of the location of the log request, -//! though that default may be overridden. Logger implementations typically use -//! the target to filter requests based on some user configuration. -//! -//! # Usage -//! -//! The basic use of the log crate is through the five logging macros: [`error!`], -//! [`warn!`], [`info!`], [`debug!`] and [`trace!`] -//! where `error!` represents the highest-priority log messages -//! and `trace!` the lowest. The log messages are filtered by configuring -//! the log level to exclude messages with a lower priority. -//! Each of these macros accept format strings similarly to [`println!`]. -//! -//! -//! [`error!`]: ./macro.error.html -//! [`warn!`]: ./macro.warn.html -//! [`info!`]: ./macro.info.html -//! [`debug!`]: ./macro.debug.html -//! [`trace!`]: ./macro.trace.html -//! [`println!`]: https://doc.rust-lang.org/stable/std/macro.println.html -//! -//! ## In libraries -//! -//! Libraries should link only to the `log` crate, and use the provided -//! macros to log whatever information will be useful to downstream consumers. -//! -//! ### Examples -//! -//! ```edition2018 -//! # #[derive(Debug)] pub struct Yak(String); -//! # impl Yak { fn shave(&mut self, _: u32) {} } -//! # fn find_a_razor() -> Result { Ok(1) } -//! use log::{info, warn}; -//! -//! pub fn shave_the_yak(yak: &mut Yak) { -//! info!(target: "yak_events", "Commencing yak shaving for {:?}", yak); -//! -//! loop { -//! match find_a_razor() { -//! Ok(razor) => { -//! info!("Razor located: {}", razor); -//! yak.shave(razor); -//! break; -//! } -//! Err(err) => { -//! warn!("Unable to locate a razor: {}, retrying", err); -//! } -//! } -//! } -//! } -//! # fn main() {} -//! ``` -//! -//! ## In executables -//! -//! Executables should choose a logging implementation and initialize it early in the -//! runtime of the program. Logging implementations will typically include a -//! function to do this. Any log messages generated before -//! the implementation is initialized will be ignored. -//! -//! The executable itself may use the `log` crate to log as well. -//! -//! ### Warning -//! -//! The logging system may only be initialized once. -//! -//! ## Structured logging -//! -//! If you enable the `kv_unstable` feature you can associate structured values -//! with your log records. If we take the example from before, we can include -//! some additional context besides what's in the formatted message: -//! -//! ```edition2018 -//! # #[macro_use] extern crate serde; -//! # #[derive(Debug, Serialize)] pub struct Yak(String); -//! # impl Yak { fn shave(&mut self, _: u32) {} } -//! # fn find_a_razor() -> Result { Ok(1) } -//! # #[cfg(feature = "kv_unstable_serde")] -//! # fn main() { -//! use log::{info, warn, as_serde, as_error}; -//! -//! pub fn shave_the_yak(yak: &mut Yak) { -//! info!(target: "yak_events", yak = as_serde!(yak); "Commencing yak shaving"); -//! -//! loop { -//! match find_a_razor() { -//! Ok(razor) => { -//! info!(razor = razor; "Razor located"); -//! yak.shave(razor); -//! break; -//! } -//! Err(err) => { -//! warn!(err = as_error!(err); "Unable to locate a razor, retrying"); -//! } -//! } -//! } -//! } -//! # } -//! # #[cfg(not(feature = "kv_unstable_serde"))] -//! # fn main() {} -//! ``` -//! -//! # Available logging implementations -//! -//! In order to produce log output executables have to use -//! a logger implementation compatible with the facade. -//! There are many available implementations to choose from, -//! here are some of the most popular ones: -//! -//! * Simple minimal loggers: -//! * [env_logger] -//! * [simple_logger] -//! * [simplelog] -//! * [pretty_env_logger] -//! * [stderrlog] -//! * [flexi_logger] -//! * Complex configurable frameworks: -//! * [log4rs] -//! * [fern] -//! * Adaptors for other facilities: -//! * [syslog] -//! * [slog-stdlog] -//! * [systemd-journal-logger] -//! * [android_log] -//! * [win_dbg_logger] -//! * [db_logger] -//! * For WebAssembly binaries: -//! * [console_log] -//! * For dynamic libraries: -//! * You may need to construct an FFI-safe wrapper over `log` to initialize in your libraries -//! -//! # Implementing a Logger -//! -//! Loggers implement the [`Log`] trait. Here's a very basic example that simply -//! logs all messages at the [`Error`][level_link], [`Warn`][level_link] or -//! [`Info`][level_link] levels to stdout: -//! -//! ```edition2018 -//! use log::{Record, Level, Metadata}; -//! -//! struct SimpleLogger; -//! -//! impl log::Log for SimpleLogger { -//! fn enabled(&self, metadata: &Metadata) -> bool { -//! metadata.level() <= Level::Info -//! } -//! -//! fn log(&self, record: &Record) { -//! if self.enabled(record.metadata()) { -//! println!("{} - {}", record.level(), record.args()); -//! } -//! } -//! -//! fn flush(&self) {} -//! } -//! -//! # fn main() {} -//! ``` -//! -//! Loggers are installed by calling the [`set_logger`] function. The maximum -//! log level also needs to be adjusted via the [`set_max_level`] function. The -//! logging facade uses this as an optimization to improve performance of log -//! messages at levels that are disabled. It's important to set it, as it -//! defaults to [`Off`][filter_link], so no log messages will ever be captured! -//! In the case of our example logger, we'll want to set the maximum log level -//! to [`Info`][filter_link], since we ignore any [`Debug`][level_link] or -//! [`Trace`][level_link] level log messages. A logging implementation should -//! provide a function that wraps a call to [`set_logger`] and -//! [`set_max_level`], handling initialization of the logger: -//! -//! ```edition2018 -//! # use log::{Level, Metadata}; -//! # struct SimpleLogger; -//! # impl log::Log for SimpleLogger { -//! # fn enabled(&self, _: &Metadata) -> bool { false } -//! # fn log(&self, _: &log::Record) {} -//! # fn flush(&self) {} -//! # } -//! # fn main() {} -//! use log::{SetLoggerError, LevelFilter}; -//! -//! static LOGGER: SimpleLogger = SimpleLogger; -//! -//! pub fn init() -> Result<(), SetLoggerError> { -//! log::set_logger(&LOGGER) -//! .map(|()| log::set_max_level(LevelFilter::Info)) -//! } -//! ``` -//! -//! Implementations that adjust their configurations at runtime should take care -//! to adjust the maximum log level as well. -//! -//! # Use with `std` -//! -//! `set_logger` requires you to provide a `&'static Log`, which can be hard to -//! obtain if your logger depends on some runtime configuration. The -//! `set_boxed_logger` function is available with the `std` Cargo feature. It is -//! identical to `set_logger` except that it takes a `Box` rather than a -//! `&'static Log`: -//! -//! ```edition2018 -//! # use log::{Level, LevelFilter, Log, SetLoggerError, Metadata}; -//! # struct SimpleLogger; -//! # impl log::Log for SimpleLogger { -//! # fn enabled(&self, _: &Metadata) -> bool { false } -//! # fn log(&self, _: &log::Record) {} -//! # fn flush(&self) {} -//! # } -//! # fn main() {} -//! # #[cfg(feature = "std")] -//! pub fn init() -> Result<(), SetLoggerError> { -//! log::set_boxed_logger(Box::new(SimpleLogger)) -//! .map(|()| log::set_max_level(LevelFilter::Info)) -//! } -//! ``` -//! -//! # Compile time filters -//! -//! Log levels can be statically disabled at compile time via Cargo features. Log invocations at -//! disabled levels will be skipped and will not even be present in the resulting binary. -//! This level is configured separately for release and debug builds. The features are: -//! -//! * `max_level_off` -//! * `max_level_error` -//! * `max_level_warn` -//! * `max_level_info` -//! * `max_level_debug` -//! * `max_level_trace` -//! * `release_max_level_off` -//! * `release_max_level_error` -//! * `release_max_level_warn` -//! * `release_max_level_info` -//! * `release_max_level_debug` -//! * `release_max_level_trace` -//! -//! These features control the value of the `STATIC_MAX_LEVEL` constant. The logging macros check -//! this value before logging a message. By default, no levels are disabled. -//! -//! Libraries should avoid using the max level features because they're global and can't be changed -//! once they're set. -//! -//! For example, a crate can disable trace level logs in debug builds and trace, debug, and info -//! level logs in release builds with the following configuration: -//! -//! ```toml -//! [dependencies] -//! log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] } -//! ``` -//! # Crate Feature Flags -//! -//! The following crate feature flags are available in addition to the filters. They are -//! configured in your `Cargo.toml`. -//! -//! * `std` allows use of `std` crate instead of the default `core`. Enables using `std::error` and -//! `set_boxed_logger` functionality. -//! * `serde` enables support for serialization and deserialization of `Level` and `LevelFilter`. -//! -//! ```toml -//! [dependencies] -//! log = { version = "0.4", features = ["std", "serde"] } -//! ``` -//! -//! # Version compatibility -//! -//! The 0.3 and 0.4 versions of the `log` crate are almost entirely compatible. Log messages -//! made using `log` 0.3 will forward transparently to a logger implementation using `log` 0.4. Log -//! messages made using `log` 0.4 will forward to a logger implementation using `log` 0.3, but the -//! module path and file name information associated with the message will unfortunately be lost. -//! -//! [`Log`]: trait.Log.html -//! [level_link]: enum.Level.html -//! [filter_link]: enum.LevelFilter.html -//! [`set_logger`]: fn.set_logger.html -//! [`set_max_level`]: fn.set_max_level.html -//! [`try_set_logger_raw`]: fn.try_set_logger_raw.html -//! [`shutdown_logger_raw`]: fn.shutdown_logger_raw.html -//! [env_logger]: https://docs.rs/env_logger/*/env_logger/ -//! [simple_logger]: https://github.com/borntyping/rust-simple_logger -//! [simplelog]: https://github.com/drakulix/simplelog.rs -//! [pretty_env_logger]: https://docs.rs/pretty_env_logger/*/pretty_env_logger/ -//! [stderrlog]: https://docs.rs/stderrlog/*/stderrlog/ -//! [flexi_logger]: https://docs.rs/flexi_logger/*/flexi_logger/ -//! [syslog]: https://docs.rs/syslog/*/syslog/ -//! [slog-stdlog]: https://docs.rs/slog-stdlog/*/slog_stdlog/ -//! [log4rs]: https://docs.rs/log4rs/*/log4rs/ -//! [fern]: https://docs.rs/fern/*/fern/ -//! [systemd-journal-logger]: https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/ -//! [android_log]: https://docs.rs/android_log/*/android_log/ -//! [win_dbg_logger]: https://docs.rs/win_dbg_logger/*/win_dbg_logger/ -//! [console_log]: https://docs.rs/console_log/*/console_log/ - -#![doc( - html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://www.rust-lang.org/favicon.ico", - html_root_url = "https://docs.rs/log/0.4.17" -)] -#![warn(missing_docs)] -#![deny(missing_debug_implementations, unconditional_recursion)] -#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] -// When compiled for the rustc compiler itself we want to make sure that this is -// an unstable crate -#![cfg_attr(rustbuild, feature(staged_api, rustc_private))] -#![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] - -#[cfg(all(not(feature = "std"), not(test)))] -extern crate core as std; - -#[macro_use] -extern crate cfg_if; - -use std::cmp; -#[cfg(feature = "std")] -use std::error; -use std::fmt; -use std::mem; -use std::str::FromStr; - -#[macro_use] -mod macros; -mod serde; - -#[cfg(feature = "kv_unstable")] -pub mod kv; - -#[cfg(has_atomics)] -use std::sync::atomic::{AtomicUsize, Ordering}; - -#[cfg(not(has_atomics))] -use std::cell::Cell; -#[cfg(not(has_atomics))] -use std::sync::atomic::Ordering; - -#[cfg(not(has_atomics))] -struct AtomicUsize { - v: Cell, -} - -#[cfg(not(has_atomics))] -impl AtomicUsize { - const fn new(v: usize) -> AtomicUsize { - AtomicUsize { v: Cell::new(v) } - } - - fn load(&self, _order: Ordering) -> usize { - self.v.get() - } - - fn store(&self, val: usize, _order: Ordering) { - self.v.set(val) - } - - #[cfg(atomic_cas)] - fn compare_exchange( - &self, - current: usize, - new: usize, - _success: Ordering, - _failure: Ordering, - ) -> Result { - let prev = self.v.get(); - if current == prev { - self.v.set(new); - } - Ok(prev) - } -} - -// Any platform without atomics is unlikely to have multiple cores, so -// writing via Cell will not be a race condition. -#[cfg(not(has_atomics))] -unsafe impl Sync for AtomicUsize {} - -// The LOGGER static holds a pointer to the global logger. It is protected by -// the STATE static which determines whether LOGGER has been initialized yet. -static mut LOGGER: &dyn Log = &NopLogger; - -static STATE: AtomicUsize = AtomicUsize::new(0); - -// There are three different states that we care about: the logger's -// uninitialized, the logger's initializing (set_logger's been called but -// LOGGER hasn't actually been set yet), or the logger's active. -const UNINITIALIZED: usize = 0; -const INITIALIZING: usize = 1; -const INITIALIZED: usize = 2; - -static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0); - -static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; - -static SET_LOGGER_ERROR: &str = "attempted to set a logger after the logging system \ - was already initialized"; -static LEVEL_PARSE_ERROR: &str = - "attempted to convert a string that doesn't match an existing log level"; - -/// An enum representing the available verbosity levels of the logger. -/// -/// Typical usage includes: checking if a certain `Level` is enabled with -/// [`log_enabled!`](macro.log_enabled.html), specifying the `Level` of -/// [`log!`](macro.log.html), and comparing a `Level` directly to a -/// [`LevelFilter`](enum.LevelFilter.html). -#[repr(usize)] -#[derive(Copy, Eq, Debug, Hash)] -pub enum Level { - /// The "error" level. - /// - /// Designates very serious errors. - // This way these line up with the discriminants for LevelFilter below - // This works because Rust treats field-less enums the same way as C does: - // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-field-less-enumerations - Error = 1, - /// The "warn" level. - /// - /// Designates hazardous situations. - Warn, - /// The "info" level. - /// - /// Designates useful information. - Info, - /// The "debug" level. - /// - /// Designates lower priority information. - Debug, - /// The "trace" level. - /// - /// Designates very low priority, often extremely verbose, information. - Trace, -} - -impl Clone for Level { - #[inline] - fn clone(&self) -> Level { - *self - } -} - -impl PartialEq for Level { - #[inline] - fn eq(&self, other: &Level) -> bool { - *self as usize == *other as usize - } -} - -impl PartialEq for Level { - #[inline] - fn eq(&self, other: &LevelFilter) -> bool { - *self as usize == *other as usize - } -} - -impl PartialOrd for Level { - #[inline] - fn partial_cmp(&self, other: &Level) -> Option { - Some(self.cmp(other)) - } - - #[inline] - fn lt(&self, other: &Level) -> bool { - (*self as usize) < *other as usize - } - - #[inline] - fn le(&self, other: &Level) -> bool { - *self as usize <= *other as usize - } - - #[inline] - fn gt(&self, other: &Level) -> bool { - *self as usize > *other as usize - } - - #[inline] - fn ge(&self, other: &Level) -> bool { - *self as usize >= *other as usize - } -} - -impl PartialOrd for Level { - #[inline] - fn partial_cmp(&self, other: &LevelFilter) -> Option { - Some((*self as usize).cmp(&(*other as usize))) - } - - #[inline] - fn lt(&self, other: &LevelFilter) -> bool { - (*self as usize) < *other as usize - } - - #[inline] - fn le(&self, other: &LevelFilter) -> bool { - *self as usize <= *other as usize - } - - #[inline] - fn gt(&self, other: &LevelFilter) -> bool { - *self as usize > *other as usize - } - - #[inline] - fn ge(&self, other: &LevelFilter) -> bool { - *self as usize >= *other as usize - } -} - -impl Ord for Level { - #[inline] - fn cmp(&self, other: &Level) -> cmp::Ordering { - (*self as usize).cmp(&(*other as usize)) - } -} - -fn ok_or(t: Option, e: E) -> Result { - match t { - Some(t) => Ok(t), - None => Err(e), - } -} - -// Reimplemented here because std::ascii is not available in libcore -fn eq_ignore_ascii_case(a: &str, b: &str) -> bool { - fn to_ascii_uppercase(c: u8) -> u8 { - if c >= b'a' && c <= b'z' { - c - b'a' + b'A' - } else { - c - } - } - - if a.len() == b.len() { - a.bytes() - .zip(b.bytes()) - .all(|(a, b)| to_ascii_uppercase(a) == to_ascii_uppercase(b)) - } else { - false - } -} - -impl FromStr for Level { - type Err = ParseLevelError; - fn from_str(level: &str) -> Result { - ok_or( - LOG_LEVEL_NAMES - .iter() - .position(|&name| eq_ignore_ascii_case(name, level)) - .into_iter() - .filter(|&idx| idx != 0) - .map(|idx| Level::from_usize(idx).unwrap()) - .next(), - ParseLevelError(()), - ) - } -} - -impl fmt::Display for Level { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.pad(self.as_str()) - } -} - -impl Level { - fn from_usize(u: usize) -> Option { - match u { - 1 => Some(Level::Error), - 2 => Some(Level::Warn), - 3 => Some(Level::Info), - 4 => Some(Level::Debug), - 5 => Some(Level::Trace), - _ => None, - } - } - - /// Returns the most verbose logging level. - #[inline] - pub fn max() -> Level { - Level::Trace - } - - /// Converts the `Level` to the equivalent `LevelFilter`. - #[inline] - pub fn to_level_filter(&self) -> LevelFilter { - LevelFilter::from_usize(*self as usize).unwrap() - } - - /// Returns the string representation of the `Level`. - /// - /// This returns the same string as the `fmt::Display` implementation. - pub fn as_str(&self) -> &'static str { - LOG_LEVEL_NAMES[*self as usize] - } - - /// Iterate through all supported logging levels. - /// - /// The order of iteration is from more severe to less severe log messages. - /// - /// # Examples - /// - /// ``` - /// use log::Level; - /// - /// let mut levels = Level::iter(); - /// - /// assert_eq!(Some(Level::Error), levels.next()); - /// assert_eq!(Some(Level::Trace), levels.last()); - /// ``` - pub fn iter() -> impl Iterator { - (1..6).map(|i| Self::from_usize(i).unwrap()) - } -} - -/// An enum representing the available verbosity level filters of the logger. -/// -/// A `LevelFilter` may be compared directly to a [`Level`]. Use this type -/// to get and set the maximum log level with [`max_level()`] and [`set_max_level`]. -/// -/// [`Level`]: enum.Level.html -/// [`max_level()`]: fn.max_level.html -/// [`set_max_level`]: fn.set_max_level.html -#[repr(usize)] -#[derive(Copy, Eq, Debug, Hash)] -pub enum LevelFilter { - /// A level lower than all log levels. - Off, - /// Corresponds to the `Error` log level. - Error, - /// Corresponds to the `Warn` log level. - Warn, - /// Corresponds to the `Info` log level. - Info, - /// Corresponds to the `Debug` log level. - Debug, - /// Corresponds to the `Trace` log level. - Trace, -} - -// Deriving generates terrible impls of these traits - -impl Clone for LevelFilter { - #[inline] - fn clone(&self) -> LevelFilter { - *self - } -} - -impl PartialEq for LevelFilter { - #[inline] - fn eq(&self, other: &LevelFilter) -> bool { - *self as usize == *other as usize - } -} - -impl PartialEq for LevelFilter { - #[inline] - fn eq(&self, other: &Level) -> bool { - other.eq(self) - } -} - -impl PartialOrd for LevelFilter { - #[inline] - fn partial_cmp(&self, other: &LevelFilter) -> Option { - Some(self.cmp(other)) - } - - #[inline] - fn lt(&self, other: &LevelFilter) -> bool { - (*self as usize) < *other as usize - } - - #[inline] - fn le(&self, other: &LevelFilter) -> bool { - *self as usize <= *other as usize - } - - #[inline] - fn gt(&self, other: &LevelFilter) -> bool { - *self as usize > *other as usize - } - - #[inline] - fn ge(&self, other: &LevelFilter) -> bool { - *self as usize >= *other as usize - } -} - -impl PartialOrd for LevelFilter { - #[inline] - fn partial_cmp(&self, other: &Level) -> Option { - Some((*self as usize).cmp(&(*other as usize))) - } - - #[inline] - fn lt(&self, other: &Level) -> bool { - (*self as usize) < *other as usize - } - - #[inline] - fn le(&self, other: &Level) -> bool { - *self as usize <= *other as usize - } - - #[inline] - fn gt(&self, other: &Level) -> bool { - *self as usize > *other as usize - } - - #[inline] - fn ge(&self, other: &Level) -> bool { - *self as usize >= *other as usize - } -} - -impl Ord for LevelFilter { - #[inline] - fn cmp(&self, other: &LevelFilter) -> cmp::Ordering { - (*self as usize).cmp(&(*other as usize)) - } -} - -impl FromStr for LevelFilter { - type Err = ParseLevelError; - fn from_str(level: &str) -> Result { - ok_or( - LOG_LEVEL_NAMES - .iter() - .position(|&name| eq_ignore_ascii_case(name, level)) - .map(|p| LevelFilter::from_usize(p).unwrap()), - ParseLevelError(()), - ) - } -} - -impl fmt::Display for LevelFilter { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.pad(self.as_str()) - } -} - -impl LevelFilter { - fn from_usize(u: usize) -> Option { - match u { - 0 => Some(LevelFilter::Off), - 1 => Some(LevelFilter::Error), - 2 => Some(LevelFilter::Warn), - 3 => Some(LevelFilter::Info), - 4 => Some(LevelFilter::Debug), - 5 => Some(LevelFilter::Trace), - _ => None, - } - } - - /// Returns the most verbose logging level filter. - #[inline] - pub fn max() -> LevelFilter { - LevelFilter::Trace - } - - /// Converts `self` to the equivalent `Level`. - /// - /// Returns `None` if `self` is `LevelFilter::Off`. - #[inline] - pub fn to_level(&self) -> Option { - Level::from_usize(*self as usize) - } - - /// Returns the string representation of the `LevelFilter`. - /// - /// This returns the same string as the `fmt::Display` implementation. - pub fn as_str(&self) -> &'static str { - LOG_LEVEL_NAMES[*self as usize] - } - - /// Iterate through all supported filtering levels. - /// - /// The order of iteration is from less to more verbose filtering. - /// - /// # Examples - /// - /// ``` - /// use log::LevelFilter; - /// - /// let mut levels = LevelFilter::iter(); - /// - /// assert_eq!(Some(LevelFilter::Off), levels.next()); - /// assert_eq!(Some(LevelFilter::Trace), levels.last()); - /// ``` - pub fn iter() -> impl Iterator { - (0..6).map(|i| Self::from_usize(i).unwrap()) - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -enum MaybeStaticStr<'a> { - Static(&'static str), - Borrowed(&'a str), -} - -impl<'a> MaybeStaticStr<'a> { - #[inline] - fn get(&self) -> &'a str { - match *self { - MaybeStaticStr::Static(s) => s, - MaybeStaticStr::Borrowed(s) => s, - } - } -} - -/// The "payload" of a log message. -/// -/// # Use -/// -/// `Record` structures are passed as parameters to the [`log`][method.log] -/// method of the [`Log`] trait. Logger implementors manipulate these -/// structures in order to display log messages. `Record`s are automatically -/// created by the [`log!`] macro and so are not seen by log users. -/// -/// Note that the [`level()`] and [`target()`] accessors are equivalent to -/// `self.metadata().level()` and `self.metadata().target()` respectively. -/// These methods are provided as a convenience for users of this structure. -/// -/// # Example -/// -/// The following example shows a simple logger that displays the level, -/// module path, and message of any `Record` that is passed to it. -/// -/// ```edition2018 -/// struct SimpleLogger; -/// -/// impl log::Log for SimpleLogger { -/// fn enabled(&self, metadata: &log::Metadata) -> bool { -/// true -/// } -/// -/// fn log(&self, record: &log::Record) { -/// if !self.enabled(record.metadata()) { -/// return; -/// } -/// -/// println!("{}:{} -- {}", -/// record.level(), -/// record.target(), -/// record.args()); -/// } -/// fn flush(&self) {} -/// } -/// ``` -/// -/// [method.log]: trait.Log.html#tymethod.log -/// [`Log`]: trait.Log.html -/// [`log!`]: macro.log.html -/// [`level()`]: struct.Record.html#method.level -/// [`target()`]: struct.Record.html#method.target -#[derive(Clone, Debug)] -pub struct Record<'a> { - metadata: Metadata<'a>, - args: fmt::Arguments<'a>, - module_path: Option>, - file: Option>, - line: Option, - #[cfg(feature = "kv_unstable")] - key_values: KeyValues<'a>, -} - -// This wrapper type is only needed so we can -// `#[derive(Debug)]` on `Record`. It also -// provides a useful `Debug` implementation for -// the underlying `Source`. -#[cfg(feature = "kv_unstable")] -#[derive(Clone)] -struct KeyValues<'a>(&'a dyn kv::Source); - -#[cfg(feature = "kv_unstable")] -impl<'a> fmt::Debug for KeyValues<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut visitor = f.debug_map(); - self.0.visit(&mut visitor).map_err(|_| fmt::Error)?; - visitor.finish() - } -} - -impl<'a> Record<'a> { - /// Returns a new builder. - #[inline] - pub fn builder() -> RecordBuilder<'a> { - RecordBuilder::new() - } - - /// The message body. - #[inline] - pub fn args(&self) -> &fmt::Arguments<'a> { - &self.args - } - - /// Metadata about the log directive. - #[inline] - pub fn metadata(&self) -> &Metadata<'a> { - &self.metadata - } - - /// The verbosity level of the message. - #[inline] - pub fn level(&self) -> Level { - self.metadata.level() - } - - /// The name of the target of the directive. - #[inline] - pub fn target(&self) -> &'a str { - self.metadata.target() - } - - /// The module path of the message. - #[inline] - pub fn module_path(&self) -> Option<&'a str> { - self.module_path.map(|s| s.get()) - } - - /// The module path of the message, if it is a `'static` string. - #[inline] - pub fn module_path_static(&self) -> Option<&'static str> { - match self.module_path { - Some(MaybeStaticStr::Static(s)) => Some(s), - _ => None, - } - } - - /// The source file containing the message. - #[inline] - pub fn file(&self) -> Option<&'a str> { - self.file.map(|s| s.get()) - } - - /// The module path of the message, if it is a `'static` string. - #[inline] - pub fn file_static(&self) -> Option<&'static str> { - match self.file { - Some(MaybeStaticStr::Static(s)) => Some(s), - _ => None, - } - } - - /// The line containing the message. - #[inline] - pub fn line(&self) -> Option { - self.line - } - - /// The structured key-value pairs associated with the message. - #[cfg(feature = "kv_unstable")] - #[inline] - pub fn key_values(&self) -> &dyn kv::Source { - self.key_values.0 - } - - /// Create a new [`RecordBuilder`](struct.RecordBuilder.html) based on this record. - #[cfg(feature = "kv_unstable")] - #[inline] - pub fn to_builder(&self) -> RecordBuilder { - RecordBuilder { - record: Record { - metadata: Metadata { - level: self.metadata.level, - target: self.metadata.target, - }, - args: self.args, - module_path: self.module_path, - file: self.file, - line: self.line, - key_values: self.key_values.clone(), - }, - } - } -} - -/// Builder for [`Record`](struct.Record.html). -/// -/// Typically should only be used by log library creators or for testing and "shim loggers". -/// The `RecordBuilder` can set the different parameters of `Record` object, and returns -/// the created object when `build` is called. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::{Level, Record}; -/// -/// let record = Record::builder() -/// .args(format_args!("Error!")) -/// .level(Level::Error) -/// .target("myApp") -/// .file(Some("server.rs")) -/// .line(Some(144)) -/// .module_path(Some("server")) -/// .build(); -/// ``` -/// -/// Alternatively, use [`MetadataBuilder`](struct.MetadataBuilder.html): -/// -/// ```edition2018 -/// use log::{Record, Level, MetadataBuilder}; -/// -/// let error_metadata = MetadataBuilder::new() -/// .target("myApp") -/// .level(Level::Error) -/// .build(); -/// -/// let record = Record::builder() -/// .metadata(error_metadata) -/// .args(format_args!("Error!")) -/// .line(Some(433)) -/// .file(Some("app.rs")) -/// .module_path(Some("server")) -/// .build(); -/// ``` -#[derive(Debug)] -pub struct RecordBuilder<'a> { - record: Record<'a>, -} - -impl<'a> RecordBuilder<'a> { - /// Construct new `RecordBuilder`. - /// - /// The default options are: - /// - /// - `args`: [`format_args!("")`] - /// - `metadata`: [`Metadata::builder().build()`] - /// - `module_path`: `None` - /// - `file`: `None` - /// - `line`: `None` - /// - /// [`format_args!("")`]: https://doc.rust-lang.org/std/macro.format_args.html - /// [`Metadata::builder().build()`]: struct.MetadataBuilder.html#method.build - #[inline] - pub fn new() -> RecordBuilder<'a> { - RecordBuilder { - record: Record { - args: format_args!(""), - metadata: Metadata::builder().build(), - module_path: None, - file: None, - line: None, - #[cfg(feature = "kv_unstable")] - key_values: KeyValues(&Option::None::<(kv::Key, kv::Value)>), - }, - } - } - - /// Set [`args`](struct.Record.html#method.args). - #[inline] - pub fn args(&mut self, args: fmt::Arguments<'a>) -> &mut RecordBuilder<'a> { - self.record.args = args; - self - } - - /// Set [`metadata`](struct.Record.html#method.metadata). Construct a `Metadata` object with [`MetadataBuilder`](struct.MetadataBuilder.html). - #[inline] - pub fn metadata(&mut self, metadata: Metadata<'a>) -> &mut RecordBuilder<'a> { - self.record.metadata = metadata; - self - } - - /// Set [`Metadata::level`](struct.Metadata.html#method.level). - #[inline] - pub fn level(&mut self, level: Level) -> &mut RecordBuilder<'a> { - self.record.metadata.level = level; - self - } - - /// Set [`Metadata::target`](struct.Metadata.html#method.target) - #[inline] - pub fn target(&mut self, target: &'a str) -> &mut RecordBuilder<'a> { - self.record.metadata.target = target; - self - } - - /// Set [`module_path`](struct.Record.html#method.module_path) - #[inline] - pub fn module_path(&mut self, path: Option<&'a str>) -> &mut RecordBuilder<'a> { - self.record.module_path = path.map(MaybeStaticStr::Borrowed); - self - } - - /// Set [`module_path`](struct.Record.html#method.module_path) to a `'static` string - #[inline] - pub fn module_path_static(&mut self, path: Option<&'static str>) -> &mut RecordBuilder<'a> { - self.record.module_path = path.map(MaybeStaticStr::Static); - self - } - - /// Set [`file`](struct.Record.html#method.file) - #[inline] - pub fn file(&mut self, file: Option<&'a str>) -> &mut RecordBuilder<'a> { - self.record.file = file.map(MaybeStaticStr::Borrowed); - self - } - - /// Set [`file`](struct.Record.html#method.file) to a `'static` string. - #[inline] - pub fn file_static(&mut self, file: Option<&'static str>) -> &mut RecordBuilder<'a> { - self.record.file = file.map(MaybeStaticStr::Static); - self - } - - /// Set [`line`](struct.Record.html#method.line) - #[inline] - pub fn line(&mut self, line: Option) -> &mut RecordBuilder<'a> { - self.record.line = line; - self - } - - /// Set [`key_values`](struct.Record.html#method.key_values) - #[cfg(feature = "kv_unstable")] - #[inline] - pub fn key_values(&mut self, kvs: &'a dyn kv::Source) -> &mut RecordBuilder<'a> { - self.record.key_values = KeyValues(kvs); - self - } - - /// Invoke the builder and return a `Record` - #[inline] - pub fn build(&self) -> Record<'a> { - self.record.clone() - } -} - -/// Metadata about a log message. -/// -/// # Use -/// -/// `Metadata` structs are created when users of the library use -/// logging macros. -/// -/// They are consumed by implementations of the `Log` trait in the -/// `enabled` method. -/// -/// `Record`s use `Metadata` to determine the log message's severity -/// and target. -/// -/// Users should use the `log_enabled!` macro in their code to avoid -/// constructing expensive log messages. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::{Record, Level, Metadata}; -/// -/// struct MyLogger; -/// -/// impl log::Log for MyLogger { -/// fn enabled(&self, metadata: &Metadata) -> bool { -/// metadata.level() <= Level::Info -/// } -/// -/// fn log(&self, record: &Record) { -/// if self.enabled(record.metadata()) { -/// println!("{} - {}", record.level(), record.args()); -/// } -/// } -/// fn flush(&self) {} -/// } -/// -/// # fn main(){} -/// ``` -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct Metadata<'a> { - level: Level, - target: &'a str, -} - -impl<'a> Metadata<'a> { - /// Returns a new builder. - #[inline] - pub fn builder() -> MetadataBuilder<'a> { - MetadataBuilder::new() - } - - /// The verbosity level of the message. - #[inline] - pub fn level(&self) -> Level { - self.level - } - - /// The name of the target of the directive. - #[inline] - pub fn target(&self) -> &'a str { - self.target - } -} - -/// Builder for [`Metadata`](struct.Metadata.html). -/// -/// Typically should only be used by log library creators or for testing and "shim loggers". -/// The `MetadataBuilder` can set the different parameters of a `Metadata` object, and returns -/// the created object when `build` is called. -/// -/// # Example -/// -/// ```edition2018 -/// let target = "myApp"; -/// use log::{Level, MetadataBuilder}; -/// let metadata = MetadataBuilder::new() -/// .level(Level::Debug) -/// .target(target) -/// .build(); -/// ``` -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct MetadataBuilder<'a> { - metadata: Metadata<'a>, -} - -impl<'a> MetadataBuilder<'a> { - /// Construct a new `MetadataBuilder`. - /// - /// The default options are: - /// - /// - `level`: `Level::Info` - /// - `target`: `""` - #[inline] - pub fn new() -> MetadataBuilder<'a> { - MetadataBuilder { - metadata: Metadata { - level: Level::Info, - target: "", - }, - } - } - - /// Setter for [`level`](struct.Metadata.html#method.level). - #[inline] - pub fn level(&mut self, arg: Level) -> &mut MetadataBuilder<'a> { - self.metadata.level = arg; - self - } - - /// Setter for [`target`](struct.Metadata.html#method.target). - #[inline] - pub fn target(&mut self, target: &'a str) -> &mut MetadataBuilder<'a> { - self.metadata.target = target; - self - } - - /// Returns a `Metadata` object. - #[inline] - pub fn build(&self) -> Metadata<'a> { - self.metadata.clone() - } -} - -/// A trait encapsulating the operations required of a logger. -pub trait Log: Sync + Send { - /// Determines if a log message with the specified metadata would be - /// logged. - /// - /// This is used by the `log_enabled!` macro to allow callers to avoid - /// expensive computation of log message arguments if the message would be - /// discarded anyway. - /// - /// # For implementors - /// - /// This method isn't called automatically by the `log!` macros. - /// It's up to an implementation of the `Log` trait to call `enabled` in its own - /// `log` method implementation to guarantee that filtering is applied. - fn enabled(&self, metadata: &Metadata) -> bool; - - /// Logs the `Record`. - /// - /// # For implementors - /// - /// Note that `enabled` is *not* necessarily called before this method. - /// Implementations of `log` should perform all necessary filtering - /// internally. - fn log(&self, record: &Record); - - /// Flushes any buffered records. - fn flush(&self); -} - -// Just used as a dummy initial value for LOGGER -struct NopLogger; - -impl Log for NopLogger { - fn enabled(&self, _: &Metadata) -> bool { - false - } - - fn log(&self, _: &Record) {} - fn flush(&self) {} -} - -impl Log for &'_ T -where - T: ?Sized + Log, -{ - fn enabled(&self, metadata: &Metadata) -> bool { - (**self).enabled(metadata) - } - - fn log(&self, record: &Record) { - (**self).log(record) - } - fn flush(&self) { - (**self).flush() - } -} - -#[cfg(feature = "std")] -impl Log for std::boxed::Box -where - T: ?Sized + Log, -{ - fn enabled(&self, metadata: &Metadata) -> bool { - self.as_ref().enabled(metadata) - } - - fn log(&self, record: &Record) { - self.as_ref().log(record) - } - fn flush(&self) { - self.as_ref().flush() - } -} - -#[cfg(feature = "std")] -impl Log for std::sync::Arc -where - T: ?Sized + Log, -{ - fn enabled(&self, metadata: &Metadata) -> bool { - self.as_ref().enabled(metadata) - } - - fn log(&self, record: &Record) { - self.as_ref().log(record) - } - fn flush(&self) { - self.as_ref().flush() - } -} - -/// Sets the global maximum log level. -/// -/// Generally, this should only be called by the active logging implementation. -/// -/// Note that `Trace` is the maximum level, because it provides the maximum amount of detail in the emitted logs. -#[inline] -pub fn set_max_level(level: LevelFilter) { - MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed) -} - -/// Returns the current maximum log level. -/// -/// The [`log!`], [`error!`], [`warn!`], [`info!`], [`debug!`], and [`trace!`] macros check -/// this value and discard any message logged at a higher level. The maximum -/// log level is set by the [`set_max_level`] function. -/// -/// [`log!`]: macro.log.html -/// [`error!`]: macro.error.html -/// [`warn!`]: macro.warn.html -/// [`info!`]: macro.info.html -/// [`debug!`]: macro.debug.html -/// [`trace!`]: macro.trace.html -/// [`set_max_level`]: fn.set_max_level.html -#[inline(always)] -pub fn max_level() -> LevelFilter { - // Since `LevelFilter` is `repr(usize)`, - // this transmute is sound if and only if `MAX_LOG_LEVEL_FILTER` - // is set to a usize that is a valid discriminant for `LevelFilter`. - // Since `MAX_LOG_LEVEL_FILTER` is private, the only time it's set - // is by `set_max_level` above, i.e. by casting a `LevelFilter` to `usize`. - // So any usize stored in `MAX_LOG_LEVEL_FILTER` is a valid discriminant. - unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) } -} - -/// Sets the global logger to a `Box`. -/// -/// This is a simple convenience wrapper over `set_logger`, which takes a -/// `Box` rather than a `&'static Log`. See the documentation for -/// [`set_logger`] for more details. -/// -/// Requires the `std` feature. -/// -/// # Errors -/// -/// An error is returned if a logger has already been set. -/// -/// [`set_logger`]: fn.set_logger.html -#[cfg(all(feature = "std", atomic_cas))] -pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { - set_logger_inner(|| Box::leak(logger)) -} - -/// Sets the global logger to a `&'static Log`. -/// -/// This function may only be called once in the lifetime of a program. Any log -/// events that occur before the call to `set_logger` completes will be ignored. -/// -/// This function does not typically need to be called manually. Logger -/// implementations should provide an initialization method that installs the -/// logger internally. -/// -/// # Availability -/// -/// This method is available even when the `std` feature is disabled. However, -/// it is currently unavailable on `thumbv6` targets, which lack support for -/// some atomic operations which are used by this function. Even on those -/// targets, [`set_logger_racy`] will be available. -/// -/// # Errors -/// -/// An error is returned if a logger has already been set. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::{error, info, warn, Record, Level, Metadata, LevelFilter}; -/// -/// static MY_LOGGER: MyLogger = MyLogger; -/// -/// struct MyLogger; -/// -/// impl log::Log for MyLogger { -/// fn enabled(&self, metadata: &Metadata) -> bool { -/// metadata.level() <= Level::Info -/// } -/// -/// fn log(&self, record: &Record) { -/// if self.enabled(record.metadata()) { -/// println!("{} - {}", record.level(), record.args()); -/// } -/// } -/// fn flush(&self) {} -/// } -/// -/// # fn main(){ -/// log::set_logger(&MY_LOGGER).unwrap(); -/// log::set_max_level(LevelFilter::Info); -/// -/// info!("hello log"); -/// warn!("warning"); -/// error!("oops"); -/// # } -/// ``` -/// -/// [`set_logger_racy`]: fn.set_logger_racy.html -#[cfg(atomic_cas)] -pub fn set_logger(logger: &'static dyn Log) -> Result<(), SetLoggerError> { - set_logger_inner(|| logger) -} - -#[cfg(atomic_cas)] -fn set_logger_inner(make_logger: F) -> Result<(), SetLoggerError> -where - F: FnOnce() -> &'static dyn Log, -{ - let old_state = match STATE.compare_exchange( - UNINITIALIZED, - INITIALIZING, - Ordering::SeqCst, - Ordering::SeqCst, - ) { - Ok(s) | Err(s) => s, - }; - match old_state { - UNINITIALIZED => { - unsafe { - LOGGER = make_logger(); - } - STATE.store(INITIALIZED, Ordering::SeqCst); - Ok(()) - } - INITIALIZING => { - while STATE.load(Ordering::SeqCst) == INITIALIZING { - // TODO: replace with `hint::spin_loop` once MSRV is 1.49.0. - #[allow(deprecated)] - std::sync::atomic::spin_loop_hint(); - } - Err(SetLoggerError(())) - } - _ => Err(SetLoggerError(())), - } -} - -/// A thread-unsafe version of [`set_logger`]. -/// -/// This function is available on all platforms, even those that do not have -/// support for atomics that is needed by [`set_logger`]. -/// -/// In almost all cases, [`set_logger`] should be preferred. -/// -/// # Safety -/// -/// This function is only safe to call when no other logger initialization -/// function is called while this function still executes. -/// -/// This can be upheld by (for example) making sure that **there are no other -/// threads**, and (on embedded) that **interrupts are disabled**. -/// -/// It is safe to use other logging functions while this function runs -/// (including all logging macros). -/// -/// [`set_logger`]: fn.set_logger.html -pub unsafe fn set_logger_racy(logger: &'static dyn Log) -> Result<(), SetLoggerError> { - match STATE.load(Ordering::SeqCst) { - UNINITIALIZED => { - LOGGER = logger; - STATE.store(INITIALIZED, Ordering::SeqCst); - Ok(()) - } - INITIALIZING => { - // This is just plain UB, since we were racing another initialization function - unreachable!("set_logger_racy must not be used with other initialization functions") - } - _ => Err(SetLoggerError(())), - } -} - -/// The type returned by [`set_logger`] if [`set_logger`] has already been called. -/// -/// [`set_logger`]: fn.set_logger.html -#[allow(missing_copy_implementations)] -#[derive(Debug)] -pub struct SetLoggerError(()); - -impl fmt::Display for SetLoggerError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(SET_LOGGER_ERROR) - } -} - -// The Error trait is not available in libcore -#[cfg(feature = "std")] -impl error::Error for SetLoggerError {} - -/// The type returned by [`from_str`] when the string doesn't match any of the log levels. -/// -/// [`from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str -#[allow(missing_copy_implementations)] -#[derive(Debug, PartialEq)] -pub struct ParseLevelError(()); - -impl fmt::Display for ParseLevelError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(LEVEL_PARSE_ERROR) - } -} - -// The Error trait is not available in libcore -#[cfg(feature = "std")] -impl error::Error for ParseLevelError {} - -/// Returns a reference to the logger. -/// -/// If a logger has not been set, a no-op implementation is returned. -pub fn logger() -> &'static dyn Log { - if STATE.load(Ordering::SeqCst) != INITIALIZED { - static NOP: NopLogger = NopLogger; - &NOP - } else { - unsafe { LOGGER } - } -} - -// WARNING: this is not part of the crate's public API and is subject to change at any time -#[doc(hidden)] -#[cfg(not(feature = "kv_unstable"))] -pub fn __private_api_log( - args: fmt::Arguments, - level: Level, - &(target, module_path, file, line): &(&str, &'static str, &'static str, u32), - kvs: Option<&[(&str, &str)]>, -) { - if kvs.is_some() { - panic!( - "key-value support is experimental and must be enabled using the `kv_unstable` feature" - ) - } - - logger().log( - &Record::builder() - .args(args) - .level(level) - .target(target) - .module_path_static(Some(module_path)) - .file_static(Some(file)) - .line(Some(line)) - .build(), - ); -} - -// WARNING: this is not part of the crate's public API and is subject to change at any time -#[doc(hidden)] -#[cfg(feature = "kv_unstable")] -pub fn __private_api_log( - args: fmt::Arguments, - level: Level, - &(target, module_path, file, line): &(&str, &'static str, &'static str, u32), - kvs: Option<&[(&str, &dyn kv::ToValue)]>, -) { - logger().log( - &Record::builder() - .args(args) - .level(level) - .target(target) - .module_path_static(Some(module_path)) - .file_static(Some(file)) - .line(Some(line)) - .key_values(&kvs) - .build(), - ); -} - -// WARNING: this is not part of the crate's public API and is subject to change at any time -#[doc(hidden)] -pub fn __private_api_enabled(level: Level, target: &str) -> bool { - logger().enabled(&Metadata::builder().level(level).target(target).build()) -} - -// WARNING: this is not part of the crate's public API and is subject to change at any time -#[doc(hidden)] -pub mod __private_api { - pub use std::option::Option; -} - -/// The statically resolved maximum log level. -/// -/// See the crate level documentation for information on how to configure this. -/// -/// This value is checked by the log macros, but not by the `Log`ger returned by -/// the [`logger`] function. Code that manually calls functions on that value -/// should compare the level against this value. -/// -/// [`logger`]: fn.logger.html -pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL_INNER; - -cfg_if! { - if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace; - } else if #[cfg(feature = "max_level_off")] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off; - } else if #[cfg(feature = "max_level_error")] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error; - } else if #[cfg(feature = "max_level_warn")] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn; - } else if #[cfg(feature = "max_level_info")] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info; - } else if #[cfg(feature = "max_level_debug")] { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug; - } else { - const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace; - } -} - -#[cfg(test)] -mod tests { - extern crate std; - use super::{Level, LevelFilter, ParseLevelError}; - use tests::std::string::ToString; - - #[test] - fn test_levelfilter_from_str() { - let tests = [ - ("off", Ok(LevelFilter::Off)), - ("error", Ok(LevelFilter::Error)), - ("warn", Ok(LevelFilter::Warn)), - ("info", Ok(LevelFilter::Info)), - ("debug", Ok(LevelFilter::Debug)), - ("trace", Ok(LevelFilter::Trace)), - ("OFF", Ok(LevelFilter::Off)), - ("ERROR", Ok(LevelFilter::Error)), - ("WARN", Ok(LevelFilter::Warn)), - ("INFO", Ok(LevelFilter::Info)), - ("DEBUG", Ok(LevelFilter::Debug)), - ("TRACE", Ok(LevelFilter::Trace)), - ("asdf", Err(ParseLevelError(()))), - ]; - for &(s, ref expected) in &tests { - assert_eq!(expected, &s.parse()); - } - } - - #[test] - fn test_level_from_str() { - let tests = [ - ("OFF", Err(ParseLevelError(()))), - ("error", Ok(Level::Error)), - ("warn", Ok(Level::Warn)), - ("info", Ok(Level::Info)), - ("debug", Ok(Level::Debug)), - ("trace", Ok(Level::Trace)), - ("ERROR", Ok(Level::Error)), - ("WARN", Ok(Level::Warn)), - ("INFO", Ok(Level::Info)), - ("DEBUG", Ok(Level::Debug)), - ("TRACE", Ok(Level::Trace)), - ("asdf", Err(ParseLevelError(()))), - ]; - for &(s, ref expected) in &tests { - assert_eq!(expected, &s.parse()); - } - } - - #[test] - fn test_level_as_str() { - let tests = &[ - (Level::Error, "ERROR"), - (Level::Warn, "WARN"), - (Level::Info, "INFO"), - (Level::Debug, "DEBUG"), - (Level::Trace, "TRACE"), - ]; - for (input, expected) in tests { - assert_eq!(*expected, input.as_str()); - } - } - - #[test] - fn test_level_show() { - assert_eq!("INFO", Level::Info.to_string()); - assert_eq!("ERROR", Level::Error.to_string()); - } - - #[test] - fn test_levelfilter_show() { - assert_eq!("OFF", LevelFilter::Off.to_string()); - assert_eq!("ERROR", LevelFilter::Error.to_string()); - } - - #[test] - fn test_cross_cmp() { - assert!(Level::Debug > LevelFilter::Error); - assert!(LevelFilter::Warn < Level::Trace); - assert!(LevelFilter::Off < Level::Error); - } - - #[test] - fn test_cross_eq() { - assert!(Level::Error == LevelFilter::Error); - assert!(LevelFilter::Off != Level::Error); - assert!(Level::Trace == LevelFilter::Trace); - } - - #[test] - fn test_to_level() { - assert_eq!(Some(Level::Error), LevelFilter::Error.to_level()); - assert_eq!(None, LevelFilter::Off.to_level()); - assert_eq!(Some(Level::Debug), LevelFilter::Debug.to_level()); - } - - #[test] - fn test_to_level_filter() { - assert_eq!(LevelFilter::Error, Level::Error.to_level_filter()); - assert_eq!(LevelFilter::Trace, Level::Trace.to_level_filter()); - } - - #[test] - fn test_level_filter_as_str() { - let tests = &[ - (LevelFilter::Off, "OFF"), - (LevelFilter::Error, "ERROR"), - (LevelFilter::Warn, "WARN"), - (LevelFilter::Info, "INFO"), - (LevelFilter::Debug, "DEBUG"), - (LevelFilter::Trace, "TRACE"), - ]; - for (input, expected) in tests { - assert_eq!(*expected, input.as_str()); - } - } - - #[test] - #[cfg(feature = "std")] - fn test_error_trait() { - use super::SetLoggerError; - let e = SetLoggerError(()); - assert_eq!( - &e.to_string(), - "attempted to set a logger after the logging system \ - was already initialized" - ); - } - - #[test] - fn test_metadata_builder() { - use super::MetadataBuilder; - let target = "myApp"; - let metadata_test = MetadataBuilder::new() - .level(Level::Debug) - .target(target) - .build(); - assert_eq!(metadata_test.level(), Level::Debug); - assert_eq!(metadata_test.target(), "myApp"); - } - - #[test] - fn test_metadata_convenience_builder() { - use super::Metadata; - let target = "myApp"; - let metadata_test = Metadata::builder() - .level(Level::Debug) - .target(target) - .build(); - assert_eq!(metadata_test.level(), Level::Debug); - assert_eq!(metadata_test.target(), "myApp"); - } - - #[test] - fn test_record_builder() { - use super::{MetadataBuilder, RecordBuilder}; - let target = "myApp"; - let metadata = MetadataBuilder::new().target(target).build(); - let fmt_args = format_args!("hello"); - let record_test = RecordBuilder::new() - .args(fmt_args) - .metadata(metadata) - .module_path(Some("foo")) - .file(Some("bar")) - .line(Some(30)) - .build(); - assert_eq!(record_test.metadata().target(), "myApp"); - assert_eq!(record_test.module_path(), Some("foo")); - assert_eq!(record_test.file(), Some("bar")); - assert_eq!(record_test.line(), Some(30)); - } - - #[test] - fn test_record_convenience_builder() { - use super::{Metadata, Record}; - let target = "myApp"; - let metadata = Metadata::builder().target(target).build(); - let fmt_args = format_args!("hello"); - let record_test = Record::builder() - .args(fmt_args) - .metadata(metadata) - .module_path(Some("foo")) - .file(Some("bar")) - .line(Some(30)) - .build(); - assert_eq!(record_test.target(), "myApp"); - assert_eq!(record_test.module_path(), Some("foo")); - assert_eq!(record_test.file(), Some("bar")); - assert_eq!(record_test.line(), Some(30)); - } - - #[test] - fn test_record_complete_builder() { - use super::{Level, Record}; - let target = "myApp"; - let record_test = Record::builder() - .module_path(Some("foo")) - .file(Some("bar")) - .line(Some(30)) - .target(target) - .level(Level::Error) - .build(); - assert_eq!(record_test.target(), "myApp"); - assert_eq!(record_test.level(), Level::Error); - assert_eq!(record_test.module_path(), Some("foo")); - assert_eq!(record_test.file(), Some("bar")); - assert_eq!(record_test.line(), Some(30)); - } - - #[test] - #[cfg(feature = "kv_unstable")] - fn test_record_key_values_builder() { - use super::Record; - use kv::{self, Visitor}; - - struct TestVisitor { - seen_pairs: usize, - } - - impl<'kvs> Visitor<'kvs> for TestVisitor { - fn visit_pair( - &mut self, - _: kv::Key<'kvs>, - _: kv::Value<'kvs>, - ) -> Result<(), kv::Error> { - self.seen_pairs += 1; - Ok(()) - } - } - - let kvs: &[(&str, i32)] = &[("a", 1), ("b", 2)]; - let record_test = Record::builder().key_values(&kvs).build(); - - let mut visitor = TestVisitor { seen_pairs: 0 }; - - record_test.key_values().visit(&mut visitor).unwrap(); - - assert_eq!(2, visitor.seen_pairs); - } - - #[test] - #[cfg(feature = "kv_unstable")] - fn test_record_key_values_get_coerce() { - use super::Record; - - let kvs: &[(&str, &str)] = &[("a", "1"), ("b", "2")]; - let record = Record::builder().key_values(&kvs).build(); - - assert_eq!( - "2", - record - .key_values() - .get("b".into()) - .expect("missing key") - .to_borrowed_str() - .expect("invalid value") - ); - } - - // Test that the `impl Log for Foo` blocks work - // This test mostly operates on a type level, so failures will be compile errors - #[test] - fn test_foreign_impl() { - use super::Log; - #[cfg(feature = "std")] - use std::sync::Arc; - - fn assert_is_log() {} - - assert_is_log::<&dyn Log>(); - - #[cfg(feature = "std")] - assert_is_log::>(); - - #[cfg(feature = "std")] - assert_is_log::>(); - - // Assert these statements for all T: Log + ?Sized - #[allow(unused)] - fn forall() { - #[cfg(feature = "std")] - assert_is_log::>(); - - assert_is_log::<&T>(); - - #[cfg(feature = "std")] - assert_is_log::>(); - } - } -} +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A lightweight logging facade. +//! +//! The `log` crate provides a single logging API that abstracts over the +//! actual logging implementation. Libraries can use the logging API provided +//! by this crate, and the consumer of those libraries can choose the logging +//! implementation that is most suitable for its use case. +//! +//! If no logging implementation is selected, the facade falls back to a "noop" +//! implementation that ignores all log messages. The overhead in this case +//! is very small - just an integer load, comparison and jump. +//! +//! A log request consists of a _target_, a _level_, and a _body_. A target is a +//! string which defaults to the module path of the location of the log request, +//! though that default may be overridden. Logger implementations typically use +//! the target to filter requests based on some user configuration. +//! +//! # Usage +//! +//! The basic use of the log crate is through the five logging macros: [`error!`], +//! [`warn!`], [`info!`], [`debug!`] and [`trace!`] +//! where `error!` represents the highest-priority log messages +//! and `trace!` the lowest. The log messages are filtered by configuring +//! the log level to exclude messages with a lower priority. +//! Each of these macros accept format strings similarly to [`println!`]. +//! +//! +//! [`error!`]: ./macro.error.html +//! [`warn!`]: ./macro.warn.html +//! [`info!`]: ./macro.info.html +//! [`debug!`]: ./macro.debug.html +//! [`trace!`]: ./macro.trace.html +//! [`println!`]: https://doc.rust-lang.org/stable/std/macro.println.html +//! +//! ## In libraries +//! +//! Libraries should link only to the `log` crate, and use the provided +//! macros to log whatever information will be useful to downstream consumers. +//! +//! ### Examples +//! +//! ```edition2018 +//! # #[derive(Debug)] pub struct Yak(String); +//! # impl Yak { fn shave(&mut self, _: u32) {} } +//! # fn find_a_razor() -> Result { Ok(1) } +//! use log::{info, warn}; +//! +//! pub fn shave_the_yak(yak: &mut Yak) { +//! info!(target: "yak_events", "Commencing yak shaving for {:?}", yak); +//! +//! loop { +//! match find_a_razor() { +//! Ok(razor) => { +//! info!("Razor located: {}", razor); +//! yak.shave(razor); +//! break; +//! } +//! Err(err) => { +//! warn!("Unable to locate a razor: {}, retrying", err); +//! } +//! } +//! } +//! } +//! # fn main() {} +//! ``` +//! +//! ## In executables +//! +//! Executables should choose a logging implementation and initialize it early in the +//! runtime of the program. Logging implementations will typically include a +//! function to do this. Any log messages generated before +//! the implementation is initialized will be ignored. +//! +//! The executable itself may use the `log` crate to log as well. +//! +//! ### Warning +//! +//! The logging system may only be initialized once. +//! +//! ## Structured logging +//! +//! If you enable the `kv_unstable` feature you can associate structured values +//! with your log records. If we take the example from before, we can include +//! some additional context besides what's in the formatted message: +//! +//! ```edition2018 +//! # #[macro_use] extern crate serde; +//! # #[derive(Debug, Serialize)] pub struct Yak(String); +//! # impl Yak { fn shave(&mut self, _: u32) {} } +//! # fn find_a_razor() -> Result { Ok(1) } +//! # #[cfg(feature = "kv_unstable_serde")] +//! # fn main() { +//! use log::{info, warn, as_serde, as_error}; +//! +//! pub fn shave_the_yak(yak: &mut Yak) { +//! info!(target: "yak_events", yak = as_serde!(yak); "Commencing yak shaving"); +//! +//! loop { +//! match find_a_razor() { +//! Ok(razor) => { +//! info!(razor = razor; "Razor located"); +//! yak.shave(razor); +//! break; +//! } +//! Err(err) => { +//! warn!(err = as_error!(err); "Unable to locate a razor, retrying"); +//! } +//! } +//! } +//! } +//! # } +//! # #[cfg(not(feature = "kv_unstable_serde"))] +//! # fn main() {} +//! ``` +//! +//! # Available logging implementations +//! +//! In order to produce log output executables have to use +//! a logger implementation compatible with the facade. +//! There are many available implementations to choose from, +//! here are some of the most popular ones: +//! +//! * Simple minimal loggers: +//! * [env_logger] +//! * [simple_logger] +//! * [simplelog] +//! * [pretty_env_logger] +//! * [stderrlog] +//! * [flexi_logger] +//! * Complex configurable frameworks: +//! * [log4rs] +//! * [fern] +//! * Adaptors for other facilities: +//! * [syslog] +//! * [slog-stdlog] +//! * [systemd-journal-logger] +//! * [android_log] +//! * [win_dbg_logger] +//! * [db_logger] +//! * For WebAssembly binaries: +//! * [console_log] +//! * For dynamic libraries: +//! * You may need to construct an FFI-safe wrapper over `log` to initialize in your libraries +//! +//! # Implementing a Logger +//! +//! Loggers implement the [`Log`] trait. Here's a very basic example that simply +//! logs all messages at the [`Error`][level_link], [`Warn`][level_link] or +//! [`Info`][level_link] levels to stdout: +//! +//! ```edition2018 +//! use log::{Record, Level, Metadata}; +//! +//! struct SimpleLogger; +//! +//! impl log::Log for SimpleLogger { +//! fn enabled(&self, metadata: &Metadata) -> bool { +//! metadata.level() <= Level::Info +//! } +//! +//! fn log(&self, record: &Record) { +//! if self.enabled(record.metadata()) { +//! println!("{} - {}", record.level(), record.args()); +//! } +//! } +//! +//! fn flush(&self) {} +//! } +//! +//! # fn main() {} +//! ``` +//! +//! Loggers are installed by calling the [`set_logger`] function. The maximum +//! log level also needs to be adjusted via the [`set_max_level`] function. The +//! logging facade uses this as an optimization to improve performance of log +//! messages at levels that are disabled. It's important to set it, as it +//! defaults to [`Off`][filter_link], so no log messages will ever be captured! +//! In the case of our example logger, we'll want to set the maximum log level +//! to [`Info`][filter_link], since we ignore any [`Debug`][level_link] or +//! [`Trace`][level_link] level log messages. A logging implementation should +//! provide a function that wraps a call to [`set_logger`] and +//! [`set_max_level`], handling initialization of the logger: +//! +//! ```edition2018 +//! # use log::{Level, Metadata}; +//! # struct SimpleLogger; +//! # impl log::Log for SimpleLogger { +//! # fn enabled(&self, _: &Metadata) -> bool { false } +//! # fn log(&self, _: &log::Record) {} +//! # fn flush(&self) {} +//! # } +//! # fn main() {} +//! use log::{SetLoggerError, LevelFilter}; +//! +//! static LOGGER: SimpleLogger = SimpleLogger; +//! +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_logger(&LOGGER) +//! .map(|()| log::set_max_level(LevelFilter::Info)) +//! } +//! ``` +//! +//! Implementations that adjust their configurations at runtime should take care +//! to adjust the maximum log level as well. +//! +//! # Use with `std` +//! +//! `set_logger` requires you to provide a `&'static Log`, which can be hard to +//! obtain if your logger depends on some runtime configuration. The +//! `set_boxed_logger` function is available with the `std` Cargo feature. It is +//! identical to `set_logger` except that it takes a `Box` rather than a +//! `&'static Log`: +//! +//! ```edition2018 +//! # use log::{Level, LevelFilter, Log, SetLoggerError, Metadata}; +//! # struct SimpleLogger; +//! # impl log::Log for SimpleLogger { +//! # fn enabled(&self, _: &Metadata) -> bool { false } +//! # fn log(&self, _: &log::Record) {} +//! # fn flush(&self) {} +//! # } +//! # fn main() {} +//! # #[cfg(feature = "std")] +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_boxed_logger(Box::new(SimpleLogger)) +//! .map(|()| log::set_max_level(LevelFilter::Info)) +//! } +//! ``` +//! +//! # Compile time filters +//! +//! Log levels can be statically disabled at compile time via Cargo features. Log invocations at +//! disabled levels will be skipped and will not even be present in the resulting binary. +//! This level is configured separately for release and debug builds. The features are: +//! +//! * `max_level_off` +//! * `max_level_error` +//! * `max_level_warn` +//! * `max_level_info` +//! * `max_level_debug` +//! * `max_level_trace` +//! * `release_max_level_off` +//! * `release_max_level_error` +//! * `release_max_level_warn` +//! * `release_max_level_info` +//! * `release_max_level_debug` +//! * `release_max_level_trace` +//! +//! These features control the value of the `STATIC_MAX_LEVEL` constant. The logging macros check +//! this value before logging a message. By default, no levels are disabled. +//! +//! Libraries should avoid using the max level features because they're global and can't be changed +//! once they're set. +//! +//! For example, a crate can disable trace level logs in debug builds and trace, debug, and info +//! level logs in release builds with the following configuration: +//! +//! ```toml +//! [dependencies] +//! log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] } +//! ``` +//! # Crate Feature Flags +//! +//! The following crate feature flags are available in addition to the filters. They are +//! configured in your `Cargo.toml`. +//! +//! * `std` allows use of `std` crate instead of the default `core`. Enables using `std::error` and +//! `set_boxed_logger` functionality. +//! * `serde` enables support for serialization and deserialization of `Level` and `LevelFilter`. +//! +//! ```toml +//! [dependencies] +//! log = { version = "0.4", features = ["std", "serde"] } +//! ``` +//! +//! # Version compatibility +//! +//! The 0.3 and 0.4 versions of the `log` crate are almost entirely compatible. Log messages +//! made using `log` 0.3 will forward transparently to a logger implementation using `log` 0.4. Log +//! messages made using `log` 0.4 will forward to a logger implementation using `log` 0.3, but the +//! module path and file name information associated with the message will unfortunately be lost. +//! +//! [`Log`]: trait.Log.html +//! [level_link]: enum.Level.html +//! [filter_link]: enum.LevelFilter.html +//! [`set_logger`]: fn.set_logger.html +//! [`set_max_level`]: fn.set_max_level.html +//! [`try_set_logger_raw`]: fn.try_set_logger_raw.html +//! [`shutdown_logger_raw`]: fn.shutdown_logger_raw.html +//! [env_logger]: https://docs.rs/env_logger/*/env_logger/ +//! [simple_logger]: https://github.com/borntyping/rust-simple_logger +//! [simplelog]: https://github.com/drakulix/simplelog.rs +//! [pretty_env_logger]: https://docs.rs/pretty_env_logger/*/pretty_env_logger/ +//! [stderrlog]: https://docs.rs/stderrlog/*/stderrlog/ +//! [flexi_logger]: https://docs.rs/flexi_logger/*/flexi_logger/ +//! [syslog]: https://docs.rs/syslog/*/syslog/ +//! [slog-stdlog]: https://docs.rs/slog-stdlog/*/slog_stdlog/ +//! [log4rs]: https://docs.rs/log4rs/*/log4rs/ +//! [fern]: https://docs.rs/fern/*/fern/ +//! [systemd-journal-logger]: https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/ +//! [android_log]: https://docs.rs/android_log/*/android_log/ +//! [win_dbg_logger]: https://docs.rs/win_dbg_logger/*/win_dbg_logger/ +//! [console_log]: https://docs.rs/console_log/*/console_log/ + +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/log/0.4.17" +)] +#![warn(missing_docs)] +#![deny(missing_debug_implementations, unconditional_recursion)] +#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] +// When compiled for the rustc compiler itself we want to make sure that this is +// an unstable crate +#![cfg_attr(rustbuild, feature(staged_api, rustc_private))] +#![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] + +#[cfg(all(not(feature = "std"), not(test)))] +extern crate core as std; + +#[macro_use] +extern crate cfg_if; + +use std::cmp; +#[cfg(feature = "std")] +use std::error; +use std::fmt; +use std::mem; +use std::str::FromStr; + +#[macro_use] +mod macros; +mod serde; + +#[cfg(feature = "kv_unstable")] +pub mod kv; + +#[cfg(has_atomics)] +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[cfg(not(has_atomics))] +use std::cell::Cell; +#[cfg(not(has_atomics))] +use std::sync::atomic::Ordering; + +#[cfg(not(has_atomics))] +struct AtomicUsize { + v: Cell, +} + +#[cfg(not(has_atomics))] +impl AtomicUsize { + const fn new(v: usize) -> AtomicUsize { + AtomicUsize { v: Cell::new(v) } + } + + fn load(&self, _order: Ordering) -> usize { + self.v.get() + } + + fn store(&self, val: usize, _order: Ordering) { + self.v.set(val) + } + + #[cfg(atomic_cas)] + fn compare_exchange( + &self, + current: usize, + new: usize, + _success: Ordering, + _failure: Ordering, + ) -> Result { + let prev = self.v.get(); + if current == prev { + self.v.set(new); + } + Ok(prev) + } +} + +// Any platform without atomics is unlikely to have multiple cores, so +// writing via Cell will not be a race condition. +#[cfg(not(has_atomics))] +unsafe impl Sync for AtomicUsize {} + +// The LOGGER static holds a pointer to the global logger. It is protected by +// the STATE static which determines whether LOGGER has been initialized yet. +static mut LOGGER: &dyn Log = &NopLogger; + +static STATE: AtomicUsize = AtomicUsize::new(0); + +// There are three different states that we care about: the logger's +// uninitialized, the logger's initializing (set_logger's been called but +// LOGGER hasn't actually been set yet), or the logger's active. +const UNINITIALIZED: usize = 0; +const INITIALIZING: usize = 1; +const INITIALIZED: usize = 2; + +static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0); + +static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; + +static SET_LOGGER_ERROR: &str = "attempted to set a logger after the logging system \ + was already initialized"; +static LEVEL_PARSE_ERROR: &str = + "attempted to convert a string that doesn't match an existing log level"; + +/// An enum representing the available verbosity levels of the logger. +/// +/// Typical usage includes: checking if a certain `Level` is enabled with +/// [`log_enabled!`](macro.log_enabled.html), specifying the `Level` of +/// [`log!`](macro.log.html), and comparing a `Level` directly to a +/// [`LevelFilter`](enum.LevelFilter.html). +#[repr(usize)] +#[derive(Copy, Eq, Debug, Hash)] +pub enum Level { + /// The "error" level. + /// + /// Designates very serious errors. + // This way these line up with the discriminants for LevelFilter below + // This works because Rust treats field-less enums the same way as C does: + // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-field-less-enumerations + Error = 1, + /// The "warn" level. + /// + /// Designates hazardous situations. + Warn, + /// The "info" level. + /// + /// Designates useful information. + Info, + /// The "debug" level. + /// + /// Designates lower priority information. + Debug, + /// The "trace" level. + /// + /// Designates very low priority, often extremely verbose, information. + Trace, +} + +impl Clone for Level { + #[inline] + fn clone(&self) -> Level { + *self + } +} + +impl PartialEq for Level { + #[inline] + fn eq(&self, other: &Level) -> bool { + *self as usize == *other as usize + } +} + +impl PartialEq for Level { + #[inline] + fn eq(&self, other: &LevelFilter) -> bool { + *self as usize == *other as usize + } +} + +impl PartialOrd for Level { + #[inline] + fn partial_cmp(&self, other: &Level) -> Option { + Some(self.cmp(other)) + } + + #[inline] + fn lt(&self, other: &Level) -> bool { + (*self as usize) < *other as usize + } + + #[inline] + fn le(&self, other: &Level) -> bool { + *self as usize <= *other as usize + } + + #[inline] + fn gt(&self, other: &Level) -> bool { + *self as usize > *other as usize + } + + #[inline] + fn ge(&self, other: &Level) -> bool { + *self as usize >= *other as usize + } +} + +impl PartialOrd for Level { + #[inline] + fn partial_cmp(&self, other: &LevelFilter) -> Option { + Some((*self as usize).cmp(&(*other as usize))) + } + + #[inline] + fn lt(&self, other: &LevelFilter) -> bool { + (*self as usize) < *other as usize + } + + #[inline] + fn le(&self, other: &LevelFilter) -> bool { + *self as usize <= *other as usize + } + + #[inline] + fn gt(&self, other: &LevelFilter) -> bool { + *self as usize > *other as usize + } + + #[inline] + fn ge(&self, other: &LevelFilter) -> bool { + *self as usize >= *other as usize + } +} + +impl Ord for Level { + #[inline] + fn cmp(&self, other: &Level) -> cmp::Ordering { + (*self as usize).cmp(&(*other as usize)) + } +} + +fn ok_or(t: Option, e: E) -> Result { + match t { + Some(t) => Ok(t), + None => Err(e), + } +} + +// Reimplemented here because std::ascii is not available in libcore +fn eq_ignore_ascii_case(a: &str, b: &str) -> bool { + fn to_ascii_uppercase(c: u8) -> u8 { + if c >= b'a' && c <= b'z' { + c - b'a' + b'A' + } else { + c + } + } + + if a.len() == b.len() { + a.bytes() + .zip(b.bytes()) + .all(|(a, b)| to_ascii_uppercase(a) == to_ascii_uppercase(b)) + } else { + false + } +} + +impl FromStr for Level { + type Err = ParseLevelError; + fn from_str(level: &str) -> Result { + ok_or( + LOG_LEVEL_NAMES + .iter() + .position(|&name| eq_ignore_ascii_case(name, level)) + .into_iter() + .filter(|&idx| idx != 0) + .map(|idx| Level::from_usize(idx).unwrap()) + .next(), + ParseLevelError(()), + ) + } +} + +impl fmt::Display for Level { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.pad(self.as_str()) + } +} + +impl Level { + fn from_usize(u: usize) -> Option { + match u { + 1 => Some(Level::Error), + 2 => Some(Level::Warn), + 3 => Some(Level::Info), + 4 => Some(Level::Debug), + 5 => Some(Level::Trace), + _ => None, + } + } + + /// Returns the most verbose logging level. + #[inline] + pub fn max() -> Level { + Level::Trace + } + + /// Converts the `Level` to the equivalent `LevelFilter`. + #[inline] + pub fn to_level_filter(&self) -> LevelFilter { + LevelFilter::from_usize(*self as usize).unwrap() + } + + /// Returns the string representation of the `Level`. + /// + /// This returns the same string as the `fmt::Display` implementation. + pub fn as_str(&self) -> &'static str { + LOG_LEVEL_NAMES[*self as usize] + } + + /// Iterate through all supported logging levels. + /// + /// The order of iteration is from more severe to less severe log messages. + /// + /// # Examples + /// + /// ``` + /// use log::Level; + /// + /// let mut levels = Level::iter(); + /// + /// assert_eq!(Some(Level::Error), levels.next()); + /// assert_eq!(Some(Level::Trace), levels.last()); + /// ``` + pub fn iter() -> impl Iterator { + (1..6).map(|i| Self::from_usize(i).unwrap()) + } +} + +/// An enum representing the available verbosity level filters of the logger. +/// +/// A `LevelFilter` may be compared directly to a [`Level`]. Use this type +/// to get and set the maximum log level with [`max_level()`] and [`set_max_level`]. +/// +/// [`Level`]: enum.Level.html +/// [`max_level()`]: fn.max_level.html +/// [`set_max_level`]: fn.set_max_level.html +#[repr(usize)] +#[derive(Copy, Eq, Debug, Hash)] +pub enum LevelFilter { + /// A level lower than all log levels. + Off, + /// Corresponds to the `Error` log level. + Error, + /// Corresponds to the `Warn` log level. + Warn, + /// Corresponds to the `Info` log level. + Info, + /// Corresponds to the `Debug` log level. + Debug, + /// Corresponds to the `Trace` log level. + Trace, +} + +// Deriving generates terrible impls of these traits + +impl Clone for LevelFilter { + #[inline] + fn clone(&self) -> LevelFilter { + *self + } +} + +impl PartialEq for LevelFilter { + #[inline] + fn eq(&self, other: &LevelFilter) -> bool { + *self as usize == *other as usize + } +} + +impl PartialEq for LevelFilter { + #[inline] + fn eq(&self, other: &Level) -> bool { + other.eq(self) + } +} + +impl PartialOrd for LevelFilter { + #[inline] + fn partial_cmp(&self, other: &LevelFilter) -> Option { + Some(self.cmp(other)) + } + + #[inline] + fn lt(&self, other: &LevelFilter) -> bool { + (*self as usize) < *other as usize + } + + #[inline] + fn le(&self, other: &LevelFilter) -> bool { + *self as usize <= *other as usize + } + + #[inline] + fn gt(&self, other: &LevelFilter) -> bool { + *self as usize > *other as usize + } + + #[inline] + fn ge(&self, other: &LevelFilter) -> bool { + *self as usize >= *other as usize + } +} + +impl PartialOrd for LevelFilter { + #[inline] + fn partial_cmp(&self, other: &Level) -> Option { + Some((*self as usize).cmp(&(*other as usize))) + } + + #[inline] + fn lt(&self, other: &Level) -> bool { + (*self as usize) < *other as usize + } + + #[inline] + fn le(&self, other: &Level) -> bool { + *self as usize <= *other as usize + } + + #[inline] + fn gt(&self, other: &Level) -> bool { + *self as usize > *other as usize + } + + #[inline] + fn ge(&self, other: &Level) -> bool { + *self as usize >= *other as usize + } +} + +impl Ord for LevelFilter { + #[inline] + fn cmp(&self, other: &LevelFilter) -> cmp::Ordering { + (*self as usize).cmp(&(*other as usize)) + } +} + +impl FromStr for LevelFilter { + type Err = ParseLevelError; + fn from_str(level: &str) -> Result { + ok_or( + LOG_LEVEL_NAMES + .iter() + .position(|&name| eq_ignore_ascii_case(name, level)) + .map(|p| LevelFilter::from_usize(p).unwrap()), + ParseLevelError(()), + ) + } +} + +impl fmt::Display for LevelFilter { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.pad(self.as_str()) + } +} + +impl LevelFilter { + fn from_usize(u: usize) -> Option { + match u { + 0 => Some(LevelFilter::Off), + 1 => Some(LevelFilter::Error), + 2 => Some(LevelFilter::Warn), + 3 => Some(LevelFilter::Info), + 4 => Some(LevelFilter::Debug), + 5 => Some(LevelFilter::Trace), + _ => None, + } + } + + /// Returns the most verbose logging level filter. + #[inline] + pub fn max() -> LevelFilter { + LevelFilter::Trace + } + + /// Converts `self` to the equivalent `Level`. + /// + /// Returns `None` if `self` is `LevelFilter::Off`. + #[inline] + pub fn to_level(&self) -> Option { + Level::from_usize(*self as usize) + } + + /// Returns the string representation of the `LevelFilter`. + /// + /// This returns the same string as the `fmt::Display` implementation. + pub fn as_str(&self) -> &'static str { + LOG_LEVEL_NAMES[*self as usize] + } + + /// Iterate through all supported filtering levels. + /// + /// The order of iteration is from less to more verbose filtering. + /// + /// # Examples + /// + /// ``` + /// use log::LevelFilter; + /// + /// let mut levels = LevelFilter::iter(); + /// + /// assert_eq!(Some(LevelFilter::Off), levels.next()); + /// assert_eq!(Some(LevelFilter::Trace), levels.last()); + /// ``` + pub fn iter() -> impl Iterator { + (0..6).map(|i| Self::from_usize(i).unwrap()) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), +} + +impl<'a> MaybeStaticStr<'a> { + #[inline] + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Static(s) => s, + MaybeStaticStr::Borrowed(s) => s, + } + } +} + +/// The "payload" of a log message. +/// +/// # Use +/// +/// `Record` structures are passed as parameters to the [`log`][method.log] +/// method of the [`Log`] trait. Logger implementors manipulate these +/// structures in order to display log messages. `Record`s are automatically +/// created by the [`log!`] macro and so are not seen by log users. +/// +/// Note that the [`level()`] and [`target()`] accessors are equivalent to +/// `self.metadata().level()` and `self.metadata().target()` respectively. +/// These methods are provided as a convenience for users of this structure. +/// +/// # Example +/// +/// The following example shows a simple logger that displays the level, +/// module path, and message of any `Record` that is passed to it. +/// +/// ```edition2018 +/// struct SimpleLogger; +/// +/// impl log::Log for SimpleLogger { +/// fn enabled(&self, metadata: &log::Metadata) -> bool { +/// true +/// } +/// +/// fn log(&self, record: &log::Record) { +/// if !self.enabled(record.metadata()) { +/// return; +/// } +/// +/// println!("{}:{} -- {}", +/// record.level(), +/// record.target(), +/// record.args()); +/// } +/// fn flush(&self) {} +/// } +/// ``` +/// +/// [method.log]: trait.Log.html#tymethod.log +/// [`Log`]: trait.Log.html +/// [`log!`]: macro.log.html +/// [`level()`]: struct.Record.html#method.level +/// [`target()`]: struct.Record.html#method.target +#[derive(Clone, Debug)] +pub struct Record<'a> { + metadata: Metadata<'a>, + args: fmt::Arguments<'a>, + module_path: Option>, + file: Option>, + line: Option, + #[cfg(feature = "kv_unstable")] + key_values: KeyValues<'a>, +} + +// This wrapper type is only needed so we can +// `#[derive(Debug)]` on `Record`. It also +// provides a useful `Debug` implementation for +// the underlying `Source`. +#[cfg(feature = "kv_unstable")] +#[derive(Clone)] +struct KeyValues<'a>(&'a dyn kv::Source); + +#[cfg(feature = "kv_unstable")] +impl<'a> fmt::Debug for KeyValues<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut visitor = f.debug_map(); + self.0.visit(&mut visitor).map_err(|_| fmt::Error)?; + visitor.finish() + } +} + +impl<'a> Record<'a> { + /// Returns a new builder. + #[inline] + pub fn builder() -> RecordBuilder<'a> { + RecordBuilder::new() + } + + /// The message body. + #[inline] + pub fn args(&self) -> &fmt::Arguments<'a> { + &self.args + } + + /// Metadata about the log directive. + #[inline] + pub fn metadata(&self) -> &Metadata<'a> { + &self.metadata + } + + /// The verbosity level of the message. + #[inline] + pub fn level(&self) -> Level { + self.metadata.level() + } + + /// The name of the target of the directive. + #[inline] + pub fn target(&self) -> &'a str { + self.metadata.target() + } + + /// The module path of the message. + #[inline] + pub fn module_path(&self) -> Option<&'a str> { + self.module_path.map(|s| s.get()) + } + + /// The module path of the message, if it is a `'static` string. + #[inline] + pub fn module_path_static(&self) -> Option<&'static str> { + match self.module_path { + Some(MaybeStaticStr::Static(s)) => Some(s), + _ => None, + } + } + + /// The source file containing the message. + #[inline] + pub fn file(&self) -> Option<&'a str> { + self.file.map(|s| s.get()) + } + + /// The module path of the message, if it is a `'static` string. + #[inline] + pub fn file_static(&self) -> Option<&'static str> { + match self.file { + Some(MaybeStaticStr::Static(s)) => Some(s), + _ => None, + } + } + + /// The line containing the message. + #[inline] + pub fn line(&self) -> Option { + self.line + } + + /// The structured key-value pairs associated with the message. + #[cfg(feature = "kv_unstable")] + #[inline] + pub fn key_values(&self) -> &dyn kv::Source { + self.key_values.0 + } + + /// Create a new [`RecordBuilder`](struct.RecordBuilder.html) based on this record. + #[cfg(feature = "kv_unstable")] + #[inline] + pub fn to_builder(&self) -> RecordBuilder { + RecordBuilder { + record: Record { + metadata: Metadata { + level: self.metadata.level, + target: self.metadata.target, + }, + args: self.args, + module_path: self.module_path, + file: self.file, + line: self.line, + key_values: self.key_values.clone(), + }, + } + } +} + +/// Builder for [`Record`](struct.Record.html). +/// +/// Typically should only be used by log library creators or for testing and "shim loggers". +/// The `RecordBuilder` can set the different parameters of `Record` object, and returns +/// the created object when `build` is called. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::{Level, Record}; +/// +/// let record = Record::builder() +/// .args(format_args!("Error!")) +/// .level(Level::Error) +/// .target("myApp") +/// .file(Some("server.rs")) +/// .line(Some(144)) +/// .module_path(Some("server")) +/// .build(); +/// ``` +/// +/// Alternatively, use [`MetadataBuilder`](struct.MetadataBuilder.html): +/// +/// ```edition2018 +/// use log::{Record, Level, MetadataBuilder}; +/// +/// let error_metadata = MetadataBuilder::new() +/// .target("myApp") +/// .level(Level::Error) +/// .build(); +/// +/// let record = Record::builder() +/// .metadata(error_metadata) +/// .args(format_args!("Error!")) +/// .line(Some(433)) +/// .file(Some("app.rs")) +/// .module_path(Some("server")) +/// .build(); +/// ``` +#[derive(Debug)] +pub struct RecordBuilder<'a> { + record: Record<'a>, +} + +impl<'a> RecordBuilder<'a> { + /// Construct new `RecordBuilder`. + /// + /// The default options are: + /// + /// - `args`: [`format_args!("")`] + /// - `metadata`: [`Metadata::builder().build()`] + /// - `module_path`: `None` + /// - `file`: `None` + /// - `line`: `None` + /// + /// [`format_args!("")`]: https://doc.rust-lang.org/std/macro.format_args.html + /// [`Metadata::builder().build()`]: struct.MetadataBuilder.html#method.build + #[inline] + pub fn new() -> RecordBuilder<'a> { + RecordBuilder { + record: Record { + args: format_args!(""), + metadata: Metadata::builder().build(), + module_path: None, + file: None, + line: None, + #[cfg(feature = "kv_unstable")] + key_values: KeyValues(&Option::None::<(kv::Key, kv::Value)>), + }, + } + } + + /// Set [`args`](struct.Record.html#method.args). + #[inline] + pub fn args(&mut self, args: fmt::Arguments<'a>) -> &mut RecordBuilder<'a> { + self.record.args = args; + self + } + + /// Set [`metadata`](struct.Record.html#method.metadata). Construct a `Metadata` object with [`MetadataBuilder`](struct.MetadataBuilder.html). + #[inline] + pub fn metadata(&mut self, metadata: Metadata<'a>) -> &mut RecordBuilder<'a> { + self.record.metadata = metadata; + self + } + + /// Set [`Metadata::level`](struct.Metadata.html#method.level). + #[inline] + pub fn level(&mut self, level: Level) -> &mut RecordBuilder<'a> { + self.record.metadata.level = level; + self + } + + /// Set [`Metadata::target`](struct.Metadata.html#method.target) + #[inline] + pub fn target(&mut self, target: &'a str) -> &mut RecordBuilder<'a> { + self.record.metadata.target = target; + self + } + + /// Set [`module_path`](struct.Record.html#method.module_path) + #[inline] + pub fn module_path(&mut self, path: Option<&'a str>) -> &mut RecordBuilder<'a> { + self.record.module_path = path.map(MaybeStaticStr::Borrowed); + self + } + + /// Set [`module_path`](struct.Record.html#method.module_path) to a `'static` string + #[inline] + pub fn module_path_static(&mut self, path: Option<&'static str>) -> &mut RecordBuilder<'a> { + self.record.module_path = path.map(MaybeStaticStr::Static); + self + } + + /// Set [`file`](struct.Record.html#method.file) + #[inline] + pub fn file(&mut self, file: Option<&'a str>) -> &mut RecordBuilder<'a> { + self.record.file = file.map(MaybeStaticStr::Borrowed); + self + } + + /// Set [`file`](struct.Record.html#method.file) to a `'static` string. + #[inline] + pub fn file_static(&mut self, file: Option<&'static str>) -> &mut RecordBuilder<'a> { + self.record.file = file.map(MaybeStaticStr::Static); + self + } + + /// Set [`line`](struct.Record.html#method.line) + #[inline] + pub fn line(&mut self, line: Option) -> &mut RecordBuilder<'a> { + self.record.line = line; + self + } + + /// Set [`key_values`](struct.Record.html#method.key_values) + #[cfg(feature = "kv_unstable")] + #[inline] + pub fn key_values(&mut self, kvs: &'a dyn kv::Source) -> &mut RecordBuilder<'a> { + self.record.key_values = KeyValues(kvs); + self + } + + /// Invoke the builder and return a `Record` + #[inline] + pub fn build(&self) -> Record<'a> { + self.record.clone() + } +} + +/// Metadata about a log message. +/// +/// # Use +/// +/// `Metadata` structs are created when users of the library use +/// logging macros. +/// +/// They are consumed by implementations of the `Log` trait in the +/// `enabled` method. +/// +/// `Record`s use `Metadata` to determine the log message's severity +/// and target. +/// +/// Users should use the `log_enabled!` macro in their code to avoid +/// constructing expensive log messages. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::{Record, Level, Metadata}; +/// +/// struct MyLogger; +/// +/// impl log::Log for MyLogger { +/// fn enabled(&self, metadata: &Metadata) -> bool { +/// metadata.level() <= Level::Info +/// } +/// +/// fn log(&self, record: &Record) { +/// if self.enabled(record.metadata()) { +/// println!("{} - {}", record.level(), record.args()); +/// } +/// } +/// fn flush(&self) {} +/// } +/// +/// # fn main(){} +/// ``` +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct Metadata<'a> { + level: Level, + target: &'a str, +} + +impl<'a> Metadata<'a> { + /// Returns a new builder. + #[inline] + pub fn builder() -> MetadataBuilder<'a> { + MetadataBuilder::new() + } + + /// The verbosity level of the message. + #[inline] + pub fn level(&self) -> Level { + self.level + } + + /// The name of the target of the directive. + #[inline] + pub fn target(&self) -> &'a str { + self.target + } +} + +/// Builder for [`Metadata`](struct.Metadata.html). +/// +/// Typically should only be used by log library creators or for testing and "shim loggers". +/// The `MetadataBuilder` can set the different parameters of a `Metadata` object, and returns +/// the created object when `build` is called. +/// +/// # Example +/// +/// ```edition2018 +/// let target = "myApp"; +/// use log::{Level, MetadataBuilder}; +/// let metadata = MetadataBuilder::new() +/// .level(Level::Debug) +/// .target(target) +/// .build(); +/// ``` +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct MetadataBuilder<'a> { + metadata: Metadata<'a>, +} + +impl<'a> MetadataBuilder<'a> { + /// Construct a new `MetadataBuilder`. + /// + /// The default options are: + /// + /// - `level`: `Level::Info` + /// - `target`: `""` + #[inline] + pub fn new() -> MetadataBuilder<'a> { + MetadataBuilder { + metadata: Metadata { + level: Level::Info, + target: "", + }, + } + } + + /// Setter for [`level`](struct.Metadata.html#method.level). + #[inline] + pub fn level(&mut self, arg: Level) -> &mut MetadataBuilder<'a> { + self.metadata.level = arg; + self + } + + /// Setter for [`target`](struct.Metadata.html#method.target). + #[inline] + pub fn target(&mut self, target: &'a str) -> &mut MetadataBuilder<'a> { + self.metadata.target = target; + self + } + + /// Returns a `Metadata` object. + #[inline] + pub fn build(&self) -> Metadata<'a> { + self.metadata.clone() + } +} + +/// A trait encapsulating the operations required of a logger. +pub trait Log: Sync + Send { + /// Determines if a log message with the specified metadata would be + /// logged. + /// + /// This is used by the `log_enabled!` macro to allow callers to avoid + /// expensive computation of log message arguments if the message would be + /// discarded anyway. + /// + /// # For implementors + /// + /// This method isn't called automatically by the `log!` macros. + /// It's up to an implementation of the `Log` trait to call `enabled` in its own + /// `log` method implementation to guarantee that filtering is applied. + fn enabled(&self, metadata: &Metadata) -> bool; + + /// Logs the `Record`. + /// + /// # For implementors + /// + /// Note that `enabled` is *not* necessarily called before this method. + /// Implementations of `log` should perform all necessary filtering + /// internally. + fn log(&self, record: &Record); + + /// Flushes any buffered records. + fn flush(&self); +} + +// Just used as a dummy initial value for LOGGER +struct NopLogger; + +impl Log for NopLogger { + fn enabled(&self, _: &Metadata) -> bool { + false + } + + fn log(&self, _: &Record) {} + fn flush(&self) {} +} + +impl Log for &'_ T +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + (**self).enabled(metadata) + } + + fn log(&self, record: &Record) { + (**self).log(record) + } + fn flush(&self) { + (**self).flush() + } +} + +#[cfg(feature = "std")] +impl Log for std::boxed::Box +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + self.as_ref().enabled(metadata) + } + + fn log(&self, record: &Record) { + self.as_ref().log(record) + } + fn flush(&self) { + self.as_ref().flush() + } +} + +#[cfg(feature = "std")] +impl Log for std::sync::Arc +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + self.as_ref().enabled(metadata) + } + + fn log(&self, record: &Record) { + self.as_ref().log(record) + } + fn flush(&self) { + self.as_ref().flush() + } +} + +/// Sets the global maximum log level. +/// +/// Generally, this should only be called by the active logging implementation. +/// +/// Note that `Trace` is the maximum level, because it provides the maximum amount of detail in the emitted logs. +#[inline] +pub fn set_max_level(level: LevelFilter) { + MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed) +} + +/// Returns the current maximum log level. +/// +/// The [`log!`], [`error!`], [`warn!`], [`info!`], [`debug!`], and [`trace!`] macros check +/// this value and discard any message logged at a higher level. The maximum +/// log level is set by the [`set_max_level`] function. +/// +/// [`log!`]: macro.log.html +/// [`error!`]: macro.error.html +/// [`warn!`]: macro.warn.html +/// [`info!`]: macro.info.html +/// [`debug!`]: macro.debug.html +/// [`trace!`]: macro.trace.html +/// [`set_max_level`]: fn.set_max_level.html +#[inline(always)] +pub fn max_level() -> LevelFilter { + // Since `LevelFilter` is `repr(usize)`, + // this transmute is sound if and only if `MAX_LOG_LEVEL_FILTER` + // is set to a usize that is a valid discriminant for `LevelFilter`. + // Since `MAX_LOG_LEVEL_FILTER` is private, the only time it's set + // is by `set_max_level` above, i.e. by casting a `LevelFilter` to `usize`. + // So any usize stored in `MAX_LOG_LEVEL_FILTER` is a valid discriminant. + unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) } +} + +/// Sets the global logger to a `Box`. +/// +/// This is a simple convenience wrapper over `set_logger`, which takes a +/// `Box` rather than a `&'static Log`. See the documentation for +/// [`set_logger`] for more details. +/// +/// Requires the `std` feature. +/// +/// # Errors +/// +/// An error is returned if a logger has already been set. +/// +/// [`set_logger`]: fn.set_logger.html +#[cfg(all(feature = "std", atomic_cas))] +pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { + set_logger_inner(|| Box::leak(logger)) +} + +/// Sets the global logger to a `&'static Log`. +/// +/// This function may only be called once in the lifetime of a program. Any log +/// events that occur before the call to `set_logger` completes will be ignored. +/// +/// This function does not typically need to be called manually. Logger +/// implementations should provide an initialization method that installs the +/// logger internally. +/// +/// # Availability +/// +/// This method is available even when the `std` feature is disabled. However, +/// it is currently unavailable on `thumbv6` targets, which lack support for +/// some atomic operations which are used by this function. Even on those +/// targets, [`set_logger_racy`] will be available. +/// +/// # Errors +/// +/// An error is returned if a logger has already been set. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::{error, info, warn, Record, Level, Metadata, LevelFilter}; +/// +/// static MY_LOGGER: MyLogger = MyLogger; +/// +/// struct MyLogger; +/// +/// impl log::Log for MyLogger { +/// fn enabled(&self, metadata: &Metadata) -> bool { +/// metadata.level() <= Level::Info +/// } +/// +/// fn log(&self, record: &Record) { +/// if self.enabled(record.metadata()) { +/// println!("{} - {}", record.level(), record.args()); +/// } +/// } +/// fn flush(&self) {} +/// } +/// +/// # fn main(){ +/// log::set_logger(&MY_LOGGER).unwrap(); +/// log::set_max_level(LevelFilter::Info); +/// +/// info!("hello log"); +/// warn!("warning"); +/// error!("oops"); +/// # } +/// ``` +/// +/// [`set_logger_racy`]: fn.set_logger_racy.html +#[cfg(atomic_cas)] +pub fn set_logger(logger: &'static dyn Log) -> Result<(), SetLoggerError> { + set_logger_inner(|| logger) +} + +#[cfg(atomic_cas)] +fn set_logger_inner(make_logger: F) -> Result<(), SetLoggerError> +where + F: FnOnce() -> &'static dyn Log, +{ + let old_state = match STATE.compare_exchange( + UNINITIALIZED, + INITIALIZING, + Ordering::SeqCst, + Ordering::SeqCst, + ) { + Ok(s) | Err(s) => s, + }; + match old_state { + UNINITIALIZED => { + unsafe { + LOGGER = make_logger(); + } + STATE.store(INITIALIZED, Ordering::SeqCst); + Ok(()) + } + INITIALIZING => { + while STATE.load(Ordering::SeqCst) == INITIALIZING { + // TODO: replace with `hint::spin_loop` once MSRV is 1.49.0. + #[allow(deprecated)] + std::sync::atomic::spin_loop_hint(); + } + Err(SetLoggerError(())) + } + _ => Err(SetLoggerError(())), + } +} + +/// A thread-unsafe version of [`set_logger`]. +/// +/// This function is available on all platforms, even those that do not have +/// support for atomics that is needed by [`set_logger`]. +/// +/// In almost all cases, [`set_logger`] should be preferred. +/// +/// # Safety +/// +/// This function is only safe to call when no other logger initialization +/// function is called while this function still executes. +/// +/// This can be upheld by (for example) making sure that **there are no other +/// threads**, and (on embedded) that **interrupts are disabled**. +/// +/// It is safe to use other logging functions while this function runs +/// (including all logging macros). +/// +/// [`set_logger`]: fn.set_logger.html +pub unsafe fn set_logger_racy(logger: &'static dyn Log) -> Result<(), SetLoggerError> { + match STATE.load(Ordering::SeqCst) { + UNINITIALIZED => { + LOGGER = logger; + STATE.store(INITIALIZED, Ordering::SeqCst); + Ok(()) + } + INITIALIZING => { + // This is just plain UB, since we were racing another initialization function + unreachable!("set_logger_racy must not be used with other initialization functions") + } + _ => Err(SetLoggerError(())), + } +} + +/// The type returned by [`set_logger`] if [`set_logger`] has already been called. +/// +/// [`set_logger`]: fn.set_logger.html +#[allow(missing_copy_implementations)] +#[derive(Debug)] +pub struct SetLoggerError(()); + +impl fmt::Display for SetLoggerError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(SET_LOGGER_ERROR) + } +} + +// The Error trait is not available in libcore +#[cfg(feature = "std")] +impl error::Error for SetLoggerError {} + +/// The type returned by [`from_str`] when the string doesn't match any of the log levels. +/// +/// [`from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str +#[allow(missing_copy_implementations)] +#[derive(Debug, PartialEq)] +pub struct ParseLevelError(()); + +impl fmt::Display for ParseLevelError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(LEVEL_PARSE_ERROR) + } +} + +// The Error trait is not available in libcore +#[cfg(feature = "std")] +impl error::Error for ParseLevelError {} + +/// Returns a reference to the logger. +/// +/// If a logger has not been set, a no-op implementation is returned. +pub fn logger() -> &'static dyn Log { + if STATE.load(Ordering::SeqCst) != INITIALIZED { + static NOP: NopLogger = NopLogger; + &NOP + } else { + unsafe { LOGGER } + } +} + +// WARNING: this is not part of the crate's public API and is subject to change at any time +#[doc(hidden)] +#[cfg(not(feature = "kv_unstable"))] +pub fn __private_api_log( + args: fmt::Arguments, + level: Level, + &(target, module_path, file, line): &(&str, &'static str, &'static str, u32), + kvs: Option<&[(&str, &str)]>, +) { + if kvs.is_some() { + panic!( + "key-value support is experimental and must be enabled using the `kv_unstable` feature" + ) + } + + logger().log( + &Record::builder() + .args(args) + .level(level) + .target(target) + .module_path_static(Some(module_path)) + .file_static(Some(file)) + .line(Some(line)) + .build(), + ); +} + +// WARNING: this is not part of the crate's public API and is subject to change at any time +#[doc(hidden)] +#[cfg(feature = "kv_unstable")] +pub fn __private_api_log( + args: fmt::Arguments, + level: Level, + &(target, module_path, file, line): &(&str, &'static str, &'static str, u32), + kvs: Option<&[(&str, &dyn kv::ToValue)]>, +) { + logger().log( + &Record::builder() + .args(args) + .level(level) + .target(target) + .module_path_static(Some(module_path)) + .file_static(Some(file)) + .line(Some(line)) + .key_values(&kvs) + .build(), + ); +} + +// WARNING: this is not part of the crate's public API and is subject to change at any time +#[doc(hidden)] +pub fn __private_api_enabled(level: Level, target: &str) -> bool { + logger().enabled(&Metadata::builder().level(level).target(target).build()) +} + +// WARNING: this is not part of the crate's public API and is subject to change at any time +#[doc(hidden)] +pub mod __private_api { + pub use std::option::Option; +} + +/// The statically resolved maximum log level. +/// +/// See the crate level documentation for information on how to configure this. +/// +/// This value is checked by the log macros, but not by the `Log`ger returned by +/// the [`logger`] function. Code that manually calls functions on that value +/// should compare the level against this value. +/// +/// [`logger`]: fn.logger.html +pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL_INNER; + +cfg_if! { + if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug; + } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace; + } else if #[cfg(feature = "max_level_off")] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off; + } else if #[cfg(feature = "max_level_error")] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error; + } else if #[cfg(feature = "max_level_warn")] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn; + } else if #[cfg(feature = "max_level_info")] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info; + } else if #[cfg(feature = "max_level_debug")] { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug; + } else { + const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace; + } +} + +#[cfg(test)] +mod tests { + extern crate std; + use super::{Level, LevelFilter, ParseLevelError}; + use tests::std::string::ToString; + + #[test] + fn test_levelfilter_from_str() { + let tests = [ + ("off", Ok(LevelFilter::Off)), + ("error", Ok(LevelFilter::Error)), + ("warn", Ok(LevelFilter::Warn)), + ("info", Ok(LevelFilter::Info)), + ("debug", Ok(LevelFilter::Debug)), + ("trace", Ok(LevelFilter::Trace)), + ("OFF", Ok(LevelFilter::Off)), + ("ERROR", Ok(LevelFilter::Error)), + ("WARN", Ok(LevelFilter::Warn)), + ("INFO", Ok(LevelFilter::Info)), + ("DEBUG", Ok(LevelFilter::Debug)), + ("TRACE", Ok(LevelFilter::Trace)), + ("asdf", Err(ParseLevelError(()))), + ]; + for &(s, ref expected) in &tests { + assert_eq!(expected, &s.parse()); + } + } + + #[test] + fn test_level_from_str() { + let tests = [ + ("OFF", Err(ParseLevelError(()))), + ("error", Ok(Level::Error)), + ("warn", Ok(Level::Warn)), + ("info", Ok(Level::Info)), + ("debug", Ok(Level::Debug)), + ("trace", Ok(Level::Trace)), + ("ERROR", Ok(Level::Error)), + ("WARN", Ok(Level::Warn)), + ("INFO", Ok(Level::Info)), + ("DEBUG", Ok(Level::Debug)), + ("TRACE", Ok(Level::Trace)), + ("asdf", Err(ParseLevelError(()))), + ]; + for &(s, ref expected) in &tests { + assert_eq!(expected, &s.parse()); + } + } + + #[test] + fn test_level_as_str() { + let tests = &[ + (Level::Error, "ERROR"), + (Level::Warn, "WARN"), + (Level::Info, "INFO"), + (Level::Debug, "DEBUG"), + (Level::Trace, "TRACE"), + ]; + for (input, expected) in tests { + assert_eq!(*expected, input.as_str()); + } + } + + #[test] + fn test_level_show() { + assert_eq!("INFO", Level::Info.to_string()); + assert_eq!("ERROR", Level::Error.to_string()); + } + + #[test] + fn test_levelfilter_show() { + assert_eq!("OFF", LevelFilter::Off.to_string()); + assert_eq!("ERROR", LevelFilter::Error.to_string()); + } + + #[test] + fn test_cross_cmp() { + assert!(Level::Debug > LevelFilter::Error); + assert!(LevelFilter::Warn < Level::Trace); + assert!(LevelFilter::Off < Level::Error); + } + + #[test] + fn test_cross_eq() { + assert!(Level::Error == LevelFilter::Error); + assert!(LevelFilter::Off != Level::Error); + assert!(Level::Trace == LevelFilter::Trace); + } + + #[test] + fn test_to_level() { + assert_eq!(Some(Level::Error), LevelFilter::Error.to_level()); + assert_eq!(None, LevelFilter::Off.to_level()); + assert_eq!(Some(Level::Debug), LevelFilter::Debug.to_level()); + } + + #[test] + fn test_to_level_filter() { + assert_eq!(LevelFilter::Error, Level::Error.to_level_filter()); + assert_eq!(LevelFilter::Trace, Level::Trace.to_level_filter()); + } + + #[test] + fn test_level_filter_as_str() { + let tests = &[ + (LevelFilter::Off, "OFF"), + (LevelFilter::Error, "ERROR"), + (LevelFilter::Warn, "WARN"), + (LevelFilter::Info, "INFO"), + (LevelFilter::Debug, "DEBUG"), + (LevelFilter::Trace, "TRACE"), + ]; + for (input, expected) in tests { + assert_eq!(*expected, input.as_str()); + } + } + + #[test] + #[cfg(feature = "std")] + fn test_error_trait() { + use super::SetLoggerError; + let e = SetLoggerError(()); + assert_eq!( + &e.to_string(), + "attempted to set a logger after the logging system \ + was already initialized" + ); + } + + #[test] + fn test_metadata_builder() { + use super::MetadataBuilder; + let target = "myApp"; + let metadata_test = MetadataBuilder::new() + .level(Level::Debug) + .target(target) + .build(); + assert_eq!(metadata_test.level(), Level::Debug); + assert_eq!(metadata_test.target(), "myApp"); + } + + #[test] + fn test_metadata_convenience_builder() { + use super::Metadata; + let target = "myApp"; + let metadata_test = Metadata::builder() + .level(Level::Debug) + .target(target) + .build(); + assert_eq!(metadata_test.level(), Level::Debug); + assert_eq!(metadata_test.target(), "myApp"); + } + + #[test] + fn test_record_builder() { + use super::{MetadataBuilder, RecordBuilder}; + let target = "myApp"; + let metadata = MetadataBuilder::new().target(target).build(); + let fmt_args = format_args!("hello"); + let record_test = RecordBuilder::new() + .args(fmt_args) + .metadata(metadata) + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .build(); + assert_eq!(record_test.metadata().target(), "myApp"); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + fn test_record_convenience_builder() { + use super::{Metadata, Record}; + let target = "myApp"; + let metadata = Metadata::builder().target(target).build(); + let fmt_args = format_args!("hello"); + let record_test = Record::builder() + .args(fmt_args) + .metadata(metadata) + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .build(); + assert_eq!(record_test.target(), "myApp"); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + fn test_record_complete_builder() { + use super::{Level, Record}; + let target = "myApp"; + let record_test = Record::builder() + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .target(target) + .level(Level::Error) + .build(); + assert_eq!(record_test.target(), "myApp"); + assert_eq!(record_test.level(), Level::Error); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + #[cfg(feature = "kv_unstable")] + fn test_record_key_values_builder() { + use super::Record; + use kv::{self, Visitor}; + + struct TestVisitor { + seen_pairs: usize, + } + + impl<'kvs> Visitor<'kvs> for TestVisitor { + fn visit_pair( + &mut self, + _: kv::Key<'kvs>, + _: kv::Value<'kvs>, + ) -> Result<(), kv::Error> { + self.seen_pairs += 1; + Ok(()) + } + } + + let kvs: &[(&str, i32)] = &[("a", 1), ("b", 2)]; + let record_test = Record::builder().key_values(&kvs).build(); + + let mut visitor = TestVisitor { seen_pairs: 0 }; + + record_test.key_values().visit(&mut visitor).unwrap(); + + assert_eq!(2, visitor.seen_pairs); + } + + #[test] + #[cfg(feature = "kv_unstable")] + fn test_record_key_values_get_coerce() { + use super::Record; + + let kvs: &[(&str, &str)] = &[("a", "1"), ("b", "2")]; + let record = Record::builder().key_values(&kvs).build(); + + assert_eq!( + "2", + record + .key_values() + .get("b".into()) + .expect("missing key") + .to_borrowed_str() + .expect("invalid value") + ); + } + + // Test that the `impl Log for Foo` blocks work + // This test mostly operates on a type level, so failures will be compile errors + #[test] + fn test_foreign_impl() { + use super::Log; + #[cfg(feature = "std")] + use std::sync::Arc; + + fn assert_is_log() {} + + assert_is_log::<&dyn Log>(); + + #[cfg(feature = "std")] + assert_is_log::>(); + + #[cfg(feature = "std")] + assert_is_log::>(); + + // Assert these statements for all T: Log + ?Sized + #[allow(unused)] + fn forall() { + #[cfg(feature = "std")] + assert_is_log::>(); + + assert_is_log::<&T>(); + + #[cfg(feature = "std")] + assert_is_log::>(); + } + } +} diff --git a/vendor/log/src/macros.rs b/vendor/log/src/macros.rs index a1d71d9..f214d0d 100644 --- a/vendor/log/src/macros.rs +++ b/vendor/log/src/macros.rs @@ -1,280 +1,280 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/// The standard logging macro. -/// -/// This macro will generically log with the specified `Level` and `format!` -/// based argument list. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::{log, Level}; -/// -/// # fn main() { -/// let data = (42, "Forty-two"); -/// let private_data = "private"; -/// -/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1); -/// log!(target: "app_events", Level::Warn, "App warning: {}, {}, {}", -/// data.0, data.1, private_data); -/// # } -/// ``` -#[macro_export(local_inner_macros)] -macro_rules! log { - // log!(target: "my_target", Level::Info; key1 = 42, key2 = true; "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($key:tt = $value:expr),+; $($arg:tt)+) => ({ - let lvl = $lvl; - if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { - $crate::__private_api_log( - __log_format_args!($($arg)+), - lvl, - &($target, __log_module_path!(), __log_file!(), __log_line!()), - $crate::__private_api::Option::Some(&[$((__log_key!($key), &$value)),+]) - ); - } - }); - - // log!(target: "my_target", Level::Info; "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ - let lvl = $lvl; - if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { - $crate::__private_api_log( - __log_format_args!($($arg)+), - lvl, - &($target, __log_module_path!(), __log_file!(), __log_line!()), - $crate::__private_api::Option::None, - ); - } - }); - - // log!(Level::Info, "a log event") - ($lvl:expr, $($arg:tt)+) => (log!(target: __log_module_path!(), $lvl, $($arg)+)); -} - -/// Logs a message at the error level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::error; -/// -/// # fn main() { -/// let (err_info, port) = ("No connection", 22); -/// -/// error!("Error: {} on port {}", err_info, port); -/// error!(target: "app_events", "App Error: {}, Port: {}", err_info, 22); -/// # } -/// ``` -#[macro_export(local_inner_macros)] -macro_rules! error { - // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // error!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Error, $($arg)+)); - - // error!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Error, $($arg)+)) -} - -/// Logs a message at the warn level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::warn; -/// -/// # fn main() { -/// let warn_description = "Invalid Input"; -/// -/// warn!("Warning! {}!", warn_description); -/// warn!(target: "input_events", "App received warning: {}", warn_description); -/// # } -/// ``` -#[macro_export(local_inner_macros)] -macro_rules! warn { - // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // warn!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Warn, $($arg)+)); - - // warn!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Warn, $($arg)+)) -} - -/// Logs a message at the info level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::info; -/// -/// # fn main() { -/// # struct Connection { port: u32, speed: f32 } -/// let conn_info = Connection { port: 40, speed: 3.20 }; -/// -/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); -/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}", -/// conn_info.port, conn_info.speed); -/// # } -/// ``` -#[macro_export(local_inner_macros)] -macro_rules! info { - // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // info!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Info, $($arg)+)); - - // info!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Info, $($arg)+)) -} - -/// Logs a message at the debug level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::debug; -/// -/// # fn main() { -/// # struct Position { x: f32, y: f32 } -/// let pos = Position { x: 3.234, y: -1.223 }; -/// -/// debug!("New position: x: {}, y: {}", pos.x, pos.y); -/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); -/// # } -/// ``` -#[macro_export(local_inner_macros)] -macro_rules! debug { - // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // debug!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Debug, $($arg)+)); - - // debug!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Debug, $($arg)+)) -} - -/// Logs a message at the trace level. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::trace; -/// -/// # fn main() { -/// # struct Position { x: f32, y: f32 } -/// let pos = Position { x: 3.234, y: -1.223 }; -/// -/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); -/// trace!(target: "app_events", "x is {} and y is {}", -/// if pos.x >= 0.0 { "positive" } else { "negative" }, -/// if pos.y >= 0.0 { "positive" } else { "negative" }); -/// # } -/// ``` -#[macro_export(local_inner_macros)] -macro_rules! trace { - // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") - // trace!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Trace, $($arg)+)); - - // trace!("a {} event", "log") - ($($arg:tt)+) => (log!($crate::Level::Trace, $($arg)+)) -} - -/// Determines if a message logged at the specified level in that module will -/// be logged. -/// -/// This can be used to avoid expensive computation of log message arguments if -/// the message would be ignored anyway. -/// -/// # Examples -/// -/// ```edition2018 -/// use log::Level::Debug; -/// use log::{debug, log_enabled}; -/// -/// # fn foo() { -/// if log_enabled!(Debug) { -/// let data = expensive_call(); -/// debug!("expensive debug data: {} {}", data.x, data.y); -/// } -/// if log_enabled!(target: "Global", Debug) { -/// let data = expensive_call(); -/// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); -/// } -/// # } -/// # struct Data { x: u32, y: u32 } -/// # fn expensive_call() -> Data { Data { x: 0, y: 0 } } -/// # fn main() {} -/// ``` -#[macro_export(local_inner_macros)] -macro_rules! log_enabled { - (target: $target:expr, $lvl:expr) => {{ - let lvl = $lvl; - lvl <= $crate::STATIC_MAX_LEVEL - && lvl <= $crate::max_level() - && $crate::__private_api_enabled(lvl, $target) - }}; - ($lvl:expr) => { - log_enabled!(target: __log_module_path!(), $lvl) - }; -} - -// The log macro above cannot invoke format_args directly because it uses -// local_inner_macros. A format_args invocation there would resolve to -// $crate::format_args which does not exist. Instead invoke format_args here -// outside of local_inner_macros so that it resolves (probably) to -// core::format_args or std::format_args. Same for the several macros that -// follow. -// -// This is a workaround until we drop support for pre-1.30 compilers. At that -// point we can remove use of local_inner_macros, use $crate:: when invoking -// local macros, and invoke format_args directly. -#[doc(hidden)] -#[macro_export] -macro_rules! __log_format_args { - ($($args:tt)*) => { - format_args!($($args)*) - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __log_module_path { - () => { - module_path!() - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __log_file { - () => { - file!() - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __log_line { - () => { - line!() - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __log_key { - // key1 = 42 - ($($args:ident)*) => { - stringify!($($args)*) - }; - // "key1" = 42 - ($($args:expr)*) => { - $($args)* - }; -} +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The standard logging macro. +/// +/// This macro will generically log with the specified `Level` and `format!` +/// based argument list. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::{log, Level}; +/// +/// # fn main() { +/// let data = (42, "Forty-two"); +/// let private_data = "private"; +/// +/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1); +/// log!(target: "app_events", Level::Warn, "App warning: {}, {}, {}", +/// data.0, data.1, private_data); +/// # } +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! log { + // log!(target: "my_target", Level::Info; key1 = 42, key2 = true; "a {} event", "log"); + (target: $target:expr, $lvl:expr, $($key:tt = $value:expr),+; $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api_log( + __log_format_args!($($arg)+), + lvl, + &($target, __log_module_path!(), __log_file!(), __log_line!()), + $crate::__private_api::Option::Some(&[$((__log_key!($key), &$value)),+]) + ); + } + }); + + // log!(target: "my_target", Level::Info; "a {} event", "log"); + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api_log( + __log_format_args!($($arg)+), + lvl, + &($target, __log_module_path!(), __log_file!(), __log_line!()), + $crate::__private_api::Option::None, + ); + } + }); + + // log!(Level::Info, "a log event") + ($lvl:expr, $($arg:tt)+) => (log!(target: __log_module_path!(), $lvl, $($arg)+)); +} + +/// Logs a message at the error level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::error; +/// +/// # fn main() { +/// let (err_info, port) = ("No connection", 22); +/// +/// error!("Error: {} on port {}", err_info, port); +/// error!(target: "app_events", "App Error: {}, Port: {}", err_info, 22); +/// # } +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! error { + // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // error!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Error, $($arg)+)); + + // error!("a {} event", "log") + ($($arg:tt)+) => (log!($crate::Level::Error, $($arg)+)) +} + +/// Logs a message at the warn level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::warn; +/// +/// # fn main() { +/// let warn_description = "Invalid Input"; +/// +/// warn!("Warning! {}!", warn_description); +/// warn!(target: "input_events", "App received warning: {}", warn_description); +/// # } +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! warn { + // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // warn!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Warn, $($arg)+)); + + // warn!("a {} event", "log") + ($($arg:tt)+) => (log!($crate::Level::Warn, $($arg)+)) +} + +/// Logs a message at the info level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::info; +/// +/// # fn main() { +/// # struct Connection { port: u32, speed: f32 } +/// let conn_info = Connection { port: 40, speed: 3.20 }; +/// +/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); +/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}", +/// conn_info.port, conn_info.speed); +/// # } +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! info { + // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // info!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Info, $($arg)+)); + + // info!("a {} event", "log") + ($($arg:tt)+) => (log!($crate::Level::Info, $($arg)+)) +} + +/// Logs a message at the debug level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::debug; +/// +/// # fn main() { +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// debug!("New position: x: {}, y: {}", pos.x, pos.y); +/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); +/// # } +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! debug { + // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // debug!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Debug, $($arg)+)); + + // debug!("a {} event", "log") + ($($arg:tt)+) => (log!($crate::Level::Debug, $($arg)+)) +} + +/// Logs a message at the trace level. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::trace; +/// +/// # fn main() { +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); +/// trace!(target: "app_events", "x is {} and y is {}", +/// if pos.x >= 0.0 { "positive" } else { "negative" }, +/// if pos.y >= 0.0 { "positive" } else { "negative" }); +/// # } +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! trace { + // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // trace!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => (log!(target: $target, $crate::Level::Trace, $($arg)+)); + + // trace!("a {} event", "log") + ($($arg:tt)+) => (log!($crate::Level::Trace, $($arg)+)) +} + +/// Determines if a message logged at the specified level in that module will +/// be logged. +/// +/// This can be used to avoid expensive computation of log message arguments if +/// the message would be ignored anyway. +/// +/// # Examples +/// +/// ```edition2018 +/// use log::Level::Debug; +/// use log::{debug, log_enabled}; +/// +/// # fn foo() { +/// if log_enabled!(Debug) { +/// let data = expensive_call(); +/// debug!("expensive debug data: {} {}", data.x, data.y); +/// } +/// if log_enabled!(target: "Global", Debug) { +/// let data = expensive_call(); +/// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); +/// } +/// # } +/// # struct Data { x: u32, y: u32 } +/// # fn expensive_call() -> Data { Data { x: 0, y: 0 } } +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! log_enabled { + (target: $target:expr, $lvl:expr) => {{ + let lvl = $lvl; + lvl <= $crate::STATIC_MAX_LEVEL + && lvl <= $crate::max_level() + && $crate::__private_api_enabled(lvl, $target) + }}; + ($lvl:expr) => { + log_enabled!(target: __log_module_path!(), $lvl) + }; +} + +// The log macro above cannot invoke format_args directly because it uses +// local_inner_macros. A format_args invocation there would resolve to +// $crate::format_args which does not exist. Instead invoke format_args here +// outside of local_inner_macros so that it resolves (probably) to +// core::format_args or std::format_args. Same for the several macros that +// follow. +// +// This is a workaround until we drop support for pre-1.30 compilers. At that +// point we can remove use of local_inner_macros, use $crate:: when invoking +// local macros, and invoke format_args directly. +#[doc(hidden)] +#[macro_export] +macro_rules! __log_format_args { + ($($args:tt)*) => { + format_args!($($args)*) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __log_module_path { + () => { + module_path!() + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __log_file { + () => { + file!() + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __log_line { + () => { + line!() + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __log_key { + // key1 = 42 + ($($args:ident)*) => { + stringify!($($args)*) + }; + // "key1" = 42 + ($($args:expr)*) => { + $($args)* + }; +} diff --git a/vendor/log/src/serde.rs b/vendor/log/src/serde.rs index 5012833..e691321 100644 --- a/vendor/log/src/serde.rs +++ b/vendor/log/src/serde.rs @@ -1,400 +1,400 @@ -#![cfg(feature = "serde")] - -extern crate serde; -use self::serde::de::{ - Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error, Unexpected, VariantAccess, - Visitor, -}; -use self::serde::ser::{Serialize, Serializer}; - -use {Level, LevelFilter, LOG_LEVEL_NAMES}; - -use std::fmt; -use std::str::{self, FromStr}; - -// The Deserialize impls are handwritten to be case insensitive using FromStr. - -impl Serialize for Level { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match *self { - Level::Error => serializer.serialize_unit_variant("Level", 0, "ERROR"), - Level::Warn => serializer.serialize_unit_variant("Level", 1, "WARN"), - Level::Info => serializer.serialize_unit_variant("Level", 2, "INFO"), - Level::Debug => serializer.serialize_unit_variant("Level", 3, "DEBUG"), - Level::Trace => serializer.serialize_unit_variant("Level", 4, "TRACE"), - } - } -} - -impl<'de> Deserialize<'de> for Level { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct LevelIdentifier; - - impl<'de> Visitor<'de> for LevelIdentifier { - type Value = Level; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("log level") - } - - fn visit_str(self, s: &str) -> Result - where - E: Error, - { - // Case insensitive. - FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES[1..])) - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: Error, - { - let variant = str::from_utf8(value) - .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; - - self.visit_str(variant) - } - - fn visit_u64(self, v: u64) -> Result - where - E: Error, - { - let variant = LOG_LEVEL_NAMES[1..] - .get(v as usize) - .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; - - self.visit_str(variant) - } - } - - impl<'de> DeserializeSeed<'de> for LevelIdentifier { - type Value = Level; - - fn deserialize(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_identifier(LevelIdentifier) - } - } - - struct LevelEnum; - - impl<'de> Visitor<'de> for LevelEnum { - type Value = Level; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("log level") - } - - fn visit_enum(self, value: A) -> Result - where - A: EnumAccess<'de>, - { - let (level, variant) = value.variant_seed(LevelIdentifier)?; - // Every variant is a unit variant. - variant.unit_variant()?; - Ok(level) - } - } - - deserializer.deserialize_enum("Level", &LOG_LEVEL_NAMES[1..], LevelEnum) - } -} - -impl Serialize for LevelFilter { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match *self { - LevelFilter::Off => serializer.serialize_unit_variant("LevelFilter", 0, "OFF"), - LevelFilter::Error => serializer.serialize_unit_variant("LevelFilter", 1, "ERROR"), - LevelFilter::Warn => serializer.serialize_unit_variant("LevelFilter", 2, "WARN"), - LevelFilter::Info => serializer.serialize_unit_variant("LevelFilter", 3, "INFO"), - LevelFilter::Debug => serializer.serialize_unit_variant("LevelFilter", 4, "DEBUG"), - LevelFilter::Trace => serializer.serialize_unit_variant("LevelFilter", 5, "TRACE"), - } - } -} - -impl<'de> Deserialize<'de> for LevelFilter { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct LevelFilterIdentifier; - - impl<'de> Visitor<'de> for LevelFilterIdentifier { - type Value = LevelFilter; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("log level filter") - } - - fn visit_str(self, s: &str) -> Result - where - E: Error, - { - // Case insensitive. - FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES)) - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: Error, - { - let variant = str::from_utf8(value) - .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; - - self.visit_str(variant) - } - - fn visit_u64(self, v: u64) -> Result - where - E: Error, - { - let variant = LOG_LEVEL_NAMES - .get(v as usize) - .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; - - self.visit_str(variant) - } - } - - impl<'de> DeserializeSeed<'de> for LevelFilterIdentifier { - type Value = LevelFilter; - - fn deserialize(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_identifier(LevelFilterIdentifier) - } - } - - struct LevelFilterEnum; - - impl<'de> Visitor<'de> for LevelFilterEnum { - type Value = LevelFilter; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("log level filter") - } - - fn visit_enum(self, value: A) -> Result - where - A: EnumAccess<'de>, - { - let (level_filter, variant) = value.variant_seed(LevelFilterIdentifier)?; - // Every variant is a unit variant. - variant.unit_variant()?; - Ok(level_filter) - } - } - - deserializer.deserialize_enum("LevelFilter", &LOG_LEVEL_NAMES, LevelFilterEnum) - } -} - -#[cfg(test)] -mod tests { - extern crate serde_test; - use self::serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; - - use {Level, LevelFilter}; - - fn level_token(variant: &'static str) -> Token { - Token::UnitVariant { - name: "Level", - variant: variant, - } - } - - fn level_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { - [ - Token::Enum { name: "Level" }, - Token::Bytes(variant), - Token::Unit, - ] - } - - fn level_variant_tokens(variant: u32) -> [Token; 3] { - [ - Token::Enum { name: "Level" }, - Token::U32(variant), - Token::Unit, - ] - } - - fn level_filter_token(variant: &'static str) -> Token { - Token::UnitVariant { - name: "LevelFilter", - variant: variant, - } - } - - fn level_filter_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { - [ - Token::Enum { - name: "LevelFilter", - }, - Token::Bytes(variant), - Token::Unit, - ] - } - - fn level_filter_variant_tokens(variant: u32) -> [Token; 3] { - [ - Token::Enum { - name: "LevelFilter", - }, - Token::U32(variant), - Token::Unit, - ] - } - - #[test] - fn test_level_ser_de() { - let cases = [ - (Level::Error, [level_token("ERROR")]), - (Level::Warn, [level_token("WARN")]), - (Level::Info, [level_token("INFO")]), - (Level::Debug, [level_token("DEBUG")]), - (Level::Trace, [level_token("TRACE")]), - ]; - - for &(s, expected) in &cases { - assert_tokens(&s, &expected); - } - } - - #[test] - fn test_level_case_insensitive() { - let cases = [ - (Level::Error, [level_token("error")]), - (Level::Warn, [level_token("warn")]), - (Level::Info, [level_token("info")]), - (Level::Debug, [level_token("debug")]), - (Level::Trace, [level_token("trace")]), - ]; - - for &(s, expected) in &cases { - assert_de_tokens(&s, &expected); - } - } - - #[test] - fn test_level_de_bytes() { - let cases = [ - (Level::Error, level_bytes_tokens(b"ERROR")), - (Level::Warn, level_bytes_tokens(b"WARN")), - (Level::Info, level_bytes_tokens(b"INFO")), - (Level::Debug, level_bytes_tokens(b"DEBUG")), - (Level::Trace, level_bytes_tokens(b"TRACE")), - ]; - - for &(value, tokens) in &cases { - assert_de_tokens(&value, &tokens); - } - } - - #[test] - fn test_level_de_variant_index() { - let cases = [ - (Level::Error, level_variant_tokens(0)), - (Level::Warn, level_variant_tokens(1)), - (Level::Info, level_variant_tokens(2)), - (Level::Debug, level_variant_tokens(3)), - (Level::Trace, level_variant_tokens(4)), - ]; - - for &(value, tokens) in &cases { - assert_de_tokens(&value, &tokens); - } - } - - #[test] - fn test_level_de_error() { - let msg = "unknown variant `errorx`, expected one of \ - `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; - assert_de_tokens_error::(&[level_token("errorx")], msg); - } - - #[test] - fn test_level_filter_ser_de() { - let cases = [ - (LevelFilter::Off, [level_filter_token("OFF")]), - (LevelFilter::Error, [level_filter_token("ERROR")]), - (LevelFilter::Warn, [level_filter_token("WARN")]), - (LevelFilter::Info, [level_filter_token("INFO")]), - (LevelFilter::Debug, [level_filter_token("DEBUG")]), - (LevelFilter::Trace, [level_filter_token("TRACE")]), - ]; - - for &(s, expected) in &cases { - assert_tokens(&s, &expected); - } - } - - #[test] - fn test_level_filter_case_insensitive() { - let cases = [ - (LevelFilter::Off, [level_filter_token("off")]), - (LevelFilter::Error, [level_filter_token("error")]), - (LevelFilter::Warn, [level_filter_token("warn")]), - (LevelFilter::Info, [level_filter_token("info")]), - (LevelFilter::Debug, [level_filter_token("debug")]), - (LevelFilter::Trace, [level_filter_token("trace")]), - ]; - - for &(s, expected) in &cases { - assert_de_tokens(&s, &expected); - } - } - - #[test] - fn test_level_filter_de_bytes() { - let cases = [ - (LevelFilter::Off, level_filter_bytes_tokens(b"OFF")), - (LevelFilter::Error, level_filter_bytes_tokens(b"ERROR")), - (LevelFilter::Warn, level_filter_bytes_tokens(b"WARN")), - (LevelFilter::Info, level_filter_bytes_tokens(b"INFO")), - (LevelFilter::Debug, level_filter_bytes_tokens(b"DEBUG")), - (LevelFilter::Trace, level_filter_bytes_tokens(b"TRACE")), - ]; - - for &(value, tokens) in &cases { - assert_de_tokens(&value, &tokens); - } - } - - #[test] - fn test_level_filter_de_variant_index() { - let cases = [ - (LevelFilter::Off, level_filter_variant_tokens(0)), - (LevelFilter::Error, level_filter_variant_tokens(1)), - (LevelFilter::Warn, level_filter_variant_tokens(2)), - (LevelFilter::Info, level_filter_variant_tokens(3)), - (LevelFilter::Debug, level_filter_variant_tokens(4)), - (LevelFilter::Trace, level_filter_variant_tokens(5)), - ]; - - for &(value, tokens) in &cases { - assert_de_tokens(&value, &tokens); - } - } - - #[test] - fn test_level_filter_de_error() { - let msg = "unknown variant `errorx`, expected one of \ - `OFF`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; - assert_de_tokens_error::(&[level_filter_token("errorx")], msg); - } -} +#![cfg(feature = "serde")] + +extern crate serde; +use self::serde::de::{ + Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error, Unexpected, VariantAccess, + Visitor, +}; +use self::serde::ser::{Serialize, Serializer}; + +use {Level, LevelFilter, LOG_LEVEL_NAMES}; + +use std::fmt; +use std::str::{self, FromStr}; + +// The Deserialize impls are handwritten to be case insensitive using FromStr. + +impl Serialize for Level { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Level::Error => serializer.serialize_unit_variant("Level", 0, "ERROR"), + Level::Warn => serializer.serialize_unit_variant("Level", 1, "WARN"), + Level::Info => serializer.serialize_unit_variant("Level", 2, "INFO"), + Level::Debug => serializer.serialize_unit_variant("Level", 3, "DEBUG"), + Level::Trace => serializer.serialize_unit_variant("Level", 4, "TRACE"), + } + } +} + +impl<'de> Deserialize<'de> for Level { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LevelIdentifier; + + impl<'de> Visitor<'de> for LevelIdentifier { + type Value = Level; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level") + } + + fn visit_str(self, s: &str) -> Result + where + E: Error, + { + // Case insensitive. + FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES[1..])) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + let variant = str::from_utf8(value) + .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; + + self.visit_str(variant) + } + + fn visit_u64(self, v: u64) -> Result + where + E: Error, + { + let variant = LOG_LEVEL_NAMES[1..] + .get(v as usize) + .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; + + self.visit_str(variant) + } + } + + impl<'de> DeserializeSeed<'de> for LevelIdentifier { + type Value = Level; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(LevelIdentifier) + } + } + + struct LevelEnum; + + impl<'de> Visitor<'de> for LevelEnum { + type Value = Level; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level") + } + + fn visit_enum(self, value: A) -> Result + where + A: EnumAccess<'de>, + { + let (level, variant) = value.variant_seed(LevelIdentifier)?; + // Every variant is a unit variant. + variant.unit_variant()?; + Ok(level) + } + } + + deserializer.deserialize_enum("Level", &LOG_LEVEL_NAMES[1..], LevelEnum) + } +} + +impl Serialize for LevelFilter { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + LevelFilter::Off => serializer.serialize_unit_variant("LevelFilter", 0, "OFF"), + LevelFilter::Error => serializer.serialize_unit_variant("LevelFilter", 1, "ERROR"), + LevelFilter::Warn => serializer.serialize_unit_variant("LevelFilter", 2, "WARN"), + LevelFilter::Info => serializer.serialize_unit_variant("LevelFilter", 3, "INFO"), + LevelFilter::Debug => serializer.serialize_unit_variant("LevelFilter", 4, "DEBUG"), + LevelFilter::Trace => serializer.serialize_unit_variant("LevelFilter", 5, "TRACE"), + } + } +} + +impl<'de> Deserialize<'de> for LevelFilter { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LevelFilterIdentifier; + + impl<'de> Visitor<'de> for LevelFilterIdentifier { + type Value = LevelFilter; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level filter") + } + + fn visit_str(self, s: &str) -> Result + where + E: Error, + { + // Case insensitive. + FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES)) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + let variant = str::from_utf8(value) + .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; + + self.visit_str(variant) + } + + fn visit_u64(self, v: u64) -> Result + where + E: Error, + { + let variant = LOG_LEVEL_NAMES + .get(v as usize) + .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; + + self.visit_str(variant) + } + } + + impl<'de> DeserializeSeed<'de> for LevelFilterIdentifier { + type Value = LevelFilter; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(LevelFilterIdentifier) + } + } + + struct LevelFilterEnum; + + impl<'de> Visitor<'de> for LevelFilterEnum { + type Value = LevelFilter; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level filter") + } + + fn visit_enum(self, value: A) -> Result + where + A: EnumAccess<'de>, + { + let (level_filter, variant) = value.variant_seed(LevelFilterIdentifier)?; + // Every variant is a unit variant. + variant.unit_variant()?; + Ok(level_filter) + } + } + + deserializer.deserialize_enum("LevelFilter", &LOG_LEVEL_NAMES, LevelFilterEnum) + } +} + +#[cfg(test)] +mod tests { + extern crate serde_test; + use self::serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; + + use {Level, LevelFilter}; + + fn level_token(variant: &'static str) -> Token { + Token::UnitVariant { + name: "Level", + variant: variant, + } + } + + fn level_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { + [ + Token::Enum { name: "Level" }, + Token::Bytes(variant), + Token::Unit, + ] + } + + fn level_variant_tokens(variant: u32) -> [Token; 3] { + [ + Token::Enum { name: "Level" }, + Token::U32(variant), + Token::Unit, + ] + } + + fn level_filter_token(variant: &'static str) -> Token { + Token::UnitVariant { + name: "LevelFilter", + variant: variant, + } + } + + fn level_filter_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { + [ + Token::Enum { + name: "LevelFilter", + }, + Token::Bytes(variant), + Token::Unit, + ] + } + + fn level_filter_variant_tokens(variant: u32) -> [Token; 3] { + [ + Token::Enum { + name: "LevelFilter", + }, + Token::U32(variant), + Token::Unit, + ] + } + + #[test] + fn test_level_ser_de() { + let cases = [ + (Level::Error, [level_token("ERROR")]), + (Level::Warn, [level_token("WARN")]), + (Level::Info, [level_token("INFO")]), + (Level::Debug, [level_token("DEBUG")]), + (Level::Trace, [level_token("TRACE")]), + ]; + + for &(s, expected) in &cases { + assert_tokens(&s, &expected); + } + } + + #[test] + fn test_level_case_insensitive() { + let cases = [ + (Level::Error, [level_token("error")]), + (Level::Warn, [level_token("warn")]), + (Level::Info, [level_token("info")]), + (Level::Debug, [level_token("debug")]), + (Level::Trace, [level_token("trace")]), + ]; + + for &(s, expected) in &cases { + assert_de_tokens(&s, &expected); + } + } + + #[test] + fn test_level_de_bytes() { + let cases = [ + (Level::Error, level_bytes_tokens(b"ERROR")), + (Level::Warn, level_bytes_tokens(b"WARN")), + (Level::Info, level_bytes_tokens(b"INFO")), + (Level::Debug, level_bytes_tokens(b"DEBUG")), + (Level::Trace, level_bytes_tokens(b"TRACE")), + ]; + + for &(value, tokens) in &cases { + assert_de_tokens(&value, &tokens); + } + } + + #[test] + fn test_level_de_variant_index() { + let cases = [ + (Level::Error, level_variant_tokens(0)), + (Level::Warn, level_variant_tokens(1)), + (Level::Info, level_variant_tokens(2)), + (Level::Debug, level_variant_tokens(3)), + (Level::Trace, level_variant_tokens(4)), + ]; + + for &(value, tokens) in &cases { + assert_de_tokens(&value, &tokens); + } + } + + #[test] + fn test_level_de_error() { + let msg = "unknown variant `errorx`, expected one of \ + `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; + assert_de_tokens_error::(&[level_token("errorx")], msg); + } + + #[test] + fn test_level_filter_ser_de() { + let cases = [ + (LevelFilter::Off, [level_filter_token("OFF")]), + (LevelFilter::Error, [level_filter_token("ERROR")]), + (LevelFilter::Warn, [level_filter_token("WARN")]), + (LevelFilter::Info, [level_filter_token("INFO")]), + (LevelFilter::Debug, [level_filter_token("DEBUG")]), + (LevelFilter::Trace, [level_filter_token("TRACE")]), + ]; + + for &(s, expected) in &cases { + assert_tokens(&s, &expected); + } + } + + #[test] + fn test_level_filter_case_insensitive() { + let cases = [ + (LevelFilter::Off, [level_filter_token("off")]), + (LevelFilter::Error, [level_filter_token("error")]), + (LevelFilter::Warn, [level_filter_token("warn")]), + (LevelFilter::Info, [level_filter_token("info")]), + (LevelFilter::Debug, [level_filter_token("debug")]), + (LevelFilter::Trace, [level_filter_token("trace")]), + ]; + + for &(s, expected) in &cases { + assert_de_tokens(&s, &expected); + } + } + + #[test] + fn test_level_filter_de_bytes() { + let cases = [ + (LevelFilter::Off, level_filter_bytes_tokens(b"OFF")), + (LevelFilter::Error, level_filter_bytes_tokens(b"ERROR")), + (LevelFilter::Warn, level_filter_bytes_tokens(b"WARN")), + (LevelFilter::Info, level_filter_bytes_tokens(b"INFO")), + (LevelFilter::Debug, level_filter_bytes_tokens(b"DEBUG")), + (LevelFilter::Trace, level_filter_bytes_tokens(b"TRACE")), + ]; + + for &(value, tokens) in &cases { + assert_de_tokens(&value, &tokens); + } + } + + #[test] + fn test_level_filter_de_variant_index() { + let cases = [ + (LevelFilter::Off, level_filter_variant_tokens(0)), + (LevelFilter::Error, level_filter_variant_tokens(1)), + (LevelFilter::Warn, level_filter_variant_tokens(2)), + (LevelFilter::Info, level_filter_variant_tokens(3)), + (LevelFilter::Debug, level_filter_variant_tokens(4)), + (LevelFilter::Trace, level_filter_variant_tokens(5)), + ]; + + for &(value, tokens) in &cases { + assert_de_tokens(&value, &tokens); + } + } + + #[test] + fn test_level_filter_de_error() { + let msg = "unknown variant `errorx`, expected one of \ + `OFF`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; + assert_de_tokens_error::(&[level_filter_token("errorx")], msg); + } +} diff --git a/vendor/log/triagebot.toml b/vendor/log/triagebot.toml index fa0824a..84659a1 100644 --- a/vendor/log/triagebot.toml +++ b/vendor/log/triagebot.toml @@ -1 +1 @@ -[assign] +[assign] diff --git a/vendor/remove_dir_all/LICENCE-APACHE b/vendor/remove_dir_all/LICENCE-APACHE index 325f80b..01de392 100644 --- a/vendor/remove_dir_all/LICENCE-APACHE +++ b/vendor/remove_dir_all/LICENCE-APACHE @@ -1,191 +1,191 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -Copyright 2017 Aaron Power - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +Copyright 2017 Aaron Power + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/vendor/remove_dir_all/LICENCE-MIT b/vendor/remove_dir_all/LICENCE-MIT index 3c50c82..627895f 100644 --- a/vendor/remove_dir_all/LICENCE-MIT +++ b/vendor/remove_dir_all/LICENCE-MIT @@ -1,26 +1,26 @@ -Copyright (c) 2017 Aaron Power - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - +Copyright (c) 2017 Aaron Power + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/vendor/remove_dir_all/README.md b/vendor/remove_dir_all/README.md index 958d811..f6b21b2 100644 --- a/vendor/remove_dir_all/README.md +++ b/vendor/remove_dir_all/README.md @@ -1,20 +1,20 @@ -# remove_dir_all - -[![Latest Version](https://img.shields.io/crates/v/remove_dir_all.svg)](https://crates.io/crates/remove_dir_all) -[![Docs](https://docs.rs/remove_dir_all/badge.svg)](https://docs.rs/remove_dir_all) -[![License](https://img.shields.io/github/license/XAMPPRocky/remove_dir_all.svg)](https://github.com/XAMPPRocky/remove_dir_all) - -## Description - -A reliable implementation of `remove_dir_all` for Windows. For Unix systems -re-exports `std::fs::remove_dir_all`. - -```rust,no_run -extern crate remove_dir_all; - -use remove_dir_all::*; - -fn main() { - remove_dir_all("./temp/").unwrap(); -} -``` +# remove_dir_all + +[![Latest Version](https://img.shields.io/crates/v/remove_dir_all.svg)](https://crates.io/crates/remove_dir_all) +[![Docs](https://docs.rs/remove_dir_all/badge.svg)](https://docs.rs/remove_dir_all) +[![License](https://img.shields.io/github/license/XAMPPRocky/remove_dir_all.svg)](https://github.com/XAMPPRocky/remove_dir_all) + +## Description + +A reliable implementation of `remove_dir_all` for Windows. For Unix systems +re-exports `std::fs::remove_dir_all`. + +```rust,no_run +extern crate remove_dir_all; + +use remove_dir_all::*; + +fn main() { + remove_dir_all("./temp/").unwrap(); +} +``` diff --git a/vendor/remove_dir_all/src/fs.rs b/vendor/remove_dir_all/src/fs.rs index c63e817..d81bd65 100644 --- a/vendor/remove_dir_all/src/fs.rs +++ b/vendor/remove_dir_all/src/fs.rs @@ -1,278 +1,278 @@ -use std::ffi::OsString; -use std::fs::{self, File, OpenOptions}; -use std::os::windows::prelude::*; -use std::path::{Path, PathBuf}; -use std::{io, ptr}; - -use winapi::shared::minwindef::*; -use winapi::shared::winerror::*; -use winapi::um::errhandlingapi::*; -use winapi::um::fileapi::*; -use winapi::um::minwinbase::*; -use winapi::um::winbase::*; -use winapi::um::winnt::*; - -pub const VOLUME_NAME_DOS: DWORD = 0x0; - -struct RmdirContext<'a> { - base_dir: &'a Path, - readonly: bool, - counter: u64, -} - -/// Reliably removes a directory and all of its children. -/// -/// ```rust -/// extern crate remove_dir_all; -/// -/// use std::fs; -/// use remove_dir_all::*; -/// -/// fn main() { -/// fs::create_dir("./temp/").unwrap(); -/// remove_dir_all("./temp/").unwrap(); -/// } -/// ``` -pub fn remove_dir_all>(path: P) -> io::Result<()> { - // On Windows it is not enough to just recursively remove the contents of a - // directory and then the directory itself. Deleting does not happen - // instantaneously, but is scheduled. - // To work around this, we move the file or directory to some `base_dir` - // right before deletion to avoid races. - // - // As `base_dir` we choose the parent dir of the directory we want to - // remove. We very probably have permission to create files here, as we - // already need write permission in this dir to delete the directory. And it - // should be on the same volume. - // - // To handle files with names like `CON` and `morse .. .`, and when a - // directory structure is so deep it needs long path names the path is first - // converted to a `//?/`-path with `get_path()`. - // - // To make sure we don't leave a moved file laying around if the process - // crashes before we can delete the file, we do all operations on an file - // handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will - // always delete the file when the handle closes. - // - // All files are renamed to be in the `base_dir`, and have their name - // changed to "rm-". After every rename the counter is increased. - // Rename should not overwrite possibly existing files in the base dir. So - // if it fails with `AlreadyExists`, we just increase the counter and try - // again. - // - // For read-only files and directories we first have to remove the read-only - // attribute before we can move or delete them. This also removes the - // attribute from possible hardlinks to the file, so just before closing we - // restore the read-only attribute. - // - // If 'path' points to a directory symlink or junction we should not - // recursively remove the target of the link, but only the link itself. - // - // Moving and deleting is guaranteed to succeed if we are able to open the - // file with `DELETE` permission. If others have the file open we only have - // `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can - // also delete the file now, but it will not disappear until all others have - // closed the file. But no-one can open the file after we have flagged it - // for deletion. - - // Open the path once to get the canonical path, file type and attributes. - let (path, metadata) = { - let path = path.as_ref(); - let mut opts = OpenOptions::new(); - opts.access_mode(FILE_READ_ATTRIBUTES); - opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); - let file = opts.open(path)?; - (get_path(&file)?, path.metadata()?) - }; - - let mut ctx = RmdirContext { - base_dir: match path.parent() { - Some(dir) => dir, - None => { - return Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "Can't delete root directory", - )) - } - }, - readonly: metadata.permissions().readonly(), - counter: 0, - }; - - let filetype = metadata.file_type(); - if filetype.is_dir() { - if !filetype.is_symlink() { - remove_dir_all_recursive(path.as_ref(), &mut ctx) - } else { - remove_item(path.as_ref(), &mut ctx) - } - } else { - Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "Not a directory", - )) - } -} - -fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { - if ctx.readonly { - // remove read-only permision - let mut permissions = path.metadata()?.permissions(); - permissions.set_readonly(false); - - fs::set_permissions(path, permissions)?; - } - - let mut opts = OpenOptions::new(); - opts.access_mode(DELETE); - opts.custom_flags( - FILE_FLAG_BACKUP_SEMANTICS | // delete directory - FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink - FILE_FLAG_DELETE_ON_CLOSE, - ); - let file = opts.open(path)?; - move_item(&file, ctx)?; - - if ctx.readonly { - // restore read-only flag just in case there are other hard links - match fs::metadata(&path) { - Ok(metadata) => { - let mut perm = metadata.permissions(); - perm.set_readonly(true); - fs::set_permissions(&path, perm)?; - } - Err(ref err) if err.kind() == io::ErrorKind::NotFound => {} - err => return err.map(|_| ()), - } - } - - Ok(()) -} - -fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> { - let mut tmpname = ctx.base_dir.join(format! {"rm-{}", ctx.counter}); - ctx.counter += 1; - - // Try to rename the file. If it already exists, just retry with an other - // filename. - while let Err(err) = rename(file, &tmpname, false) { - if err.kind() != io::ErrorKind::AlreadyExists { - return Err(err); - }; - tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter)); - ctx.counter += 1; - } - - Ok(()) -} - -fn rename(file: &File, new: &Path, replace: bool) -> io::Result<()> { - // &self must be opened with DELETE permission - use std::iter; - #[cfg(target_pointer_width = "32")] - const STRUCT_SIZE: usize = 12; - #[cfg(target_pointer_width = "64")] - const STRUCT_SIZE: usize = 20; - - // FIXME: check for internal NULs in 'new' - let mut data: Vec = iter::repeat(0u16) - .take(STRUCT_SIZE / 2) - .chain(new.as_os_str().encode_wide()) - .collect(); - data.push(0); - let size = data.len() * 2; - - unsafe { - // Thanks to alignment guarantees on Windows this works - // (8 for 32-bit and 16 for 64-bit) - let info = data.as_mut_ptr() as *mut FILE_RENAME_INFO; - // The type of ReplaceIfExists is BOOL, but it actually expects a - // BOOLEAN. This means true is -1, not c::TRUE. - (*info).ReplaceIfExists = if replace { -1 } else { FALSE }; - (*info).RootDirectory = ptr::null_mut(); - (*info).FileNameLength = (size - STRUCT_SIZE) as DWORD; - let result = SetFileInformationByHandle( - file.as_raw_handle(), - FileRenameInfo, - data.as_mut_ptr() as *mut _ as *mut _, - size as DWORD, - ); - - if result == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } - } -} - -fn get_path(f: &File) -> io::Result { - fill_utf16_buf( - |buf, sz| unsafe { GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, VOLUME_NAME_DOS) }, - |buf| PathBuf::from(OsString::from_wide(buf)), - ) -} - -fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { - let dir_readonly = ctx.readonly; - for child in fs::read_dir(path)? { - let child = child?; - let child_type = child.file_type()?; - ctx.readonly = child.metadata()?.permissions().readonly(); - if child_type.is_dir() { - remove_dir_all_recursive(&child.path(), ctx)?; - } else { - remove_item(&child.path().as_ref(), ctx)?; - } - } - ctx.readonly = dir_readonly; - remove_item(path, ctx) -} - -fn fill_utf16_buf(mut f1: F1, f2: F2) -> io::Result -where - F1: FnMut(*mut u16, DWORD) -> DWORD, - F2: FnOnce(&[u16]) -> T, -{ - // Start off with a stack buf but then spill over to the heap if we end up - // needing more space. - let mut stack_buf = [0u16; 512]; - let mut heap_buf = Vec::new(); - unsafe { - let mut n = stack_buf.len(); - - loop { - let buf = if n <= stack_buf.len() { - &mut stack_buf[..] - } else { - let extra = n - heap_buf.len(); - heap_buf.reserve(extra); - heap_buf.set_len(n); - &mut heap_buf[..] - }; - - // This function is typically called on windows API functions which - // will return the correct length of the string, but these functions - // also return the `0` on error. In some cases, however, the - // returned "correct length" may actually be 0! - // - // To handle this case we call `SetLastError` to reset it to 0 and - // then check it again if we get the "0 error value". If the "last - // error" is still 0 then we interpret it as a 0 length buffer and - // not an actual error. - SetLastError(0); - let k = match f1(buf.as_mut_ptr(), n as DWORD) { - 0 if GetLastError() == 0 => 0, - 0 => return Err(io::Error::last_os_error()), - n => n, - } as usize; - if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER { - n *= 2; - } else if k >= n { - n = k; - } else { - return Ok(f2(&buf[..k])); - } - } - } -} +use std::ffi::OsString; +use std::fs::{self, File, OpenOptions}; +use std::os::windows::prelude::*; +use std::path::{Path, PathBuf}; +use std::{io, ptr}; + +use winapi::shared::minwindef::*; +use winapi::shared::winerror::*; +use winapi::um::errhandlingapi::*; +use winapi::um::fileapi::*; +use winapi::um::minwinbase::*; +use winapi::um::winbase::*; +use winapi::um::winnt::*; + +pub const VOLUME_NAME_DOS: DWORD = 0x0; + +struct RmdirContext<'a> { + base_dir: &'a Path, + readonly: bool, + counter: u64, +} + +/// Reliably removes a directory and all of its children. +/// +/// ```rust +/// extern crate remove_dir_all; +/// +/// use std::fs; +/// use remove_dir_all::*; +/// +/// fn main() { +/// fs::create_dir("./temp/").unwrap(); +/// remove_dir_all("./temp/").unwrap(); +/// } +/// ``` +pub fn remove_dir_all>(path: P) -> io::Result<()> { + // On Windows it is not enough to just recursively remove the contents of a + // directory and then the directory itself. Deleting does not happen + // instantaneously, but is scheduled. + // To work around this, we move the file or directory to some `base_dir` + // right before deletion to avoid races. + // + // As `base_dir` we choose the parent dir of the directory we want to + // remove. We very probably have permission to create files here, as we + // already need write permission in this dir to delete the directory. And it + // should be on the same volume. + // + // To handle files with names like `CON` and `morse .. .`, and when a + // directory structure is so deep it needs long path names the path is first + // converted to a `//?/`-path with `get_path()`. + // + // To make sure we don't leave a moved file laying around if the process + // crashes before we can delete the file, we do all operations on an file + // handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will + // always delete the file when the handle closes. + // + // All files are renamed to be in the `base_dir`, and have their name + // changed to "rm-". After every rename the counter is increased. + // Rename should not overwrite possibly existing files in the base dir. So + // if it fails with `AlreadyExists`, we just increase the counter and try + // again. + // + // For read-only files and directories we first have to remove the read-only + // attribute before we can move or delete them. This also removes the + // attribute from possible hardlinks to the file, so just before closing we + // restore the read-only attribute. + // + // If 'path' points to a directory symlink or junction we should not + // recursively remove the target of the link, but only the link itself. + // + // Moving and deleting is guaranteed to succeed if we are able to open the + // file with `DELETE` permission. If others have the file open we only have + // `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can + // also delete the file now, but it will not disappear until all others have + // closed the file. But no-one can open the file after we have flagged it + // for deletion. + + // Open the path once to get the canonical path, file type and attributes. + let (path, metadata) = { + let path = path.as_ref(); + let mut opts = OpenOptions::new(); + opts.access_mode(FILE_READ_ATTRIBUTES); + opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); + let file = opts.open(path)?; + (get_path(&file)?, path.metadata()?) + }; + + let mut ctx = RmdirContext { + base_dir: match path.parent() { + Some(dir) => dir, + None => { + return Err(io::Error::new( + io::ErrorKind::PermissionDenied, + "Can't delete root directory", + )) + } + }, + readonly: metadata.permissions().readonly(), + counter: 0, + }; + + let filetype = metadata.file_type(); + if filetype.is_dir() { + if !filetype.is_symlink() { + remove_dir_all_recursive(path.as_ref(), &mut ctx) + } else { + remove_item(path.as_ref(), &mut ctx) + } + } else { + Err(io::Error::new( + io::ErrorKind::PermissionDenied, + "Not a directory", + )) + } +} + +fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { + if ctx.readonly { + // remove read-only permision + let mut permissions = path.metadata()?.permissions(); + permissions.set_readonly(false); + + fs::set_permissions(path, permissions)?; + } + + let mut opts = OpenOptions::new(); + opts.access_mode(DELETE); + opts.custom_flags( + FILE_FLAG_BACKUP_SEMANTICS | // delete directory + FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink + FILE_FLAG_DELETE_ON_CLOSE, + ); + let file = opts.open(path)?; + move_item(&file, ctx)?; + + if ctx.readonly { + // restore read-only flag just in case there are other hard links + match fs::metadata(&path) { + Ok(metadata) => { + let mut perm = metadata.permissions(); + perm.set_readonly(true); + fs::set_permissions(&path, perm)?; + } + Err(ref err) if err.kind() == io::ErrorKind::NotFound => {} + err => return err.map(|_| ()), + } + } + + Ok(()) +} + +fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> { + let mut tmpname = ctx.base_dir.join(format! {"rm-{}", ctx.counter}); + ctx.counter += 1; + + // Try to rename the file. If it already exists, just retry with an other + // filename. + while let Err(err) = rename(file, &tmpname, false) { + if err.kind() != io::ErrorKind::AlreadyExists { + return Err(err); + }; + tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter)); + ctx.counter += 1; + } + + Ok(()) +} + +fn rename(file: &File, new: &Path, replace: bool) -> io::Result<()> { + // &self must be opened with DELETE permission + use std::iter; + #[cfg(target_pointer_width = "32")] + const STRUCT_SIZE: usize = 12; + #[cfg(target_pointer_width = "64")] + const STRUCT_SIZE: usize = 20; + + // FIXME: check for internal NULs in 'new' + let mut data: Vec = iter::repeat(0u16) + .take(STRUCT_SIZE / 2) + .chain(new.as_os_str().encode_wide()) + .collect(); + data.push(0); + let size = data.len() * 2; + + unsafe { + // Thanks to alignment guarantees on Windows this works + // (8 for 32-bit and 16 for 64-bit) + let info = data.as_mut_ptr() as *mut FILE_RENAME_INFO; + // The type of ReplaceIfExists is BOOL, but it actually expects a + // BOOLEAN. This means true is -1, not c::TRUE. + (*info).ReplaceIfExists = if replace { -1 } else { FALSE }; + (*info).RootDirectory = ptr::null_mut(); + (*info).FileNameLength = (size - STRUCT_SIZE) as DWORD; + let result = SetFileInformationByHandle( + file.as_raw_handle(), + FileRenameInfo, + data.as_mut_ptr() as *mut _ as *mut _, + size as DWORD, + ); + + if result == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } +} + +fn get_path(f: &File) -> io::Result { + fill_utf16_buf( + |buf, sz| unsafe { GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, VOLUME_NAME_DOS) }, + |buf| PathBuf::from(OsString::from_wide(buf)), + ) +} + +fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { + let dir_readonly = ctx.readonly; + for child in fs::read_dir(path)? { + let child = child?; + let child_type = child.file_type()?; + ctx.readonly = child.metadata()?.permissions().readonly(); + if child_type.is_dir() { + remove_dir_all_recursive(&child.path(), ctx)?; + } else { + remove_item(&child.path().as_ref(), ctx)?; + } + } + ctx.readonly = dir_readonly; + remove_item(path, ctx) +} + +fn fill_utf16_buf(mut f1: F1, f2: F2) -> io::Result +where + F1: FnMut(*mut u16, DWORD) -> DWORD, + F2: FnOnce(&[u16]) -> T, +{ + // Start off with a stack buf but then spill over to the heap if we end up + // needing more space. + let mut stack_buf = [0u16; 512]; + let mut heap_buf = Vec::new(); + unsafe { + let mut n = stack_buf.len(); + + loop { + let buf = if n <= stack_buf.len() { + &mut stack_buf[..] + } else { + let extra = n - heap_buf.len(); + heap_buf.reserve(extra); + heap_buf.set_len(n); + &mut heap_buf[..] + }; + + // This function is typically called on windows API functions which + // will return the correct length of the string, but these functions + // also return the `0` on error. In some cases, however, the + // returned "correct length" may actually be 0! + // + // To handle this case we call `SetLastError` to reset it to 0 and + // then check it again if we get the "0 error value". If the "last + // error" is still 0 then we interpret it as a 0 length buffer and + // not an actual error. + SetLastError(0); + let k = match f1(buf.as_mut_ptr(), n as DWORD) { + 0 if GetLastError() == 0 => 0, + 0 => return Err(io::Error::last_os_error()), + n => n, + } as usize; + if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER { + n *= 2; + } else if k >= n { + n = k; + } else { + return Ok(f2(&buf[..k])); + } + } + } +} diff --git a/vendor/remove_dir_all/src/lib.rs b/vendor/remove_dir_all/src/lib.rs index ffbef07..f7b3dc6 100644 --- a/vendor/remove_dir_all/src/lib.rs +++ b/vendor/remove_dir_all/src/lib.rs @@ -1,26 +1,26 @@ -//! Reliably remove a directory and all of its children. -//! -//! This library provides a reliable implementation of `remove_dir_all` for Windows. -//! For Unix systems, it re-exports `std::fs::remove_dir_all`. - -#![deny(missing_debug_implementations)] -#![deny(missing_docs)] - -#[cfg(windows)] -extern crate winapi; - -#[cfg(doctest)] -#[macro_use] -extern crate doc_comment; - -#[cfg(doctest)] -doctest!("../README.md"); - -#[cfg(windows)] -mod fs; - -#[cfg(windows)] -pub use self::fs::remove_dir_all; - -#[cfg(not(windows))] -pub use std::fs::remove_dir_all; +//! Reliably remove a directory and all of its children. +//! +//! This library provides a reliable implementation of `remove_dir_all` for Windows. +//! For Unix systems, it re-exports `std::fs::remove_dir_all`. + +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] + +#[cfg(windows)] +extern crate winapi; + +#[cfg(doctest)] +#[macro_use] +extern crate doc_comment; + +#[cfg(doctest)] +doctest!("../README.md"); + +#[cfg(windows)] +mod fs; + +#[cfg(windows)] +pub use self::fs::remove_dir_all; + +#[cfg(not(windows))] +pub use std::fs::remove_dir_all;