diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-10 13:11:11 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-10 13:11:11 -0600 |
| commit | 01959b16a21b22b5df5f16569c2a8e8f92beecef (patch) | |
| tree | 32afa5d747c5466345c59ec52161a7cba3d6d755 /vendor/anstream | |
| parent | ff30574117a996df332e23d1fb6f65259b316b5b (diff) | |
chore: vendor dependencies
Diffstat (limited to 'vendor/anstream')
| -rw-r--r-- | vendor/anstream/.cargo-checksum.json | 1 | ||||
| -rw-r--r-- | vendor/anstream/Cargo.lock | 705 | ||||
| -rw-r--r-- | vendor/anstream/Cargo.toml | 220 | ||||
| -rw-r--r-- | vendor/anstream/LICENSE-APACHE | 202 | ||||
| -rw-r--r-- | vendor/anstream/LICENSE-MIT | 19 | ||||
| -rw-r--r-- | vendor/anstream/README.md | 34 | ||||
| -rw-r--r-- | vendor/anstream/examples/dump-stream.rs | 130 | ||||
| -rw-r--r-- | vendor/anstream/examples/query-stream.rs | 22 | ||||
| -rw-r--r-- | vendor/anstream/src/_macros.rs | 364 | ||||
| -rw-r--r-- | vendor/anstream/src/adapter/mod.rs | 15 | ||||
| -rw-r--r-- | vendor/anstream/src/adapter/strip.rs | 511 | ||||
| -rw-r--r-- | vendor/anstream/src/adapter/wincon.rs | 378 | ||||
| -rw-r--r-- | vendor/anstream/src/auto.rs | 302 | ||||
| -rw-r--r-- | vendor/anstream/src/buffer.rs | 56 | ||||
| -rw-r--r-- | vendor/anstream/src/fmt.rs | 54 | ||||
| -rw-r--r-- | vendor/anstream/src/lib.rs | 90 | ||||
| -rw-r--r-- | vendor/anstream/src/stream.rs | 316 | ||||
| -rw-r--r-- | vendor/anstream/src/strip.rs | 227 | ||||
| -rw-r--r-- | vendor/anstream/src/wincon.rs | 226 |
19 files changed, 3872 insertions, 0 deletions
diff --git a/vendor/anstream/.cargo-checksum.json b/vendor/anstream/.cargo-checksum.json new file mode 100644 index 00000000..cdcd1885 --- /dev/null +++ b/vendor/anstream/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"b024d6d428ff4fddba14421c31f79d675c3acbcd1f80d602908c77002b8c4a82","Cargo.toml":"1771559805c603fa2dd152b75b6cf45abaa293a37ac1491c908e70b1f125ec11","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"7b22f0ac46116cae53ea847f9bc008d172276765e9955924cdbf5ff82cae5fca","examples/dump-stream.rs":"6dbd8fca846ac8addd43ae941fa1220a7c31862896ac2ae4971afe792480523d","examples/query-stream.rs":"3796dec1ffe79cc49d088086eb27214764e8e0cff0370d60150aeecdadd47d30","src/_macros.rs":"04590ef86f86bb67fb11580ab101caffde4d36da84652537da20424b72c8729a","src/adapter/mod.rs":"baf4237ea0b18df63609e49d93572ca27c2202a4cbec0220adb5a7e815c7d8ed","src/adapter/strip.rs":"642375f9dc5836eb5aa6eba7be849a84786d22764d470672bf1f646061254c1c","src/adapter/wincon.rs":"88607742fb7bbfb85e122a8dcc3188ef7c15ef332063af3a8939701dad05b90a","src/auto.rs":"e513fd903153df148fe1718471db3363fb3ec6a3d7bc167426033ed37dd307ba","src/buffer.rs":"597cc2a109a49f0a48888c7d882058d0ec4b771da34348d4da384ba056c6ea1c","src/fmt.rs":"cc11b005c4559843bd908a57958a13c8d0922fae6aff5261f3583c90e60da73c","src/lib.rs":"41885c714cd46e11b7c717ade9039a736a4bedae4f148f4a0efebe54708f3685","src/stream.rs":"680ef529d62377365cd543ebcd51076c909fffc06d1f52231fb69c68bbd05218","src/strip.rs":"155fa7bb0bc333b7648236df44d14ff742139c28659332bf03ed00b2943b8fac","src/wincon.rs":"018b824680576e5279830b2ca1ede0e36a97044d4d30cfd50199e1df7a5fb0f4"},"package":"301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"}
\ No newline at end of file diff --git a/vendor/anstream/Cargo.lock b/vendor/anstream/Cargo.lock new file mode 100644 index 00000000..30984393 --- /dev/null +++ b/vendor/anstream/Cargo.lock @@ -0,0 +1,705 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.19" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "divan", + "is_terminal_polyfill", + "lexopt", + "owo-colors", + "proptest", + "strip-ansi-escapes", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e" +dependencies = [ + "anstyle", + "clap_lex", + "terminal_size", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "condtype" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" + +[[package]] +name = "divan" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e05d17bd4ff1c1e7998ed4623d2efd91f72f1e24141ac33aac9377974270e1f" +dependencies = [ + "cfg-if", + "clap", + "condtype", + "divan-macros", + "libc", + "regex-lite", +] + +[[package]] +name = "divan-macros" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b4464d46ce68bfc7cb76389248c7c254def7baca8bece0693b02b83842c4c88" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2736dd548daf35f50d261bbad35a83890bb9b461797f15de528485fbf206ab15" +dependencies = [ + "is-terminal", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lexopt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "owo-colors" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.9.0", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "strip-ansi-escapes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] + +[[package]] +name = "syn" +version = "2.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix 0.38.38", + "windows-sys 0.59.0", +] + +[[package]] +name = "terminal_size" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +dependencies = [ + "rustix 0.37.27", + "windows-sys 0.48.0", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/vendor/anstream/Cargo.toml b/vendor/anstream/Cargo.toml new file mode 100644 index 00000000..4dc0828b --- /dev/null +++ b/vendor/anstream/Cargo.toml @@ -0,0 +1,220 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.66.0" +name = "anstream" +version = "0.6.19" +build = false +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "Cargo.lock", + "LICENSE*", + "README.md", + "examples/**/*", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A simple cross platform library for writing colored text to a terminal." +readme = "README.md" +keywords = [ + "ansi", + "terminal", + "color", + "strip", + "wincon", +] +categories = ["command-line-interface"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-cli/anstyle.git" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", + "--generate-link-to-definition", +] + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{version}}" +search = "Unreleased" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = "...{{tag_name}}" +search = '\.\.\.HEAD' + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{date}}" +search = "ReleaseDate" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ +<!-- next-header --> +## [Unreleased] - ReleaseDate +""" +search = "<!-- next-header -->" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ +<!-- next-url --> +[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD""" +search = "<!-- next-url -->" + +[features] +auto = ["dep:anstyle-query"] +default = [ + "auto", + "wincon", +] +test = [] +wincon = ["dep:anstyle-wincon"] + +[lib] +name = "anstream" +path = "src/lib.rs" + +[[example]] +name = "dump-stream" +path = "examples/dump-stream.rs" +required-features = ["auto"] + +[[example]] +name = "query-stream" +path = "examples/query-stream.rs" +required-features = ["auto"] + +[dependencies.anstyle] +version = "1.0.0" + +[dependencies.anstyle-parse] +version = "0.2.0" + +[dependencies.anstyle-query] +version = "1.0.0" +optional = true + +[dependencies.colorchoice] +version = "1.0.0" + +[dependencies.is_terminal_polyfill] +version = "1.48" + +[dependencies.utf8parse] +version = "0.2.1" + +[dev-dependencies.divan] +version = "0.1.11" + +[dev-dependencies.lexopt] +version = "0.3.0" + +[dev-dependencies.owo-colors] +version = "4.0.0" + +[dev-dependencies.proptest] +version = "1.4.0" + +[dev-dependencies.strip-ansi-escapes] +version = "0.2.0" + +[target."cfg(windows)".dependencies.anstyle-wincon] +version = "3.0.5" +optional = true + +[lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "allow" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +result_large_err = "allow" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +uninlined_format_args = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + +[lints.rust] +unnameable_types = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[lints.rust.rust_2018_idioms] +level = "warn" +priority = -1 diff --git a/vendor/anstream/LICENSE-APACHE b/vendor/anstream/LICENSE-APACHE new file mode 100644 index 00000000..8f71f43f --- /dev/null +++ b/vendor/anstream/LICENSE-APACHE @@ -0,0 +1,202 @@ + 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/anstream/LICENSE-MIT b/vendor/anstream/LICENSE-MIT new file mode 100644 index 00000000..a2d01088 --- /dev/null +++ b/vendor/anstream/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +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/anstream/README.md b/vendor/anstream/README.md new file mode 100644 index 00000000..7e751d65 --- /dev/null +++ b/vendor/anstream/README.md @@ -0,0 +1,34 @@ +# anstream + +> A simple cross platform library for writing colored text to a terminal. + +*A portmanteau of "ansi stream"* + +[][Documentation] + +[](https://crates.io/crates/anstream) + +Specialized `stdout` and `stderr` that accept ANSI escape codes and adapt them +based on the terminal's capabilities. + +`anstream::adapter::strip_str` may also be of interest on its own for low +overhead stripping of ANSI escape codes. + +## 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. + +## [Contribute](../../CONTRIBUTING.md) + +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. + +[Crates.io]: https://crates.io/crates/anstream +[Documentation]: https://docs.rs/anstream diff --git a/vendor/anstream/examples/dump-stream.rs b/vendor/anstream/examples/dump-stream.rs new file mode 100644 index 00000000..18bd2eb2 --- /dev/null +++ b/vendor/anstream/examples/dump-stream.rs @@ -0,0 +1,130 @@ +//! Write colored text, adapting to the terminals capabilities + +use std::io::Write; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + let stdout = anstream::stdout(); + let mut stdout = stdout.lock(); + + for fixed in 0..16 { + let color = anstyle::Ansi256Color(fixed) + .into_ansi() + .expect("within 4-bit color range"); + let style = style(color, args.layer, args.effects); + let _ = print_number(&mut stdout, fixed, style); + if fixed == 7 || fixed == 15 { + let _ = writeln!(&mut stdout); + } + } + + for fixed in 16..232 { + let col = (fixed - 16) % 36; + if col == 0 { + let _ = writeln!(stdout); + } + let color = anstyle::Ansi256Color(fixed); + let style = style(color, args.layer, args.effects); + let _ = print_number(&mut stdout, fixed, style); + } + + let _ = writeln!(stdout); + let _ = writeln!(stdout); + for fixed in 232..=255 { + let color = anstyle::Ansi256Color(fixed); + let style = style(color, args.layer, args.effects); + let _ = print_number(&mut stdout, fixed, style); + } + + let _ = writeln!(stdout); + + Ok(()) +} + +fn style( + color: impl Into<anstyle::Color>, + layer: Layer, + effects: anstyle::Effects, +) -> anstyle::Style { + let color = color.into(); + (match layer { + Layer::Fg => anstyle::Style::new().fg_color(Some(color)), + Layer::Bg => anstyle::Style::new().bg_color(Some(color)), + Layer::Underline => anstyle::Style::new().underline_color(Some(color)), + }) | effects +} + +fn print_number(stdout: &mut impl Write, fixed: u8, style: anstyle::Style) -> std::io::Result<()> { + write!(stdout, "{style}{fixed:>3X}{style:#}",) +} + +#[derive(Default)] +struct Args { + effects: anstyle::Effects, + layer: Layer, +} + +#[derive(Copy, Clone, Default)] +enum Layer { + #[default] + Fg, + Bg, + Underline, +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Long("layer") => { + res.layer = args.value()?.parse_with(|s| match s { + "fg" => Ok(Layer::Fg), + "bg" => Ok(Layer::Bg), + "underline" => Ok(Layer::Underline), + _ => Err("expected values fg, bg, underline"), + })?; + } + Long("effect") => { + const EFFECTS: [(&str, anstyle::Effects); 12] = [ + ("bold", anstyle::Effects::BOLD), + ("dimmed", anstyle::Effects::DIMMED), + ("italic", anstyle::Effects::ITALIC), + ("underline", anstyle::Effects::UNDERLINE), + ("double_underline", anstyle::Effects::DOUBLE_UNDERLINE), + ("curly_underline", anstyle::Effects::CURLY_UNDERLINE), + ("dotted_underline", anstyle::Effects::DOTTED_UNDERLINE), + ("dashed_underline", anstyle::Effects::DASHED_UNDERLINE), + ("blink", anstyle::Effects::BLINK), + ("invert", anstyle::Effects::INVERT), + ("hidden", anstyle::Effects::HIDDEN), + ("strikethrough", anstyle::Effects::STRIKETHROUGH), + ]; + let effect = args.value()?.parse_with(|s| { + EFFECTS + .into_iter() + .find(|(name, _)| *name == s) + .map(|(_, effect)| effect) + .ok_or_else(|| { + format!( + "expected one of {}", + EFFECTS + .into_iter() + .map(|(n, _)| n) + .collect::<Vec<_>>() + .join(", ") + ) + }) + })?; + res.effects = res.effects.insert(effect); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} diff --git a/vendor/anstream/examples/query-stream.rs b/vendor/anstream/examples/query-stream.rs new file mode 100644 index 00000000..1c8670ec --- /dev/null +++ b/vendor/anstream/examples/query-stream.rs @@ -0,0 +1,22 @@ +//! Report a terminal's capabilities + +fn main() { + println!("stdout:"); + println!( + " choice: {:?}", + anstream::AutoStream::choice(&std::io::stdout()) + ); + println!( + " choice: {:?}", + anstream::AutoStream::auto(std::io::stdout()).current_choice() + ); + println!("stderr:"); + println!( + " choice: {:?}", + anstream::AutoStream::choice(&std::io::stderr()) + ); + println!( + " choice: {:?}", + anstream::AutoStream::auto(std::io::stderr()).current_choice() + ); +} diff --git a/vendor/anstream/src/_macros.rs b/vendor/anstream/src/_macros.rs new file mode 100644 index 00000000..a1fa97d3 --- /dev/null +++ b/vendor/anstream/src/_macros.rs @@ -0,0 +1,364 @@ +/// Prints to [`stdout`][crate::stdout]. +/// +/// Equivalent to the [`println!`] macro except that a newline is not printed at +/// the end of the message. +/// +/// Note that stdout is frequently line-buffered by default so it may be +/// necessary to use [`std::io::Write::flush()`] to ensure the output is emitted +/// immediately. +/// +/// **NOTE:** The `print!` macro will lock the standard output on each call. If you call +/// `print!` within a hot loop, this behavior may be the bottleneck of the loop. +/// To avoid this, lock stdout with [`AutoStream::lock`][crate::AutoStream::lock]: +/// ``` +/// # #[cfg(feature = "auto")] { +/// use std::io::Write as _; +/// +/// let mut lock = anstream::stdout().lock(); +/// write!(lock, "hello world").unwrap(); +/// # } +/// ``` +/// +/// Use `print!` only for the primary output of your program. Use +/// [`eprint!`] instead to print error and progress messages. +/// +/// **NOTE:** Not all `print!` calls will be captured in tests like [`std::print!`] +/// - Capturing will automatically be activated in test binaries +/// - Otherwise, only when the `test` feature is enabled +/// +/// # Panics +/// +/// Panics if writing to `stdout` fails for any reason **except** broken pipe. +/// +/// Writing to non-blocking stdout can cause an error, which will lead +/// this macro to panic. +/// +/// # Examples +/// +/// ``` +/// # #[cfg(feature = "auto")] { +/// use std::io::Write as _; +/// use anstream::print; +/// use anstream::stdout; +/// +/// print!("this "); +/// print!("will "); +/// print!("be "); +/// print!("on "); +/// print!("the "); +/// print!("same "); +/// print!("line "); +/// +/// stdout().flush().unwrap(); +/// +/// print!("this string has a newline, why not choose println! instead?\n"); +/// +/// stdout().flush().unwrap(); +/// # } +/// ``` +#[cfg(feature = "auto")] +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => {{ + if cfg!(test) || $crate::_macros::FEATURE_TEST_ACTIVATED { + let target_stream = std::io::stdout(); + let buffer = $crate::_macros::to_adapted_string(&format_args!($($arg)*), &target_stream); + ::std::print!("{}", buffer) + } else { + use std::io::Write as _; + + let mut stream = $crate::stdout(); + match ::std::write!(&mut stream, $($arg)*) { + Err(e) if e.kind() != ::std::io::ErrorKind::BrokenPipe => { + ::std::panic!("failed printing to stdout: {e}"); + } + Err(_) | Ok(_) => {} + } + } + }}; +} + +/// Prints to [`stdout`][crate::stdout], with a newline. +/// +/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone +/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)). +/// +/// This macro uses the same syntax as [`format!`], but writes to the standard output instead. +/// See [`std::fmt`] for more information. +/// +/// **NOTE:** The `println!` macro will lock the standard output on each call. If you call +/// `println!` within a hot loop, this behavior may be the bottleneck of the loop. +/// To avoid this, lock stdout with [`AutoStream::lock`][crate::AutoStream::lock]: +/// ``` +/// # #[cfg(feature = "auto")] { +/// use std::io::Write as _; +/// +/// let mut lock = anstream::stdout().lock(); +/// writeln!(lock, "hello world").unwrap(); +/// # } +/// ``` +/// +/// Use `println!` only for the primary output of your program. Use +/// [`eprintln!`] instead to print error and progress messages. +/// +/// **NOTE:** Not all `println!` calls will be captured in tests like [`std::println!`] +/// - Capturing will automatically be activated in test binaries +/// - Otherwise, only when the `test` feature is enabled +/// +/// # Panics +/// +/// Panics if writing to `stdout` fails for any reason **except** broken pipe. +/// +/// Writing to non-blocking stdout can cause an error, which will lead +/// this macro to panic. +/// +/// # Examples +/// +/// ``` +/// # #[cfg(feature = "auto")] { +/// use anstream::println; +/// +/// println!(); // prints just a newline +/// println!("hello there!"); +/// println!("format {} arguments", "some"); +/// let local_variable = "some"; +/// println!("format {local_variable} arguments"); +/// # } +/// ``` +#[cfg(feature = "auto")] +#[macro_export] +macro_rules! println { + () => { + $crate::print!("\n") + }; + ($($arg:tt)*) => {{ + if cfg!(test) || $crate::_macros::FEATURE_TEST_ACTIVATED { + let target_stream = std::io::stdout(); + let buffer = $crate::_macros::to_adapted_string(&format_args!($($arg)*), &target_stream); + ::std::println!("{}", buffer) + } else { + use std::io::Write as _; + + let mut stream = $crate::stdout(); + match ::std::writeln!(&mut stream, $($arg)*) { + Err(e) if e.kind() != ::std::io::ErrorKind::BrokenPipe => { + ::std::panic!("failed printing to stdout: {e}"); + } + Err(_) | Ok(_) => {} + } + } + }}; +} + +/// Prints to [`stderr`][crate::stderr]. +/// +/// Equivalent to the [`print!`] macro, except that output goes to +/// `stderr` instead of `stdout`. See [`print!`] for +/// example usage. +/// +/// Use `eprint!` only for error and progress messages. Use `print!` +/// instead for the primary output of your program. +/// +/// **NOTE:** Not all `eprint!` calls will be captured in tests like [`std::eprint!`] +/// - Capturing will automatically be activated in test binaries +/// - Otherwise, only when the `test` feature is enabled +/// +/// # Panics +/// +/// Panics if writing to `stderr` fails for any reason **except** broken pipe. +/// +/// Writing to non-blocking stdout can cause an error, which will lead +/// this macro to panic. +/// +/// # Examples +/// +/// ``` +/// # #[cfg(feature = "auto")] { +/// use anstream::eprint; +/// +/// eprint!("Error: Could not complete task"); +/// # } +/// ``` +#[cfg(feature = "auto")] +#[macro_export] +macro_rules! eprint { + ($($arg:tt)*) => {{ + if cfg!(test) || $crate::_macros::FEATURE_TEST_ACTIVATED { + let target_stream = std::io::stderr(); + let buffer = $crate::_macros::to_adapted_string(&format_args!($($arg)*), &target_stream); + ::std::eprint!("{}", buffer) + } else { + use std::io::Write as _; + + let mut stream = $crate::stderr(); + match ::std::write!(&mut stream, $($arg)*) { + Err(e) if e.kind() != ::std::io::ErrorKind::BrokenPipe => { + ::std::panic!("failed printing to stderr: {e}"); + } + Err(_) | Ok(_) => {} + } + } + }}; +} + +/// Prints to [`stderr`][crate::stderr], with a newline. +/// +/// Equivalent to the [`println!`] macro, except that output goes to +/// `stderr` instead of `stdout`. See [`println!`] for +/// example usage. +/// +/// Use `eprintln!` only for error and progress messages. Use `println!` +/// instead for the primary output of your program. +/// +/// **NOTE:** Not all `eprintln!` calls will be captured in tests like [`std::eprintln!`] +/// - Capturing will automatically be activated in test binaries +/// - Otherwise, only when the `test` feature is enabled +/// +/// # Panics +/// +/// Panics if writing to `stderr` fails for any reason **except** broken pipe. +/// +/// Writing to non-blocking stdout can cause an error, which will lead +/// this macro to panic. +/// +/// # Examples +/// +/// ``` +/// # #[cfg(feature = "auto")] { +/// use anstream::eprintln; +/// +/// eprintln!("Error: Could not complete task"); +/// # } +/// ``` +#[cfg(feature = "auto")] +#[macro_export] +macro_rules! eprintln { + () => { + $crate::eprint!("\n") + }; + ($($arg:tt)*) => {{ + if cfg!(test) || $crate::_macros::FEATURE_TEST_ACTIVATED { + let target_stream = std::io::stderr(); + let buffer = $crate::_macros::to_adapted_string(&format_args!($($arg)*), &target_stream); + ::std::eprintln!("{}", buffer) + } else { + use std::io::Write as _; + + let mut stream = $crate::stderr(); + match ::std::writeln!(&mut stream, $($arg)*) { + Err(e) if e.kind() != ::std::io::ErrorKind::BrokenPipe => { + ::std::panic!("failed printing to stderr: {e}"); + } + Err(_) | Ok(_) => {} + } + } + }}; +} + +/// Panics the current thread. +/// +/// This allows a program to terminate immediately and provide feedback +/// to the caller of the program. +/// +/// This macro is the perfect way to assert conditions in example code and in +/// tests. `panic!` is closely tied with the `unwrap` method of both +/// [`Option`][ounwrap] and [`Result`][runwrap] enums. Both implementations call +/// `panic!` when they are set to [`None`] or [`Err`] variants. +/// +/// When using `panic!()` you can specify a string payload, that is built using +/// the [`format!`] syntax. That payload is used when injecting the panic into +/// the calling Rust thread, causing the thread to panic entirely. +/// +/// The behavior of the default `std` hook, i.e. the code that runs directly +/// after the panic is invoked, is to print the message payload to +/// `stderr` along with the file/line/column information of the `panic!()` +/// call. You can override the panic hook using [`std::panic::set_hook()`]. +/// Inside the hook a panic can be accessed as a `&dyn Any + Send`, +/// which contains either a `&str` or `String` for regular `panic!()` invocations. +/// To panic with a value of another other type, [`panic_any`] can be used. +/// +/// See also the macro [`compile_error!`], for raising errors during compilation. +/// +/// # When to use `panic!` vs `Result` +/// +/// The Rust language provides two complementary systems for constructing / +/// representing, reporting, propagating, reacting to, and discarding errors. These +/// responsibilities are collectively known as "error handling." `panic!` and +/// `Result` are similar in that they are each the primary interface of their +/// respective error handling systems; however, the meaning these interfaces attach +/// to their errors and the responsibilities they fulfill within their respective +/// error handling systems differ. +/// +/// The `panic!` macro is used to construct errors that represent a bug that has +/// been detected in your program. With `panic!` you provide a message that +/// describes the bug and the language then constructs an error with that message, +/// reports it, and propagates it for you. +/// +/// `Result` on the other hand is used to wrap other types that represent either +/// the successful result of some computation, `Ok(T)`, or error types that +/// represent an anticipated runtime failure mode of that computation, `Err(E)`. +/// `Result` is used alongside user defined types which represent the various +/// anticipated runtime failure modes that the associated computation could +/// encounter. `Result` must be propagated manually, often with the help of the +/// `?` operator and `Try` trait, and they must be reported manually, often with +/// the help of the `Error` trait. +/// +/// For more detailed information about error handling check out the [book] or the +/// [`std::result`] module docs. +/// +/// [ounwrap]: Option::unwrap +/// [runwrap]: Result::unwrap +/// [`std::panic::set_hook()`]: ../std/panic/fn.set_hook.html +/// [`panic_any`]: ../std/panic/fn.panic_any.html +/// [`Box`]: ../std/boxed/struct.Box.html +/// [`format!`]: ../std/macro.format.html +/// [book]: ../book/ch09-00-error-handling.html +/// [`std::result`]: ../std/result/index.html +/// +/// # Current implementation +/// +/// If the main thread panics it will terminate all your threads and end your +/// program with code `101`. +/// +/// # Examples +/// +/// ```should_panic +/// # #![allow(unreachable_code)] +/// use anstream::panic; +/// panic!(); +/// panic!("this is a terrible mistake!"); +/// panic!("this is a {} {message}", "fancy", message = "message"); +/// ``` +#[cfg(feature = "auto")] +#[macro_export] +macro_rules! panic { + () => { + ::std::panic!() + }; + ($($arg:tt)*) => {{ + let target_stream = std::io::stderr(); + let buffer = $crate::_macros::to_adapted_string(&format_args!($($arg)*), &target_stream); + ::std::panic!("{}", buffer) + }}; +} + +#[cfg(feature = "auto")] +pub const FEATURE_TEST_ACTIVATED: bool = cfg!(feature = "test"); + +#[cfg(feature = "auto")] +pub fn to_adapted_string( + display: &dyn std::fmt::Display, + stream: &impl crate::stream::RawStream, +) -> String { + use std::io::Write as _; + + let choice = crate::AutoStream::choice(stream); + let buffer = Vec::new(); + let mut stream = crate::AutoStream::new(buffer, choice); + // Ignore errors rather than panic + let _ = ::std::write!(&mut stream, "{display}"); + let buffer = stream.into_inner(); + // Should be UTF-8 but not wanting to panic + let buffer = String::from_utf8_lossy(&buffer).into_owned(); + buffer +} diff --git a/vendor/anstream/src/adapter/mod.rs b/vendor/anstream/src/adapter/mod.rs new file mode 100644 index 00000000..f266b680 --- /dev/null +++ b/vendor/anstream/src/adapter/mod.rs @@ -0,0 +1,15 @@ +//! Gracefully degrade styled output + +mod strip; +mod wincon; + +pub use strip::strip_bytes; +pub use strip::strip_str; +pub use strip::StripBytes; +pub use strip::StripBytesIter; +pub use strip::StripStr; +pub use strip::StripStrIter; +pub use strip::StrippedBytes; +pub use strip::StrippedStr; +pub use wincon::WinconBytes; +pub use wincon::WinconBytesIter; diff --git a/vendor/anstream/src/adapter/strip.rs b/vendor/anstream/src/adapter/strip.rs new file mode 100644 index 00000000..dca414bb --- /dev/null +++ b/vendor/anstream/src/adapter/strip.rs @@ -0,0 +1,511 @@ +use anstyle_parse::state::state_change; +use anstyle_parse::state::Action; +use anstyle_parse::state::State; + +/// Strip ANSI escapes from a `&str`, returning the printable content +/// +/// This can be used to take output from a program that includes escape sequences and write it +/// somewhere that does not easily support them, such as a log file. +/// +/// For non-contiguous data, see [`StripStr`]. +/// +/// # Example +/// +/// ```rust +/// use std::io::Write as _; +/// +/// let styled_text = "\x1b[32mfoo\x1b[m bar"; +/// let plain_str = anstream::adapter::strip_str(&styled_text).to_string(); +/// assert_eq!(plain_str, "foo bar"); +/// ``` +#[inline] +pub fn strip_str(data: &str) -> StrippedStr<'_> { + StrippedStr::new(data) +} + +/// See [`strip_str`] +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct StrippedStr<'s> { + bytes: &'s [u8], + state: State, +} + +impl<'s> StrippedStr<'s> { + #[inline] + fn new(data: &'s str) -> Self { + Self { + bytes: data.as_bytes(), + state: State::Ground, + } + } + + /// Create a [`String`] of the printable content + #[inline] + #[allow(clippy::inherent_to_string_shadow_display)] // Single-allocation implementation + pub fn to_string(&self) -> String { + use std::fmt::Write as _; + let mut stripped = String::with_capacity(self.bytes.len()); + let _ = write!(&mut stripped, "{self}"); + stripped + } +} + +impl std::fmt::Display for StrippedStr<'_> { + /// **Note:** this does *not* exhaust the [`Iterator`] + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let iter = Self { + bytes: self.bytes, + state: self.state, + }; + for printable in iter { + printable.fmt(f)?; + } + Ok(()) + } +} + +impl<'s> Iterator for StrippedStr<'s> { + type Item = &'s str; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + next_str(&mut self.bytes, &mut self.state) + } +} + +/// Incrementally strip non-contiguous data +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct StripStr { + state: State, +} + +impl StripStr { + /// Initial state + pub fn new() -> Self { + Default::default() + } + + /// Strip the next segment of data + pub fn strip_next<'s>(&'s mut self, data: &'s str) -> StripStrIter<'s> { + StripStrIter { + bytes: data.as_bytes(), + state: &mut self.state, + } + } +} + +/// See [`StripStr`] +#[derive(Debug, PartialEq, Eq)] +pub struct StripStrIter<'s> { + bytes: &'s [u8], + state: &'s mut State, +} + +impl<'s> Iterator for StripStrIter<'s> { + type Item = &'s str; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + next_str(&mut self.bytes, self.state) + } +} + +#[inline] +fn next_str<'s>(bytes: &mut &'s [u8], state: &mut State) -> Option<&'s str> { + let offset = bytes.iter().copied().position(|b| { + let (next_state, action) = state_change(*state, b); + if next_state != State::Anywhere { + *state = next_state; + } + is_printable_bytes(action, b) + }); + let (_, next) = bytes.split_at(offset.unwrap_or(bytes.len())); + *bytes = next; + *state = State::Ground; + + let offset = bytes.iter().copied().position(|b| { + let (_next_state, action) = state_change(State::Ground, b); + !(is_printable_bytes(action, b) || is_utf8_continuation(b)) + }); + let (printable, next) = bytes.split_at(offset.unwrap_or(bytes.len())); + *bytes = next; + if printable.is_empty() { + None + } else { + let printable = unsafe { + from_utf8_unchecked( + printable, + "`bytes` was validated as UTF-8, the parser preserves UTF-8 continuations", + ) + }; + Some(printable) + } +} + +#[inline] +unsafe fn from_utf8_unchecked<'b>(bytes: &'b [u8], safety_justification: &'static str) -> &'b str { + unsafe { + if cfg!(debug_assertions) { + // Catch problems more quickly when testing + std::str::from_utf8(bytes).expect(safety_justification) + } else { + std::str::from_utf8_unchecked(bytes) + } + } +} + +#[inline] +fn is_utf8_continuation(b: u8) -> bool { + matches!(b, 0x80..=0xbf) +} + +/// Strip ANSI escapes from bytes, returning the printable content +/// +/// This can be used to take output from a program that includes escape sequences and write it +/// somewhere that does not easily support them, such as a log file. +/// +/// # Example +/// +/// ```rust +/// use std::io::Write as _; +/// +/// let styled_text = "\x1b[32mfoo\x1b[m bar"; +/// let plain_str = anstream::adapter::strip_bytes(styled_text.as_bytes()).into_vec(); +/// assert_eq!(plain_str.as_slice(), &b"foo bar"[..]); +/// ``` +#[inline] +pub fn strip_bytes(data: &[u8]) -> StrippedBytes<'_> { + StrippedBytes::new(data) +} + +/// See [`strip_bytes`] +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct StrippedBytes<'s> { + bytes: &'s [u8], + state: State, + utf8parser: Utf8Parser, +} + +impl<'s> StrippedBytes<'s> { + /// See [`strip_bytes`] + #[inline] + pub fn new(bytes: &'s [u8]) -> Self { + Self { + bytes, + state: State::Ground, + utf8parser: Default::default(), + } + } + + /// Strip the next slice of bytes + /// + /// Used when the content is in several non-contiguous slices + /// + /// # Panic + /// + /// May panic if it is not exhausted / empty + #[inline] + pub fn extend(&mut self, bytes: &'s [u8]) { + debug_assert!( + self.is_empty(), + "current bytes must be processed to ensure we end at the right state" + ); + self.bytes = bytes; + } + + /// Report the bytes has been exhausted + #[inline] + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } + + /// Create a [`Vec`] of the printable content + #[inline] + pub fn into_vec(self) -> Vec<u8> { + let mut stripped = Vec::with_capacity(self.bytes.len()); + for printable in self { + stripped.extend(printable); + } + stripped + } +} + +impl<'s> Iterator for StrippedBytes<'s> { + type Item = &'s [u8]; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + next_bytes(&mut self.bytes, &mut self.state, &mut self.utf8parser) + } +} + +/// Incrementally strip non-contiguous data +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct StripBytes { + state: State, + utf8parser: Utf8Parser, +} + +impl StripBytes { + /// Initial state + pub fn new() -> Self { + Default::default() + } + + /// Strip the next segment of data + pub fn strip_next<'s>(&'s mut self, bytes: &'s [u8]) -> StripBytesIter<'s> { + StripBytesIter { + bytes, + state: &mut self.state, + utf8parser: &mut self.utf8parser, + } + } +} + +/// See [`StripBytes`] +#[derive(Debug, PartialEq, Eq)] +pub struct StripBytesIter<'s> { + bytes: &'s [u8], + state: &'s mut State, + utf8parser: &'s mut Utf8Parser, +} + +impl<'s> Iterator for StripBytesIter<'s> { + type Item = &'s [u8]; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + next_bytes(&mut self.bytes, self.state, self.utf8parser) + } +} + +#[inline] +fn next_bytes<'s>( + bytes: &mut &'s [u8], + state: &mut State, + utf8parser: &mut Utf8Parser, +) -> Option<&'s [u8]> { + let offset = bytes.iter().copied().position(|b| { + if *state == State::Utf8 { + true + } else { + let (next_state, action) = state_change(*state, b); + if next_state != State::Anywhere { + *state = next_state; + } + is_printable_bytes(action, b) + } + }); + let (_, next) = bytes.split_at(offset.unwrap_or(bytes.len())); + *bytes = next; + + let offset = bytes.iter().copied().position(|b| { + if *state == State::Utf8 { + if utf8parser.add(b) { + *state = State::Ground; + } + false + } else { + let (next_state, action) = state_change(State::Ground, b); + if next_state != State::Anywhere { + *state = next_state; + } + if *state == State::Utf8 { + utf8parser.add(b); + false + } else { + !is_printable_bytes(action, b) + } + } + }); + let (printable, next) = bytes.split_at(offset.unwrap_or(bytes.len())); + *bytes = next; + if printable.is_empty() { + None + } else { + Some(printable) + } +} + +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub(crate) struct Utf8Parser { + utf8_parser: utf8parse::Parser, +} + +impl Utf8Parser { + fn add(&mut self, byte: u8) -> bool { + let mut b = false; + let mut receiver = VtUtf8Receiver(&mut b); + self.utf8_parser.advance(&mut receiver, byte); + b + } +} + +struct VtUtf8Receiver<'a>(&'a mut bool); + +impl utf8parse::Receiver for VtUtf8Receiver<'_> { + fn codepoint(&mut self, _: char) { + *self.0 = true; + } + + fn invalid_sequence(&mut self) { + *self.0 = true; + } +} + +#[inline] +fn is_printable_bytes(action: Action, byte: u8) -> bool { + // VT320 considered 0x7f to be `Print`able but we expect to be working in UTF-8 systems and not + // ISO Latin-1, making it DEL and non-printable + const DEL: u8 = 0x7f; + + // Continuations aren't included as they may also be control codes, requiring more context + (action == Action::Print && byte != DEL) + || action == Action::BeginUtf8 + || (action == Action::Execute && byte.is_ascii_whitespace()) +} + +#[cfg(test)] +mod test { + use super::*; + use proptest::prelude::*; + + /// Model based off full parser + fn parser_strip(bytes: &[u8]) -> String { + #[derive(Default)] + struct Strip(String); + impl Strip { + fn with_capacity(capacity: usize) -> Self { + Self(String::with_capacity(capacity)) + } + } + impl anstyle_parse::Perform for Strip { + fn print(&mut self, c: char) { + self.0.push(c); + } + + fn execute(&mut self, byte: u8) { + if byte.is_ascii_whitespace() { + self.0.push(byte as char); + } + } + } + + let mut stripped = Strip::with_capacity(bytes.len()); + let mut parser = anstyle_parse::Parser::<anstyle_parse::DefaultCharAccumulator>::new(); + for byte in bytes { + parser.advance(&mut stripped, *byte); + } + stripped.0 + } + + /// Model verifying incremental parsing + fn strip_char(mut s: &str) -> String { + let mut result = String::new(); + let mut state = StripStr::new(); + while !s.is_empty() { + let mut indices = s.char_indices(); + indices.next(); // current + let offset = indices.next().map(|(i, _)| i).unwrap_or_else(|| s.len()); + let (current, remainder) = s.split_at(offset); + for printable in state.strip_next(current) { + result.push_str(printable); + } + s = remainder; + } + result + } + + /// Model verifying incremental parsing + fn strip_byte(s: &[u8]) -> Vec<u8> { + let mut result = Vec::new(); + let mut state = StripBytes::default(); + for start in 0..s.len() { + let current = &s[start..=start]; + for printable in state.strip_next(current) { + result.extend(printable); + } + } + result + } + + #[test] + fn test_strip_bytes_multibyte() { + let bytes = [240, 145, 141, 139]; + let expected = parser_strip(&bytes); + let actual = String::from_utf8(strip_bytes(&bytes).into_vec()).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn test_strip_byte_multibyte() { + let bytes = [240, 145, 141, 139]; + let expected = parser_strip(&bytes); + let actual = String::from_utf8(strip_byte(&bytes).clone()).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn test_strip_str_del() { + let input = std::str::from_utf8(&[0x7f]).unwrap(); + let expected = ""; + let actual = strip_str(input).to_string(); + assert_eq!(expected, actual); + } + + #[test] + fn test_strip_byte_del() { + let bytes = [0x7f]; + let expected = ""; + let actual = String::from_utf8(strip_byte(&bytes).clone()).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn test_strip_str_handles_broken_sequence() { + // valid utf8: \xc3\xb6 then \x1b then \xf0\x9f\x98\x80 + let s = "ö\x1b😀hello😀goodbye"; + let mut it = strip_str(s); + assert_eq!("ö", it.next().unwrap()); + assert_eq!("ello😀goodbye", it.next().unwrap()); + } + + proptest! { + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn strip_str_no_escapes(s in "\\PC*") { + let expected = parser_strip(s.as_bytes()); + let actual = strip_str(&s).to_string(); + assert_eq!(expected, actual); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn strip_char_no_escapes(s in "\\PC*") { + let expected = parser_strip(s.as_bytes()); + let actual = strip_char(&s); + assert_eq!(expected, actual); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn strip_bytes_no_escapes(s in "\\PC*") { + dbg!(&s); + dbg!(s.as_bytes()); + let expected = parser_strip(s.as_bytes()); + let actual = String::from_utf8(strip_bytes(s.as_bytes()).into_vec()).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn strip_byte_no_escapes(s in "\\PC*") { + dbg!(&s); + dbg!(s.as_bytes()); + let expected = parser_strip(s.as_bytes()); + let actual = String::from_utf8(strip_byte(s.as_bytes()).clone()).unwrap(); + assert_eq!(expected, actual); + } + } +} diff --git a/vendor/anstream/src/adapter/wincon.rs b/vendor/anstream/src/adapter/wincon.rs new file mode 100644 index 00000000..c2114919 --- /dev/null +++ b/vendor/anstream/src/adapter/wincon.rs @@ -0,0 +1,378 @@ +/// Incrementally convert to wincon calls for non-contiguous data +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct WinconBytes { + parser: anstyle_parse::Parser, + capture: WinconCapture, +} + +impl WinconBytes { + /// Initial state + pub fn new() -> Self { + Default::default() + } + + /// Strip the next segment of data + pub fn extract_next<'s>(&'s mut self, bytes: &'s [u8]) -> WinconBytesIter<'s> { + self.capture.reset(); + self.capture.printable.reserve(bytes.len()); + WinconBytesIter { + bytes, + parser: &mut self.parser, + capture: &mut self.capture, + } + } +} + +/// See [`WinconBytes`] +#[derive(Debug, PartialEq, Eq)] +pub struct WinconBytesIter<'s> { + bytes: &'s [u8], + parser: &'s mut anstyle_parse::Parser, + capture: &'s mut WinconCapture, +} + +impl Iterator for WinconBytesIter<'_> { + type Item = (anstyle::Style, String); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + next_bytes(&mut self.bytes, self.parser, self.capture) + } +} + +#[inline] +fn next_bytes( + bytes: &mut &[u8], + parser: &mut anstyle_parse::Parser, + capture: &mut WinconCapture, +) -> Option<(anstyle::Style, String)> { + capture.reset(); + while capture.ready.is_none() { + let byte = if let Some((byte, remainder)) = (*bytes).split_first() { + *bytes = remainder; + *byte + } else { + break; + }; + parser.advance(capture, byte); + } + if capture.printable.is_empty() { + return None; + } + + let style = capture.ready.unwrap_or(capture.style); + Some((style, std::mem::take(&mut capture.printable))) +} + +#[derive(Default, Clone, Debug, PartialEq, Eq)] +struct WinconCapture { + style: anstyle::Style, + printable: String, + ready: Option<anstyle::Style>, +} + +impl WinconCapture { + fn reset(&mut self) { + self.ready = None; + } +} + +impl anstyle_parse::Perform for WinconCapture { + /// Draw a character to the screen and update states. + fn print(&mut self, c: char) { + self.printable.push(c); + } + + /// Execute a C0 or C1 control function. + fn execute(&mut self, byte: u8) { + if byte.is_ascii_whitespace() { + self.printable.push(byte as char); + } + } + + fn csi_dispatch( + &mut self, + params: &anstyle_parse::Params, + _intermediates: &[u8], + ignore: bool, + action: u8, + ) { + if ignore { + return; + } + if action != b'm' { + return; + } + + let mut style = self.style; + // param/value differences are dependent on the escape code + let mut state = CsiState::Normal; + let mut r = None; + let mut g = None; + let mut color_target = ColorTarget::Fg; + for param in params { + for value in param { + match (state, *value) { + (CsiState::Normal, 0) => { + style = anstyle::Style::default(); + break; + } + (CsiState::Normal, 1) => { + style = style.bold(); + break; + } + (CsiState::Normal, 2) => { + style = style.dimmed(); + break; + } + (CsiState::Normal, 3) => { + style = style.italic(); + break; + } + (CsiState::Normal, 4) => { + style = style.underline(); + state = CsiState::Underline; + } + (CsiState::Normal, 21) => { + style |= anstyle::Effects::DOUBLE_UNDERLINE; + break; + } + (CsiState::Normal, 7) => { + style = style.invert(); + break; + } + (CsiState::Normal, 8) => { + style = style.hidden(); + break; + } + (CsiState::Normal, 9) => { + style = style.strikethrough(); + break; + } + (CsiState::Normal, 30..=37) => { + let color = to_ansi_color(value - 30).expect("within 4-bit range"); + style = style.fg_color(Some(color.into())); + break; + } + (CsiState::Normal, 38) => { + color_target = ColorTarget::Fg; + state = CsiState::PrepareCustomColor; + } + (CsiState::Normal, 39) => { + style = style.fg_color(None); + break; + } + (CsiState::Normal, 40..=47) => { + let color = to_ansi_color(value - 40).expect("within 4-bit range"); + style = style.bg_color(Some(color.into())); + break; + } + (CsiState::Normal, 48) => { + color_target = ColorTarget::Bg; + state = CsiState::PrepareCustomColor; + } + (CsiState::Normal, 49) => { + style = style.bg_color(None); + break; + } + (CsiState::Normal, 58) => { + color_target = ColorTarget::Underline; + state = CsiState::PrepareCustomColor; + } + (CsiState::Normal, 90..=97) => { + let color = to_ansi_color(value - 90) + .expect("within 4-bit range") + .bright(true); + style = style.fg_color(Some(color.into())); + break; + } + (CsiState::Normal, 100..=107) => { + let color = to_ansi_color(value - 100) + .expect("within 4-bit range") + .bright(true); + style = style.bg_color(Some(color.into())); + break; + } + (CsiState::PrepareCustomColor, 5) => { + state = CsiState::Ansi256; + } + (CsiState::PrepareCustomColor, 2) => { + state = CsiState::Rgb; + r = None; + g = None; + } + (CsiState::Ansi256, n) => { + let color = anstyle::Ansi256Color(n as u8); + style = match color_target { + ColorTarget::Fg => style.fg_color(Some(color.into())), + ColorTarget::Bg => style.bg_color(Some(color.into())), + ColorTarget::Underline => style.underline_color(Some(color.into())), + }; + break; + } + (CsiState::Rgb, b) => match (r, g) { + (None, _) => { + r = Some(b); + } + (Some(_), None) => { + g = Some(b); + } + (Some(r), Some(g)) => { + let color = anstyle::RgbColor(r as u8, g as u8, b as u8); + style = match color_target { + ColorTarget::Fg => style.fg_color(Some(color.into())), + ColorTarget::Bg => style.bg_color(Some(color.into())), + ColorTarget::Underline => style.underline_color(Some(color.into())), + }; + break; + } + }, + (CsiState::Underline, 0) => { + style = + style.effects(style.get_effects().remove(anstyle::Effects::UNDERLINE)); + } + (CsiState::Underline, 1) => { + // underline already set + } + (CsiState::Underline, 2) => { + style = style + .effects(style.get_effects().remove(anstyle::Effects::UNDERLINE)) + | anstyle::Effects::DOUBLE_UNDERLINE; + } + (CsiState::Underline, 3) => { + style = style + .effects(style.get_effects().remove(anstyle::Effects::UNDERLINE)) + | anstyle::Effects::CURLY_UNDERLINE; + } + (CsiState::Underline, 4) => { + style = style + .effects(style.get_effects().remove(anstyle::Effects::UNDERLINE)) + | anstyle::Effects::DOTTED_UNDERLINE; + } + (CsiState::Underline, 5) => { + style = style + .effects(style.get_effects().remove(anstyle::Effects::UNDERLINE)) + | anstyle::Effects::DASHED_UNDERLINE; + } + _ => { + break; + } + } + } + } + + if style != self.style && !self.printable.is_empty() { + self.ready = Some(self.style); + } + self.style = style; + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum CsiState { + Normal, + PrepareCustomColor, + Ansi256, + Rgb, + Underline, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ColorTarget { + Fg, + Bg, + Underline, +} + +fn to_ansi_color(digit: u16) -> Option<anstyle::AnsiColor> { + match digit { + 0 => Some(anstyle::AnsiColor::Black), + 1 => Some(anstyle::AnsiColor::Red), + 2 => Some(anstyle::AnsiColor::Green), + 3 => Some(anstyle::AnsiColor::Yellow), + 4 => Some(anstyle::AnsiColor::Blue), + 5 => Some(anstyle::AnsiColor::Magenta), + 6 => Some(anstyle::AnsiColor::Cyan), + 7 => Some(anstyle::AnsiColor::White), + _ => None, + } +} + +#[cfg(test)] +mod test { + use super::*; + use proptest::prelude::*; + + #[track_caller] + fn verify(input: &str, expected: Vec<(anstyle::Style, &str)>) { + let expected = expected + .into_iter() + .map(|(style, value)| (style, value.to_owned())) + .collect::<Vec<_>>(); + let mut state = WinconBytes::new(); + let actual = state.extract_next(input.as_bytes()).collect::<Vec<_>>(); + assert_eq!(expected, actual, "{input:?}"); + } + + #[test] + fn start() { + let green_on_red = anstyle::AnsiColor::Green.on(anstyle::AnsiColor::Red); + let input = format!("{green_on_red}Hello{green_on_red:#} world!"); + let expected = vec![ + (green_on_red, "Hello"), + (anstyle::Style::default(), " world!"), + ]; + verify(&input, expected); + } + + #[test] + fn middle() { + let green_on_red = anstyle::AnsiColor::Green.on(anstyle::AnsiColor::Red); + let input = format!("Hello {green_on_red}world{green_on_red:#}!"); + let expected = vec![ + (anstyle::Style::default(), "Hello "), + (green_on_red, "world"), + (anstyle::Style::default(), "!"), + ]; + verify(&input, expected); + } + + #[test] + fn end() { + let green_on_red = anstyle::AnsiColor::Green.on(anstyle::AnsiColor::Red); + let input = format!("Hello {green_on_red}world!{green_on_red:#}"); + let expected = vec![ + (anstyle::Style::default(), "Hello "), + (green_on_red, "world!"), + ]; + verify(&input, expected); + } + + #[test] + fn ansi256_colors() { + let ansi_11 = anstyle::Ansi256Color(11).on_default(); + // termcolor only supports "brights" via these + let input = format!("Hello {ansi_11}world{ansi_11:#}!"); + let expected = vec![ + (anstyle::Style::default(), "Hello "), + (ansi_11, "world"), + (anstyle::Style::default(), "!"), + ]; + verify(&input, expected); + } + + proptest! { + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn wincon_no_escapes(s in "\\PC*") { + let expected = if s.is_empty() { + vec![] + } else { + vec![(anstyle::Style::default(), s.clone())] + }; + let mut state = WinconBytes::new(); + let actual = state.extract_next(s.as_bytes()).collect::<Vec<_>>(); + assert_eq!(expected, actual); + } + } +} diff --git a/vendor/anstream/src/auto.rs b/vendor/anstream/src/auto.rs new file mode 100644 index 00000000..743640f8 --- /dev/null +++ b/vendor/anstream/src/auto.rs @@ -0,0 +1,302 @@ +use crate::stream::AsLockedWrite; +use crate::stream::RawStream; +use crate::ColorChoice; +use crate::StripStream; +#[cfg(all(windows, feature = "wincon"))] +use crate::WinconStream; + +/// [`std::io::Write`] that adapts ANSI escape codes to the underlying `Write`s capabilities +/// +/// This includes +/// - Stripping colors for non-terminals +/// - Respecting env variables like [NO_COLOR](https://no-color.org/) or [CLICOLOR](https://bixense.com/clicolors/) +/// - *(windows)* Falling back to the wincon API where [ENABLE_VIRTUAL_TERMINAL_PROCESSING](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences) is unsupported +/// +/// You can customize auto-detection by calling into +/// [anstyle_query](https://docs.rs/anstyle-query/latest/anstyle_query/) +/// to get a [`ColorChoice`] and then calling [`AutoStream::new(stream, choice)`]. +#[derive(Debug)] +pub struct AutoStream<S: RawStream> { + inner: StreamInner<S>, +} + +#[derive(Debug)] +enum StreamInner<S: RawStream> { + PassThrough(S), + Strip(StripStream<S>), + #[cfg(all(windows, feature = "wincon"))] + Wincon(WinconStream<S>), +} + +impl<S> AutoStream<S> +where + S: RawStream, +{ + /// Runtime control over styling behavior + /// + /// # Example + /// + /// ```rust + /// # #[cfg(feature = "auto")] { + /// # use std::io::IsTerminal as _; + /// // Like `AutoStream::choice` but without `NO_COLOR`, `CLICOLOR_FORCE`, `CI` + /// fn choice(raw: &dyn anstream::stream::RawStream) -> anstream::ColorChoice { + /// let choice = anstream::ColorChoice::global(); + /// if choice == anstream::ColorChoice::Auto { + /// if raw.is_terminal() && anstyle_query::term_supports_color() { + /// anstream::ColorChoice::Always + /// } else { + /// anstream::ColorChoice::Never + /// } + /// } else { + /// choice + /// } + /// } + /// + /// let stream = std::io::stdout(); + /// let choice = choice(&stream); + /// let auto = anstream::AutoStream::new(stream, choice); + /// # } + /// ``` + #[inline] + pub fn new(raw: S, choice: ColorChoice) -> Self { + match choice { + #[cfg(feature = "auto")] + ColorChoice::Auto => Self::auto(raw), + #[cfg(not(feature = "auto"))] + ColorChoice::Auto => Self::never(raw), + ColorChoice::AlwaysAnsi => Self::always_ansi(raw), + ColorChoice::Always => Self::always(raw), + ColorChoice::Never => Self::never(raw), + } + } + + /// Auto-adapt for the stream's capabilities + #[cfg(feature = "auto")] + #[inline] + pub fn auto(raw: S) -> Self { + let choice = Self::choice(&raw); + debug_assert_ne!(choice, ColorChoice::Auto); + Self::new(raw, choice) + } + + /// Report the desired choice for the given stream + #[cfg(feature = "auto")] + pub fn choice(raw: &S) -> ColorChoice { + choice(raw) + } + + /// Force ANSI escape codes to be passed through as-is, no matter what the inner `Write` + /// supports. + #[inline] + pub fn always_ansi(raw: S) -> Self { + #[cfg(feature = "auto")] + { + if raw.is_terminal() { + let _ = anstyle_query::windows::enable_ansi_colors(); + } + } + Self::always_ansi_(raw) + } + + #[inline] + fn always_ansi_(raw: S) -> Self { + let inner = StreamInner::PassThrough(raw); + AutoStream { inner } + } + + /// Force color, no matter what the inner `Write` supports. + #[inline] + pub fn always(raw: S) -> Self { + if cfg!(windows) { + #[cfg(feature = "auto")] + let use_wincon = raw.is_terminal() + && !anstyle_query::windows::enable_ansi_colors().unwrap_or(true) + && !anstyle_query::term_supports_ansi_color(); + #[cfg(not(feature = "auto"))] + let use_wincon = true; + if use_wincon { + Self::wincon(raw).unwrap_or_else(|raw| Self::always_ansi_(raw)) + } else { + Self::always_ansi_(raw) + } + } else { + Self::always_ansi(raw) + } + } + + /// Only pass printable data to the inner `Write`. + #[inline] + pub fn never(raw: S) -> Self { + let inner = StreamInner::Strip(StripStream::new(raw)); + AutoStream { inner } + } + + #[inline] + fn wincon(raw: S) -> Result<Self, S> { + #[cfg(all(windows, feature = "wincon"))] + { + Ok(Self { + inner: StreamInner::Wincon(WinconStream::new(raw)), + }) + } + #[cfg(not(all(windows, feature = "wincon")))] + { + Err(raw) + } + } + + /// Get the wrapped [`RawStream`] + #[inline] + pub fn into_inner(self) -> S { + match self.inner { + StreamInner::PassThrough(w) => w, + StreamInner::Strip(w) => w.into_inner(), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => w.into_inner(), + } + } + + /// Returns `true` if the descriptor/handle refers to a terminal/tty. + #[inline] + pub fn is_terminal(&self) -> bool { + match &self.inner { + StreamInner::PassThrough(w) => w.is_terminal(), + StreamInner::Strip(w) => w.is_terminal(), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(_) => true, // its only ever a terminal + } + } + + /// Prefer [`AutoStream::choice`] + /// + /// This doesn't report what is requested but what is currently active. + #[inline] + #[cfg(feature = "auto")] + pub fn current_choice(&self) -> ColorChoice { + match &self.inner { + StreamInner::PassThrough(_) => ColorChoice::AlwaysAnsi, + StreamInner::Strip(_) => ColorChoice::Never, + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(_) => ColorChoice::Always, + } + } +} + +#[cfg(feature = "auto")] +fn choice(raw: &dyn RawStream) -> ColorChoice { + let choice = ColorChoice::global(); + match choice { + ColorChoice::Auto => { + let clicolor = anstyle_query::clicolor(); + let clicolor_enabled = clicolor.unwrap_or(false); + let clicolor_disabled = !clicolor.unwrap_or(true); + if anstyle_query::no_color() { + ColorChoice::Never + } else if anstyle_query::clicolor_force() { + ColorChoice::Always + } else if clicolor_disabled { + ColorChoice::Never + } else if raw.is_terminal() + && (anstyle_query::term_supports_color() + || clicolor_enabled + || anstyle_query::is_ci()) + { + ColorChoice::Always + } else { + ColorChoice::Never + } + } + ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Never => choice, + } +} + +impl AutoStream<std::io::Stdout> { + /// Get exclusive access to the `AutoStream` + /// + /// Why? + /// - Faster performance when writing in a loop + /// - Avoid other threads interleaving output with the current thread + #[inline] + pub fn lock(self) -> AutoStream<std::io::StdoutLock<'static>> { + let inner = match self.inner { + StreamInner::PassThrough(w) => StreamInner::PassThrough(w.lock()), + StreamInner::Strip(w) => StreamInner::Strip(w.lock()), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => StreamInner::Wincon(w.lock()), + }; + AutoStream { inner } + } +} + +impl AutoStream<std::io::Stderr> { + /// Get exclusive access to the `AutoStream` + /// + /// Why? + /// - Faster performance when writing in a loop + /// - Avoid other threads interleaving output with the current thread + #[inline] + pub fn lock(self) -> AutoStream<std::io::StderrLock<'static>> { + let inner = match self.inner { + StreamInner::PassThrough(w) => StreamInner::PassThrough(w.lock()), + StreamInner::Strip(w) => StreamInner::Strip(w.lock()), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => StreamInner::Wincon(w.lock()), + }; + AutoStream { inner } + } +} + +impl<S> std::io::Write for AutoStream<S> +where + S: RawStream + AsLockedWrite, +{ + // Must forward all calls to ensure locking happens appropriately + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { + match &mut self.inner { + StreamInner::PassThrough(w) => w.as_locked_write().write(buf), + StreamInner::Strip(w) => w.write(buf), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => w.write(buf), + } + } + #[inline] + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> { + match &mut self.inner { + StreamInner::PassThrough(w) => w.as_locked_write().write_vectored(bufs), + StreamInner::Strip(w) => w.write_vectored(bufs), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => w.write_vectored(bufs), + } + } + // is_write_vectored: nightly only + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + match &mut self.inner { + StreamInner::PassThrough(w) => w.as_locked_write().flush(), + StreamInner::Strip(w) => w.flush(), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => w.flush(), + } + } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + match &mut self.inner { + StreamInner::PassThrough(w) => w.as_locked_write().write_all(buf), + StreamInner::Strip(w) => w.write_all(buf), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => w.write_all(buf), + } + } + // write_all_vectored: nightly only + #[inline] + fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { + match &mut self.inner { + StreamInner::PassThrough(w) => w.as_locked_write().write_fmt(args), + StreamInner::Strip(w) => w.write_fmt(args), + #[cfg(all(windows, feature = "wincon"))] + StreamInner::Wincon(w) => w.write_fmt(args), + } + } +} diff --git a/vendor/anstream/src/buffer.rs b/vendor/anstream/src/buffer.rs new file mode 100644 index 00000000..92e5aafe --- /dev/null +++ b/vendor/anstream/src/buffer.rs @@ -0,0 +1,56 @@ +#![allow(deprecated)] + +/// In-memory [`RawStream`][crate::stream::RawStream] +#[derive(Clone, Default, Debug, PartialEq, Eq)] +#[deprecated(since = "0.6.2", note = "Use Vec")] +#[doc(hidden)] +pub struct Buffer(Vec<u8>); + +impl Buffer { + #[inline] + pub fn new() -> Self { + Default::default() + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity)) + } + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for Buffer { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl std::io::Write for Buffer { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { + self.0.extend(buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +#[cfg(all(windows, feature = "wincon"))] +impl anstyle_wincon::WinconStream for Buffer { + fn write_colored( + &mut self, + fg: Option<anstyle::AnsiColor>, + bg: Option<anstyle::AnsiColor>, + data: &[u8], + ) -> std::io::Result<usize> { + self.0.write_colored(fg, bg, data) + } +} diff --git a/vendor/anstream/src/fmt.rs b/vendor/anstream/src/fmt.rs new file mode 100644 index 00000000..e673c26d --- /dev/null +++ b/vendor/anstream/src/fmt.rs @@ -0,0 +1,54 @@ +/// A shim which allows a [`std::io::Write`] to be implemented in terms of a [`std::fmt::Write`] +/// +/// This saves off I/O errors. instead of discarding them +pub(crate) struct Adapter<W> +where + W: FnMut(&[u8]) -> std::io::Result<()>, +{ + writer: W, + error: std::io::Result<()>, +} + +impl<W> Adapter<W> +where + W: FnMut(&[u8]) -> std::io::Result<()>, +{ + pub(crate) fn new(writer: W) -> Self { + Adapter { + writer, + error: Ok(()), + } + } + + pub(crate) fn write_fmt(mut self, fmt: std::fmt::Arguments<'_>) -> std::io::Result<()> { + match std::fmt::write(&mut self, fmt) { + Ok(()) => Ok(()), + Err(..) => { + // check if the error came from the underlying `Write` or not + if self.error.is_err() { + self.error + } else { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "formatter error", + )) + } + } + } + } +} + +impl<W> std::fmt::Write for Adapter<W> +where + W: FnMut(&[u8]) -> std::io::Result<()>, +{ + fn write_str(&mut self, s: &str) -> std::fmt::Result { + match (self.writer)(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(std::fmt::Error) + } + } + } +} diff --git a/vendor/anstream/src/lib.rs b/vendor/anstream/src/lib.rs new file mode 100644 index 00000000..9233df7c --- /dev/null +++ b/vendor/anstream/src/lib.rs @@ -0,0 +1,90 @@ +//! **Auto-adapting [`stdout`] / [`stderr`] streams** +//! +//! *A portmanteau of "ansi stream"* +//! +//! [`AutoStream`] always accepts [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code), +//! [adapting to the user's terminal's capabilities][AutoStream]. +//! +//! Benefits +//! - Allows the caller to not be concerned with the terminal's capabilities +//! - Semver safe way of passing styled text between crates as ANSI escape codes offer more +//! compatibility than most crate APIs. +//! +//! Available styling crates: +//! - [anstyle](https://docs.rs/anstyle) for minimal runtime styling, designed to go in public APIs +//! - [owo-colors](https://docs.rs/owo-colors) for feature-rich runtime styling +//! - [color-print](https://docs.rs/color-print) for feature-rich compile-time styling +//! +//! # Example +//! +//! ``` +//! # #[cfg(feature = "auto")] { +//! use anstream::println; +//! use owo_colors::OwoColorize as _; +//! +//! // Foreground colors +//! println!("My number is {:#x}!", 10.green()); +//! // Background colors +//! println!("My number is not {}!", 4.on_red()); +//! # } +//! ``` +//! +//! And this will correctly handle piping to a file, etc + +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_docs)] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] + +pub mod adapter; +pub mod stream; +#[doc(hidden)] +#[macro_use] +pub mod _macros; + +mod auto; +mod buffer; +mod fmt; +mod strip; +#[cfg(all(windows, feature = "wincon"))] +mod wincon; + +pub use auto::AutoStream; +pub use strip::StripStream; +#[cfg(all(windows, feature = "wincon"))] +pub use wincon::WinconStream; + +#[allow(deprecated)] +pub use buffer::Buffer; + +/// An adaptive wrapper around the global standard output stream of the current process +pub type Stdout = AutoStream<std::io::Stdout>; +/// An adaptive wrapper around the global standard error stream of the current process +pub type Stderr = AutoStream<std::io::Stderr>; + +/// Create an ANSI escape code compatible stdout +/// +/// **Note:** Call [`AutoStream::lock`] in loops to avoid the performance hit of acquiring/releasing +/// from the implicit locking in each [`std::io::Write`] call +#[cfg(feature = "auto")] +pub fn stdout() -> Stdout { + let stdout = std::io::stdout(); + AutoStream::auto(stdout) +} + +/// Create an ANSI escape code compatible stderr +/// +/// **Note:** Call [`AutoStream::lock`] in loops to avoid the performance hit of acquiring/releasing +/// from the implicit locking in each [`std::io::Write`] call +#[cfg(feature = "auto")] +pub fn stderr() -> Stderr { + let stderr = std::io::stderr(); + AutoStream::auto(stderr) +} + +/// Selection for overriding color output +pub use colorchoice::ColorChoice; + +#[doc = include_str!("../README.md")] +#[cfg(doctest)] +pub struct ReadmeDoctests; diff --git a/vendor/anstream/src/stream.rs b/vendor/anstream/src/stream.rs new file mode 100644 index 00000000..875455e2 --- /dev/null +++ b/vendor/anstream/src/stream.rs @@ -0,0 +1,316 @@ +//! Higher-level traits to describe writeable streams + +/// Required functionality for underlying [`std::io::Write`] for adaptation +#[cfg(not(all(windows, feature = "wincon")))] +pub trait RawStream: std::io::Write + IsTerminal + private::Sealed {} + +/// Required functionality for underlying [`std::io::Write`] for adaptation +#[cfg(all(windows, feature = "wincon"))] +pub trait RawStream: + std::io::Write + IsTerminal + anstyle_wincon::WinconStream + private::Sealed +{ +} + +impl<T: RawStream + ?Sized> RawStream for &mut T {} +impl<T: RawStream + ?Sized> RawStream for Box<T> {} + +impl RawStream for std::io::Stdout {} + +impl RawStream for std::io::StdoutLock<'_> {} + +impl RawStream for std::io::Stderr {} + +impl RawStream for std::io::StderrLock<'_> {} + +impl RawStream for dyn std::io::Write {} +impl RawStream for dyn std::io::Write + Send {} +impl RawStream for dyn std::io::Write + Send + Sync {} + +impl RawStream for Vec<u8> {} + +impl RawStream for std::fs::File {} + +#[allow(deprecated)] +impl RawStream for crate::Buffer {} + +/// Trait to determine if a descriptor/handle refers to a terminal/tty. +pub trait IsTerminal: private::Sealed { + /// Returns `true` if the descriptor/handle refers to a terminal/tty. + fn is_terminal(&self) -> bool; +} + +impl<T: IsTerminal + ?Sized> IsTerminal for &T { + #[inline] + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + +impl<T: IsTerminal + ?Sized> IsTerminal for &mut T { + #[inline] + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + +impl<T: IsTerminal + ?Sized> IsTerminal for Box<T> { + #[inline] + fn is_terminal(&self) -> bool { + (**self).is_terminal() + } +} + +impl IsTerminal for std::io::Stdout { + #[inline] + fn is_terminal(&self) -> bool { + is_terminal_polyfill::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for std::io::StdoutLock<'_> { + #[inline] + fn is_terminal(&self) -> bool { + is_terminal_polyfill::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for std::io::Stderr { + #[inline] + fn is_terminal(&self) -> bool { + is_terminal_polyfill::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for std::io::StderrLock<'_> { + #[inline] + fn is_terminal(&self) -> bool { + is_terminal_polyfill::IsTerminal::is_terminal(self) + } +} + +impl IsTerminal for dyn std::io::Write { + #[inline] + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for dyn std::io::Write + Send { + #[inline] + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for dyn std::io::Write + Send + Sync { + #[inline] + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for Vec<u8> { + #[inline] + fn is_terminal(&self) -> bool { + false + } +} + +impl IsTerminal for std::fs::File { + #[inline] + fn is_terminal(&self) -> bool { + is_terminal_polyfill::IsTerminal::is_terminal(self) + } +} + +#[allow(deprecated)] +impl IsTerminal for crate::Buffer { + #[inline] + fn is_terminal(&self) -> bool { + false + } +} + +/// Lock a stream +pub trait AsLockedWrite: private::Sealed { + /// Locked writer type + type Write<'w>: RawStream + 'w + where + Self: 'w; + + /// Lock a stream + fn as_locked_write(&mut self) -> Self::Write<'_>; +} + +impl<T: AsLockedWrite + ?Sized> AsLockedWrite for &mut T { + type Write<'w> + = T::Write<'w> + where + Self: 'w; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + (**self).as_locked_write() + } +} + +impl<T: AsLockedWrite + ?Sized> AsLockedWrite for Box<T> { + type Write<'w> + = T::Write<'w> + where + Self: 'w; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + (**self).as_locked_write() + } +} + +impl AsLockedWrite for std::io::Stdout { + type Write<'w> = std::io::StdoutLock<'w>; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self.lock() + } +} + +impl AsLockedWrite for std::io::StdoutLock<'static> { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +impl AsLockedWrite for std::io::Stderr { + type Write<'w> = std::io::StderrLock<'w>; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self.lock() + } +} + +impl AsLockedWrite for std::io::StderrLock<'static> { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +impl AsLockedWrite for dyn std::io::Write { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +impl AsLockedWrite for dyn std::io::Write + Send { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +impl AsLockedWrite for dyn std::io::Write + Send + Sync { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +impl AsLockedWrite for Vec<u8> { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +impl AsLockedWrite for std::fs::File { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +#[allow(deprecated)] +impl AsLockedWrite for crate::Buffer { + type Write<'w> = &'w mut Self; + + #[inline] + fn as_locked_write(&mut self) -> Self::Write<'_> { + self + } +} + +mod private { + #[allow(unnameable_types)] + pub trait Sealed {} + + impl<T: Sealed + ?Sized> Sealed for &T {} + impl<T: Sealed + ?Sized> Sealed for &mut T {} + impl<T: Sealed + ?Sized> Sealed for Box<T> {} + + impl Sealed for std::io::Stdout {} + + impl Sealed for std::io::StdoutLock<'_> {} + + impl Sealed for std::io::Stderr {} + + impl Sealed for std::io::StderrLock<'_> {} + + impl Sealed for dyn std::io::Write {} + impl Sealed for dyn std::io::Write + Send {} + impl Sealed for dyn std::io::Write + Send + Sync {} + + impl Sealed for Vec<u8> {} + + impl Sealed for std::fs::File {} + + #[allow(deprecated)] + impl Sealed for crate::Buffer {} +} + +#[cfg(test)] +mod tests { + use super::*; + + fn assert_raw_stream<T: RawStream>() + where + crate::AutoStream<T>: std::io::Write, + { + } + + #[test] + fn test() { + assert_raw_stream::<Box<dyn std::io::Write>>(); + assert_raw_stream::<Box<dyn std::io::Write + 'static>>(); + assert_raw_stream::<Box<dyn std::io::Write + Send>>(); + assert_raw_stream::<Box<dyn std::io::Write + Send + Sync>>(); + + assert_raw_stream::<&mut (dyn std::io::Write)>(); + assert_raw_stream::<&mut (dyn std::io::Write + 'static)>(); + assert_raw_stream::<&mut (dyn std::io::Write + Send)>(); + assert_raw_stream::<&mut (dyn std::io::Write + Send + Sync)>(); + + assert_raw_stream::<Vec<u8>>(); + assert_raw_stream::<&mut Vec<u8>>(); + + assert_raw_stream::<std::fs::File>(); + assert_raw_stream::<&mut std::fs::File>(); + } +} diff --git a/vendor/anstream/src/strip.rs b/vendor/anstream/src/strip.rs new file mode 100644 index 00000000..9f0d28bd --- /dev/null +++ b/vendor/anstream/src/strip.rs @@ -0,0 +1,227 @@ +use crate::adapter::StripBytes; +use crate::stream::AsLockedWrite; +use crate::stream::IsTerminal; + +/// Only pass printable data to the inner `Write` +#[derive(Debug)] +pub struct StripStream<S> +where + S: std::io::Write, +{ + raw: S, + state: StripBytes, +} + +impl<S> StripStream<S> +where + S: std::io::Write, +{ + /// Only pass printable data to the inner `Write` + #[inline] + pub fn new(raw: S) -> Self { + Self { + raw, + state: Default::default(), + } + } + + /// Get the wrapped [`std::io::Write`] + #[inline] + pub fn into_inner(self) -> S { + self.raw + } +} + +impl<S> StripStream<S> +where + S: std::io::Write, + S: IsTerminal, +{ + /// Returns `true` if the descriptor/handle refers to a terminal/tty. + #[inline] + pub fn is_terminal(&self) -> bool { + self.raw.is_terminal() + } +} + +impl StripStream<std::io::Stdout> { + /// Get exclusive access to the `StripStream` + /// + /// Why? + /// - Faster performance when writing in a loop + /// - Avoid other threads interleaving output with the current thread + #[inline] + pub fn lock(self) -> StripStream<std::io::StdoutLock<'static>> { + StripStream { + raw: self.raw.lock(), + state: self.state, + } + } +} + +impl StripStream<std::io::Stderr> { + /// Get exclusive access to the `StripStream` + /// + /// Why? + /// - Faster performance when writing in a loop + /// - Avoid other threads interleaving output with the current thread + #[inline] + pub fn lock(self) -> StripStream<std::io::StderrLock<'static>> { + StripStream { + raw: self.raw.lock(), + state: self.state, + } + } +} + +impl<S> std::io::Write for StripStream<S> +where + S: std::io::Write, + S: AsLockedWrite, +{ + // Must forward all calls to ensure locking happens appropriately + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { + write(&mut self.raw.as_locked_write(), &mut self.state, buf) + } + #[inline] + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> { + let buf = bufs + .iter() + .find(|b| !b.is_empty()) + .map(|b| &**b) + .unwrap_or(&[][..]); + self.write(buf) + } + // is_write_vectored: nightly only + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + self.raw.as_locked_write().flush() + } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + write_all(&mut self.raw.as_locked_write(), &mut self.state, buf) + } + // write_all_vectored: nightly only + #[inline] + fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { + write_fmt(&mut self.raw.as_locked_write(), &mut self.state, args) + } +} + +fn write( + raw: &mut dyn std::io::Write, + state: &mut StripBytes, + buf: &[u8], +) -> std::io::Result<usize> { + let initial_state = state.clone(); + + for printable in state.strip_next(buf) { + let possible = printable.len(); + let written = raw.write(printable)?; + if possible != written { + let divergence = &printable[written..]; + let offset = offset_to(buf, divergence); + let consumed = &buf[offset..]; + *state = initial_state; + state.strip_next(consumed).last(); + return Ok(offset); + } + } + Ok(buf.len()) +} + +fn write_all( + raw: &mut dyn std::io::Write, + state: &mut StripBytes, + buf: &[u8], +) -> std::io::Result<()> { + for printable in state.strip_next(buf) { + raw.write_all(printable)?; + } + Ok(()) +} + +fn write_fmt( + raw: &mut dyn std::io::Write, + state: &mut StripBytes, + args: std::fmt::Arguments<'_>, +) -> std::io::Result<()> { + let write_all = |buf: &[u8]| write_all(raw, state, buf); + crate::fmt::Adapter::new(write_all).write_fmt(args) +} + +#[inline] +fn offset_to(total: &[u8], subslice: &[u8]) -> usize { + let total = total.as_ptr(); + let subslice = subslice.as_ptr(); + + debug_assert!( + total <= subslice, + "`Offset::offset_to` only accepts slices of `self`" + ); + subslice as usize - total as usize +} + +#[cfg(test)] +mod test { + use super::*; + use proptest::prelude::*; + use std::io::Write as _; + + proptest! { + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_all_no_escapes(s in "\\PC*") { + let buffer = Vec::new(); + let mut stream = StripStream::new(buffer); + stream.write_all(s.as_bytes()).unwrap(); + let buffer = stream.into_inner(); + let actual = std::str::from_utf8(buffer.as_ref()).unwrap(); + assert_eq!(s, actual); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_byte_no_escapes(s in "\\PC*") { + let buffer = Vec::new(); + let mut stream = StripStream::new(buffer); + for byte in s.as_bytes() { + stream.write_all(&[*byte]).unwrap(); + } + let buffer = stream.into_inner(); + let actual = std::str::from_utf8(buffer.as_ref()).unwrap(); + assert_eq!(s, actual); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_all_random(s in any::<Vec<u8>>()) { + let buffer = Vec::new(); + let mut stream = StripStream::new(buffer); + stream.write_all(s.as_slice()).unwrap(); + let buffer = stream.into_inner(); + if let Ok(actual) = std::str::from_utf8(buffer.as_ref()) { + for char in actual.chars() { + assert!(!char.is_ascii() || !char.is_control() || char.is_ascii_whitespace(), "{:?} -> {:?}: {:?}", String::from_utf8_lossy(&s), actual, char); + } + } + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_byte_random(s in any::<Vec<u8>>()) { + let buffer = Vec::new(); + let mut stream = StripStream::new(buffer); + for byte in s.as_slice() { + stream.write_all(&[*byte]).unwrap(); + } + let buffer = stream.into_inner(); + if let Ok(actual) = std::str::from_utf8(buffer.as_ref()) { + for char in actual.chars() { + assert!(!char.is_ascii() || !char.is_control() || char.is_ascii_whitespace(), "{:?} -> {:?}: {:?}", String::from_utf8_lossy(&s), actual, char); + } + } + } + } +} diff --git a/vendor/anstream/src/wincon.rs b/vendor/anstream/src/wincon.rs new file mode 100644 index 00000000..570511dd --- /dev/null +++ b/vendor/anstream/src/wincon.rs @@ -0,0 +1,226 @@ +use crate::adapter::WinconBytes; +use crate::stream::AsLockedWrite; +use crate::stream::IsTerminal; + +/// Only pass printable data to the inner `Write` +#[cfg(feature = "wincon")] // here mostly for documentation purposes +#[derive(Debug)] +pub struct WinconStream<S> +where + S: anstyle_wincon::WinconStream, +{ + raw: S, + // `WinconBytes` is especially large compared to other variants of `AutoStream`, so boxing it + // here so `AutoStream` doesn't have to discard one allocation and create another one when + // calling `AutoStream::lock` + state: Box<WinconBytes>, +} + +impl<S> WinconStream<S> +where + S: anstyle_wincon::WinconStream, +{ + /// Only pass printable data to the inner `Write` + #[inline] + pub fn new(raw: S) -> Self { + Self { + raw, + state: Default::default(), + } + } + + /// Get the wrapped [`anstyle_wincon::WinconStream`] + #[inline] + pub fn into_inner(self) -> S { + self.raw + } +} + +impl<S> WinconStream<S> +where + S: anstyle_wincon::WinconStream, + S: IsTerminal, +{ + /// Returns `true` if the descriptor/handle refers to a terminal/tty. + #[inline] + pub fn is_terminal(&self) -> bool { + self.raw.is_terminal() + } +} + +impl WinconStream<std::io::Stdout> { + /// Get exclusive access to the `WinconStream` + /// + /// Why? + /// - Faster performance when writing in a loop + /// - Avoid other threads interleaving output with the current thread + #[inline] + pub fn lock(self) -> WinconStream<std::io::StdoutLock<'static>> { + WinconStream { + raw: self.raw.lock(), + state: self.state, + } + } +} + +impl WinconStream<std::io::Stderr> { + /// Get exclusive access to the `WinconStream` + /// + /// Why? + /// - Faster performance when writing in a loop + /// - Avoid other threads interleaving output with the current thread + #[inline] + pub fn lock(self) -> WinconStream<std::io::StderrLock<'static>> { + WinconStream { + raw: self.raw.lock(), + state: self.state, + } + } +} + +impl<S> std::io::Write for WinconStream<S> +where + S: anstyle_wincon::WinconStream, + S: AsLockedWrite, +{ + // Must forward all calls to ensure locking happens appropriately + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { + write(&mut self.raw.as_locked_write(), &mut self.state, buf) + } + #[inline] + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> { + let buf = bufs + .iter() + .find(|b| !b.is_empty()) + .map(|b| &**b) + .unwrap_or(&[][..]); + self.write(buf) + } + // is_write_vectored: nightly only + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + self.raw.as_locked_write().flush() + } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + write_all(&mut self.raw.as_locked_write(), &mut self.state, buf) + } + // write_all_vectored: nightly only + #[inline] + fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { + write_fmt(&mut self.raw.as_locked_write(), &mut self.state, args) + } +} + +fn write( + raw: &mut dyn anstyle_wincon::WinconStream, + state: &mut WinconBytes, + buf: &[u8], +) -> std::io::Result<usize> { + for (style, printable) in state.extract_next(buf) { + let fg = style.get_fg_color().and_then(cap_wincon_color); + let bg = style.get_bg_color().and_then(cap_wincon_color); + let written = raw.write_colored(fg, bg, printable.as_bytes())?; + let possible = printable.len(); + if possible != written { + // HACK: Unsupported atm + break; + } + } + Ok(buf.len()) +} + +fn write_all( + raw: &mut dyn anstyle_wincon::WinconStream, + state: &mut WinconBytes, + buf: &[u8], +) -> std::io::Result<()> { + for (style, printable) in state.extract_next(buf) { + let mut buf = printable.as_bytes(); + let fg = style.get_fg_color().and_then(cap_wincon_color); + let bg = style.get_bg_color().and_then(cap_wincon_color); + while !buf.is_empty() { + match raw.write_colored(fg, bg, buf) { + Ok(0) => { + return Err(std::io::Error::new( + std::io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => buf = &buf[n..], + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + } + Ok(()) +} + +fn write_fmt( + raw: &mut dyn anstyle_wincon::WinconStream, + state: &mut WinconBytes, + args: std::fmt::Arguments<'_>, +) -> std::io::Result<()> { + let write_all = |buf: &[u8]| write_all(raw, state, buf); + crate::fmt::Adapter::new(write_all).write_fmt(args) +} + +fn cap_wincon_color(color: anstyle::Color) -> Option<anstyle::AnsiColor> { + match color { + anstyle::Color::Ansi(c) => Some(c), + anstyle::Color::Ansi256(c) => c.into_ansi(), + anstyle::Color::Rgb(_) => None, + } +} + +#[cfg(test)] +mod test { + use super::*; + use proptest::prelude::*; + use std::io::Write as _; + + proptest! { + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_all_no_escapes(s in "\\PC*") { + let buffer = Vec::new(); + let mut stream = WinconStream::new(buffer); + stream.write_all(s.as_bytes()).unwrap(); + let buffer = stream.into_inner(); + let actual = std::str::from_utf8(buffer.as_ref()).unwrap(); + assert_eq!(s, actual); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_byte_no_escapes(s in "\\PC*") { + let buffer = Vec::new(); + let mut stream = WinconStream::new(buffer); + for byte in s.as_bytes() { + stream.write_all(&[*byte]).unwrap(); + } + let buffer = stream.into_inner(); + let actual = std::str::from_utf8(buffer.as_ref()).unwrap(); + assert_eq!(s, actual); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_all_random(s in any::<Vec<u8>>()) { + let buffer = Vec::new(); + let mut stream = WinconStream::new(buffer); + stream.write_all(s.as_slice()).unwrap(); + } + + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn write_byte_random(s in any::<Vec<u8>>()) { + let buffer = Vec::new(); + let mut stream = WinconStream::new(buffer); + for byte in s.as_slice() { + stream.write_all(&[*byte]).unwrap(); + } + } + } +} |
