From 826cecfafb1a233e074715fdc3d1c38cb01ac3be Mon Sep 17 00:00:00 2001 From: Jan Pikl Date: Fri, 5 Apr 2024 23:03:30 +0200 Subject: [PATCH] Setup term transcript tests --- .vscode/extensions.json | 1 + .vscode/settings.json | 3 + Cargo.lock | 315 +++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + docs/reference/rew.md | 3 +- snapshots/error.svg | 83 ++++++++++ snapshots/examples.svg | 246 ++++++++++++++++++++++++++++ snapshots/help.svg | 130 +++++++++++++++ snapshots/invalid_usage.svg | 84 ++++++++++ src/app.rs | 2 + src/commands/x.rs | 30 +++- tests/transcript.rs | 61 +++++++ 12 files changed, 955 insertions(+), 4 deletions(-) create mode 100644 snapshots/error.svg create mode 100644 snapshots/examples.svg create mode 100644 snapshots/help.svg create mode 100644 snapshots/invalid_usage.svg create mode 100644 tests/transcript.rs diff --git a/.vscode/extensions.json b/.vscode/extensions.json index cdf7e03..7b6b7d4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -6,5 +6,6 @@ "timonwong.shellcheck", "foxundermoon.shell-format", "ryanluker.vscode-coverage-gutters", + "jock.svg", ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 21ba9ad..782795b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,4 +2,7 @@ "editor.formatOnSave": true, "rust-analyzer.check.command": "clippy", "rust-analyzer.rustfmt.extraArgs": ["+nightly"], + "[svg]": { + "editor.formatOnSave": false, + }, } diff --git a/Cargo.lock b/Cargo.lock index e118e8f..7d9485b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -98,6 +109,15 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.9.0" @@ -109,6 +129,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "cc" version = "1.0.87" @@ -189,6 +215,25 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "derive_more" version = "1.0.0-beta.6" @@ -217,12 +262,28 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94" +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -348,18 +409,51 @@ dependencies = [ "rew", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "home" version = "0.5.9" @@ -369,6 +463,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "itoap" version = "1.0.1" @@ -398,6 +498,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + [[package]] name = "memchr" version = "2.7.1" @@ -410,6 +516,61 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "pest" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -449,6 +610,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.78" @@ -458,6 +629,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.35" @@ -469,9 +649,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -517,6 +697,7 @@ dependencies = [ "itoap", "memchr", "rstest", + "term-transcript", "terminal_size", "unicode-width", "which", @@ -573,6 +754,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "semver" version = "1.0.22" @@ -599,6 +786,28 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "slab" version = "0.4.9" @@ -625,6 +834,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "term-transcript" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab2925c31fe1c0cdd428dc846bccd040fbfbc628851c8949b2148e9f0ead90d" +dependencies = [ + "atty", + "bytecount", + "handlebars", + "os_pipe", + "pretty_assertions", + "quick-xml", + "serde", + "serde_json", + "termcolor", + "unicode-width", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "terminal_size" version = "0.3.0" @@ -641,6 +877,38 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -671,6 +939,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -693,6 +967,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" @@ -833,3 +1138,9 @@ dependencies = [ "clap", "rew", ] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index 20714dd..d84c2e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ which = "6.0.0" assert_cmd = "2.0.13" claims = "0.7.1" rstest = "0.18.2" +term-transcript = "0.3.0" [profile.release] codegen-units = 1 diff --git a/docs/reference/rew.md b/docs/reference/rew.md index 790ab0f..1f63677 100644 --- a/docs/reference/rew.md +++ b/docs/reference/rew.md @@ -1,6 +1,7 @@ # rew -The Swiss Army Knife of line-oriented text processing. Transform text by composing parallel shell pipelines! +The Swiss Army Knife of line-oriented text processing. +Transform text by composing parallel shell pipelines! ## Usage diff --git a/snapshots/error.svg b/snapshots/error.svg new file mode 100644 index 0000000..69bec3d --- /dev/null +++ b/snapshots/error.svg @@ -0,0 +1,83 @@ + + + + + + + + +
+
$ rew x "{cat --unknown}"
+
rew cat (spawned by 'rew x'): invalid usage: unexpected argument found
+rew x: error: failed execution of expression {cat --unknown}
+rew x: └─> failed execution of command REW_BUF_MODE="full" REW_BUF_SIZE="32768" 
REW_NULL="false" _REW_SPAWNED_BY="rew x" "/home/jpikl/.local/bin/rew" "cat" "--u
nknown"
+rew x: └─> child process exited with code 2
+
+
+
+
+ + HTML embedding not supported. + Consult term-transcript docs for details. + +
+
diff --git a/snapshots/examples.svg b/snapshots/examples.svg new file mode 100644 index 0000000..e883689 --- /dev/null +++ b/snapshots/examples.svg @@ -0,0 +1,246 @@ + + + + + + + + +
+
$ rew x --examples
+
+Empty expression {} is replaced by input line. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x 'Hello {}'                      
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "Hello first" 
+         "second"            "Hello second"
+         "third"             "Hello third" 
+
+Expressions can call other rew commands to process the input. 
+ 
+Here, we call the rew upper command which converts text to uppercase. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x 'Hello {upper}'                 
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "Hello FIRST" 
+         "second"            "Hello SECOND"
+         "third"             "Hello THIRD" 
+
+Expressions can also call any external command. 
+ 
+Let's remove all aeiou characters from text using tr. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x 'Hello {tr -d aeiou}'           
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "Hello frst"
+         "second"            "Hello scnd"
+         "third"             "Hello thrd"
+
+Multiple commands can be joined into a pipeline. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x 'Hello {tr -d aeiou | upper}'   
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "Hello FRST"
+         "second"            "Hello SCND"
+         "third"             "Hello THRD"
+
+Multiple expressions are run in parallel and their output is combined. The 
+excution runs until one of the expressions no longer produces any output. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x '{seq}. {tr -d aeiou | upper}'  
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "1. FRST"
+         "second"            "2. SCND"
+         "third"             "3. THRD"
+
+Arguments containing whitepaces must be wrapped in single '' or double quotes 
+"". 
+ 
+Here, we replace aeiou characters with space ' '. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x 'Hello {tr aeiou " " | upper}'  
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "Hello F RST" 
+         "second"            "Hello S C ND"
+         "third"             "Hello TH RD" 
+
+The ! marker denotes an external command. 
+ 
+Let's call the standard seq command instead of the built-in rew seq. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x '{!seq 1 3}. {}'                
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "1. first" 
+         "second"            "2. second"
+         "third"             "3. third" 
+
+The # marker denotes "raw shell expression". Everything after it will be 
+interpreted by the current shell. 
+ 
+For example, the following expression is equivalent to {sh -c 'printf "%s\n" a 
+b c'} 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x '{# printf "%s\n" a b c}. {}'   
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "a. first" 
+         "second"            "b. second"
+         "third"             "c. third" 
+
+A specific shell for {# ...} can be set using the -s, --shell option or the 
+SHELL environment variable. 
+
+ ╭──────────────────────────────────────────────────────────────╮
+ │$ rew x -s bash '{# for((i=0;i<3;i++)); do echo $i; done}. {}'
+ ╰──────────────────────────────────────────────────────────────╯
+  stdin: "first"     stdout: "0. first" 
+         "second"            "1. second"
+         "third"             "2. third" 
+
+The : marker is a hint that an expression does not consume stdin. Without it, 
+the overall execution might get stuck forever due to blocked IO calls. 
+ 
+Only external commands need : to be explicitely specified. For built-in 
+commands, : is detected automatically. 
+
+ ╭─────────────────────────────────────────────────────────────╮
+ │$ rew x '{seq 1..3} {: !seq 1 3} {:# echo 1; echo 2; echo 3}'
+ ╰─────────────────────────────────────────────────────────────╯
+  stdout: "1 1 1"
+          "2 2 2"
+          "3 3 3"
+
+Backslash \ can be used to escape special characters 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x '\{ "{}": {seq} \}'             
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "{ "first": 1 }" 
+         "second"            "{ "second": 2 }"
+         "third"             "{ "third": 3 }" 
+
+A custom escape character can be set using the -e, --escape option. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x -e% '%{ "{}": {seq} %}'         
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "{ "first": 1 }" 
+         "second"            "{ "second": 2 }"
+         "third"             "{ "third": 3 }" 
+
+Certain special characters like | must be escaped only within a specific 
+context. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x '| {echo "|"} {echo \|}'        
+ ╰────────────────────────────────────────╯
+  stdout: "| | |"
+
+Escape character can be also used to produce line feed \n, carriage return \r 
+or tab \t. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x '{seq}:\n\t{}'                  
+ ╰────────────────────────────────────────╯
+  stdin: "first"     stdout: "1:"        
+         "second"            "    first" 
+         "third"             "2:"        
+                             "    second"
+                             "3:"        
+                             "    third" 
+
+You can enable automatic expression quoting using -q, --quote flag. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x -q 'mv {} {lower | tr " " "_"}' 
+ ╰────────────────────────────────────────╯
+  stdin: "IMG 1.jpg"    stdout: "mv 'IMG 1.jpg' 'img_1.jpg'"
+         "IMG 2.jpg"            "mv 'IMG 2.jpg' 'img_2.jpg'"
+
+Double the -q, --quote to use double quotes instead of single quotes. 
+
+ ╭────────────────────────────────────────╮
+ │$ rew x -qq 'mv {} {lower | tr " " "_"}'
+ ╰────────────────────────────────────────╯
+  stdin: "IMG 1.jpg"    stdout: "mv "IMG 1.jpg" "img_1.jpg""
+         "IMG 2.jpg"            "mv "IMG 2.jpg" "img_2.jpg""
+
+All global options -0, --null, --buf-size and --buf-mode are propagated to rew 
+subcommands. Do not forget configure NUL separator manually for any external com
mands. + + ╭────────────────────────────────────────────────────╮ + │$ rew x --null '{upper | sed --null-data "s/^.//g"}' + ╰────────────────────────────────────────────────────╯ + stdin: "aa\0bb\0cc\0" stdout: "A\0B\0C\0" +
+
+
+
+
+ + HTML embedding not supported. + Consult term-transcript docs for details. + +
+
diff --git a/snapshots/help.svg b/snapshots/help.svg new file mode 100644 index 0000000..d683624 --- /dev/null +++ b/snapshots/help.svg @@ -0,0 +1,130 @@ + + + + + + + + +
+
$ rew seq --help
+
Print sequence of numbers as lines
+
+Usage: rew seq [OPTIONS] [FROM..[TO]] [STEP]
+
+Arguments:
+  [FROM..[TO]]
+          Sequence range.
+          
+          Both FROM and TO are integers.
+          
+          TO may be ommited to produce an infinite sequence.
+          
+          [default: 1..]
+
+  [STEP]
+          Increment between numbers in sequence.
+          
+          Default value: 1 (for increasing sequence), -1 (for decreasing sequenc
e) + +Options: + -h, --help + Print help (see a summary with '-h') + + --examples + Print examples of the command usage + +Global options: + -0, --null + Line delimiter is NUL, not newline + + [env: REW_NULL=] + + --buf-mode <MODE> + Output buffering mode + + [env: REW_BUF_MODE=] + + Possible values: + - line: Writes to stdout after a line was processed or when the output
buffer is full. + Enabled by default when stdout is TTY (for interactive usage) + - full: Writes to stdout only when the output buffer is full. Enabled
by default when + stdout is not TTY (for maximal throughput) + + --buf-size <BYTES> + Size of a buffer used for IO operations. + + Smaller values will reduce memory consumption but could negatively aff
ect througput. + + Larger values will increase memory consumption but may improve troughp
ut in some cases. + + Certain commands (which can only operate with whole lines) won't be ab
le to fetch a line + bigger than this limit and will abort their execution instead. + + [env: REW_BUF_SIZE=] + [default: 32768] + +Visit https://jpikl.github.io/rew/reference/rew-seq.html.html for a complete ref
erence and examples.
+
+
+
+
+ + HTML embedding not supported. + Consult term-transcript docs for details. + +
+
diff --git a/snapshots/invalid_usage.svg b/snapshots/invalid_usage.svg new file mode 100644 index 0000000..7fb4fc2 --- /dev/null +++ b/snapshots/invalid_usage.svg @@ -0,0 +1,84 @@ + + + + + + + + +
+
$ rew --unkown
+
rew: invalid usage: unexpected argument '--unkown' found
+
+Usage: rew [OPTIONS] <COMMAND>
+
+For more information, try '--help'.
+
+
+
+
+ + HTML embedding not supported. + Consult term-transcript docs for details. + +
+
diff --git a/src/app.rs b/src/app.rs index 8096f78..955cfeb 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,6 +3,7 @@ use crate::colors::BOLD; use crate::colors::RESET; use crate::command; use clap::command; +use clap::crate_description; use clap::crate_name; use clap::crate_version; use clap::Args; @@ -14,6 +15,7 @@ const REFERENCE_URL: &str = "https://jpikl.github.io/rew/reference"; pub fn build(metas: &[&'static command::Meta]) -> Command { let mut app = command!() .version(get_version()) + .about(crate_description!().replace(". ", ".\n")) .after_help(get_after_help(None)) .subcommand_required(true); diff --git a/src/commands/x.rs b/src/commands/x.rs index 2faf0fb..b64d433 100644 --- a/src/commands/x.rs +++ b/src/commands/x.rs @@ -35,6 +35,7 @@ use std::env::current_exe; use std::io; use std::io::Write; use std::panic::resume_unwind; +use std::path::Path; use std::process::Child; use std::process::ChildStdin; use std::process::ChildStdout; @@ -364,7 +365,7 @@ struct EvalContext { impl EvalContext { fn from_command(command: &Command) -> Self { Self { - raw_command: Some(format!("{command:?}")), + raw_command: Some(format_command(command).unwrap_or_else(|_| format!("{command:?}"))), raw_expr: None, } } @@ -399,6 +400,33 @@ impl EvalContext { } } +fn format_command(command: &Command) -> Result { + use std::fmt::Write; + let mut output = String::new(); + + for (key, val) in command.get_envs() { + let key = key.to_string_lossy(); + let val = val.unwrap_or_default(); + write!(&mut output, "{key}={val:?} ",)?; + } + + // We want to obfuscate program path to make "transcript" tests reproducible. + if cfg!(debug_assertions) && env::var_os("NEXTEST").is_some() { + let program = Path::new(command.get_program()) + .file_stem() + .unwrap_or_default(); + write!(&mut output, "{program:?}")?; + } else { + write!(&mut output, "{:?}", command.get_program())?; + } + + for arg in command.get_args() { + write!(&mut output, " {arg:?}")?; + } + + Ok(output) +} + trait WithEvalContext { fn with_eval_context(self, context: &EvalContext, message: &str) -> Result; } diff --git a/tests/transcript.rs b/tests/transcript.rs new file mode 100644 index 0000000..c26cdeb --- /dev/null +++ b/tests/transcript.rs @@ -0,0 +1,61 @@ +use assert_cmd::crate_name; +use std::env; +use std::path::Path; +use term_transcript::svg::NamedPalette; +use term_transcript::svg::Template; +use term_transcript::svg::TemplateOptions; +use term_transcript::test::MatchKind; +use term_transcript::test::TestConfig; +use term_transcript::ExitStatus; +use term_transcript::ShellOptions; + +#[test] +fn help() { + // We want to check custom help formatting done by `Colorizer` + transcript("help", "seq --help"); +} + +#[test] +fn invalid_usage() { + transcript("invalid_usage", "--unkown"); +} + +#[test] +fn error() { + // Use double quotes `""` because `''` do not work on Windows `cmd` + transcript("error", "x \"{cat --unknown}\""); +} + +#[test] +fn examples() { + transcript("examples", "x --examples"); +} + +#[cfg(target_family = "unix")] +const STATUS_COMMAND: &str = "echo $?"; +#[cfg(target_family = "windows")] +const STATUS_COMMAND: &str = "echo %errorlevel%"; + +fn transcript(snapshot: &str, args: &str) { + let shell_options = ShellOptions::default() + .with_cargo_path() + .with_env("CLICOLOR_FORCE", "1") + .with_current_dir(env!("CARGO_MANIFEST_DIR")) + .with_status_check(STATUS_COMMAND, |output| { + let response = output.to_plaintext().ok()?; + response.trim().parse().ok().map(ExitStatus) + }); + + let template_options = TemplateOptions { + palette: NamedPalette::Xterm.into(), + ..TemplateOptions::default() + }; + + let path = Path::new("snapshots").join(snapshot).with_extension("svg"); + let input = format!("{} {args}", crate_name!()); + + TestConfig::new(shell_options) + .with_match_kind(MatchKind::Precise) + .with_template(Template::new(template_options)) + .test(path, [input.as_ref()]); +}