mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
--no-edit
This commit is contained in:
parent
388fc24191
commit
cdb0eeafa2
112
Cargo.lock
generated
112
Cargo.lock
generated
@ -314,6 +314,17 @@ dependencies = [
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clicolors-control"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard"
|
||||
version = "0.5.0"
|
||||
@ -365,6 +376,21 @@ dependencies = [
|
||||
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.4"
|
||||
@ -814,6 +840,11 @@ dependencies = [
|
||||
"futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core-preview"
|
||||
version = "0.3.0-alpha.19"
|
||||
@ -834,6 +865,17 @@ name = "futures-io-preview"
|
||||
version = "0.3.0-alpha.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-preview"
|
||||
version = "0.3.0-alpha.19"
|
||||
@ -852,11 +894,30 @@ name = "futures-sink-preview"
|
||||
version = "0.3.0-alpha.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-util-preview"
|
||||
version = "0.3.0-alpha.19"
|
||||
@ -1616,6 +1677,7 @@ dependencies = [
|
||||
"chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"console 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossterm 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1624,6 +1686,7 @@ dependencies = [
|
||||
"dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getset 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"git2 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1645,6 +1708,7 @@ dependencies = [
|
||||
"num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty-hex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1672,6 +1736,7 @@ dependencies = [
|
||||
"syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trash 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1917,6 +1982,14 @@ name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pretty"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty-hex"
|
||||
version = "0.1.1"
|
||||
@ -1956,6 +2029,21 @@ dependencies = [
|
||||
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.6"
|
||||
@ -2654,6 +2742,14 @@ dependencies = [
|
||||
"wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
@ -2731,6 +2827,11 @@ dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.11.2"
|
||||
@ -3127,11 +3228,13 @@ dependencies = [
|
||||
"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68"
|
||||
"checksum chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2ff48a655fe8d2dae9a39e66af7fd8ff32a879e8c4e27422c25596a8b5e90d"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e"
|
||||
"checksum clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7"
|
||||
"checksum clipboard-win 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum codepage 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8b0e9222c0cdf2c6ac27d73f664f9520266fa911c3106329d359f8861cb8bde9"
|
||||
"checksum config 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9107d78ed62b3fa5a86e7d18e647abed48cfd8f8fab6c72f4cdb982d196f7e6"
|
||||
"checksum console 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f5d540c2d34ac9dd0deb5f3b5f54c36c79efa78f6b3ad19106a554d07a7b5d9f"
|
||||
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
|
||||
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
|
||||
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
|
||||
@ -3184,12 +3287,16 @@ dependencies = [
|
||||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
|
||||
"checksum futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a"
|
||||
"checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866"
|
||||
"checksum futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a"
|
||||
"checksum futures-executor-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98"
|
||||
"checksum futures-io-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda"
|
||||
"checksum futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764"
|
||||
"checksum futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e"
|
||||
"checksum futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec"
|
||||
"checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9"
|
||||
"checksum futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6"
|
||||
"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76"
|
||||
"checksum futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d"
|
||||
"checksum futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "36552cd31353fd135114510d53b8d120758120c36aa636a9341970f9efb1e4a0"
|
||||
"checksum gethostname 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4ab273ca2a31eb6ca40b15837ccf1aa59a43c5db69ac10c542be342fae2e01d"
|
||||
@ -3297,10 +3404,13 @@ dependencies = [
|
||||
"checksum png 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8422b27bb2c013dd97b9aef69e161ce262236f49aaf46a0489011c8ff0264602"
|
||||
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
|
||||
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
"checksum pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f60c0d9f6fc88ecdd245d90c1920ff76a430ab34303fc778d33b1d0a4c3bf6d3"
|
||||
"checksum pretty-hex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be91bcc43e73799dc46a6c194a55e7aae1d86cc867c860fd4a436019af21bd8c"
|
||||
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
|
||||
"checksum pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074"
|
||||
"checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e"
|
||||
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
|
||||
"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e"
|
||||
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
||||
"checksum ptree 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0a3be00b19ee7bd33238c1c523a7ab4df697345f6b36f90827a7860ea938d4"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
@ -3377,6 +3487,7 @@ dependencies = [
|
||||
"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
|
||||
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
||||
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
|
||||
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
|
||||
@ -3386,6 +3497,7 @@ dependencies = [
|
||||
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
|
||||
"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf"
|
||||
"checksum trash 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f24d31505f49e989b1ee2c03c323251f6763d5907d471b71192dac92e323f8"
|
||||
"checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"
|
||||
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||
"checksum umask 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "d3ec2e5aeb4aadd510db9124513a7fec4a9c3a331b7f57aa519440dab3707067"
|
||||
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
|
@ -79,6 +79,10 @@ cfg-if = "0.1"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
calamine = "0.16"
|
||||
umask = "0.1"
|
||||
futures-util = "0.3.0"
|
||||
pretty = { version = "0.5.2" }
|
||||
termcolor = "1.0.5"
|
||||
console = "0.9.1"
|
||||
|
||||
neso = { version = "0.5.0", optional = true }
|
||||
crossterm = { version = "0.10.2", optional = true }
|
||||
@ -92,6 +96,7 @@ ptree = {version = "0.2" }
|
||||
image = { version = "0.22.2", default_features = false, features = ["png_codec", "jpeg"], optional = true }
|
||||
starship = { version = "0.26.4", optional = true}
|
||||
|
||||
|
||||
[features]
|
||||
default = ["textview", "sys", "ps"]
|
||||
raw-key = ["rawkey", "neso"]
|
||||
|
@ -4,21 +4,12 @@ command = "lalrpop"
|
||||
args = ["src/parser/parser.lalrpop"]
|
||||
|
||||
[tasks.baseline]
|
||||
dependencies = ["lalrpop"]
|
||||
|
||||
[tasks.build]
|
||||
command = "cargo"
|
||||
args = ["build"]
|
||||
dependencies = ["lalrpop"]
|
||||
args = ["build", "--bins"]
|
||||
|
||||
[tasks.run]
|
||||
command = "cargo"
|
||||
args = ["run", "--release"]
|
||||
dependencies = ["baseline"]
|
||||
|
||||
[tasks.release]
|
||||
command = "cargo"
|
||||
args = ["build", "--release"]
|
||||
args = ["run"]
|
||||
dependencies = ["baseline"]
|
||||
|
||||
[tasks.test]
|
||||
|
56
src/cli.rs
56
src/cli.rs
@ -9,6 +9,7 @@ use crate::context::Context;
|
||||
use crate::data::config;
|
||||
use crate::data::Value;
|
||||
pub(crate) use crate::errors::ShellError;
|
||||
use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult};
|
||||
#[cfg(not(feature = "starship-prompt"))]
|
||||
use crate::git::current_branch;
|
||||
use crate::parser::registry::Signature;
|
||||
@ -260,7 +261,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
whole_stream_command(Nth),
|
||||
whole_stream_command(Next),
|
||||
whole_stream_command(Previous),
|
||||
whole_stream_command(Debug),
|
||||
// whole_stream_command(Debug),
|
||||
whole_stream_command(Shells),
|
||||
whole_stream_command(SplitColumn),
|
||||
whole_stream_command(SplitRow),
|
||||
@ -325,7 +326,9 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
whole_stream_command(SplitBy),
|
||||
whole_stream_command(Table),
|
||||
whole_stream_command(Version),
|
||||
whole_stream_command(What),
|
||||
whole_stream_command(Which),
|
||||
whole_stream_command(DebugValue),
|
||||
]);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
@ -419,13 +422,47 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
let mut readline = Err(ReadlineError::Eof);
|
||||
while let Some(ref cmd) = initial_command {
|
||||
readline = rl.readline_with_initial(&prompt, (&cmd, ""));
|
||||
if let Err(ReadlineError::Eof) = &readline {
|
||||
// Fuzzy search in history
|
||||
let lines = rl.history().iter().rev().map(|s| s.as_str()).collect();
|
||||
let selection = interactive_fuzzy_search(&lines, 5); // Clears last line with prompt
|
||||
match selection {
|
||||
SelectionResult::Selected(line) => {
|
||||
outln!("{}{}", &prompt, &line); // TODO: colorize prompt
|
||||
readline = Ok(line.clone());
|
||||
initial_command = None;
|
||||
}
|
||||
SelectionResult::Edit(line) => {
|
||||
initial_command = Some(line);
|
||||
}
|
||||
SelectionResult::NoSelection => {
|
||||
readline = Ok("".to_string());
|
||||
initial_command = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
initial_command = None;
|
||||
}
|
||||
}
|
||||
|
||||
match process_line(readline, &mut context).await {
|
||||
let line = process_line(readline, &mut context).await;
|
||||
|
||||
match line {
|
||||
LineResult::Success(line) => {
|
||||
rl.add_history_entry(line.clone());
|
||||
let _ = rl.save_history(&History::path());
|
||||
context.maybe_print_errors(Text::from(line));
|
||||
}
|
||||
|
||||
LineResult::Error(line, err) => {
|
||||
rl.add_history_entry(line.clone());
|
||||
let _ = rl.save_history(&History::path());
|
||||
|
||||
context.with_host(|host| {
|
||||
print_err(err, host, &Text::from(line.clone()));
|
||||
});
|
||||
|
||||
context.maybe_print_errors(Text::from(line.clone()));
|
||||
}
|
||||
|
||||
LineResult::CtrlC => {
|
||||
@ -451,15 +488,6 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
LineResult::Error(line, err) => {
|
||||
rl.add_history_entry(line.clone());
|
||||
let _ = rl.save_history(&History::path());
|
||||
|
||||
context.with_host(|host| {
|
||||
print_err(err, host, &Text::from(line));
|
||||
})
|
||||
}
|
||||
|
||||
LineResult::Break => {
|
||||
break;
|
||||
}
|
||||
@ -703,7 +731,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
||||
Err(ReadlineError::Eof) => LineResult::Break,
|
||||
Err(err) => {
|
||||
println!("Error: {:?}", err);
|
||||
outln!("Error: {:?}", err);
|
||||
LineResult::Break
|
||||
}
|
||||
}
|
||||
@ -725,9 +753,9 @@ fn classify_pipeline(
|
||||
.map_err(|err| err.into());
|
||||
|
||||
if log_enabled!(target: "nu::expand_syntax", log::Level::Debug) {
|
||||
println!("");
|
||||
outln!("");
|
||||
ptree::print_tree(&iterator.expand_tracer().print(source.clone())).unwrap();
|
||||
println!("");
|
||||
outln!("");
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -16,6 +16,7 @@ pub(crate) mod count;
|
||||
pub(crate) mod cp;
|
||||
pub(crate) mod date;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod debug_value;
|
||||
pub(crate) mod echo;
|
||||
pub(crate) mod enter;
|
||||
pub(crate) mod env;
|
||||
@ -85,6 +86,7 @@ pub(crate) mod to_url;
|
||||
pub(crate) mod to_yaml;
|
||||
pub(crate) mod trim;
|
||||
pub(crate) mod version;
|
||||
pub(crate) mod what;
|
||||
pub(crate) mod where_;
|
||||
pub(crate) mod which_;
|
||||
|
||||
@ -101,7 +103,7 @@ pub(crate) use config::Config;
|
||||
pub(crate) use count::Count;
|
||||
pub(crate) use cp::Cpy;
|
||||
pub(crate) use date::Date;
|
||||
pub(crate) use debug::Debug;
|
||||
pub(crate) use debug_value::DebugValue;
|
||||
pub(crate) use echo::Echo;
|
||||
pub(crate) use enter::Enter;
|
||||
pub(crate) use env::Env;
|
||||
@ -173,5 +175,6 @@ pub(crate) use to_url::ToURL;
|
||||
pub(crate) use to_yaml::ToYAML;
|
||||
pub(crate) use trim::Trim;
|
||||
pub(crate) use version::Version;
|
||||
pub(crate) use what::What;
|
||||
pub(crate) use where_::Where;
|
||||
pub(crate) use which_::Which;
|
||||
|
@ -46,7 +46,9 @@ pub fn autoview(
|
||||
Ok(OutputStream::new(async_stream! {
|
||||
let mut output_stream: OutputStream = context.input.into();
|
||||
|
||||
match output_stream.try_next().await {
|
||||
let next = output_stream.try_next().await;
|
||||
|
||||
match next {
|
||||
Ok(Some(x)) => {
|
||||
match output_stream.try_next().await {
|
||||
Ok(Some(y)) => {
|
||||
@ -91,7 +93,25 @@ pub fn autoview(
|
||||
|
||||
let raw = raw.clone();
|
||||
|
||||
let mut command_args = raw.with_input(new_input.into());
|
||||
let input: Vec<Tagged<Value>> = new_input.into();
|
||||
|
||||
if input.len() > 0 && input.iter().all(|value| value.is_error()) {
|
||||
let first = &input[0];
|
||||
|
||||
let mut host = context.host.clone();
|
||||
let mut host = match host.lock() {
|
||||
Err(err) => {
|
||||
errln!("Unexpected error acquiring host lock: {:?}", err);
|
||||
return;
|
||||
}
|
||||
Ok(val) => val
|
||||
};
|
||||
|
||||
crate::cli::print_err(first.item.expect_error(), &*host, &context.source);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command_args = raw.with_input(input);
|
||||
let mut named_args = NamedArguments::new();
|
||||
named_args.insert_optional("start_number", Some(Expression::number(current_idx, Tag::unknown())));
|
||||
command_args.call_info.args.named = Some(named_args);
|
||||
@ -99,6 +119,7 @@ pub fn autoview(
|
||||
let result = table.run(command_args, &context.commands);
|
||||
result.collect::<Vec<_>>().await;
|
||||
|
||||
|
||||
if finished {
|
||||
break;
|
||||
} else {
|
||||
@ -120,14 +141,14 @@ pub fn autoview(
|
||||
let result = text.run(raw.with_input(stream.into()), &context.commands);
|
||||
result.collect::<Vec<_>>().await;
|
||||
} else {
|
||||
println!("{}", s);
|
||||
outln!("{}", s);
|
||||
}
|
||||
}
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::String(s)),
|
||||
..
|
||||
} => {
|
||||
println!("{}", s);
|
||||
outln!("{}", s);
|
||||
}
|
||||
|
||||
Tagged { item: Value::Primitive(Primitive::Binary(ref b)), .. } => {
|
||||
@ -138,7 +159,7 @@ pub fn autoview(
|
||||
result.collect::<Vec<_>>().await;
|
||||
} else {
|
||||
use pretty_hex::*;
|
||||
println!("{:?}", b.hex_dump());
|
||||
outln!("{:?}", b.hex_dump());
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,7 +173,7 @@ pub fn autoview(
|
||||
let result = table.run(raw.with_input(stream.into()), &context.commands);
|
||||
result.collect::<Vec<_>>().await;
|
||||
} else {
|
||||
println!("{:?}", item);
|
||||
outln!("{:?}", item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,7 +182,7 @@ pub fn autoview(
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
//println!("<no results>");
|
||||
//outln!("<no results>");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,8 +165,7 @@ impl InternalCommand {
|
||||
trace!(target: "nu::run::internal", "{}", self.args.debug(&source));
|
||||
}
|
||||
|
||||
let objects: InputStream =
|
||||
trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects);
|
||||
let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", source: source, "input" = input.objects);
|
||||
|
||||
let command = context.expect_command(&self.name);
|
||||
|
||||
@ -180,11 +179,14 @@ impl InternalCommand {
|
||||
)
|
||||
};
|
||||
|
||||
let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result);
|
||||
let result = trace_out_stream!(target: "nu::trace_stream::internal", source: source, "output" = result);
|
||||
let mut result = result.values;
|
||||
let mut context = context.clone();
|
||||
|
||||
let stream = async_stream! {
|
||||
let mut soft_errs: Vec<ShellError> = vec![];
|
||||
let mut yielded = false;
|
||||
|
||||
while let Some(item) = result.next().await {
|
||||
match item {
|
||||
Ok(ReturnSuccess::Action(action)) => match action {
|
||||
@ -192,6 +194,10 @@ impl InternalCommand {
|
||||
context.shell_manager.set_path(path);
|
||||
}
|
||||
CommandAction::Exit => std::process::exit(0), // TODO: save history.txt
|
||||
CommandAction::Error(err) => {
|
||||
context.error(err);
|
||||
break;
|
||||
}
|
||||
CommandAction::EnterHelpShell(value) => {
|
||||
match value {
|
||||
Tagged {
|
||||
@ -237,11 +243,28 @@ impl InternalCommand {
|
||||
},
|
||||
|
||||
Ok(ReturnSuccess::Value(v)) => {
|
||||
yielded = true;
|
||||
yield Ok(v);
|
||||
}
|
||||
|
||||
Err(x) => {
|
||||
yield Ok(Value::Error(x).tagged_unknown());
|
||||
Ok(ReturnSuccess::DebugValue(v)) => {
|
||||
yielded = true;
|
||||
|
||||
let doc = v.item.pretty_doc();
|
||||
let mut buffer = termcolor::Buffer::ansi();
|
||||
|
||||
doc.render_raw(
|
||||
context.with_host(|host| host.width() - 5),
|
||||
&mut crate::parser::debug::TermColored::new(&mut buffer),
|
||||
).unwrap();
|
||||
|
||||
let value = String::from_utf8_lossy(buffer.as_slice());
|
||||
|
||||
yield Ok(Value::string(value).tagged_unknown())
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
context.error(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ impl CallInfo {
|
||||
#[derive(Getters)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct CommandArgs {
|
||||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub host: Arc<Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
@ -78,7 +78,7 @@ pub struct CommandArgs {
|
||||
#[derive(Getters, Clone)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct RawCommandArgs {
|
||||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub host: Arc<Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
@ -94,6 +94,10 @@ impl RawCommandArgs {
|
||||
input: input.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source(&self) -> Text {
|
||||
self.call_info.source.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for CommandArgs {
|
||||
@ -128,13 +132,18 @@ impl CommandArgs {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn process<'de, T: Deserialize<'de>>(
|
||||
pub fn source(&self) -> Text {
|
||||
self.call_info.source.clone()
|
||||
}
|
||||
|
||||
pub fn process<'de, T: Deserialize<'de>, O: ToOutputStream>(
|
||||
self,
|
||||
registry: &CommandRegistry,
|
||||
callback: fn(T, RunnableContext) -> Result<OutputStream, ShellError>,
|
||||
) -> Result<RunnableArgs<T>, ShellError> {
|
||||
callback: fn(T, RunnableContext) -> Result<O, ShellError>,
|
||||
) -> Result<RunnableArgs<T, O>, ShellError> {
|
||||
let shell_manager = self.shell_manager.clone();
|
||||
let host = self.host.clone();
|
||||
let source = self.source();
|
||||
let ctrl_c = self.ctrl_c.clone();
|
||||
let args = self.evaluate_once(registry)?;
|
||||
let call_info = args.call_info.clone();
|
||||
@ -147,6 +156,7 @@ impl CommandArgs {
|
||||
context: RunnableContext {
|
||||
input,
|
||||
commands: registry.clone(),
|
||||
source,
|
||||
shell_manager,
|
||||
name: name_tag,
|
||||
host,
|
||||
@ -170,6 +180,7 @@ impl CommandArgs {
|
||||
|
||||
let shell_manager = self.shell_manager.clone();
|
||||
let host = self.host.clone();
|
||||
let source = self.source();
|
||||
let ctrl_c = self.ctrl_c.clone();
|
||||
let args = self.evaluate_once(registry)?;
|
||||
let call_info = args.call_info.clone();
|
||||
@ -183,6 +194,7 @@ impl CommandArgs {
|
||||
context: RunnableContext {
|
||||
input,
|
||||
commands: registry.clone(),
|
||||
source,
|
||||
shell_manager,
|
||||
name: name_tag,
|
||||
host,
|
||||
@ -208,7 +220,8 @@ impl RunnablePerItemContext {
|
||||
pub struct RunnableContext {
|
||||
pub input: InputStream,
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub host: Arc<Mutex<Box<dyn Host>>>,
|
||||
pub source: Text,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub commands: CommandRegistry,
|
||||
pub name: Tag,
|
||||
@ -232,15 +245,15 @@ impl<T> RunnablePerItemArgs<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnableArgs<T> {
|
||||
pub struct RunnableArgs<T, O: ToOutputStream> {
|
||||
args: T,
|
||||
context: RunnableContext,
|
||||
callback: fn(T, RunnableContext) -> Result<OutputStream, ShellError>,
|
||||
callback: fn(T, RunnableContext) -> Result<O, ShellError>,
|
||||
}
|
||||
|
||||
impl<T> RunnableArgs<T> {
|
||||
impl<T, O: ToOutputStream> RunnableArgs<T, O> {
|
||||
pub fn run(self) -> Result<OutputStream, ShellError> {
|
||||
(self.callback)(self.args, self.context)
|
||||
(self.callback)(self.args, self.context).map(|v| v.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,6 +400,7 @@ impl EvaluatedCommandArgs {
|
||||
pub enum CommandAction {
|
||||
ChangePath(String),
|
||||
Exit,
|
||||
Error(ShellError),
|
||||
EnterShell(String),
|
||||
EnterValueShell(Tagged<Value>),
|
||||
EnterHelpShell(Tagged<Value>),
|
||||
@ -396,16 +410,17 @@ pub enum CommandAction {
|
||||
}
|
||||
|
||||
impl FormatDebug for CommandAction {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
match self {
|
||||
CommandAction::ChangePath(s) => write!(f, "action:change-path={}", s),
|
||||
CommandAction::Exit => write!(f, "action:exit"),
|
||||
CommandAction::Error(_) => write!(f, "action:error"),
|
||||
CommandAction::EnterShell(s) => write!(f, "action:enter-shell={}", s),
|
||||
CommandAction::EnterValueShell(t) => {
|
||||
write!(f, "action:enter-value-shell={:?}", t.debug())
|
||||
write!(f, "action:enter-value-shell={}", t.debug(source))
|
||||
}
|
||||
CommandAction::EnterHelpShell(t) => {
|
||||
write!(f, "action:enter-help-shell={:?}", t.debug())
|
||||
write!(f, "action:enter-help-shell={}", t.debug(source))
|
||||
}
|
||||
CommandAction::PreviousShell => write!(f, "action:previous-shell"),
|
||||
CommandAction::NextShell => write!(f, "action:next-shell"),
|
||||
@ -417,6 +432,7 @@ impl FormatDebug for CommandAction {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ReturnSuccess {
|
||||
Value(Tagged<Value>),
|
||||
DebugValue(Tagged<Value>),
|
||||
Action(CommandAction),
|
||||
}
|
||||
|
||||
@ -426,7 +442,8 @@ impl FormatDebug for ReturnValue {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
match self {
|
||||
Err(err) => write!(f, "{}", err.debug(source)),
|
||||
Ok(ReturnSuccess::Value(v)) => write!(f, "{:?}", v.debug()),
|
||||
Ok(ReturnSuccess::Value(v)) => write!(f, "{}", v.debug(source)),
|
||||
Ok(ReturnSuccess::DebugValue(v)) => v.fmt_debug(f, source),
|
||||
Ok(ReturnSuccess::Action(a)) => write!(f, "{}", a.debug(source)),
|
||||
}
|
||||
}
|
||||
@ -447,6 +464,10 @@ impl ReturnSuccess {
|
||||
Ok(ReturnSuccess::Value(input.into()))
|
||||
}
|
||||
|
||||
pub fn debug_value(input: impl Into<Tagged<Value>>) -> ReturnValue {
|
||||
Ok(ReturnSuccess::DebugValue(input.into()))
|
||||
}
|
||||
|
||||
pub fn action(input: CommandAction) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Action(input))
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ pub fn debug(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStr
|
||||
Ok(input
|
||||
.values
|
||||
.map(|v| {
|
||||
println!("{:?}", v);
|
||||
outln!("{:?}", v);
|
||||
ReturnSuccess::value(v)
|
||||
})
|
||||
.to_output_stream())
|
||||
|
42
src/commands/debug_value.rs
Normal file
42
src/commands/debug_value.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct DebugValue;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DebugArgs {}
|
||||
|
||||
impl WholeStreamCommand for DebugValue {
|
||||
fn name(&self) -> &str {
|
||||
"debug"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("debug")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Print the Rust debug representation of the values"
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, debug_value)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_value(
|
||||
_args: DebugArgs,
|
||||
RunnableContext { mut input, .. }: RunnableContext,
|
||||
) -> Result<impl ToOutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
while let Some(row) = input.values.next().await {
|
||||
yield ReturnSuccess::debug_value(row.clone())
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream)
|
||||
}
|
@ -71,7 +71,11 @@ fn fetch(
|
||||
) -> Box<dyn Fn(Tagged<Value>, Tag) -> Option<Tagged<Value>> + 'static> {
|
||||
Box::new(move |value: Tagged<Value>, tag| match key {
|
||||
Some(ref key_given) => {
|
||||
if let Some(Tagged { item, .. }) = value.get_data_by_key(&key_given) {
|
||||
if let Some(Tagged {
|
||||
item,
|
||||
tag: Tag { span, .. },
|
||||
}) = value.get_data_by_key(key_given[..].spanned(tag.span))
|
||||
{
|
||||
Some(item.clone().tagged(tag))
|
||||
} else {
|
||||
None
|
||||
|
@ -10,7 +10,7 @@ pub(crate) fn format(input: Vec<Value>, host: &mut dyn Host) {
|
||||
crate::format::print_view(&view, &mut *host);
|
||||
|
||||
if last != i {
|
||||
println!("");
|
||||
outln!("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::base::shape::Shapes;
|
||||
use crate::data::Value;
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
use crate::utils::did_you_mean;
|
||||
use crate::ColumnPath;
|
||||
use futures_util::pin_mut;
|
||||
use log::trace;
|
||||
|
||||
pub struct Get;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetArgs {
|
||||
member: ColumnPath,
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
@ -19,13 +21,7 @@ impl WholeStreamCommand for Get {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("get")
|
||||
.required(
|
||||
"member",
|
||||
SyntaxShape::ColumnPath,
|
||||
"the path to the data to get",
|
||||
)
|
||||
.rest(
|
||||
Signature::build("get").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally return additional data by path",
|
||||
)
|
||||
@ -44,8 +40,6 @@ impl WholeStreamCommand for Get {
|
||||
}
|
||||
}
|
||||
|
||||
pub type ColumnPath = Vec<Tagged<Value>>;
|
||||
|
||||
pub fn get_column_path(
|
||||
path: &ColumnPath,
|
||||
obj: &Tagged<Value>,
|
||||
@ -53,85 +47,52 @@ pub fn get_column_path(
|
||||
let fields = path.clone();
|
||||
|
||||
let value = obj.get_data_by_column_path(
|
||||
obj.tag(),
|
||||
path,
|
||||
Box::new(move |(obj_source, column_path_tried)| {
|
||||
Box::new(move |(obj_source, column_path_tried, error)| {
|
||||
match obj_source {
|
||||
Value::Table(rows) => {
|
||||
let total = rows.len();
|
||||
let end_tag = match fields.iter().nth_back(if fields.len() > 2 { 1 } else { 0 })
|
||||
let end_tag = match fields
|
||||
.members()
|
||||
.iter()
|
||||
.nth_back(if fields.members().len() > 2 { 1 } else { 0 })
|
||||
{
|
||||
Some(last_field) => last_field.tag(),
|
||||
None => column_path_tried.tag(),
|
||||
Some(last_field) => last_field.span(),
|
||||
None => column_path_tried.span(),
|
||||
};
|
||||
|
||||
return ShellError::labeled_error_with_secondary(
|
||||
"Row not found",
|
||||
format!(
|
||||
"There isn't a row indexed at '{}'",
|
||||
match &*column_path_tried {
|
||||
Value::Primitive(primitive) => primitive.format(None),
|
||||
_ => String::from(""),
|
||||
}
|
||||
),
|
||||
column_path_tried.tag(),
|
||||
format!("The table only has {} rows (0..{})", total, total - 1),
|
||||
format!("There isn't a row indexed at {}", **column_path_tried),
|
||||
column_path_tried.span(),
|
||||
if total == 1 {
|
||||
format!("The table only has 1 row")
|
||||
} else {
|
||||
format!("The table only has {} rows (0 to {})", total, total - 1)
|
||||
},
|
||||
end_tag,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &column_path_tried {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::Int(index)),
|
||||
..
|
||||
} => {
|
||||
return ShellError::labeled_error(
|
||||
"No rows available",
|
||||
format!(
|
||||
"Not a table. Perhaps you meant to get the column '{}' instead?",
|
||||
index
|
||||
),
|
||||
column_path_tried.tag(),
|
||||
)
|
||||
}
|
||||
_ => match did_you_mean(&obj_source, &column_path_tried) {
|
||||
match did_you_mean(&obj_source, column_path_tried) {
|
||||
Some(suggestions) => {
|
||||
return ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", suggestions[0].1),
|
||||
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||
span_for_spanned_list(fields.members().iter().map(|p| p.span())),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
return ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
"row does not contain this column",
|
||||
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||
)
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
return error;
|
||||
}),
|
||||
);
|
||||
|
||||
let res = match value {
|
||||
Ok(fetched) => match fetched {
|
||||
Some(Tagged { item: v, .. }) => Ok((v.clone()).tagged(&obj.tag)),
|
||||
None => match obj {
|
||||
// If its None check for certain values.
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::String(_)),
|
||||
..
|
||||
} => Ok(obj.clone()),
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::Path(_)),
|
||||
..
|
||||
} => Ok(obj.clone()),
|
||||
_ => Ok(Value::nothing().tagged(&obj.tag)),
|
||||
},
|
||||
},
|
||||
Ok(Tagged { item: v, tag }) => Ok((v.clone()).tagged(&tag)),
|
||||
Err(reason) => Err(reason),
|
||||
};
|
||||
|
||||
@ -139,14 +100,33 @@ pub fn get_column_path(
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
GetArgs {
|
||||
member,
|
||||
rest: fields,
|
||||
}: GetArgs,
|
||||
GetArgs { rest: mut fields }: GetArgs,
|
||||
RunnableContext { input, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
trace!("get {:?} {:?}", member, fields);
|
||||
if fields.len() == 0 {
|
||||
let stream = async_stream! {
|
||||
let values = input.values;
|
||||
pin_mut!(values);
|
||||
|
||||
let mut shapes = Shapes::new();
|
||||
let mut index = 0;
|
||||
|
||||
while let Some(row) = values.next().await {
|
||||
shapes.add(&row.item, index);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
for row in shapes.to_values() {
|
||||
yield ReturnSuccess::value(row);
|
||||
}
|
||||
};
|
||||
|
||||
let stream: BoxStream<'static, ReturnValue> = stream.boxed();
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
} else {
|
||||
let member = fields.remove(0);
|
||||
trace!("get {:?} {:?}", member, fields);
|
||||
let stream = input
|
||||
.values
|
||||
.map(move |item| {
|
||||
@ -168,25 +148,21 @@ pub fn get(
|
||||
item: Value::Table(rows),
|
||||
..
|
||||
} => {
|
||||
for row in rows {
|
||||
result.push_back(ReturnSuccess::value(
|
||||
Tagged {
|
||||
item: row.item,
|
||||
tag: Tag::from(&item.tag),
|
||||
}
|
||||
.map_anchored(&item.tag.anchor),
|
||||
))
|
||||
for item in rows {
|
||||
result.push_back(ReturnSuccess::value(item.clone()));
|
||||
}
|
||||
}
|
||||
other => result
|
||||
.push_back(ReturnSuccess::value((*other).clone().tagged(&item.tag))),
|
||||
other => result.push_back(ReturnSuccess::value(other.clone())),
|
||||
},
|
||||
Err(reason) => result.push_back(Err(reason)),
|
||||
Err(reason) => result
|
||||
.push_back(ReturnSuccess::value(Value::Error(reason).tagged_unknown())),
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ pub fn group(
|
||||
let mut groups = indexmap::IndexMap::new();
|
||||
|
||||
for value in values {
|
||||
let group_key = value.get_data_by_key(column_name);
|
||||
let group_key = value.get_data_by_key(column_name.borrow_spanned());
|
||||
|
||||
if group_key.is_none() {
|
||||
let possibilities = value.data_descriptors();
|
||||
|
@ -44,7 +44,11 @@ impl PerItemCommand for Help {
|
||||
short_desc.insert("name", cmd);
|
||||
short_desc.insert(
|
||||
"description",
|
||||
value.get_data_by_key("usage").unwrap().as_string().unwrap(),
|
||||
value
|
||||
.get_data_by_key("usage".spanned_unknown())
|
||||
.unwrap()
|
||||
.as_string()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
help.push_back(ReturnSuccess::value(short_desc.into_tagged_value()));
|
||||
|
@ -88,7 +88,8 @@ pub fn histogram(
|
||||
for percentage in start.into_iter() {
|
||||
|
||||
let mut fact = TaggedDictBuilder::new(&name);
|
||||
fact.insert_tagged(&column, group_labels.get(idx).unwrap().clone());
|
||||
let value: Tagged<String> = group_labels.get(idx).unwrap().clone();
|
||||
fact.insert_tagged(&column, Value::string(value.item).tagged(value.tag));
|
||||
|
||||
if let Tagged { item: Value::Primitive(Primitive::Int(ref num)), .. } = percentage.clone() {
|
||||
fact.insert(&frequency_column_name, std::iter::repeat("*").take(num.to_i32().unwrap() as usize).collect::<String>());
|
||||
|
@ -35,8 +35,6 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let input: InputStream = trace_stream!(target: "nu::trace_stream::lines", "input" = input);
|
||||
|
||||
let stream = input
|
||||
.values
|
||||
.map(move |v| match v.item {
|
||||
|
@ -70,7 +70,7 @@ pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result<OutputStream,
|
||||
if args.header_row {
|
||||
for i in input.clone() {
|
||||
if let Some(desc) = descs.get(0) {
|
||||
match i.get_data_by_key(&desc) {
|
||||
match i.get_data_by_key(desc[..].spanned_unknown()) {
|
||||
Some(x) => {
|
||||
if let Ok(s) = x.as_string() {
|
||||
headers.push(s);
|
||||
@ -115,7 +115,7 @@ pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result<OutputStream,
|
||||
}
|
||||
|
||||
for i in input.clone() {
|
||||
match i.get_data_by_key(&desc) {
|
||||
match i.get_data_by_key(desc[..].spanned_unknown()) {
|
||||
Some(x) => {
|
||||
dict.insert_tagged(headers[column_num].clone(), x.clone());
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ pub fn filter_plugin(
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::untagged_runtime_error(format!(
|
||||
"Error while processing filter response: {:?} {}",
|
||||
"Error while processing filter response: {:?}\n== input ==\n{}",
|
||||
e, input
|
||||
))));
|
||||
result
|
||||
|
@ -40,7 +40,7 @@ fn sort_by(
|
||||
|
||||
let calc_key = |item: &Tagged<Value>| {
|
||||
rest.iter()
|
||||
.map(|f| item.get_data_by_key(f).map(|i| i.clone()))
|
||||
.map(|f| item.get_data_by_key(f.borrow_spanned()).map(|i| i.clone()))
|
||||
.collect::<Vec<Option<Tagged<Value>>>>()
|
||||
};
|
||||
vec.sort_by_cached_key(calc_key);
|
||||
|
@ -108,7 +108,7 @@ pub fn split(
|
||||
other => {
|
||||
return Err(ShellError::type_error(
|
||||
"a table value",
|
||||
other.tagged_type_name(),
|
||||
other.spanned_type_name(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -117,7 +117,7 @@ pub fn split(
|
||||
_ => {
|
||||
return Err(ShellError::type_error(
|
||||
"a table value",
|
||||
group.tagged_type_name(),
|
||||
group.spanned_type_name(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -125,7 +125,7 @@ pub fn split(
|
||||
ref other => {
|
||||
return Err(ShellError::type_error(
|
||||
"a table value",
|
||||
other.tagged_type_name(),
|
||||
other.spanned_type_name(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ pub fn split(
|
||||
_ => {
|
||||
return Err(ShellError::type_error(
|
||||
"a table value",
|
||||
value.tagged_type_name(),
|
||||
value.spanned_type_name(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -66,8 +66,8 @@ fn t_sort_by(
|
||||
};
|
||||
|
||||
if show_columns {
|
||||
for label in columns_sorted(column_grouped_by_name, &values[0], &name).iter() {
|
||||
yield ReturnSuccess::value(label.clone());
|
||||
for label in columns_sorted(column_grouped_by_name, &values[0], &name).into_iter() {
|
||||
yield ReturnSuccess::value(Value::string(label.item).tagged(label.tag));
|
||||
}
|
||||
} else {
|
||||
match t_sort(column_grouped_by_name, None, &values[0], name) {
|
||||
@ -82,7 +82,7 @@ pub fn columns_sorted(
|
||||
_group_by_name: Option<String>,
|
||||
value: &Tagged<Value>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Vec<Tagged<Value>> {
|
||||
) -> Vec<Tagged<String>> {
|
||||
let origin_tag = tag.into();
|
||||
|
||||
match value {
|
||||
@ -110,22 +110,20 @@ pub fn columns_sorted(
|
||||
|
||||
keys.sort();
|
||||
|
||||
let keys: Vec<Value> = keys
|
||||
let keys: Vec<String> = keys
|
||||
.into_iter()
|
||||
.map(|k| {
|
||||
Value::string(match k {
|
||||
.map(|k| match k {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::Date(d)),
|
||||
..
|
||||
} => format!("{}", d.format("%B %d-%Y")),
|
||||
_ => k.as_string().unwrap(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
keys.into_iter().map(|k| k.tagged(&origin_tag)).collect()
|
||||
}
|
||||
_ => vec![Value::string("default").tagged(&origin_tag)],
|
||||
_ => vec![format!("default").tagged(&origin_tag)],
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +137,8 @@ pub fn t_sort(
|
||||
|
||||
match group_by_name {
|
||||
Some(column_name) => {
|
||||
let sorted_labels = columns_sorted(Some(column_name), value, &origin_tag);
|
||||
let sorted_labels: Vec<Tagged<String>> =
|
||||
columns_sorted(Some(column_name), value, &origin_tag);
|
||||
|
||||
match split_by_name {
|
||||
None => {
|
||||
@ -147,70 +146,41 @@ pub fn t_sort(
|
||||
dataset.insert_tagged("default", value.clone());
|
||||
let dataset = dataset.into_tagged_value();
|
||||
|
||||
let split_labels = match &dataset {
|
||||
let split_labels: Vec<Tagged<String>> = match &dataset {
|
||||
Tagged {
|
||||
item: Value::Row(rows),
|
||||
..
|
||||
} => {
|
||||
let mut keys: Vec<Tagged<Value>> = rows
|
||||
let mut keys: Vec<Tagged<String>> = rows
|
||||
.entries
|
||||
.keys()
|
||||
.map(|s| s.as_ref())
|
||||
.map(|k: &str| {
|
||||
let date = NaiveDate::parse_from_str(k, "%B %d-%Y");
|
||||
|
||||
let date = match date {
|
||||
Ok(parsed) => Value::Primitive(Primitive::Date(
|
||||
DateTime::<Utc>::from_utc(
|
||||
parsed.and_hms(12, 34, 56),
|
||||
Utc,
|
||||
),
|
||||
)),
|
||||
Err(_) => Value::string(k),
|
||||
};
|
||||
|
||||
date.tagged_unknown()
|
||||
})
|
||||
.map(|k| k.clone().tagged_unknown())
|
||||
.collect();
|
||||
|
||||
keys.sort();
|
||||
|
||||
let keys: Vec<Value> = keys
|
||||
.into_iter()
|
||||
.map(|k| {
|
||||
Value::string(match k {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::Date(d)),
|
||||
..
|
||||
} => format!("{}", d.format("%B %d-%Y")),
|
||||
_ => k.as_string().unwrap(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
keys.into_iter().map(|k| k.tagged(&origin_tag)).collect()
|
||||
keys
|
||||
}
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
let results: Vec<Vec<Tagged<Value>>> = split_labels
|
||||
.into_iter()
|
||||
.iter()
|
||||
.map(|split| {
|
||||
let groups = dataset.get_data_by_key(&split.as_string().unwrap());
|
||||
let groups = dataset.get_data_by_key(split.borrow_spanned());
|
||||
|
||||
sorted_labels
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|label| {
|
||||
let label = label.as_string().unwrap();
|
||||
|
||||
match groups {
|
||||
.map(|label| match &groups {
|
||||
Some(Tagged {
|
||||
item: Value::Row(dict),
|
||||
..
|
||||
}) => dict.get_data_by_key(&label).unwrap().clone(),
|
||||
}) => dict
|
||||
.get_data_by_key(label.borrow_spanned())
|
||||
.unwrap()
|
||||
.clone(),
|
||||
_ => Value::Table(vec![]).tagged(&origin_tag),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
@ -299,9 +269,9 @@ mod tests {
|
||||
Tag::unknown()
|
||||
),
|
||||
vec![
|
||||
string("August 23-2019"),
|
||||
string("September 24-2019"),
|
||||
string("October 10-2019")
|
||||
format!("August 23-2019").tagged_unknown(),
|
||||
format!("September 24-2019").tagged_unknown(),
|
||||
format!("October 10-2019").tagged_unknown()
|
||||
]
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::{Dictionary, Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use crate::RawPathMember;
|
||||
use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document};
|
||||
use std::convert::TryInto;
|
||||
|
||||
@ -51,6 +52,16 @@ pub fn value_to_bson_value(v: &Tagged<Value>) -> Result<Bson, ShellError> {
|
||||
}
|
||||
Value::Primitive(Primitive::Nothing) => Bson::Null,
|
||||
Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()),
|
||||
Value::Primitive(Primitive::ColumnPath(path)) => Bson::Array(
|
||||
path.iter()
|
||||
.map(|x| match &x.item {
|
||||
RawPathMember::String(string) => Ok(Bson::String(string.clone())),
|
||||
RawPathMember::Int(int) => Ok(Bson::I64(
|
||||
int.tagged(&v.tag).coerce_into("converting to BSON")?,
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<Bson>, ShellError>>()?,
|
||||
),
|
||||
Value::Primitive(Primitive::Pattern(p)) => Bson::String(p.clone()),
|
||||
Value::Primitive(Primitive::Path(s)) => Bson::String(s.display().to_string()),
|
||||
Value::Table(l) => Bson::Array(
|
||||
@ -177,7 +188,7 @@ fn get_binary_subtype<'a>(tagged_value: &'a Tagged<Value>) -> Result<BinarySubty
|
||||
)),
|
||||
_ => Err(ShellError::type_error(
|
||||
"bson binary",
|
||||
tagged_value.tagged_type_name(),
|
||||
tagged_value.type_name().spanned(tagged_value.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::data::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use csv::WriterBuilder;
|
||||
use indexmap::{indexset, IndexSet};
|
||||
|
||||
fn from_value_to_delimited_string(
|
||||
tagged_value: &Tagged<Value>,
|
||||
@ -46,15 +47,16 @@ fn from_value_to_delimited_string(
|
||||
.from_writer(vec![]);
|
||||
|
||||
let merged_descriptors = merge_descriptors(&list);
|
||||
wtr.write_record(&merged_descriptors)
|
||||
|
||||
wtr.write_record(merged_descriptors.iter().map(|item| &item.item[..]))
|
||||
.expect("can not write.");
|
||||
|
||||
for l in list {
|
||||
let mut row = vec![];
|
||||
for desc in &merged_descriptors {
|
||||
match l.item.get_data_by_key(&desc) {
|
||||
match l.item.get_data_by_key(desc.borrow_spanned()) {
|
||||
Some(s) => {
|
||||
row.push(to_string_tagged_value(s)?);
|
||||
row.push(to_string_tagged_value(&s)?);
|
||||
}
|
||||
None => {
|
||||
row.push(String::new());
|
||||
@ -127,12 +129,14 @@ fn to_string_tagged_value(v: &Tagged<Value>) -> Result<String, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_descriptors(values: &[Tagged<Value>]) -> Vec<String> {
|
||||
let mut ret = vec![];
|
||||
fn merge_descriptors(values: &[Tagged<Value>]) -> Vec<Spanned<String>> {
|
||||
let mut ret: Vec<Spanned<String>> = vec![];
|
||||
let mut seen: IndexSet<String> = indexset! {};
|
||||
for value in values {
|
||||
for desc in value.data_descriptors() {
|
||||
if !ret.contains(&desc) {
|
||||
ret.push(desc);
|
||||
if !seen.contains(&desc[..]) {
|
||||
seen.insert(desc.clone());
|
||||
ret.push(desc.spanned(value.tag.span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use crate::RawPathMember;
|
||||
|
||||
pub struct ToJSON;
|
||||
|
||||
@ -50,6 +51,19 @@ pub fn value_to_json_value(v: &Tagged<Value>) -> Result<serde_json::Value, Shell
|
||||
Value::Primitive(Primitive::Nothing) => serde_json::Value::Null,
|
||||
Value::Primitive(Primitive::Pattern(s)) => serde_json::Value::String(s.clone()),
|
||||
Value::Primitive(Primitive::String(s)) => serde_json::Value::String(s.clone()),
|
||||
Value::Primitive(Primitive::ColumnPath(path)) => serde_json::Value::Array(
|
||||
path.iter()
|
||||
.map(|x| match &x.item {
|
||||
RawPathMember::String(string) => Ok(serde_json::Value::String(string.clone())),
|
||||
RawPathMember::Int(int) => Ok(serde_json::Value::Number(
|
||||
serde_json::Number::from(CoerceInto::<i64>::coerce_into(
|
||||
int.tagged(&v.tag),
|
||||
"converting to JSON number",
|
||||
)?),
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<serde_json::Value>, ShellError>>()?,
|
||||
),
|
||||
Value::Primitive(Primitive::Path(s)) => serde_json::Value::String(s.display().to_string()),
|
||||
|
||||
Value::Table(l) => serde_json::Value::Array(json_list(l)?),
|
||||
|
@ -98,8 +98,9 @@ fn nu_value_to_sqlite_string(v: Value) -> String {
|
||||
Primitive::Date(d) => format!("'{}'", d),
|
||||
Primitive::Path(p) => format!("'{}'", p.display().to_string().replace("'", "''")),
|
||||
Primitive::Binary(u) => format!("x'{}'", encode(u)),
|
||||
Primitive::BeginningOfStream => "NULL".into(),
|
||||
Primitive::EndOfStream => "NULL".into(),
|
||||
Primitive::BeginningOfStream | Primitive::EndOfStream | Primitive::ColumnPath(_) => {
|
||||
"NULL".into()
|
||||
}
|
||||
},
|
||||
_ => "NULL".into(),
|
||||
}
|
||||
@ -179,9 +180,9 @@ fn sqlite_input_stream_to_bytes(
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
println!("{}", create);
|
||||
println!("{}", insert);
|
||||
println!("{:?}", e);
|
||||
outln!("{}", create);
|
||||
outln!("{}", insert);
|
||||
outln!("{:?}", e);
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use crate::RawPathMember;
|
||||
|
||||
pub struct ToTOML;
|
||||
|
||||
@ -48,6 +49,17 @@ pub fn value_to_toml_value(v: &Tagged<Value>) -> Result<toml::Value, ShellError>
|
||||
Value::Primitive(Primitive::Pattern(s)) => toml::Value::String(s.clone()),
|
||||
Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()),
|
||||
Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()),
|
||||
Value::Primitive(Primitive::ColumnPath(path)) => toml::Value::Array(
|
||||
path.iter()
|
||||
.map(|x| match &x.item {
|
||||
RawPathMember::String(string) => Ok(toml::Value::String(string.clone())),
|
||||
RawPathMember::Int(int) => Ok(toml::Value::Integer(
|
||||
int.tagged(&v.tag)
|
||||
.coerce_into("converting to TOML integer")?,
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<toml::Value>, ShellError>>()?,
|
||||
),
|
||||
|
||||
Value::Table(l) => toml::Value::Array(collect_values(l)?),
|
||||
Value::Error(e) => return Err(e.clone()),
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use crate::RawPathMember;
|
||||
|
||||
pub struct ToYAML;
|
||||
|
||||
@ -47,6 +48,25 @@ pub fn value_to_yaml_value(v: &Tagged<Value>) -> Result<serde_yaml::Value, Shell
|
||||
Value::Primitive(Primitive::Nothing) => serde_yaml::Value::Null,
|
||||
Value::Primitive(Primitive::Pattern(s)) => serde_yaml::Value::String(s.clone()),
|
||||
Value::Primitive(Primitive::String(s)) => serde_yaml::Value::String(s.clone()),
|
||||
Value::Primitive(Primitive::ColumnPath(path)) => {
|
||||
let mut out = vec![];
|
||||
|
||||
for member in path.iter() {
|
||||
match &member.item {
|
||||
RawPathMember::String(string) => {
|
||||
out.push(serde_yaml::Value::String(string.clone()))
|
||||
}
|
||||
RawPathMember::Int(int) => out.push(serde_yaml::Value::Number(
|
||||
serde_yaml::Number::from(CoerceInto::<i64>::coerce_into(
|
||||
int.tagged(&member.span),
|
||||
"converting to YAML number",
|
||||
)?),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
serde_yaml::Value::Sequence(out)
|
||||
}
|
||||
Value::Primitive(Primitive::Path(s)) => serde_yaml::Value::String(s.display().to_string()),
|
||||
|
||||
Value::Table(l) => {
|
||||
|
52
src/commands/what.rs
Normal file
52
src/commands/what.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::Value;
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
use futures::StreamExt;
|
||||
use futures_util::pin_mut;
|
||||
|
||||
pub struct What;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WhatArgs {}
|
||||
|
||||
impl WholeStreamCommand for What {
|
||||
fn name(&self) -> &str {
|
||||
"what?"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("what?")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Describes the objects in the stream."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
args.process(registry, what)?.run()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn what(
|
||||
WhatArgs {}: WhatArgs,
|
||||
RunnableContext { input, host, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
let values = input.values;
|
||||
pin_mut!(values);
|
||||
|
||||
while let Some(row) = values.next().await {
|
||||
let name = row.format_type(host.clone().lock().unwrap().width());
|
||||
yield ReturnSuccess::value(Value::string(name).tagged(Tag::unknown_anchor(row.tag.span)));
|
||||
}
|
||||
};
|
||||
|
||||
let stream: BoxStream<'static, ReturnValue> = stream.boxed();
|
||||
|
||||
Ok(OutputStream::from(stream))
|
||||
}
|
@ -64,7 +64,8 @@ impl CommandRegistry {
|
||||
#[derive(Clone)]
|
||||
pub struct Context {
|
||||
registry: CommandRegistry,
|
||||
host: Arc<Mutex<dyn Host + Send>>,
|
||||
host: Arc<Mutex<Box<dyn Host>>>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub(crate) shell_manager: ShellManager,
|
||||
}
|
||||
@ -85,18 +86,69 @@ impl Context {
|
||||
let registry = CommandRegistry::new();
|
||||
Ok(Context {
|
||||
registry: registry.clone(),
|
||||
host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
|
||||
host: Arc::new(Mutex::new(Box::new(crate::env::host::BasicHost))),
|
||||
current_errors: Arc::new(Mutex::new(vec![])),
|
||||
ctrl_c: Arc::new(AtomicBool::new(false)),
|
||||
shell_manager: ShellManager::basic(registry)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn with_host(&mut self, block: impl FnOnce(&mut dyn Host)) {
|
||||
pub(crate) fn error(&mut self, error: ShellError) {
|
||||
self.with_errors(|errors| errors.push(error))
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_print_errors(&mut self, source: Text) -> bool {
|
||||
let errors = self.current_errors.clone();
|
||||
let errors = errors.lock();
|
||||
|
||||
let host = self.host.clone();
|
||||
let host = host.lock();
|
||||
|
||||
let result: bool;
|
||||
|
||||
match (errors, host) {
|
||||
(Err(err), _) => {
|
||||
errln!(
|
||||
"Unexpected error attempting to acquire the lock of the current errors: {:?}",
|
||||
err
|
||||
);
|
||||
result = false;
|
||||
}
|
||||
(_, Err(err)) => {
|
||||
errln!(
|
||||
"Unexpected error attempting to acquire the lock of the current errors: {:?}",
|
||||
err
|
||||
);
|
||||
result = false;
|
||||
}
|
||||
(Ok(mut errors), Ok(host)) => {
|
||||
if errors.len() > 0 {
|
||||
let error = errors[0].clone();
|
||||
*errors = vec![];
|
||||
|
||||
crate::cli::print_err(error, &*host, &source);
|
||||
result = true;
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn with_host<T>(&mut self, block: impl FnOnce(&mut dyn Host) -> T) -> T {
|
||||
let mut host = self.host.lock().unwrap();
|
||||
|
||||
block(&mut *host)
|
||||
}
|
||||
|
||||
pub(crate) fn with_errors<T>(&mut self, block: impl FnOnce(&mut Vec<ShellError>) -> T) -> T {
|
||||
let mut errors = self.current_errors.lock().unwrap();
|
||||
|
||||
block(&mut *errors)
|
||||
}
|
||||
|
||||
pub fn add_commands(&mut self, commands: Vec<Arc<Command>>) {
|
||||
for command in commands {
|
||||
self.registry.insert(command.name().to_string(), command);
|
||||
|
552
src/data/base.rs
552
src/data/base.rs
@ -1,7 +1,13 @@
|
||||
mod debug;
|
||||
mod property_get;
|
||||
pub(crate) mod shape;
|
||||
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::data::base::shape::{Column, InlineShape, TypeShape};
|
||||
use crate::data::TaggedDictBuilder;
|
||||
use crate::errors::ShellError;
|
||||
use crate::evaluate::{evaluate_baseline_expr, Scope};
|
||||
use crate::parser::hir::path::{ColumnPath, PathMember};
|
||||
use crate::parser::{hir, Operator};
|
||||
use crate::prelude::*;
|
||||
use crate::Text;
|
||||
@ -11,7 +17,6 @@ use derive_new::new;
|
||||
use indexmap::IndexMap;
|
||||
use log::trace;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
@ -76,6 +81,7 @@ pub enum Primitive {
|
||||
Decimal(BigDecimal),
|
||||
Bytes(u64),
|
||||
String(String),
|
||||
ColumnPath(ColumnPath),
|
||||
Pattern(String),
|
||||
Boolean(bool),
|
||||
Date(DateTime<Utc>),
|
||||
@ -89,6 +95,27 @@ pub enum Primitive {
|
||||
EndOfStream,
|
||||
}
|
||||
|
||||
impl ShellTypeName for Primitive {
|
||||
fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Primitive::Nothing => "nothing",
|
||||
Primitive::Int(_) => "integer",
|
||||
Primitive::Decimal(_) => "decimal",
|
||||
Primitive::Bytes(_) => "bytes",
|
||||
Primitive::String(_) => "string",
|
||||
Primitive::ColumnPath(_) => "column path",
|
||||
Primitive::Pattern(_) => "pattern",
|
||||
Primitive::Boolean(_) => "boolean",
|
||||
Primitive::Date(_) => "date",
|
||||
Primitive::Duration(_) => "duration",
|
||||
Primitive::Path(_) => "file path",
|
||||
Primitive::Binary(_) => "binary",
|
||||
Primitive::BeginningOfStream => "marker<beginning of stream>",
|
||||
Primitive::EndOfStream => "marker<end of stream>",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigDecimal> for Primitive {
|
||||
fn from(decimal: BigDecimal) -> Primitive {
|
||||
Primitive::Decimal(decimal)
|
||||
@ -102,47 +129,6 @@ impl From<f64> for Primitive {
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
pub(crate) fn type_name(&self) -> String {
|
||||
use Primitive::*;
|
||||
|
||||
match self {
|
||||
Nothing => "nothing",
|
||||
BeginningOfStream => "beginning-of-stream",
|
||||
EndOfStream => "end-of-stream",
|
||||
Path(_) => "path",
|
||||
Int(_) => "int",
|
||||
Decimal(_) => "decimal",
|
||||
Duration(_) => "duration",
|
||||
Bytes(_) => "bytes",
|
||||
Pattern(_) => "pattern",
|
||||
String(_) => "string",
|
||||
Boolean(_) => "boolean",
|
||||
Date(_) => "date",
|
||||
Binary(_) => "binary",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Primitive::*;
|
||||
|
||||
match self {
|
||||
Nothing => write!(f, "Nothing"),
|
||||
BeginningOfStream => write!(f, "BeginningOfStream"),
|
||||
EndOfStream => write!(f, "EndOfStream"),
|
||||
Int(int) => write!(f, "{}", int),
|
||||
Path(path) => write!(f, "{}", path.display()),
|
||||
Decimal(decimal) => write!(f, "{}", decimal),
|
||||
Duration(secs) => write!(f, "{}", secs),
|
||||
Bytes(bytes) => write!(f, "{}", bytes),
|
||||
Pattern(string) => write!(f, "{:?}", string),
|
||||
String(string) => write!(f, "{:?}", string),
|
||||
Boolean(boolean) => write!(f, "{}", boolean),
|
||||
Date(date) => write!(f, "{}", date),
|
||||
Binary(binary) => write!(f, "{:?}", binary),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn number(number: impl Into<Number>) -> Primitive {
|
||||
let number = number.into();
|
||||
|
||||
@ -177,6 +163,24 @@ impl Primitive {
|
||||
Primitive::Decimal(decimal) => format!("{}", decimal),
|
||||
Primitive::Pattern(s) => format!("{}", s),
|
||||
Primitive::String(s) => format!("{}", s),
|
||||
Primitive::ColumnPath(p) => {
|
||||
let mut members = p.iter();
|
||||
let mut f = String::new();
|
||||
|
||||
f.push_str(
|
||||
&members
|
||||
.next()
|
||||
.expect("BUG: column path with zero members")
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
for member in members {
|
||||
f.push_str(".");
|
||||
f.push_str(&member.to_string())
|
||||
}
|
||||
|
||||
f
|
||||
}
|
||||
Primitive::Boolean(b) => match (b, field_name) {
|
||||
(true, None) => format!("Yes"),
|
||||
(false, None) => format!("No"),
|
||||
@ -220,7 +224,7 @@ pub struct Operation {
|
||||
pub(crate) right: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Serialize, Deserialize, new)]
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Serialize, Deserialize, new)]
|
||||
pub struct Block {
|
||||
pub(crate) expressions: Vec<hir::Expression>,
|
||||
pub(crate) source: Text,
|
||||
@ -270,6 +274,18 @@ pub enum Value {
|
||||
Block(Block),
|
||||
}
|
||||
|
||||
impl ShellTypeName for Value {
|
||||
fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Value::Primitive(p) => p.type_name(),
|
||||
Value::Row(_) => "row",
|
||||
Value::Table(_) => "table",
|
||||
Value::Error(_) => "error",
|
||||
Value::Block(_) => "block",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Value> for Number {
|
||||
fn into(self) -> Value {
|
||||
match self {
|
||||
@ -288,41 +304,16 @@ impl Into<Value> for &Number {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_list(values: &Vec<Tagged<Value>>) -> ValuesDebug<'_> {
|
||||
ValuesDebug { values }
|
||||
}
|
||||
|
||||
pub struct ValuesDebug<'a> {
|
||||
values: &'a Vec<Tagged<Value>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ValuesDebug<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_list()
|
||||
.entries(self.values.iter().map(|i| i.debug()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ValueDebug<'a> {
|
||||
value: &'a Tagged<Value>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ValueDebug<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.value.item() {
|
||||
Value::Primitive(p) => p.debug(f),
|
||||
Value::Row(o) => o.debug(f),
|
||||
Value::Table(l) => debug_list(l).fmt(f),
|
||||
Value::Block(_) => write!(f, "[[block]]"),
|
||||
Value::Error(_) => write!(f, "[[error]]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tagged<Value> {
|
||||
pub fn tagged_type_name(&self) -> Tagged<String> {
|
||||
let name = self.type_name();
|
||||
let name = self.type_name().to_string();
|
||||
name.tagged(self.tag())
|
||||
}
|
||||
}
|
||||
|
||||
impl Tagged<&Value> {
|
||||
pub fn tagged_type_name(&self) -> Tagged<String> {
|
||||
let name = self.type_name().to_string();
|
||||
name.tagged(self.tag())
|
||||
}
|
||||
}
|
||||
@ -335,7 +326,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for Block {
|
||||
Value::Block(block) => Ok(block.clone()),
|
||||
v => Err(ShellError::type_error(
|
||||
"Block",
|
||||
v.type_name().tagged(value.tag()),
|
||||
v.type_name().spanned(value.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -351,7 +342,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for i64 {
|
||||
}
|
||||
v => Err(ShellError::type_error(
|
||||
"Integer",
|
||||
v.type_name().tagged(value.tag()),
|
||||
v.type_name().spanned(value.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -365,7 +356,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for String {
|
||||
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
|
||||
v => Err(ShellError::type_error(
|
||||
"String",
|
||||
v.type_name().tagged(value.tag()),
|
||||
v.type_name().spanned(value.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -379,7 +370,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for Vec<u8> {
|
||||
Value::Primitive(Primitive::Binary(b)) => Ok(b.clone()),
|
||||
v => Err(ShellError::type_error(
|
||||
"Binary",
|
||||
v.type_name().tagged(value.tag()),
|
||||
v.type_name().spanned(value.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -393,7 +384,7 @@ impl<'a> std::convert::TryFrom<&'a Tagged<Value>> for &'a crate::data::Dictionar
|
||||
Value::Row(d) => Ok(d),
|
||||
v => Err(ShellError::type_error(
|
||||
"Dictionary",
|
||||
v.type_name().tagged(value.tag()),
|
||||
v.type_name().spanned(value.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -415,61 +406,14 @@ impl std::convert::TryFrom<Option<&Tagged<Value>>> for Switch {
|
||||
Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present),
|
||||
v => Err(ShellError::type_error(
|
||||
"Boolean",
|
||||
v.type_name().tagged(value.tag()),
|
||||
v.type_name().spanned(value.span()),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tagged<Value> {
|
||||
pub(crate) fn debug(&self) -> ValueDebug<'_> {
|
||||
ValueDebug { value: self }
|
||||
}
|
||||
|
||||
pub fn as_column_path(&self) -> Result<Tagged<Vec<Tagged<Value>>>, ShellError> {
|
||||
match &self.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
Ok(vec![Value::string(s).tagged(&self.tag)].tagged(&self.tag))
|
||||
}
|
||||
Value::Table(table) => Ok(table.to_vec().tagged(&self.tag)),
|
||||
other => Err(ShellError::type_error(
|
||||
"column name",
|
||||
other.type_name().tagged(&self.tag),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Result<String, ShellError> {
|
||||
match &self.item {
|
||||
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
|
||||
Value::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Path(x)) => Ok(format!("{}", x.display())),
|
||||
Value::Primitive(Primitive::Date(x)) => Ok(format!("{}", x.to_rfc3339())),
|
||||
// TODO: this should definitely be more general with better errors
|
||||
other => Err(ShellError::labeled_error(
|
||||
"Expected string",
|
||||
other.type_name(),
|
||||
&self.tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn type_name(&self) -> String {
|
||||
match self {
|
||||
Value::Primitive(p) => p.type_name(),
|
||||
Value::Row(_) => format!("row"),
|
||||
Value::Table(_) => format!("table"),
|
||||
Value::Block(_) => format!("block"),
|
||||
Value::Error(_) => format!("error"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data_descriptors(&self) -> Vec<String> {
|
||||
match self {
|
||||
Value::Primitive(_) => vec![],
|
||||
@ -485,201 +429,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_by_index(&self, idx: usize) -> Option<&Tagged<Value>> {
|
||||
match self {
|
||||
Value::Table(value_set) => value_set.get(idx),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_by_key(&self, name: &str) -> Option<&Tagged<Value>> {
|
||||
match self {
|
||||
Value::Row(o) => o.get_data_by_key(name),
|
||||
Value::Table(l) => {
|
||||
for item in l {
|
||||
match item {
|
||||
Tagged {
|
||||
item: Value::Row(o),
|
||||
..
|
||||
} => match o.get_data_by_key(name) {
|
||||
Some(v) => return Some(v),
|
||||
None => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut_data_by_key(&mut self, name: &str) -> Option<&mut Tagged<Value>> {
|
||||
match self {
|
||||
Value::Row(ref mut o) => o.get_mut_data_by_key(name),
|
||||
Value::Table(ref mut l) => {
|
||||
for item in l {
|
||||
match item {
|
||||
Tagged {
|
||||
item: Value::Row(ref mut o),
|
||||
..
|
||||
} => match o.get_mut_data_by_key(name) {
|
||||
Some(v) => return Some(v),
|
||||
None => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_by_column_path(
|
||||
&self,
|
||||
tag: Tag,
|
||||
path: &Vec<Tagged<Value>>,
|
||||
callback: Box<dyn FnOnce((Value, Tagged<Value>)) -> ShellError>,
|
||||
) -> Result<Option<Tagged<&Value>>, ShellError> {
|
||||
let mut column_path = vec![];
|
||||
|
||||
for value in path {
|
||||
column_path.push(
|
||||
Value::string(value.as_string().unwrap_or("".to_string())).tagged(&value.tag),
|
||||
);
|
||||
}
|
||||
|
||||
let path = column_path;
|
||||
|
||||
let mut current = self;
|
||||
|
||||
for p in path {
|
||||
let value = p.as_string().unwrap_or("".to_string());
|
||||
let value = match value.parse::<usize>() {
|
||||
Ok(number) => match current {
|
||||
Value::Table(_) => current.get_data_by_index(number),
|
||||
Value::Row(_) => current.get_data_by_key(&value),
|
||||
_ => None,
|
||||
},
|
||||
Err(_) => match self {
|
||||
Value::Table(_) | Value::Row(_) => current.get_data_by_key(&value),
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
|
||||
match value {
|
||||
Some(v) => current = v,
|
||||
None => return Err(callback((current.clone(), p.clone()))),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(current.tagged(tag)))
|
||||
}
|
||||
|
||||
pub fn insert_data_at_column_path(
|
||||
&self,
|
||||
tag: Tag,
|
||||
split_path: &Vec<Tagged<Value>>,
|
||||
new_value: Value,
|
||||
) -> Option<Tagged<Value>> {
|
||||
let split_path = split_path
|
||||
.into_iter()
|
||||
.map(|p| match p {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::String(s)),
|
||||
tag,
|
||||
} => Ok(s.clone().tagged(tag)),
|
||||
o => Err(o),
|
||||
})
|
||||
.filter_map(Result::ok)
|
||||
.collect::<Vec<Tagged<String>>>();
|
||||
|
||||
let mut new_obj = self.clone();
|
||||
|
||||
if let Value::Row(ref mut o) = new_obj {
|
||||
let mut current = o;
|
||||
|
||||
if split_path.len() == 1 {
|
||||
// Special case for inserting at the top level
|
||||
current
|
||||
.entries
|
||||
.insert(split_path[0].item.clone(), new_value.tagged(&tag));
|
||||
return Some(new_obj.tagged(&tag));
|
||||
}
|
||||
|
||||
for idx in 0..split_path.len() {
|
||||
match current.entries.get_mut(&split_path[idx].item) {
|
||||
Some(next) => {
|
||||
if idx == (split_path.len() - 2) {
|
||||
match &mut next.item {
|
||||
Value::Row(o) => {
|
||||
o.entries.insert(
|
||||
split_path[idx + 1].to_string(),
|
||||
new_value.tagged(&tag),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return Some(new_obj.tagged(&tag));
|
||||
} else {
|
||||
match next.item {
|
||||
Value::Row(ref mut o) => {
|
||||
current = o;
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn replace_data_at_column_path(
|
||||
&self,
|
||||
tag: Tag,
|
||||
split_path: &Vec<Tagged<Value>>,
|
||||
replaced_value: Value,
|
||||
) -> Option<Tagged<Value>> {
|
||||
let split_path = split_path
|
||||
.into_iter()
|
||||
.map(|p| match p {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::String(s)),
|
||||
tag,
|
||||
} => Ok(s.clone().tagged(tag)),
|
||||
o => Err(o),
|
||||
})
|
||||
.filter_map(Result::ok)
|
||||
.collect::<Vec<Tagged<String>>>();
|
||||
|
||||
let mut new_obj = self.clone();
|
||||
let mut current = &mut new_obj;
|
||||
|
||||
for idx in 0..split_path.len() {
|
||||
match current.get_mut_data_by_key(&split_path[idx].item) {
|
||||
Some(next) => {
|
||||
if idx == (split_path.len() - 1) {
|
||||
*next = replaced_value.tagged(&tag);
|
||||
return Some(new_obj.tagged(&tag));
|
||||
} else {
|
||||
current = &mut next.item;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_data(&self, desc: &String) -> MaybeOwned<'_, Value> {
|
||||
match self {
|
||||
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
|
||||
@ -690,23 +439,18 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn format_leaf(&self, desc: Option<&String>) -> String {
|
||||
match self {
|
||||
Value::Primitive(p) => p.format(desc),
|
||||
Value::Block(b) => itertools::join(
|
||||
b.expressions
|
||||
.iter()
|
||||
.map(|e| e.span.slice(&b.source).to_string()),
|
||||
"; ",
|
||||
),
|
||||
Value::Row(_) => format!("[table: 1 row]"),
|
||||
Value::Table(l) => format!(
|
||||
"[table: {} {}]",
|
||||
l.len(),
|
||||
if l.len() == 1 { "row" } else { "rows" }
|
||||
),
|
||||
Value::Error(_) => format!("[error]"),
|
||||
pub(crate) fn format_type(&self, width: usize) -> String {
|
||||
TypeShape::from_value(self).colored_string(width)
|
||||
}
|
||||
|
||||
pub(crate) fn format_leaf(&self) -> DebugDocBuilder {
|
||||
InlineShape::from_value(self).format().pretty_debug()
|
||||
}
|
||||
|
||||
pub(crate) fn format_for_column(&self, column: impl Into<Column>) -> DebugDocBuilder {
|
||||
InlineShape::from_value(self)
|
||||
.format_for_column(column)
|
||||
.pretty_debug()
|
||||
}
|
||||
|
||||
pub(crate) fn style_leaf(&self) -> &'static str {
|
||||
@ -720,7 +464,7 @@ impl Value {
|
||||
&self,
|
||||
operator: &Operator,
|
||||
other: &Value,
|
||||
) -> Result<bool, (String, String)> {
|
||||
) -> Result<bool, (&'static str, &'static str)> {
|
||||
match operator {
|
||||
_ => {
|
||||
let coerced = coerce_compare(self, other)?;
|
||||
@ -753,6 +497,20 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_error(&self) -> bool {
|
||||
match self {
|
||||
Value::Error(_err) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expect_error(&self) -> ShellError {
|
||||
match self {
|
||||
Value::Error(err) => err.clone(),
|
||||
_ => panic!("Don't call expect_error without first calling is_error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn row(entries: IndexMap<String, Tagged<Value>>) -> Value {
|
||||
Value::Row(entries.into())
|
||||
@ -766,6 +524,16 @@ impl Value {
|
||||
Value::Primitive(Primitive::String(s.into()))
|
||||
}
|
||||
|
||||
pub fn column_path(s: Vec<impl Into<PathMember>>) -> Value {
|
||||
Value::Primitive(Primitive::ColumnPath(ColumnPath::new(
|
||||
s.into_iter().map(|p| p.into()).collect(),
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn int(i: impl Into<BigInt>) -> Value {
|
||||
Value::Primitive(Primitive::Int(i.into()))
|
||||
}
|
||||
|
||||
pub fn pattern(s: impl Into<String>) -> Value {
|
||||
Value::Primitive(Primitive::String(s.into()))
|
||||
}
|
||||
@ -778,10 +546,6 @@ impl Value {
|
||||
Value::Primitive(Primitive::Bytes(s.into()))
|
||||
}
|
||||
|
||||
pub fn int(s: impl Into<BigInt>) -> Value {
|
||||
Value::Primitive(Primitive::Int(s.into()))
|
||||
}
|
||||
|
||||
pub fn decimal(s: impl Into<BigDecimal>) -> Value {
|
||||
Value::Primitive(Primitive::Decimal(s.into()))
|
||||
}
|
||||
@ -837,7 +601,7 @@ impl Tagged<Value> {
|
||||
Value::Primitive(Primitive::String(path_str)) => Ok(PathBuf::from(&path_str).clone()),
|
||||
other => Err(ShellError::type_error(
|
||||
"Path",
|
||||
other.type_name().tagged(self.tag()),
|
||||
other.type_name().spanned(self.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -900,7 +664,10 @@ impl CompareValues {
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_compare(left: &Value, right: &Value) -> Result<CompareValues, (String, String)> {
|
||||
fn coerce_compare(
|
||||
left: &Value,
|
||||
right: &Value,
|
||||
) -> Result<CompareValues, (&'static str, &'static str)> {
|
||||
match (left, right) {
|
||||
(Value::Primitive(left), Value::Primitive(right)) => coerce_compare_primitive(left, right),
|
||||
|
||||
@ -911,7 +678,7 @@ fn coerce_compare(left: &Value, right: &Value) -> Result<CompareValues, (String,
|
||||
fn coerce_compare_primitive(
|
||||
left: &Primitive,
|
||||
right: &Primitive,
|
||||
) -> Result<CompareValues, (String, String)> {
|
||||
) -> Result<CompareValues, (&'static str, &'static str)> {
|
||||
use Primitive::*;
|
||||
|
||||
Ok(match (left, right) {
|
||||
@ -941,16 +708,19 @@ fn coerce_compare_primitive(
|
||||
mod tests {
|
||||
|
||||
use crate::data::meta::*;
|
||||
use crate::parser::hir::path::PathMember;
|
||||
use crate::ColumnPath as ColumnPathValue;
|
||||
use crate::ShellError;
|
||||
use crate::Value;
|
||||
use indexmap::IndexMap;
|
||||
use num_bigint::BigInt;
|
||||
|
||||
fn string(input: impl Into<String>) -> Tagged<Value> {
|
||||
Value::string(input.into()).tagged_unknown()
|
||||
}
|
||||
|
||||
fn number(n: i64) -> Tagged<Value> {
|
||||
Value::number(n).tagged_unknown()
|
||||
fn int(input: impl Into<BigInt>) -> Tagged<Value> {
|
||||
Value::int(input.into()).tagged_unknown()
|
||||
}
|
||||
|
||||
fn row(entries: IndexMap<String, Tagged<Value>>) -> Tagged<Value> {
|
||||
@ -961,12 +731,16 @@ mod tests {
|
||||
Value::table(list).tagged_unknown()
|
||||
}
|
||||
|
||||
fn error_callback() -> impl FnOnce((Value, Tagged<Value>)) -> ShellError {
|
||||
move |(_obj_source, _column_path_tried)| ShellError::unimplemented("will never be called.")
|
||||
fn error_callback(
|
||||
reason: &'static str,
|
||||
) -> impl FnOnce((&Value, &PathMember, ShellError)) -> ShellError {
|
||||
move |(_obj_source, _column_path_tried, _err)| ShellError::unimplemented(reason)
|
||||
}
|
||||
|
||||
fn column_path(paths: &Vec<Tagged<Value>>) -> Vec<Tagged<Value>> {
|
||||
table(paths).as_column_path().unwrap().item
|
||||
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<ColumnPathValue> {
|
||||
table(&paths.iter().cloned().collect())
|
||||
.as_column_path()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -976,7 +750,7 @@ mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
*row.get_data_by_key("amigos").unwrap(),
|
||||
row.get_data_by_key("amigos".spanned_unknown()).unwrap(),
|
||||
table(&vec![
|
||||
string("andres"),
|
||||
string("jonathan"),
|
||||
@ -1000,17 +774,17 @@ mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
**value
|
||||
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
||||
.unwrap()
|
||||
*value
|
||||
.tagged(tag)
|
||||
.get_data_by_column_path(&field_path, Box::new(error_callback("package.version")))
|
||||
.unwrap(),
|
||||
version
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn column_path_that_contains_just_a_number_gets_a_row_from_a_table() {
|
||||
let field_path = column_path(&vec![string("package"), string("authors"), number(0)]);
|
||||
fn gets_first_matching_field_from_rows_with_same_field_inside_a_table() {
|
||||
let field_path = column_path(&vec![string("package"), string("authors"), string("name")]);
|
||||
|
||||
let (_, tag) = string("Andrés N. Robalino").into_parts();
|
||||
|
||||
@ -1027,9 +801,43 @@ mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
**value
|
||||
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
||||
.unwrap()
|
||||
value
|
||||
.tagged(tag)
|
||||
.get_data_by_column_path(
|
||||
&field_path,
|
||||
Box::new(error_callback("package.authors.name"))
|
||||
)
|
||||
.unwrap(),
|
||||
table(&vec![
|
||||
string("Andrés N. Robalino"),
|
||||
string("Jonathan Turner"),
|
||||
string("Yehuda Katz")
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn column_path_that_contains_just_a_number_gets_a_row_from_a_table() {
|
||||
let field_path = column_path(&vec![string("package"), string("authors"), int(0)]);
|
||||
|
||||
let (_, tag) = string("Andrés N. Robalino").into_parts();
|
||||
|
||||
let value = Value::row(indexmap! {
|
||||
"package".into() => row(indexmap! {
|
||||
"name".into() => string("nu"),
|
||||
"version".into() => string("0.4.0"),
|
||||
"authors".into() => table(&vec![
|
||||
row(indexmap!{"name".into() => string("Andrés N. Robalino")}),
|
||||
row(indexmap!{"name".into() => string("Jonathan Turner")}),
|
||||
row(indexmap!{"name".into() => string("Yehuda Katz")})
|
||||
])
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
*value
|
||||
.tagged(tag)
|
||||
.get_data_by_column_path(&field_path, Box::new(error_callback("package.authors.0")))
|
||||
.unwrap(),
|
||||
Value::row(indexmap! {
|
||||
"name".into() => string("Andrés N. Robalino")
|
||||
@ -1056,9 +864,12 @@ mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
**value
|
||||
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
||||
.unwrap()
|
||||
*value
|
||||
.tagged(tag)
|
||||
.get_data_by_column_path(
|
||||
&field_path,
|
||||
Box::new(error_callback("package.authors.\"0\""))
|
||||
)
|
||||
.unwrap(),
|
||||
Value::row(indexmap! {
|
||||
"name".into() => string("Andrés N. Robalino")
|
||||
@ -1081,7 +892,8 @@ mod tests {
|
||||
let (replacement, tag) = string("jonas").into_parts();
|
||||
|
||||
let actual = sample
|
||||
.replace_data_at_column_path(tag, &field_path, replacement)
|
||||
.tagged(tag)
|
||||
.replace_data_at_column_path(&field_path, replacement)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(actual, row(indexmap! {"amigos".into() => string("jonas")}));
|
||||
@ -1108,7 +920,8 @@ mod tests {
|
||||
let (replacement, tag) = table(&vec![string("yehuda::jonathan::andres")]).into_parts();
|
||||
|
||||
let actual = sample
|
||||
.replace_data_at_column_path(tag.clone(), &field_path, replacement.clone())
|
||||
.tagged(tag.clone())
|
||||
.replace_data_at_column_path(&field_path, replacement.clone())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@ -1159,7 +972,8 @@ mod tests {
|
||||
.into_parts();
|
||||
|
||||
let actual = sample
|
||||
.replace_data_at_column_path(tag.clone(), &field_path, replacement.clone())
|
||||
.tagged(tag.clone())
|
||||
.replace_data_at_column_path(&field_path, replacement.clone())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
|
129
src/data/base/debug.rs
Normal file
129
src/data/base/debug.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use crate::data::base::Primitive;
|
||||
use crate::prelude::*;
|
||||
use crate::traits::DebugDocBuilder as b;
|
||||
use pretty::{BoxAllocator, DocAllocator};
|
||||
use std::fmt;
|
||||
|
||||
impl FormatDebug for Tagged<Value> {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
match &self.item {
|
||||
Value::Primitive(p) => p.fmt_debug(f, source),
|
||||
Value::Row(row) => f.say_dict(
|
||||
"row",
|
||||
row.entries()
|
||||
.iter()
|
||||
.map(|(key, value)| (&key[..], format!("{}", value.debug(source))))
|
||||
.collect(),
|
||||
),
|
||||
Value::Table(table) => f.say_list(
|
||||
"table",
|
||||
table,
|
||||
|f| write!(f, "["),
|
||||
|f, item| write!(f, "{}", item.debug(source)),
|
||||
|f| write!(f, " "),
|
||||
|f| write!(f, "]"),
|
||||
),
|
||||
Value::Error(_) => f.say_simple("error"),
|
||||
Value::Block(_) => f.say_simple("block"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for Primitive {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
|
||||
match self {
|
||||
Primitive::Nothing => write!(f, "Nothing"),
|
||||
Primitive::BeginningOfStream => write!(f, "BeginningOfStream"),
|
||||
Primitive::EndOfStream => write!(f, "EndOfStream"),
|
||||
Primitive::Int(int) => write!(f, "{}", int),
|
||||
Primitive::Duration(duration) => write!(f, "{} seconds", *duration),
|
||||
Primitive::Path(path) => write!(f, "{}", path.display()),
|
||||
Primitive::Decimal(decimal) => write!(f, "{}", decimal),
|
||||
Primitive::Bytes(bytes) => write!(f, "{}", bytes),
|
||||
Primitive::Pattern(string) => write!(f, "{:?}", string),
|
||||
Primitive::String(string) => write!(f, "{:?}", string),
|
||||
Primitive::ColumnPath(path) => write!(f, "{:?}", path),
|
||||
Primitive::Boolean(boolean) => write!(f, "{}", boolean),
|
||||
Primitive::Date(date) => write!(f, "{}", date),
|
||||
Primitive::Binary(binary) => write!(f, "{:?}", binary),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyType for Primitive {
|
||||
fn pretty_type(&self) -> DebugDocBuilder {
|
||||
match self {
|
||||
Primitive::Nothing => ty("nothing"),
|
||||
Primitive::Int(_) => ty("integer"),
|
||||
Primitive::Decimal(_) => ty("decimal"),
|
||||
Primitive::Bytes(_) => ty("bytesize"),
|
||||
Primitive::String(_) => ty("string"),
|
||||
Primitive::ColumnPath(_) => ty("column-path"),
|
||||
Primitive::Pattern(_) => ty("pattern"),
|
||||
Primitive::Boolean(_) => ty("boolean"),
|
||||
Primitive::Date(_) => ty("date"),
|
||||
Primitive::Duration(_) => ty("duration"),
|
||||
Primitive::Path(_) => ty("path"),
|
||||
Primitive::Binary(_) => ty("binary"),
|
||||
Primitive::BeginningOfStream => b::keyword("beginning-of-stream"),
|
||||
Primitive::EndOfStream => b::keyword("end-of-stream"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for Primitive {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
match self {
|
||||
Primitive::Nothing => b::primitive("nothing"),
|
||||
Primitive::Int(int) => prim(format_args!("{}", int)),
|
||||
Primitive::Decimal(decimal) => prim(format_args!("{}", decimal)),
|
||||
Primitive::Bytes(bytes) => primitive_doc(bytes, "bytesize"),
|
||||
Primitive::String(string) => prim(string),
|
||||
Primitive::ColumnPath(path) => path.pretty_debug(),
|
||||
Primitive::Pattern(pattern) => primitive_doc(pattern, "pattern"),
|
||||
Primitive::Boolean(boolean) => match boolean {
|
||||
true => b::primitive("$yes"),
|
||||
false => b::primitive("$no"),
|
||||
},
|
||||
Primitive::Date(date) => primitive_doc(date, "date"),
|
||||
Primitive::Duration(duration) => primitive_doc(duration, "seconds"),
|
||||
Primitive::Path(path) => primitive_doc(path, "path"),
|
||||
Primitive::Binary(_) => b::opaque("binary"),
|
||||
Primitive::BeginningOfStream => b::keyword("beginning-of-stream"),
|
||||
Primitive::EndOfStream => b::keyword("end-of-stream"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for Value {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
match self {
|
||||
Value::Primitive(p) => p.pretty_debug(),
|
||||
Value::Row(row) => row.pretty_builder().nest(1).group().into(),
|
||||
Value::Table(table) => BoxAllocator
|
||||
.text("[")
|
||||
.append(
|
||||
BoxAllocator
|
||||
.intersperse(table.iter().map(|v| v.item.to_doc()), BoxAllocator.space())
|
||||
.nest(1)
|
||||
.group(),
|
||||
)
|
||||
.append(BoxAllocator.text("]"))
|
||||
.into(),
|
||||
Value::Error(_) => b::error("error"),
|
||||
Value::Block(_) => b::opaque("block"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prim(name: impl std::fmt::Debug) -> DebugDocBuilder {
|
||||
b::primitive(format!("{:?}", name))
|
||||
}
|
||||
|
||||
fn ty(name: impl std::fmt::Debug) -> DebugDocBuilder {
|
||||
b::kind(format!("{:?}", name))
|
||||
}
|
||||
|
||||
fn primitive_doc(name: impl std::fmt::Debug, ty: impl Into<String>) -> DebugDocBuilder {
|
||||
b::primitive(format!("{:?}", name)) + b::delimit("(", b::kind(ty.into()), ")")
|
||||
}
|
407
src/data/base/property_get.rs
Normal file
407
src/data/base/property_get.rs
Normal file
@ -0,0 +1,407 @@
|
||||
use crate::errors::ExpectedRange;
|
||||
use crate::parser::hir::path::{PathMember, RawPathMember};
|
||||
use crate::prelude::*;
|
||||
use crate::ColumnPath;
|
||||
use crate::SpannedTypeName;
|
||||
|
||||
impl Tagged<Value> {
|
||||
pub(crate) fn get_data_by_member(
|
||||
&self,
|
||||
name: &PathMember,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
match &self.item {
|
||||
// If the value is a row, the member is a column name
|
||||
Value::Row(o) => match &name.item {
|
||||
// If the member is a string, get the data
|
||||
RawPathMember::String(string) => o
|
||||
.get_data_by_key(string[..].spanned(name.span))
|
||||
.ok_or_else(|| {
|
||||
ShellError::missing_property(
|
||||
"row".spanned(self.tag.span),
|
||||
string.spanned(name.span),
|
||||
)
|
||||
}),
|
||||
|
||||
// If the member is a number, it's an error
|
||||
RawPathMember::Int(_) => Err(ShellError::invalid_integer_index(
|
||||
"row".spanned(self.tag.span),
|
||||
name.span,
|
||||
)),
|
||||
},
|
||||
|
||||
// If the value is a table
|
||||
Value::Table(l) => match &name.item {
|
||||
// If the member is a string, map over the member
|
||||
RawPathMember::String(string) => {
|
||||
let mut out = vec![];
|
||||
|
||||
for item in l {
|
||||
match item {
|
||||
Tagged {
|
||||
item: Value::Row(o),
|
||||
..
|
||||
} => match o.get_data_by_key(string[..].spanned(name.span)) {
|
||||
Some(v) => out.push(v),
|
||||
None => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if out.len() == 0 {
|
||||
Err(ShellError::missing_property(
|
||||
"table".spanned(self.tag.span),
|
||||
string.spanned(name.span),
|
||||
))
|
||||
} else {
|
||||
Ok(Value::Table(out).tagged(Tag::new(self.anchor(), name.span)))
|
||||
}
|
||||
}
|
||||
RawPathMember::Int(int) => {
|
||||
let index = int.to_usize().ok_or_else(|| {
|
||||
ShellError::range_error(
|
||||
ExpectedRange::Usize,
|
||||
&"massive integer".tagged(name.span),
|
||||
"indexing",
|
||||
)
|
||||
})?;
|
||||
|
||||
match self.get_data_by_index(index.spanned(self.tag.span)) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
None => Err(ShellError::range_error(
|
||||
0..(l.len()),
|
||||
&int.tagged(name.span),
|
||||
"indexing",
|
||||
)),
|
||||
}
|
||||
}
|
||||
},
|
||||
other => Err(ShellError::type_error(
|
||||
"row or table",
|
||||
other.spanned(self.tag.span).spanned_type_name(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_by_column_path(
|
||||
&self,
|
||||
path: &ColumnPath,
|
||||
callback: Box<dyn FnOnce((&Value, &PathMember, ShellError)) -> ShellError>,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
let mut current = self.clone();
|
||||
|
||||
for p in path.iter() {
|
||||
let value = current.get_data_by_member(p);
|
||||
|
||||
match value {
|
||||
Ok(v) => current = v.clone(),
|
||||
Err(e) => return Err(callback((¤t.clone(), &p.clone(), e))),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
pub fn insert_data_at_path(&self, path: &str, new_value: Value) -> Option<Tagged<Value>> {
|
||||
let mut new_obj = self.clone();
|
||||
|
||||
let split_path: Vec<_> = path.split(".").collect();
|
||||
|
||||
if let Value::Row(ref mut o) = new_obj.item {
|
||||
let mut current = o;
|
||||
|
||||
if split_path.len() == 1 {
|
||||
// Special case for inserting at the top level
|
||||
current
|
||||
.entries
|
||||
.insert(path.to_string(), new_value.tagged(&self.tag));
|
||||
return Some(new_obj);
|
||||
}
|
||||
|
||||
for idx in 0..split_path.len() {
|
||||
match current.entries.get_mut(split_path[idx]) {
|
||||
Some(next) => {
|
||||
if idx == (split_path.len() - 2) {
|
||||
match &mut next.item {
|
||||
Value::Row(o) => {
|
||||
o.entries.insert(
|
||||
split_path[idx + 1].to_string(),
|
||||
new_value.tagged(&self.tag),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return Some(new_obj.clone());
|
||||
} else {
|
||||
match next.item {
|
||||
Value::Row(ref mut o) => {
|
||||
current = o;
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn insert_data_at_member(
|
||||
&mut self,
|
||||
member: &PathMember,
|
||||
new_value: Tagged<Value>,
|
||||
) -> Result<(), ShellError> {
|
||||
match &mut self.item {
|
||||
Value::Row(dict) => match &member.item {
|
||||
RawPathMember::String(key) => Ok({
|
||||
dict.insert_data_at_key(key, new_value);
|
||||
}),
|
||||
RawPathMember::Int(_) => Err(ShellError::type_error(
|
||||
"column name",
|
||||
"integer".spanned(member.span),
|
||||
)),
|
||||
},
|
||||
Value::Table(array) => match &member.item {
|
||||
RawPathMember::String(_) => Err(ShellError::type_error(
|
||||
"list index",
|
||||
"string".spanned(member.span),
|
||||
)),
|
||||
RawPathMember::Int(int) => Ok({
|
||||
let int = int.to_usize().ok_or_else(|| {
|
||||
ShellError::range_error(
|
||||
ExpectedRange::Usize,
|
||||
&"bigger number".tagged(member.span),
|
||||
"inserting into a list",
|
||||
)
|
||||
})?;
|
||||
|
||||
insert_data_at_index(array, int.tagged(member.span), new_value.clone())?;
|
||||
}),
|
||||
},
|
||||
other => match &member.item {
|
||||
RawPathMember::String(_) => Err(ShellError::type_error(
|
||||
"row",
|
||||
other.type_name().spanned(self.span()),
|
||||
)),
|
||||
RawPathMember::Int(_) => Err(ShellError::type_error(
|
||||
"table",
|
||||
other.type_name().spanned(self.span()),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_data_at_column_path(
|
||||
&self,
|
||||
split_path: &ColumnPath,
|
||||
new_value: Tagged<Value>,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
let (last, front) = split_path.split_last();
|
||||
let mut original = self.clone();
|
||||
|
||||
let mut current: &mut Tagged<Value> = &mut original;
|
||||
|
||||
for member in front {
|
||||
let type_name = current.spanned_type_name();
|
||||
|
||||
current = current
|
||||
.item
|
||||
.get_mut_data_by_member(&member)
|
||||
.ok_or_else(|| {
|
||||
ShellError::missing_property(
|
||||
member.plain_string(std::usize::MAX).spanned(member.span),
|
||||
type_name,
|
||||
)
|
||||
})?
|
||||
}
|
||||
|
||||
current.insert_data_at_member(&last, new_value)?;
|
||||
|
||||
Ok(original)
|
||||
}
|
||||
|
||||
pub fn replace_data_at_column_path(
|
||||
&self,
|
||||
split_path: &ColumnPath,
|
||||
replaced_value: Value,
|
||||
) -> Option<Tagged<Value>> {
|
||||
let mut new_obj: Tagged<Value> = self.clone();
|
||||
let mut current = &mut new_obj;
|
||||
let split_path = split_path.members();
|
||||
|
||||
for idx in 0..split_path.len() {
|
||||
match current.item.get_mut_data_by_member(&split_path[idx]) {
|
||||
Some(next) => {
|
||||
if idx == (split_path.len() - 1) {
|
||||
*next = replaced_value.tagged(&self.tag);
|
||||
return Some(new_obj);
|
||||
} else {
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn as_column_path(&self) -> Result<Tagged<ColumnPath>, ShellError> {
|
||||
match &self.item {
|
||||
Value::Table(table) => {
|
||||
let mut out: Vec<PathMember> = vec![];
|
||||
|
||||
for item in table {
|
||||
out.push(item.as_path_member()?);
|
||||
}
|
||||
|
||||
Ok(ColumnPath::new(out).tagged(&self.tag))
|
||||
}
|
||||
|
||||
Value::Primitive(Primitive::ColumnPath(path)) => {
|
||||
Ok(path.clone().tagged(self.tag.clone()))
|
||||
}
|
||||
|
||||
other => Err(ShellError::type_error(
|
||||
"column path",
|
||||
other.type_name().spanned(self.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path_member(&self) -> Result<PathMember, ShellError> {
|
||||
match &self.item {
|
||||
Value::Primitive(primitive) => match primitive {
|
||||
Primitive::Int(int) => Ok(PathMember::int(int.clone(), self.tag.span)),
|
||||
Primitive::String(string) => Ok(PathMember::string(string, self.tag.span)),
|
||||
other => Err(ShellError::type_error(
|
||||
"path member",
|
||||
other.type_name().spanned(self.span()),
|
||||
)),
|
||||
},
|
||||
other => Err(ShellError::type_error(
|
||||
"path member",
|
||||
other.type_name().spanned(self.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Result<String, ShellError> {
|
||||
match &self.item {
|
||||
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
|
||||
Value::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)),
|
||||
Value::Primitive(Primitive::Path(x)) => Ok(format!("{}", x.display())),
|
||||
// TODO: this should definitely be more general with better errors
|
||||
other => Err(ShellError::labeled_error(
|
||||
"Expected string",
|
||||
other.type_name(),
|
||||
&self.tag,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_data_at_index(
|
||||
list: &mut Vec<Tagged<Value>>,
|
||||
index: Tagged<usize>,
|
||||
new_value: Tagged<Value>,
|
||||
) -> Result<(), ShellError> {
|
||||
if list.len() >= index.item {
|
||||
Err(ShellError::range_error(
|
||||
0..(list.len()),
|
||||
&format_args!("{}", index.item).tagged(index.tag.clone()),
|
||||
"insert at index",
|
||||
))
|
||||
} else {
|
||||
list[index.item] = new_value;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub(crate) fn get_data_by_index(&self, idx: Spanned<usize>) -> Option<Tagged<Value>> {
|
||||
match self {
|
||||
Value::Table(value_set) => {
|
||||
let value = value_set.get(idx.item)?;
|
||||
Some(
|
||||
value
|
||||
.item
|
||||
.clone()
|
||||
.tagged(Tag::new(value.anchor(), idx.span)),
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_by_key(&self, name: Spanned<&str>) -> Option<Tagged<Value>> {
|
||||
match self {
|
||||
Value::Row(o) => o.get_data_by_key(name),
|
||||
Value::Table(l) => {
|
||||
let mut out = vec![];
|
||||
for item in l {
|
||||
match item {
|
||||
Tagged {
|
||||
item: Value::Row(o),
|
||||
..
|
||||
} => match o.get_data_by_key(name) {
|
||||
Some(v) => out.push(v),
|
||||
None => out.push(Value::nothing().tagged_unknown()),
|
||||
},
|
||||
_ => out.push(Value::nothing().tagged_unknown()),
|
||||
}
|
||||
}
|
||||
|
||||
if out.len() > 0 {
|
||||
Some(Value::Table(out).tagged(name.span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut_data_by_member(
|
||||
&mut self,
|
||||
name: &PathMember,
|
||||
) -> Option<&mut Tagged<Value>> {
|
||||
match self {
|
||||
Value::Row(o) => match &name.item {
|
||||
RawPathMember::String(string) => o.get_mut_data_by_key(&string),
|
||||
RawPathMember::Int(_) => None,
|
||||
},
|
||||
Value::Table(l) => match &name.item {
|
||||
RawPathMember::String(string) => {
|
||||
for item in l {
|
||||
match item {
|
||||
Tagged {
|
||||
item: Value::Row(o),
|
||||
..
|
||||
} => match o.get_mut_data_by_key(&string) {
|
||||
Some(v) => return Some(v),
|
||||
None => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
RawPathMember::Int(int) => {
|
||||
let index = int.to_usize()?;
|
||||
l.get_mut(index)
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
606
src/data/base/shape.rs
Normal file
606
src/data/base/shape.rs
Normal file
@ -0,0 +1,606 @@
|
||||
use crate::data::base::{Block, ColumnPath};
|
||||
use crate::data::dict::Dictionary;
|
||||
use crate::prelude::*;
|
||||
use crate::traits::{DebugDocBuilder as b, PrettyDebug};
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono_humanize::Humanize;
|
||||
use derive_new::new;
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/**
|
||||
This file describes the structural types of the nushell system.
|
||||
|
||||
Its primary purpose today is to identify "equivalent" values for the purpose
|
||||
of merging rows into a single table or identify rows in a table that have the
|
||||
same shape for reflection.
|
||||
|
||||
It also serves as the primary vehicle for pretty-printing.
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum TypeShape {
|
||||
Nothing,
|
||||
Int,
|
||||
Decimal,
|
||||
Bytesize,
|
||||
String,
|
||||
ColumnPath,
|
||||
Pattern,
|
||||
Boolean,
|
||||
Date,
|
||||
Duration,
|
||||
Path,
|
||||
Binary,
|
||||
|
||||
Row(BTreeMap<Column, TypeShape>),
|
||||
Table(Vec<TypeShape>),
|
||||
|
||||
// TODO: Block arguments
|
||||
Block,
|
||||
// TODO: Error type
|
||||
Error,
|
||||
|
||||
// Stream markers (used as bookend markers rather than actual values)
|
||||
BeginningOfStream,
|
||||
EndOfStream,
|
||||
}
|
||||
|
||||
impl TypeShape {
|
||||
pub fn from_primitive(primitive: &Primitive) -> TypeShape {
|
||||
match primitive {
|
||||
Primitive::Nothing => TypeShape::Nothing,
|
||||
Primitive::Int(_) => TypeShape::Int,
|
||||
Primitive::Decimal(_) => TypeShape::Decimal,
|
||||
Primitive::Bytes(_) => TypeShape::Bytesize,
|
||||
Primitive::String(_) => TypeShape::String,
|
||||
Primitive::ColumnPath(_) => TypeShape::ColumnPath,
|
||||
Primitive::Pattern(_) => TypeShape::Pattern,
|
||||
Primitive::Boolean(_) => TypeShape::Boolean,
|
||||
Primitive::Date(_) => TypeShape::Date,
|
||||
Primitive::Duration(_) => TypeShape::Duration,
|
||||
Primitive::Path(_) => TypeShape::Path,
|
||||
Primitive::Binary(_) => TypeShape::Binary,
|
||||
Primitive::BeginningOfStream => TypeShape::BeginningOfStream,
|
||||
Primitive::EndOfStream => TypeShape::EndOfStream,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_dictionary(dictionary: &Dictionary) -> TypeShape {
|
||||
let mut map = BTreeMap::new();
|
||||
|
||||
for (key, value) in dictionary.entries.iter() {
|
||||
let column = Column::String(key.clone());
|
||||
map.insert(column, TypeShape::from_value(&value.item));
|
||||
}
|
||||
|
||||
TypeShape::Row(map)
|
||||
}
|
||||
|
||||
pub fn from_table<'a>(table: impl IntoIterator<Item = &'a Value>) -> TypeShape {
|
||||
let mut vec = vec![];
|
||||
|
||||
for item in table.into_iter() {
|
||||
vec.push(TypeShape::from_value(item))
|
||||
}
|
||||
|
||||
TypeShape::Table(vec)
|
||||
}
|
||||
|
||||
pub fn from_value(value: &Value) -> TypeShape {
|
||||
match value {
|
||||
Value::Primitive(p) => TypeShape::from_primitive(p),
|
||||
Value::Row(row) => TypeShape::from_dictionary(row),
|
||||
Value::Table(table) => TypeShape::from_table(table.iter().map(|i| &i.item)),
|
||||
Value::Error(_) => TypeShape::Error,
|
||||
Value::Block(_) => TypeShape::Block,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for TypeShape {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
match self {
|
||||
TypeShape::Nothing => ty("nothing"),
|
||||
TypeShape::Int => ty("integer"),
|
||||
TypeShape::Decimal => ty("decimal"),
|
||||
TypeShape::Bytesize => ty("bytesize"),
|
||||
TypeShape::String => ty("string"),
|
||||
TypeShape::ColumnPath => ty("column-path"),
|
||||
TypeShape::Pattern => ty("pattern"),
|
||||
TypeShape::Boolean => ty("boolean"),
|
||||
TypeShape::Date => ty("date"),
|
||||
TypeShape::Duration => ty("duration"),
|
||||
TypeShape::Path => ty("path"),
|
||||
TypeShape::Binary => ty("binary"),
|
||||
TypeShape::Error => b::error("error"),
|
||||
TypeShape::BeginningOfStream => b::keyword("beginning-of-stream"),
|
||||
TypeShape::EndOfStream => b::keyword("end-of-stream"),
|
||||
TypeShape::Row(row) => (b::kind("row")
|
||||
+ b::space()
|
||||
+ b::intersperse(
|
||||
row.iter().map(|(key, ty)| {
|
||||
(b::key(match key {
|
||||
Column::String(string) => string.clone(),
|
||||
Column::Value => "<value>".to_string(),
|
||||
}) + b::delimit("(", ty.pretty_debug(), ")").as_kind())
|
||||
.nest()
|
||||
}),
|
||||
b::space(),
|
||||
)
|
||||
.nest())
|
||||
.nest(),
|
||||
|
||||
TypeShape::Table(table) => {
|
||||
let mut group: Group<DebugDoc, Vec<(usize, usize)>> = Group::new();
|
||||
|
||||
for (i, item) in table.iter().enumerate() {
|
||||
group.add(item.to_doc(), i);
|
||||
}
|
||||
|
||||
(b::kind("table") + b::space() + b::keyword("of")).group()
|
||||
+ b::space()
|
||||
+ (if group.len() == 1 {
|
||||
let (doc, _) = group.into_iter().nth(0).unwrap();
|
||||
DebugDocBuilder::from_doc(doc)
|
||||
} else {
|
||||
b::intersperse(
|
||||
group.into_iter().map(|(doc, rows)| {
|
||||
(b::intersperse(
|
||||
rows.iter().map(|(from, to)| {
|
||||
if from == to {
|
||||
b::description(from)
|
||||
} else {
|
||||
(b::description(from)
|
||||
+ b::space()
|
||||
+ b::keyword("to")
|
||||
+ b::space()
|
||||
+ b::description(to))
|
||||
.group()
|
||||
}
|
||||
}),
|
||||
b::description(", "),
|
||||
) + b::description(":")
|
||||
+ b::space()
|
||||
+ DebugDocBuilder::from_doc(doc))
|
||||
.nest()
|
||||
}),
|
||||
b::space(),
|
||||
)
|
||||
})
|
||||
}
|
||||
TypeShape::Block => ty("block"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, new)]
|
||||
struct DebugEntry<'a> {
|
||||
key: &'a Column,
|
||||
value: &'a TypeShape,
|
||||
}
|
||||
|
||||
impl<'a> PrettyDebug for DebugEntry<'a> {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
(b::key(match self.key {
|
||||
Column::String(string) => string.clone(),
|
||||
Column::Value => format!("<value>"),
|
||||
}) + b::delimit("(", self.value.pretty_debug(), ")").as_kind())
|
||||
}
|
||||
}
|
||||
|
||||
fn ty(name: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
b::kind(format!("{}", name))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum InlineShape {
|
||||
Nothing,
|
||||
Int(BigInt),
|
||||
Decimal(BigDecimal),
|
||||
Bytesize(u64),
|
||||
String(String),
|
||||
ColumnPath(ColumnPath),
|
||||
Pattern(String),
|
||||
Boolean(bool),
|
||||
Date(DateTime<Utc>),
|
||||
Duration(u64),
|
||||
Path(PathBuf),
|
||||
Binary,
|
||||
|
||||
Row(BTreeMap<Column, InlineShape>),
|
||||
Table(Vec<InlineShape>),
|
||||
|
||||
// TODO: Block arguments
|
||||
Block,
|
||||
// TODO: Error type
|
||||
Error,
|
||||
|
||||
// Stream markers (used as bookend markers rather than actual values)
|
||||
BeginningOfStream,
|
||||
EndOfStream,
|
||||
}
|
||||
|
||||
pub struct FormatInlineShape {
|
||||
shape: InlineShape,
|
||||
column: Option<Column>,
|
||||
}
|
||||
|
||||
impl InlineShape {
|
||||
pub fn from_primitive(primitive: &Primitive) -> InlineShape {
|
||||
match primitive {
|
||||
Primitive::Nothing => InlineShape::Nothing,
|
||||
Primitive::Int(int) => InlineShape::Int(int.clone()),
|
||||
Primitive::Decimal(decimal) => InlineShape::Decimal(decimal.clone()),
|
||||
Primitive::Bytes(bytesize) => InlineShape::Bytesize(*bytesize),
|
||||
Primitive::String(string) => InlineShape::String(string.clone()),
|
||||
Primitive::ColumnPath(path) => InlineShape::ColumnPath(path.clone()),
|
||||
Primitive::Pattern(pattern) => InlineShape::Pattern(pattern.clone()),
|
||||
Primitive::Boolean(boolean) => InlineShape::Boolean(*boolean),
|
||||
Primitive::Date(date) => InlineShape::Date(date.clone()),
|
||||
Primitive::Duration(duration) => InlineShape::Duration(*duration),
|
||||
Primitive::Path(path) => InlineShape::Path(path.clone()),
|
||||
Primitive::Binary(_) => InlineShape::Binary,
|
||||
Primitive::BeginningOfStream => InlineShape::BeginningOfStream,
|
||||
Primitive::EndOfStream => InlineShape::EndOfStream,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_dictionary(dictionary: &Dictionary) -> InlineShape {
|
||||
let mut map = BTreeMap::new();
|
||||
|
||||
for (key, value) in dictionary.entries.iter() {
|
||||
let column = Column::String(key.clone());
|
||||
map.insert(column, InlineShape::from_value(&value.item));
|
||||
}
|
||||
|
||||
InlineShape::Row(map)
|
||||
}
|
||||
|
||||
pub fn from_table<'a>(table: impl IntoIterator<Item = &'a Value>) -> InlineShape {
|
||||
let mut vec = vec![];
|
||||
|
||||
for item in table.into_iter() {
|
||||
vec.push(InlineShape::from_value(item))
|
||||
}
|
||||
|
||||
InlineShape::Table(vec)
|
||||
}
|
||||
|
||||
pub fn from_value(value: &Value) -> InlineShape {
|
||||
match value {
|
||||
Value::Primitive(p) => InlineShape::from_primitive(p),
|
||||
Value::Row(row) => InlineShape::from_dictionary(row),
|
||||
Value::Table(table) => InlineShape::from_table(table.iter().map(|i| &i.item)),
|
||||
Value::Error(_) => InlineShape::Error,
|
||||
Value::Block(_) => InlineShape::Block,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_for_column(self, column: impl Into<Column>) -> FormatInlineShape {
|
||||
FormatInlineShape {
|
||||
shape: self,
|
||||
column: Some(column.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(self) -> FormatInlineShape {
|
||||
FormatInlineShape {
|
||||
shape: self,
|
||||
column: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for FormatInlineShape {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
let column = &self.column;
|
||||
|
||||
match &self.shape {
|
||||
InlineShape::Nothing => b::blank(),
|
||||
InlineShape::Int(int) => b::primitive(format!("{}", int)),
|
||||
InlineShape::Decimal(decimal) => b::primitive(format!("{}", decimal)),
|
||||
InlineShape::Bytesize(bytesize) => {
|
||||
let byte = byte_unit::Byte::from_bytes(*bytesize as u128);
|
||||
|
||||
if byte.get_bytes() == 0u128 {
|
||||
return b::description("—".to_string());
|
||||
}
|
||||
|
||||
let byte = byte.get_appropriate_unit(false);
|
||||
|
||||
match byte.get_unit() {
|
||||
byte_unit::ByteUnit::B => {
|
||||
(b::primitive(format!("{}", byte.get_value())) + b::space() + b::kind("B"))
|
||||
.group()
|
||||
}
|
||||
_ => b::primitive(format!("{}", byte.format(1))),
|
||||
}
|
||||
}
|
||||
InlineShape::String(string) => b::primitive(format!("{}", string)),
|
||||
InlineShape::ColumnPath(path) => b::intersperse(
|
||||
path.iter().map(|member| member.pretty_debug()),
|
||||
b::keyword("."),
|
||||
),
|
||||
InlineShape::Pattern(pattern) => b::primitive(pattern),
|
||||
InlineShape::Boolean(boolean) => b::primitive(match (boolean, column) {
|
||||
(true, None) => format!("Yes"),
|
||||
(false, None) => format!("No"),
|
||||
(true, Some(Column::String(s))) if !s.is_empty() => format!("{}", s),
|
||||
(false, Some(Column::String(s))) if !s.is_empty() => format!(""),
|
||||
(true, Some(_)) => format!("Yes"),
|
||||
(false, Some(_)) => format!("No"),
|
||||
}),
|
||||
InlineShape::Date(date) => b::primitive(date.humanize()),
|
||||
InlineShape::Duration(duration) => {
|
||||
(b::kind("duration") + b::space() + b::primitive(duration)).group()
|
||||
}
|
||||
InlineShape::Path(path) => b::primitive(path.display()),
|
||||
InlineShape::Binary => b::opaque("<binary>"),
|
||||
InlineShape::Row(row) => b::delimit(
|
||||
"[",
|
||||
b::kind("row")
|
||||
+ b::space()
|
||||
+ b::intersperse(
|
||||
row.keys().map(|key| match key {
|
||||
Column::String(string) => b::description(string),
|
||||
Column::Value => b::blank(),
|
||||
}),
|
||||
b::space(),
|
||||
),
|
||||
"]",
|
||||
)
|
||||
.group(),
|
||||
InlineShape::Table(rows) => b::delimit(
|
||||
"[",
|
||||
b::kind("table")
|
||||
+ b::space()
|
||||
+ b::primitive(rows.len())
|
||||
+ b::space()
|
||||
+ b::description("rows"),
|
||||
"]",
|
||||
)
|
||||
.group(),
|
||||
InlineShape::Block => b::opaque("block"),
|
||||
InlineShape::Error => b::error("error"),
|
||||
InlineShape::BeginningOfStream => b::blank(),
|
||||
InlineShape::EndOfStream => b::blank(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GroupedValue: Debug + Clone {
|
||||
type Item;
|
||||
|
||||
fn new() -> Self;
|
||||
fn merge(&mut self, value: Self::Item);
|
||||
}
|
||||
|
||||
impl GroupedValue for Vec<(usize, usize)> {
|
||||
type Item = usize;
|
||||
|
||||
fn new() -> Vec<(usize, usize)> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn merge(&mut self, new_value: usize) {
|
||||
match self.last_mut() {
|
||||
Some(value) if value.1 == new_value - 1 => {
|
||||
value.1 += 1;
|
||||
}
|
||||
|
||||
_ => self.push((new_value, new_value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Group<K: Debug + Eq + Hash, V: GroupedValue> {
|
||||
values: indexmap::IndexMap<K, V>,
|
||||
}
|
||||
|
||||
impl<K, G> Group<K, G>
|
||||
where
|
||||
K: Debug + Eq + Hash,
|
||||
G: GroupedValue,
|
||||
{
|
||||
pub fn new() -> Group<K, G> {
|
||||
Group {
|
||||
values: indexmap::IndexMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (K, G)> {
|
||||
self.values.into_iter()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, key: impl Into<K>, value: impl Into<G::Item>) {
|
||||
let key = key.into();
|
||||
let value = value.into();
|
||||
|
||||
let group = self.values.get_mut(&key);
|
||||
|
||||
match group {
|
||||
None => {
|
||||
self.values.insert(key, {
|
||||
let mut group = G::new();
|
||||
group.merge(value.into());
|
||||
group
|
||||
});
|
||||
}
|
||||
Some(group) => {
|
||||
group.merge(value.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum Column {
|
||||
String(String),
|
||||
Value,
|
||||
}
|
||||
|
||||
impl Into<Column> for String {
|
||||
fn into(self) -> Column {
|
||||
Column::String(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Column> for &String {
|
||||
fn into(self) -> Column {
|
||||
Column::String(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Column> for &str {
|
||||
fn into(self) -> Column {
|
||||
Column::String(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum Shape {
|
||||
Primitive(&'static str),
|
||||
Row(Vec<Column>),
|
||||
Table { from: usize, to: usize },
|
||||
Error(ShellError),
|
||||
Block(Block),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn shape(&self) -> Shape {
|
||||
Shape::for_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
pub fn for_value(value: &Value) -> Shape {
|
||||
match value {
|
||||
Value::Primitive(p) => Shape::Primitive(p.type_name()),
|
||||
Value::Row(row) => Shape::for_dict(row),
|
||||
Value::Table(table) => Shape::Table {
|
||||
from: 0,
|
||||
to: table.len(),
|
||||
},
|
||||
Value::Error(error) => Shape::Error(error.clone()),
|
||||
Value::Block(block) => Shape::Block(block.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn for_dict(dict: &Dictionary) -> Shape {
|
||||
Shape::Row(dict.keys().map(|key| Column::String(key.clone())).collect())
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> String {
|
||||
match self {
|
||||
Shape::Primitive(primitive) => primitive,
|
||||
Shape::Row(row) => {
|
||||
return row
|
||||
.iter()
|
||||
.map(|c| match c {
|
||||
Column::String(s) => s.clone(),
|
||||
Column::Value => format!("<value>"),
|
||||
})
|
||||
.join(", ")
|
||||
}
|
||||
Shape::Table { .. } => "table",
|
||||
Shape::Error(_) => "error",
|
||||
Shape::Block(_) => "block",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn describe_str(&self) -> String {
|
||||
let mut v = vec![];
|
||||
self.describe(&mut v)
|
||||
.expect("it isn't possible to fail to write into a memory buffer");
|
||||
String::from_utf8_lossy(&v[..]).to_string()
|
||||
}
|
||||
|
||||
pub fn describe(&self, w: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
match self {
|
||||
Shape::Primitive(desc) => write!(w, "[{}]", desc),
|
||||
Shape::Row(d) => write!(
|
||||
w,
|
||||
"[row: {}]",
|
||||
d.iter()
|
||||
.map(|c| match c {
|
||||
Column::String(s) => s.clone(),
|
||||
Column::Value => format!("<value>"),
|
||||
})
|
||||
.join(", ")
|
||||
),
|
||||
Shape::Table { to, .. } => {
|
||||
if *to == 1 {
|
||||
write!(w, "[table: {} row]", to)
|
||||
} else {
|
||||
write!(w, "[table: {} rows]", to)
|
||||
}
|
||||
}
|
||||
Shape::Error(_) => write!(w, "[error]"),
|
||||
Shape::Block(_) => write!(w, "[block]"),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
let mut out = vec![];
|
||||
self.describe(&mut out)
|
||||
.expect("Writing into a Vec can't fail");
|
||||
let string = String::from_utf8_lossy(&out);
|
||||
|
||||
Value::string(string)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Shapes {
|
||||
shapes: IndexMap<Shape, Vec<usize>>,
|
||||
}
|
||||
|
||||
impl Shapes {
|
||||
pub fn new() -> Shapes {
|
||||
Shapes {
|
||||
shapes: IndexMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, value: &Value, row: usize) {
|
||||
let shape = Shape::for_value(value);
|
||||
|
||||
self.shapes
|
||||
.entry(shape)
|
||||
.and_modify(|indexes| indexes.push(row))
|
||||
.or_insert_with(|| vec![row]);
|
||||
}
|
||||
|
||||
pub fn to_values(&self) -> Vec<Tagged<Value>> {
|
||||
if self.shapes.len() == 1 {
|
||||
let shape = self.shapes.keys().nth(0).unwrap();
|
||||
|
||||
vec![dict! {
|
||||
"type" => shape.to_value(),
|
||||
"rows" => Value::string("all")
|
||||
}]
|
||||
} else {
|
||||
self.shapes
|
||||
.iter()
|
||||
.map(|(shape, rows)| {
|
||||
let rows = rows.iter().map(|i| i.to_string()).join(", ");
|
||||
|
||||
dict! {
|
||||
"type" => shape.to_value(),
|
||||
"rows" => Value::string(format!("[ {} ]", rows))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
@ -98,7 +98,7 @@ pub fn read(
|
||||
Value::Row(Dictionary { entries }) => Ok(entries),
|
||||
other => Err(ShellError::type_error(
|
||||
"Dictionary",
|
||||
other.type_name().tagged(&tag),
|
||||
other.type_name().spanned(tag.span),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,69 @@
|
||||
use crate::data::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use crate::traits::{DebugDocBuilder as b, PrettyDebug};
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use indexmap::IndexMap;
|
||||
use pretty::{BoxAllocator, DocAllocator};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, new)]
|
||||
#[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, Getters, new)]
|
||||
pub struct Dictionary {
|
||||
#[get = "pub"]
|
||||
pub entries: IndexMap<String, Tagged<Value>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, new)]
|
||||
struct DebugEntry<'a> {
|
||||
key: &'a str,
|
||||
value: &'a Tagged<Value>,
|
||||
}
|
||||
|
||||
impl<'a> PrettyDebug for DebugEntry<'a> {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
(b::key(self.key.to_string()) + b::equals() + self.value.item.pretty_debug().as_value())
|
||||
.group()
|
||||
// BoxAllocator
|
||||
// .text(self.key.to_string())
|
||||
// .annotate(ShellAnnotation::style("key"))
|
||||
// .append(
|
||||
// BoxAllocator
|
||||
// .text("=")
|
||||
// .annotate(ShellAnnotation::style("equals")),
|
||||
// )
|
||||
// .append({
|
||||
// self.value
|
||||
// .item
|
||||
// .pretty_debug()
|
||||
// .inner
|
||||
// .annotate(ShellAnnotation::style("value"))
|
||||
// })
|
||||
// .group()
|
||||
// .into()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for Dictionary {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
BoxAllocator
|
||||
.text("(")
|
||||
.append(
|
||||
BoxAllocator
|
||||
.intersperse(
|
||||
self.entries()
|
||||
.iter()
|
||||
.map(|(key, value)| DebugEntry::new(key, value).to_doc()),
|
||||
BoxAllocator.space(),
|
||||
)
|
||||
.nest(1)
|
||||
.group(),
|
||||
)
|
||||
.append(BoxAllocator.text(")"))
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Dictionary {
|
||||
fn partial_cmp(&self, other: &Dictionary) -> Option<Ordering> {
|
||||
let this: Vec<&String> = self.entries.keys().collect();
|
||||
@ -78,15 +131,23 @@ impl Dictionary {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_by_key(&self, name: &str) -> Option<&Tagged<Value>> {
|
||||
match self
|
||||
pub fn keys(&self) -> impl Iterator<Item = &String> {
|
||||
self.entries.keys()
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_by_key(&self, name: Spanned<&str>) -> Option<Tagged<Value>> {
|
||||
let result = self
|
||||
.entries
|
||||
.iter()
|
||||
.find(|(desc_name, _)| *desc_name == name)
|
||||
{
|
||||
Some((_, v)) => Some(v),
|
||||
None => None,
|
||||
}
|
||||
.find(|(desc_name, _)| *desc_name == name.item)?
|
||||
.1;
|
||||
|
||||
Some(
|
||||
result
|
||||
.item
|
||||
.clone()
|
||||
.tagged(Tag::new(result.anchor(), name.span)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut_data_by_key(&mut self, name: &str) -> Option<&mut Tagged<Value>> {
|
||||
@ -100,14 +161,8 @@ impl Dictionary {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut debug = f.debug_struct("Dictionary");
|
||||
|
||||
for (desc, value) in self.entries.iter() {
|
||||
debug.field(desc, &value.debug());
|
||||
}
|
||||
|
||||
debug.finish()
|
||||
pub(crate) fn insert_data_at_key(&mut self, name: &str, value: Tagged<Value>) {
|
||||
self.entries.insert(name.to_string(), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,6 +213,12 @@ impl TaggedDictBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(tag: impl Into<Tag>, block: impl FnOnce(&mut TaggedDictBuilder)) -> Tagged<Value> {
|
||||
let mut builder = TaggedDictBuilder::new(tag);
|
||||
block(&mut builder);
|
||||
builder.into_tagged_value()
|
||||
}
|
||||
|
||||
pub fn with_capacity(tag: impl Into<Tag>, n: usize) -> TaggedDictBuilder {
|
||||
TaggedDictBuilder {
|
||||
tag: tag.into(),
|
||||
|
@ -23,6 +23,21 @@ impl<T> Spanned<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Spanned<String> {
|
||||
pub fn items<'a, U>(
|
||||
items: impl Iterator<Item = &'a Spanned<String>>,
|
||||
) -> impl Iterator<Item = &'a str> {
|
||||
items.into_iter().map(|item| &item.item[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl Spanned<String> {
|
||||
pub fn borrow_spanned(&self) -> Spanned<&str> {
|
||||
let span = self.span;
|
||||
self.item[..].spanned(span)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SpannedItem: Sized {
|
||||
fn spanned(self, span: impl Into<Span>) -> Spanned<Self> {
|
||||
Spanned {
|
||||
@ -53,6 +68,17 @@ pub struct Tagged<T> {
|
||||
pub item: T,
|
||||
}
|
||||
|
||||
impl Tagged<String> {
|
||||
pub fn borrow_spanned(&self) -> Spanned<&str> {
|
||||
let span = self.tag.span;
|
||||
self.item[..].spanned(span)
|
||||
}
|
||||
|
||||
pub fn borrow_tagged(&self) -> Tagged<&str> {
|
||||
self.item[..].tagged(self.tag.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> HasTag for Tagged<T> {
|
||||
fn tag(&self) -> Tag {
|
||||
self.tag.clone()
|
||||
@ -116,6 +142,13 @@ impl<T> Tagged<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transpose(&self) -> Tagged<&T> {
|
||||
Tagged {
|
||||
item: &self.item,
|
||||
tag: self.tag.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> Tag {
|
||||
self.tag.clone()
|
||||
}
|
||||
@ -362,6 +395,23 @@ pub fn tag_for_tagged_list(mut iter: impl Iterator<Item = Tag>) -> Tag {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn span_for_spanned_list(mut iter: impl Iterator<Item = Span>) -> Span {
|
||||
let first = iter.next();
|
||||
|
||||
let first = match first {
|
||||
None => return Span::unknown(),
|
||||
Some(first) => first,
|
||||
};
|
||||
|
||||
let last = iter.last();
|
||||
|
||||
match last {
|
||||
None => first,
|
||||
Some(last) => first.until(last),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
pub struct Span {
|
||||
start: usize,
|
||||
|
@ -27,7 +27,10 @@ impl ExtractType for bool {
|
||||
item: Value::Primitive(Primitive::Nothing),
|
||||
..
|
||||
} => Ok(false),
|
||||
other => Err(ShellError::type_error("Boolean", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"Boolean",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,7 +44,10 @@ impl ExtractType for std::path::PathBuf {
|
||||
item: Value::Primitive(Primitive::Path(p)),
|
||||
..
|
||||
} => Ok(p.clone()),
|
||||
other => Err(ShellError::type_error("Path", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"Path",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,7 +61,10 @@ impl ExtractType for i64 {
|
||||
item: Value::Primitive(Primitive::Int(int)),
|
||||
..
|
||||
} => Ok(int.tagged(&value.tag).coerce_into("converting to i64")?),
|
||||
other => Err(ShellError::type_error("Integer", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"Integer",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,7 +78,10 @@ impl ExtractType for u64 {
|
||||
item: Value::Primitive(Primitive::Int(int)),
|
||||
..
|
||||
} => Ok(int.tagged(&value.tag).coerce_into("converting to u64")?),
|
||||
other => Err(ShellError::type_error("Integer", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"Integer",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,7 +95,10 @@ impl ExtractType for String {
|
||||
item: Value::Primitive(Primitive::String(string)),
|
||||
..
|
||||
} => Ok(string.clone()),
|
||||
other => Err(ShellError::type_error("String", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"String",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
19
src/env/host.rs
vendored
19
src/env/host.rs
vendored
@ -11,6 +11,8 @@ pub trait Host: Debug + Send {
|
||||
|
||||
fn stdout(&mut self, out: &str);
|
||||
fn stderr(&mut self, out: &str);
|
||||
|
||||
fn width(&self) -> usize;
|
||||
}
|
||||
|
||||
impl Host for Box<dyn Host> {
|
||||
@ -37,6 +39,10 @@ impl Host for Box<dyn Host> {
|
||||
fn err_termcolor(&self) -> termcolor::StandardStream {
|
||||
(**self).err_termcolor()
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
(**self).width()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -53,24 +59,29 @@ impl Host for BasicHost {
|
||||
|
||||
fn stdout(&mut self, out: &str) {
|
||||
match out {
|
||||
"\n" => println!(""),
|
||||
other => println!("{}", other),
|
||||
"\n" => outln!(""),
|
||||
other => outln!("{}", other),
|
||||
}
|
||||
}
|
||||
|
||||
fn stderr(&mut self, out: &str) {
|
||||
match out {
|
||||
"\n" => eprintln!(""),
|
||||
other => eprintln!("{}", other),
|
||||
"\n" => errln!(""),
|
||||
other => errln!("{}", other),
|
||||
}
|
||||
}
|
||||
|
||||
fn out_termcolor(&self) -> termcolor::StandardStream {
|
||||
termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto)
|
||||
}
|
||||
|
||||
fn err_termcolor(&self) -> termcolor::StandardStream {
|
||||
termcolor::StandardStream::stderr(termcolor::ColorChoice::Auto)
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
std::cmp::max(textwrap::termwidth(), 20)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_unexpected<T>(
|
||||
|
296
src/errors.rs
296
src/errors.rs
@ -6,13 +6,20 @@ use derive_new::new;
|
||||
use language_reporting::{Diagnostic, Label, Severity};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
pub enum Description {
|
||||
Source(Tagged<String>),
|
||||
Source(Spanned<String>),
|
||||
Synthetic(String),
|
||||
}
|
||||
|
||||
impl<T: Into<String>> Into<Description> for Spanned<T> {
|
||||
fn into(self) -> Description {
|
||||
Description::Source(self.map(|s| s.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Description {
|
||||
fn into_label(self) -> Result<Label<Span>, String> {
|
||||
match self {
|
||||
@ -20,72 +27,53 @@ impl Description {
|
||||
Description::Synthetic(s) => Err(s),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn tag(&self) -> Tag {
|
||||
match self {
|
||||
Description::Source(tagged) => tagged.tag.clone(),
|
||||
Description::Synthetic(_) => Tag::unknown(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ParseErrorReason {
|
||||
Eof {
|
||||
expected: &'static str,
|
||||
span: Span,
|
||||
},
|
||||
Mismatch {
|
||||
expected: &'static str,
|
||||
actual: Tagged<String>,
|
||||
actual: Spanned<String>,
|
||||
},
|
||||
ArgumentError {
|
||||
command: String,
|
||||
command: Spanned<String>,
|
||||
error: ArgumentError,
|
||||
tag: Tag,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParseError {
|
||||
reason: ParseErrorReason,
|
||||
tag: Tag,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn unexpected_eof(expected: &'static str, span: Span) -> ParseError {
|
||||
ParseError {
|
||||
reason: ParseErrorReason::Eof { expected },
|
||||
tag: span.into(),
|
||||
reason: ParseErrorReason::Eof { expected, span },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mismatch(expected: &'static str, actual: Tagged<impl Into<String>>) -> ParseError {
|
||||
let Tagged { tag, item } = actual;
|
||||
pub fn mismatch(expected: &'static str, actual: Spanned<impl Into<String>>) -> ParseError {
|
||||
let Spanned { span, item } = actual;
|
||||
|
||||
ParseError {
|
||||
reason: ParseErrorReason::Mismatch {
|
||||
expected,
|
||||
actual: item.into().tagged(tag.clone()),
|
||||
actual: item.into().spanned(span),
|
||||
},
|
||||
tag,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn argument_error(
|
||||
command: impl Into<String>,
|
||||
kind: ArgumentError,
|
||||
tag: impl Into<Tag>,
|
||||
) -> ParseError {
|
||||
let tag = tag.into();
|
||||
|
||||
pub fn argument_error(command: Spanned<impl Into<String>>, kind: ArgumentError) -> ParseError {
|
||||
ParseError {
|
||||
reason: ParseErrorReason::ArgumentError {
|
||||
command: command.into(),
|
||||
command: command.item.into().spanned(command.span),
|
||||
error: kind,
|
||||
tag: tag.clone(),
|
||||
},
|
||||
tag: tag.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,20 +81,18 @@ impl ParseError {
|
||||
impl From<ParseError> for ShellError {
|
||||
fn from(error: ParseError) -> ShellError {
|
||||
match error.reason {
|
||||
ParseErrorReason::Eof { expected } => ShellError::unexpected_eof(expected, error.tag),
|
||||
ParseErrorReason::Eof { expected, span } => ShellError::unexpected_eof(expected, span),
|
||||
ParseErrorReason::Mismatch { actual, expected } => {
|
||||
ShellError::type_error(expected, actual.clone())
|
||||
}
|
||||
ParseErrorReason::ArgumentError {
|
||||
command,
|
||||
error,
|
||||
tag,
|
||||
} => ShellError::argument_error(command, error, tag),
|
||||
ParseErrorReason::ArgumentError { command, error } => {
|
||||
ShellError::argument_error(command, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, Hash, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum ArgumentError {
|
||||
MissingMandatoryFlag(String),
|
||||
MissingMandatoryPositional(String),
|
||||
@ -114,19 +100,12 @@ pub enum ArgumentError {
|
||||
InvalidExternalWord,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize, Hash)]
|
||||
pub struct ShellError {
|
||||
error: ProximateShellError,
|
||||
cause: Option<Box<ProximateShellError>>,
|
||||
}
|
||||
|
||||
impl ShellError {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn tag(&self) -> Option<Tag> {
|
||||
self.error.tag()
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for ShellError {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
self.error.fmt_debug(f, source)
|
||||
@ -145,7 +124,7 @@ impl serde::de::Error for ShellError {
|
||||
impl ShellError {
|
||||
pub fn type_error(
|
||||
expected: impl Into<String>,
|
||||
actual: Tagged<impl Into<String>>,
|
||||
actual: Spanned<impl Into<String>>,
|
||||
) -> ShellError {
|
||||
ProximateShellError::TypeError {
|
||||
expected: expected.into(),
|
||||
@ -154,6 +133,28 @@ impl ShellError {
|
||||
.start()
|
||||
}
|
||||
|
||||
pub fn missing_property(
|
||||
subpath: Spanned<impl Into<String>>,
|
||||
expr: Spanned<impl Into<String>>,
|
||||
) -> ShellError {
|
||||
ProximateShellError::MissingProperty {
|
||||
subpath: subpath.into(),
|
||||
expr: expr.into(),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
pub fn invalid_integer_index(
|
||||
subpath: Spanned<impl Into<String>>,
|
||||
integer: impl Into<Span>,
|
||||
) -> ShellError {
|
||||
ProximateShellError::InvalidIntegerIndex {
|
||||
subpath: subpath.into(),
|
||||
integer: integer.into(),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
pub fn untagged_runtime_error(error: impl Into<String>) -> ShellError {
|
||||
ProximateShellError::UntaggedRuntimeError {
|
||||
reason: error.into(),
|
||||
@ -161,10 +162,10 @@ impl ShellError {
|
||||
.start()
|
||||
}
|
||||
|
||||
pub(crate) fn unexpected_eof(expected: impl Into<String>, tag: impl Into<Tag>) -> ShellError {
|
||||
pub(crate) fn unexpected_eof(expected: impl Into<String>, span: impl Into<Span>) -> ShellError {
|
||||
ProximateShellError::UnexpectedEof {
|
||||
expected: expected.into(),
|
||||
tag: tag.into(),
|
||||
span: span.into(),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
@ -172,34 +173,26 @@ impl ShellError {
|
||||
pub(crate) fn range_error(
|
||||
expected: impl Into<ExpectedRange>,
|
||||
actual: &Tagged<impl fmt::Debug>,
|
||||
operation: String,
|
||||
operation: impl Into<String>,
|
||||
) -> ShellError {
|
||||
ProximateShellError::RangeError {
|
||||
kind: expected.into(),
|
||||
actual_kind: format!("{:?}", actual.item).tagged(actual.tag()),
|
||||
operation,
|
||||
actual_kind: format!("{:?}", actual.item).spanned(actual.span()),
|
||||
operation: operation.into(),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
pub(crate) fn syntax_error(problem: Tagged<impl Into<String>>) -> ShellError {
|
||||
pub(crate) fn syntax_error(problem: Spanned<impl Into<String>>) -> ShellError {
|
||||
ProximateShellError::SyntaxError {
|
||||
problem: problem.map(|p| p.into()),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn invalid_command(problem: impl Into<Tag>) -> ShellError {
|
||||
ProximateShellError::InvalidCommand {
|
||||
command: problem.into(),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
pub(crate) fn coerce_error(
|
||||
left: Tagged<impl Into<String>>,
|
||||
right: Tagged<impl Into<String>>,
|
||||
left: Spanned<impl Into<String>>,
|
||||
right: Spanned<impl Into<String>>,
|
||||
) -> ShellError {
|
||||
ProximateShellError::CoerceError {
|
||||
left: left.map(|l| l.into()),
|
||||
@ -208,23 +201,21 @@ impl ShellError {
|
||||
.start()
|
||||
}
|
||||
|
||||
pub(crate) fn missing_value(tag: Option<Tag>, reason: impl Into<String>) -> ShellError {
|
||||
pub(crate) fn missing_value(span: Option<Span>, reason: impl Into<String>) -> ShellError {
|
||||
ProximateShellError::MissingValue {
|
||||
tag,
|
||||
span,
|
||||
reason: reason.into(),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
pub(crate) fn argument_error(
|
||||
command: impl Into<String>,
|
||||
command: Spanned<impl Into<String>>,
|
||||
kind: ArgumentError,
|
||||
tag: impl Into<Tag>,
|
||||
) -> ShellError {
|
||||
ProximateShellError::ArgumentError {
|
||||
command: command.into(),
|
||||
command: command.map(|c| c.into()),
|
||||
error: kind,
|
||||
tag: tag.into(),
|
||||
}
|
||||
.start()
|
||||
}
|
||||
@ -262,18 +253,14 @@ impl ShellError {
|
||||
|
||||
pub(crate) fn to_diagnostic(self) -> Diagnostic<Span> {
|
||||
match self.error {
|
||||
ProximateShellError::InvalidCommand { command } => {
|
||||
Diagnostic::new(Severity::Error, "Invalid command")
|
||||
.with_label(Label::new_primary(command.span))
|
||||
}
|
||||
ProximateShellError::MissingValue { tag, reason } => {
|
||||
ProximateShellError::MissingValue { span, reason } => {
|
||||
let mut d = Diagnostic::new(
|
||||
Severity::Bug,
|
||||
format!("Internal Error (missing value) :: {}", reason),
|
||||
);
|
||||
|
||||
if let Some(tag) = tag {
|
||||
d = d.with_label(Label::new_primary(tag.span));
|
||||
if let Some(span) = span {
|
||||
d = d.with_label(Label::new_primary(span));
|
||||
}
|
||||
|
||||
d
|
||||
@ -281,80 +268,79 @@ impl ShellError {
|
||||
ProximateShellError::ArgumentError {
|
||||
command,
|
||||
error,
|
||||
tag,
|
||||
} => match error {
|
||||
ArgumentError::InvalidExternalWord => Diagnostic::new(
|
||||
Severity::Error,
|
||||
format!("Invalid bare word for Nu command (did you intend to invoke an external command?)"))
|
||||
.with_label(Label::new_primary(tag.span)),
|
||||
.with_label(Label::new_primary(command.span)),
|
||||
ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new(
|
||||
Severity::Error,
|
||||
format!(
|
||||
"{} requires {}{}",
|
||||
Color::Cyan.paint(command),
|
||||
Color::Cyan.paint(&command.item),
|
||||
Color::Black.bold().paint("--"),
|
||||
Color::Black.bold().paint(name)
|
||||
),
|
||||
)
|
||||
.with_label(Label::new_primary(tag.span)),
|
||||
.with_label(Label::new_primary(command.span)),
|
||||
ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new(
|
||||
Severity::Error,
|
||||
format!(
|
||||
"{} requires {} parameter",
|
||||
Color::Cyan.paint(command),
|
||||
Color::Cyan.paint(&command.item),
|
||||
Color::Green.bold().paint(name.clone())
|
||||
),
|
||||
)
|
||||
.with_label(
|
||||
Label::new_primary(tag.span).with_message(format!("requires {} parameter", name)),
|
||||
Label::new_primary(command.span).with_message(format!("requires {} parameter", name)),
|
||||
),
|
||||
ArgumentError::MissingValueForName(name) => Diagnostic::new(
|
||||
Severity::Error,
|
||||
format!(
|
||||
"{} is missing value for flag {}{}",
|
||||
Color::Cyan.paint(command),
|
||||
Color::Cyan.paint(&command.item),
|
||||
Color::Black.bold().paint("--"),
|
||||
Color::Black.bold().paint(name)
|
||||
),
|
||||
)
|
||||
.with_label(Label::new_primary(tag.span)),
|
||||
.with_label(Label::new_primary(command.span)),
|
||||
},
|
||||
ProximateShellError::TypeError {
|
||||
expected,
|
||||
actual:
|
||||
Tagged {
|
||||
Spanned {
|
||||
item: Some(actual),
|
||||
tag,
|
||||
span,
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Type Error").with_label(
|
||||
Label::new_primary(tag.span)
|
||||
Label::new_primary(span)
|
||||
.with_message(format!("Expected {}, found {}", expected, actual)),
|
||||
),
|
||||
ProximateShellError::TypeError {
|
||||
expected,
|
||||
actual:
|
||||
Tagged {
|
||||
Spanned {
|
||||
item: None,
|
||||
tag
|
||||
span
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Type Error")
|
||||
.with_label(Label::new_primary(tag.span).with_message(expected)),
|
||||
.with_label(Label::new_primary(span).with_message(expected)),
|
||||
|
||||
ProximateShellError::UnexpectedEof {
|
||||
expected, tag
|
||||
expected, span
|
||||
} => Diagnostic::new(Severity::Error, format!("Unexpected end of input"))
|
||||
.with_label(Label::new_primary(tag.span).with_message(format!("Expected {}", expected))),
|
||||
.with_label(Label::new_primary(span).with_message(format!("Expected {}", expected))),
|
||||
|
||||
ProximateShellError::RangeError {
|
||||
kind,
|
||||
operation,
|
||||
actual_kind:
|
||||
Tagged {
|
||||
Spanned {
|
||||
item,
|
||||
tag
|
||||
span
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Range Error").with_label(
|
||||
Label::new_primary(tag.span).with_message(format!(
|
||||
Label::new_primary(span).with_message(format!(
|
||||
"Expected to convert {} to {} while {}, but it was out of range",
|
||||
item,
|
||||
kind.desc(),
|
||||
@ -364,12 +350,12 @@ impl ShellError {
|
||||
|
||||
ProximateShellError::SyntaxError {
|
||||
problem:
|
||||
Tagged {
|
||||
tag,
|
||||
Spanned {
|
||||
span,
|
||||
item
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Syntax Error")
|
||||
.with_label(Label::new_primary(tag.span).with_message(item)),
|
||||
.with_label(Label::new_primary(span).with_message(item)),
|
||||
|
||||
ProximateShellError::MissingProperty { subpath, expr, .. } => {
|
||||
let subpath = subpath.into_label();
|
||||
@ -389,11 +375,26 @@ impl ShellError {
|
||||
diag
|
||||
}
|
||||
|
||||
ProximateShellError::InvalidIntegerIndex { subpath,integer } => {
|
||||
let subpath = subpath.into_label();
|
||||
|
||||
let mut diag = Diagnostic::new(Severity::Error, "Invalid integer property");
|
||||
|
||||
match subpath {
|
||||
Ok(label) => diag = diag.with_label(label),
|
||||
Err(ty) => diag.message = format!("Invalid integer property (for {})", ty)
|
||||
}
|
||||
|
||||
diag = diag.with_label(Label::new_secondary(integer).with_message("integer"));
|
||||
|
||||
diag
|
||||
}
|
||||
|
||||
ProximateShellError::Diagnostic(diag) => diag.diagnostic,
|
||||
ProximateShellError::CoerceError { left, right } => {
|
||||
Diagnostic::new(Severity::Error, "Coercion error")
|
||||
.with_label(Label::new_primary(left.tag().span).with_message(left.item))
|
||||
.with_label(Label::new_secondary(right.tag().span).with_message(right.item))
|
||||
.with_label(Label::new_primary(left.span).with_message(left.item))
|
||||
.with_label(Label::new_secondary(right.span).with_message(right.item))
|
||||
}
|
||||
|
||||
ProximateShellError::UntaggedRuntimeError { reason } => Diagnostic::new(Severity::Error, format!("Error: {}", reason))
|
||||
@ -447,7 +448,7 @@ impl ShellError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Serialize, Deserialize)]
|
||||
pub enum ExpectedRange {
|
||||
I8,
|
||||
I16,
|
||||
@ -461,12 +462,24 @@ pub enum ExpectedRange {
|
||||
U128,
|
||||
F32,
|
||||
F64,
|
||||
Usize,
|
||||
Size,
|
||||
BigInt,
|
||||
BigDecimal,
|
||||
Range { start: usize, end: usize },
|
||||
}
|
||||
|
||||
impl From<Range<usize>> for ExpectedRange {
|
||||
fn from(range: Range<usize>) -> Self {
|
||||
ExpectedRange::Range {
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpectedRange {
|
||||
fn desc(&self) -> &'static str {
|
||||
fn desc(&self) -> String {
|
||||
match self {
|
||||
ExpectedRange::I8 => "an 8-bit signed integer",
|
||||
ExpectedRange::I16 => "a 16-bit signed integer",
|
||||
@ -480,51 +493,54 @@ impl ExpectedRange {
|
||||
ExpectedRange::U128 => "a 128-bit unsigned integer",
|
||||
ExpectedRange::F32 => "a 32-bit float",
|
||||
ExpectedRange::F64 => "a 64-bit float",
|
||||
ExpectedRange::Usize => "an list index",
|
||||
ExpectedRange::Size => "a list offset",
|
||||
ExpectedRange::BigDecimal => "a decimal",
|
||||
ExpectedRange::BigInt => "an integer",
|
||||
ExpectedRange::Range { start, end } => return format!("{} to {}", start, end),
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
pub enum ProximateShellError {
|
||||
SyntaxError {
|
||||
problem: Tagged<String>,
|
||||
problem: Spanned<String>,
|
||||
},
|
||||
UnexpectedEof {
|
||||
expected: String,
|
||||
tag: Tag,
|
||||
},
|
||||
InvalidCommand {
|
||||
command: Tag,
|
||||
span: Span,
|
||||
},
|
||||
TypeError {
|
||||
expected: String,
|
||||
actual: Tagged<Option<String>>,
|
||||
actual: Spanned<Option<String>>,
|
||||
},
|
||||
MissingProperty {
|
||||
subpath: Description,
|
||||
expr: Description,
|
||||
tag: Tag,
|
||||
},
|
||||
InvalidIntegerIndex {
|
||||
subpath: Description,
|
||||
integer: Span,
|
||||
},
|
||||
MissingValue {
|
||||
tag: Option<Tag>,
|
||||
span: Option<Span>,
|
||||
reason: String,
|
||||
},
|
||||
ArgumentError {
|
||||
command: String,
|
||||
command: Spanned<String>,
|
||||
error: ArgumentError,
|
||||
tag: Tag,
|
||||
},
|
||||
RangeError {
|
||||
kind: ExpectedRange,
|
||||
actual_kind: Tagged<String>,
|
||||
actual_kind: Spanned<String>,
|
||||
operation: String,
|
||||
},
|
||||
Diagnostic(ShellDiagnostic),
|
||||
CoerceError {
|
||||
left: Tagged<String>,
|
||||
right: Tagged<String>,
|
||||
left: Spanned<String>,
|
||||
right: Spanned<String>,
|
||||
},
|
||||
UntaggedRuntimeError {
|
||||
reason: String,
|
||||
@ -539,21 +555,22 @@ impl ProximateShellError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tag(&self) -> Option<Tag> {
|
||||
Some(match self {
|
||||
ProximateShellError::SyntaxError { problem } => problem.tag(),
|
||||
ProximateShellError::UnexpectedEof { tag, .. } => tag.clone(),
|
||||
ProximateShellError::InvalidCommand { command } => command.clone(),
|
||||
ProximateShellError::TypeError { actual, .. } => actual.tag.clone(),
|
||||
ProximateShellError::MissingProperty { tag, .. } => tag.clone(),
|
||||
ProximateShellError::MissingValue { tag, .. } => return tag.clone(),
|
||||
ProximateShellError::ArgumentError { tag, .. } => tag.clone(),
|
||||
ProximateShellError::RangeError { actual_kind, .. } => actual_kind.tag.clone(),
|
||||
ProximateShellError::Diagnostic(..) => return None,
|
||||
ProximateShellError::UntaggedRuntimeError { .. } => return None,
|
||||
ProximateShellError::CoerceError { left, right } => left.tag.until(&right.tag),
|
||||
})
|
||||
}
|
||||
// pub(crate) fn tag(&self) -> Option<Tag> {
|
||||
// Some(match self {
|
||||
// ProximateShellError::SyntaxError { problem } => problem.tag(),
|
||||
// ProximateShellError::UnexpectedEof { tag, .. } => tag.clone(),
|
||||
// ProximateShellError::InvalidCommand { command } => command.clone(),
|
||||
// ProximateShellError::TypeError { actual, .. } => actual.tag.clone(),
|
||||
// ProximateShellError::MissingProperty { tag, .. } => tag.clone(),
|
||||
// ProximateShellError::MissingValue { tag, .. } => return tag.clone(),
|
||||
// ProximateShellError::ArgumentError { tag, .. } => tag.clone(),
|
||||
// ProximateShellError::RangeError { actual_kind, .. } => actual_kind.tag.clone(),
|
||||
// ProximateShellError::InvalidIntegerIndex { integer, .. } => integer.into(),
|
||||
// ProximateShellError::Diagnostic(..) => return None,
|
||||
// ProximateShellError::UntaggedRuntimeError { .. } => return None,
|
||||
// ProximateShellError::CoerceError { left, right } => left.tag.until(&right.tag),
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
impl FormatDebug for ProximateShellError {
|
||||
@ -568,6 +585,23 @@ pub struct ShellDiagnostic {
|
||||
pub(crate) diagnostic: Diagnostic<Span>,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for ShellDiagnostic {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.diagnostic.severity.hash(state);
|
||||
self.diagnostic.code.hash(state);
|
||||
self.diagnostic.message.hash(state);
|
||||
|
||||
for label in &self.diagnostic.labels {
|
||||
label.span.hash(state);
|
||||
label.message.hash(state);
|
||||
match label.style {
|
||||
language_reporting::LabelStyle::Primary => 0.hash(state),
|
||||
language_reporting::LabelStyle::Secondary => 1.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ShellDiagnostic {
|
||||
fn eq(&self, _other: &ShellDiagnostic) -> bool {
|
||||
false
|
||||
@ -598,10 +632,10 @@ impl std::fmt::Display for ShellError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match &self.error {
|
||||
ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"),
|
||||
ProximateShellError::InvalidCommand { .. } => write!(f, "InvalidCommand"),
|
||||
ProximateShellError::TypeError { .. } => write!(f, "TypeError"),
|
||||
ProximateShellError::UnexpectedEof { .. } => write!(f, "UnexpectedEof"),
|
||||
ProximateShellError::RangeError { .. } => write!(f, "RangeError"),
|
||||
ProximateShellError::InvalidIntegerIndex { .. } => write!(f, "InvalidIntegerIndex"),
|
||||
ProximateShellError::SyntaxError { .. } => write!(f, "SyntaxError"),
|
||||
ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
|
||||
ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"),
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::data::base::Block;
|
||||
use crate::errors::ArgumentError;
|
||||
use crate::parser::hir::path::{ColumnPath, RawPathMember};
|
||||
use crate::parser::{
|
||||
hir::{self, Expression, RawExpression},
|
||||
CommandRegistry, Text,
|
||||
@ -62,9 +63,8 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
match &expr.item {
|
||||
RawExpression::Literal(literal) => Ok(evaluate_literal(literal.tagged(tag), source)),
|
||||
RawExpression::ExternalWord => Err(ShellError::argument_error(
|
||||
"Invalid external word",
|
||||
"Invalid external word".spanned(tag.span),
|
||||
ArgumentError::InvalidExternalWord,
|
||||
tag,
|
||||
)),
|
||||
RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(tag)),
|
||||
RawExpression::Synthetic(hir::Synthetic::String(s)) => {
|
||||
@ -82,14 +82,8 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
match left.compare(binary.op(), &*right) {
|
||||
Ok(result) => Ok(Value::boolean(result).tagged(tag)),
|
||||
Err((left_type, right_type)) => Err(ShellError::coerce_error(
|
||||
left_type.tagged(Tag {
|
||||
span: binary.left().span,
|
||||
anchor: None,
|
||||
}),
|
||||
right_type.tagged(Tag {
|
||||
span: binary.right().span,
|
||||
anchor: None,
|
||||
}),
|
||||
left_type.spanned(binary.left().span),
|
||||
right_type.spanned(binary.right().span),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -110,13 +104,14 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
let value = evaluate_baseline_expr(path.head(), registry, scope, source)?;
|
||||
let mut item = value;
|
||||
|
||||
for name in path.tail() {
|
||||
let next = item.get_data_by_key(name);
|
||||
for member in path.tail() {
|
||||
let next = item.get_data_by_member(member);
|
||||
|
||||
match next {
|
||||
None => {
|
||||
Err(err) => {
|
||||
let possibilities = item.data_descriptors();
|
||||
|
||||
if let RawPathMember::String(name) = &member.item {
|
||||
let mut possible_matches: Vec<_> = possibilities
|
||||
.iter()
|
||||
.map(|x| (natural::distance::levenshtein_distance(x, &name), x))
|
||||
@ -131,14 +126,11 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
&tag,
|
||||
));
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
"row does not have this column",
|
||||
&tag,
|
||||
));
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Some(next) => {
|
||||
}
|
||||
Ok(next) => {
|
||||
item = next.clone().item.tagged(&tag);
|
||||
}
|
||||
};
|
||||
@ -152,6 +144,14 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
|
||||
fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged<Value> {
|
||||
let result = match literal.item {
|
||||
hir::Literal::ColumnPath(path) => {
|
||||
let members = path
|
||||
.iter()
|
||||
.map(|member| member.to_path_member(source))
|
||||
.collect();
|
||||
|
||||
Value::Primitive(Primitive::ColumnPath(ColumnPath::new(members)))
|
||||
}
|
||||
hir::Literal::Number(int) => int.into(),
|
||||
hir::Literal::Size(int, unit) => unit.compute(int),
|
||||
hir::Literal::String(tag) => Value::string(tag.slice(source)),
|
||||
@ -212,10 +212,12 @@ fn evaluate_external(
|
||||
_source: &Text,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
Err(ShellError::syntax_error(
|
||||
"Unexpected external command".tagged(*external.name()),
|
||||
"Unexpected external command".spanned(*external.name()),
|
||||
))
|
||||
}
|
||||
|
||||
fn evaluate_command(tag: Tag, _scope: &Scope, _source: &Text) -> Result<Tagged<Value>, ShellError> {
|
||||
Err(ShellError::syntax_error("Unexpected command".tagged(tag)))
|
||||
Err(ShellError::syntax_error(
|
||||
"Unexpected command".spanned(tag.span),
|
||||
))
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ impl EntriesView {
|
||||
for desc in descs {
|
||||
let value = value.get_data(&desc);
|
||||
|
||||
let formatted_value = value.borrow().format_leaf(None);
|
||||
let formatted_value = value.borrow().format_leaf().plain_string(75);
|
||||
|
||||
entries.push((desc.clone(), formatted_value))
|
||||
}
|
||||
@ -39,7 +39,7 @@ impl RenderView for EntriesView {
|
||||
let max_name_size: usize = self.entries.iter().map(|(n, _)| n.len()).max().unwrap();
|
||||
|
||||
for (name, value) in &self.entries {
|
||||
println!("{:width$} : {}", name, value, width = max_name_size)
|
||||
outln!("{:width$} : {}", name, value, width = max_name_size)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -30,7 +30,7 @@ impl RenderView for GenericView<'_> {
|
||||
}
|
||||
|
||||
b @ Value::Block(_) => {
|
||||
let printed = b.format_leaf(None);
|
||||
let printed = b.format_leaf().plain_string(host.width());
|
||||
let view = EntriesView::from_value(&Value::string(printed));
|
||||
view.render_view(host)?;
|
||||
Ok(())
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::data::Value;
|
||||
use crate::format::RenderView;
|
||||
use crate::prelude::*;
|
||||
use crate::traits::{DebugDocBuilder, DebugDocBuilder as b, PrettyDebug};
|
||||
use derive_new::new;
|
||||
use textwrap::fill;
|
||||
|
||||
@ -57,63 +58,33 @@ impl TableView {
|
||||
let mut entries = vec![];
|
||||
|
||||
for (idx, value) in values.iter().enumerate() {
|
||||
// let mut row: Vec<(String, &'static str)> = match value {
|
||||
// Tagged {
|
||||
// item: Value::Row(..),
|
||||
// ..
|
||||
// } => headers
|
||||
// .iter()
|
||||
// .enumerate()
|
||||
// .map(|(i, d)| {
|
||||
// let data = value.get_data(d);
|
||||
// return (
|
||||
// data.borrow().format_leaf(Some(&headers[i])),
|
||||
// data.borrow().style_leaf(),
|
||||
// );
|
||||
// })
|
||||
// .collect(),
|
||||
// x => vec![(x.format_leaf(None), x.style_leaf())],
|
||||
// };
|
||||
|
||||
let mut row: Vec<(String, &'static str)> = headers
|
||||
let mut row: Vec<(DebugDocBuilder, &'static str)> = match value {
|
||||
Tagged {
|
||||
item: Value::Row(..),
|
||||
..
|
||||
} => headers
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, d)| {
|
||||
if d == "<value>" {
|
||||
match value {
|
||||
Tagged {
|
||||
item: Value::Row(..),
|
||||
..
|
||||
} => (
|
||||
Value::nothing().format_leaf(None),
|
||||
Value::nothing().style_leaf(),
|
||||
),
|
||||
_ => (value.format_leaf(None), value.style_leaf()),
|
||||
}
|
||||
} else {
|
||||
match value {
|
||||
Tagged {
|
||||
item: Value::Row(..),
|
||||
..
|
||||
} => {
|
||||
let data = value.get_data(d);
|
||||
(
|
||||
data.borrow().format_leaf(Some(&headers[i])),
|
||||
return (
|
||||
data.borrow().format_for_column(&headers[i]),
|
||||
data.borrow().style_leaf(),
|
||||
)
|
||||
}
|
||||
_ => (
|
||||
Value::nothing().format_leaf(None),
|
||||
Value::nothing().style_leaf(),
|
||||
),
|
||||
}
|
||||
}
|
||||
);
|
||||
})
|
||||
.collect();
|
||||
.collect(),
|
||||
x => vec![(x.format_leaf(), x.style_leaf())],
|
||||
};
|
||||
|
||||
if values.len() > 1 {
|
||||
// Indices are black, bold, right-aligned:
|
||||
row.insert(0, (format!("{}", (starting_idx + idx).to_string()), "Fdbr"));
|
||||
row.insert(
|
||||
0,
|
||||
(
|
||||
b::primitive(format!("{}", (starting_idx + idx).to_string())),
|
||||
"Fdbr",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
entries.push(row);
|
||||
@ -125,10 +96,13 @@ impl TableView {
|
||||
headers.insert(0, format!("#"));
|
||||
}
|
||||
|
||||
// Different platforms want different amounts of buffer, not sure why
|
||||
let termwidth = std::cmp::max(textwrap::termwidth(), 20);
|
||||
|
||||
for head in 0..headers.len() {
|
||||
let mut current_col_max = 0;
|
||||
for row in 0..values.len() {
|
||||
let value_length = entries[row][head].0.chars().count();
|
||||
let value_length = entries[row][head].0.plain_string(termwidth).chars().count();
|
||||
if value_length > current_col_max {
|
||||
current_col_max = value_length;
|
||||
}
|
||||
@ -140,9 +114,6 @@ impl TableView {
|
||||
));
|
||||
}
|
||||
|
||||
// Different platforms want different amounts of buffer, not sure why
|
||||
let termwidth = std::cmp::max(textwrap::termwidth(), 20);
|
||||
|
||||
// Make sure we have enough space for the columns we have
|
||||
let max_num_of_columns = termwidth / 10;
|
||||
|
||||
@ -155,7 +126,7 @@ impl TableView {
|
||||
|
||||
headers.push("...".to_string());
|
||||
for row in 0..entries.len() {
|
||||
entries[row].push(("...".to_string(), "c")); // ellipsis is centred
|
||||
entries[row].push((b::description("..."), "c")); // ellipsis is centred
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,20 +198,54 @@ impl TableView {
|
||||
99999
|
||||
};
|
||||
|
||||
let mut out_entries: Vec<Vec<(String, &'static str)>> = Vec::with_capacity(entries.len());
|
||||
|
||||
for row in entries.iter() {
|
||||
let mut out_row = Vec::with_capacity(row.len());
|
||||
|
||||
for _ in row {
|
||||
out_row.push((String::new(), "Fdbr"));
|
||||
}
|
||||
|
||||
out_entries.push(out_row);
|
||||
}
|
||||
|
||||
// Wrap cells as needed
|
||||
for head in 0..headers.len() {
|
||||
if max_per_column[head] > max_naive_column_width {
|
||||
if true_width(&headers[head]) > max_column_width {
|
||||
headers[head] = fill(&headers[head], max_column_width);
|
||||
for row in 0..entries.len() {
|
||||
entries[row][head].0 = fill(&entries[row][head].0, max_column_width);
|
||||
}
|
||||
|
||||
for (i, row) in entries.iter().enumerate() {
|
||||
let column = &row[head].0;
|
||||
let column = column.plain_string(max_column_width);
|
||||
|
||||
out_entries[i][head] = (column, row[head].1);
|
||||
}
|
||||
} else {
|
||||
for (i, row) in entries.iter().enumerate() {
|
||||
let column = &row[head].0;
|
||||
let column = column.plain_string(max_column_width);
|
||||
|
||||
out_entries[i][head] = (column, row[head].1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(TableView { headers, entries })
|
||||
Some(TableView {
|
||||
headers,
|
||||
entries: out_entries,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn true_width(string: &str) -> usize {
|
||||
let stripped = console::strip_ansi_codes(string);
|
||||
|
||||
stripped.lines().map(|line| line.len()).max().unwrap_or(0)
|
||||
}
|
||||
|
||||
impl RenderView for TableView {
|
||||
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
|
||||
if self.entries.len() == 0 {
|
||||
|
187
src/fuzzysearch.rs
Normal file
187
src/fuzzysearch.rs
Normal file
@ -0,0 +1,187 @@
|
||||
use ansi_term::{ANSIString, ANSIStrings, Colour, Style};
|
||||
#[cfg(feature = "crossterm")]
|
||||
use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen};
|
||||
use std::io::Write;
|
||||
use sublime_fuzzy::best_match;
|
||||
|
||||
pub enum SelectionResult {
|
||||
Selected(String),
|
||||
Edit(String),
|
||||
NoSelection,
|
||||
}
|
||||
|
||||
pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> SelectionResult {
|
||||
#[derive(PartialEq)]
|
||||
enum State {
|
||||
Selecting,
|
||||
Quit,
|
||||
Selected(String),
|
||||
Edit(String),
|
||||
}
|
||||
let mut state = State::Selecting;
|
||||
#[cfg(feature = "crossterm")]
|
||||
{
|
||||
if let Ok(_raw) = RawScreen::into_raw_mode() {
|
||||
// User input for search
|
||||
let mut searchinput = String::new();
|
||||
let mut selected = 0;
|
||||
|
||||
let mut cursor = cursor();
|
||||
let _ = cursor.hide();
|
||||
let input = crossterm::input();
|
||||
let mut sync_stdin = input.read_sync();
|
||||
|
||||
while state == State::Selecting {
|
||||
let mut selected_lines = fuzzy_search(&searchinput, &lines, max_results);
|
||||
let num_lines = selected_lines.len();
|
||||
paint_selection_list(&selected_lines, selected);
|
||||
if let Some(ev) = sync_stdin.next() {
|
||||
match ev {
|
||||
InputEvent::Keyboard(k) => match k {
|
||||
KeyEvent::Esc | KeyEvent::Ctrl('c') => {
|
||||
state = State::Quit;
|
||||
}
|
||||
KeyEvent::Up => {
|
||||
if selected > 0 {
|
||||
selected -= 1;
|
||||
}
|
||||
}
|
||||
KeyEvent::Down => {
|
||||
if selected + 1 < selected_lines.len() {
|
||||
selected += 1;
|
||||
}
|
||||
}
|
||||
KeyEvent::Char('\n') => {
|
||||
state = if selected_lines.len() > 0 {
|
||||
State::Selected(selected_lines.remove(selected).text)
|
||||
} else {
|
||||
State::Edit("".to_string())
|
||||
};
|
||||
}
|
||||
KeyEvent::Char('\t') | KeyEvent::Right => {
|
||||
state = if selected_lines.len() > 0 {
|
||||
State::Edit(selected_lines.remove(selected).text)
|
||||
} else {
|
||||
State::Edit("".to_string())
|
||||
};
|
||||
}
|
||||
KeyEvent::Char(ch) => {
|
||||
searchinput.push(ch);
|
||||
selected = 0;
|
||||
}
|
||||
KeyEvent::Backspace => {
|
||||
searchinput.pop();
|
||||
selected = 0;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if num_lines > 0 {
|
||||
cursor.move_up(num_lines as u16);
|
||||
}
|
||||
}
|
||||
let (_x, y) = cursor.pos();
|
||||
let _ = cursor.goto(0, y - 1);
|
||||
let _ = cursor.show();
|
||||
let _ = RawScreen::disable_raw_mode();
|
||||
}
|
||||
terminal().clear(ClearType::FromCursorDown).unwrap();
|
||||
}
|
||||
match state {
|
||||
State::Selected(line) => SelectionResult::Selected(line),
|
||||
State::Edit(line) => SelectionResult::Edit(line),
|
||||
_ => SelectionResult::NoSelection,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Match {
|
||||
text: String,
|
||||
char_matches: Vec<(usize, usize)>,
|
||||
}
|
||||
|
||||
pub fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> Vec<Match> {
|
||||
if searchstr.is_empty() {
|
||||
return lines
|
||||
.iter()
|
||||
.take(max_results)
|
||||
.map(|line| Match {
|
||||
text: line.to_string(),
|
||||
char_matches: Vec::new(),
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
let mut matches = lines
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, line)| (idx, best_match(&searchstr, line)))
|
||||
.filter(|(_i, m)| m.is_some())
|
||||
.map(|(i, m)| (i, m.unwrap()))
|
||||
.collect::<Vec<_>>();
|
||||
matches.sort_by(|a, b| b.1.score().cmp(&a.1.score()));
|
||||
|
||||
let results: Vec<Match> = matches
|
||||
.iter()
|
||||
.take(max_results)
|
||||
.map(|(i, m)| Match {
|
||||
text: lines[*i].to_string(),
|
||||
char_matches: m.continuous_matches(),
|
||||
})
|
||||
.collect();
|
||||
results
|
||||
}
|
||||
|
||||
#[cfg(feature = "crossterm")]
|
||||
fn highlight(textmatch: &Match, normal: Style, highlighted: Style) -> Vec<ANSIString> {
|
||||
let text = &textmatch.text;
|
||||
let mut ansi_strings = vec![];
|
||||
let mut idx = 0;
|
||||
for (match_idx, len) in &textmatch.char_matches {
|
||||
ansi_strings.push(normal.paint(&text[idx..*match_idx]));
|
||||
idx = match_idx + len;
|
||||
ansi_strings.push(highlighted.paint(&text[*match_idx..idx]));
|
||||
}
|
||||
if idx < text.len() {
|
||||
ansi_strings.push(normal.paint(&text[idx..text.len()]));
|
||||
}
|
||||
ansi_strings
|
||||
}
|
||||
|
||||
#[cfg(feature = "crossterm")]
|
||||
fn paint_selection_list(lines: &Vec<Match>, selected: usize) {
|
||||
let terminal = terminal();
|
||||
let size = terminal.terminal_size();
|
||||
let width = size.0 as usize;
|
||||
let cursor = cursor();
|
||||
let (_x, y) = cursor.pos();
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
let _ = cursor.goto(0, y + (i as u16));
|
||||
let (style, highlighted) = if selected == i {
|
||||
(Colour::White.normal(), Colour::Cyan.normal())
|
||||
} else {
|
||||
(Colour::White.dimmed(), Colour::Cyan.normal())
|
||||
};
|
||||
let mut ansi_strings = highlight(line, style, highlighted);
|
||||
for _ in line.text.len()..width {
|
||||
ansi_strings.push(style.paint(' '.to_string()));
|
||||
}
|
||||
outln!("{}", ANSIStrings(&ansi_strings));
|
||||
}
|
||||
let _ = cursor.goto(0, y + (lines.len() as u16));
|
||||
print!(
|
||||
"{}",
|
||||
Colour::Blue.paint("[ESC to quit, Enter to execute, Tab to edit]")
|
||||
);
|
||||
|
||||
let _ = std::io::stdout().flush();
|
||||
// Clear additional lines from previous selection
|
||||
terminal.clear(ClearType::FromCursorDown).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fuzzy_match() {
|
||||
let matches = fuzzy_search("cb", &vec!["abc", "cargo build"], 1);
|
||||
assert_eq!(matches[0].text, "cargo build");
|
||||
}
|
@ -15,6 +15,7 @@ mod env;
|
||||
mod errors;
|
||||
mod evaluate;
|
||||
mod format;
|
||||
mod fuzzysearch;
|
||||
mod git;
|
||||
mod parser;
|
||||
mod plugin;
|
||||
@ -26,18 +27,19 @@ mod utils;
|
||||
pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
|
||||
pub use crate::context::AnchorLocation;
|
||||
pub use crate::env::host::BasicHost;
|
||||
pub use crate::parser::hir::path::{ColumnPath, PathMember, RawPathMember};
|
||||
pub use crate::parser::hir::SyntaxShape;
|
||||
pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
|
||||
pub use crate::plugin::{serve_plugin, Plugin};
|
||||
pub use crate::traits::{DebugFormatter, FormatDebug, ToDebug};
|
||||
pub use crate::traits::{DebugFormatter, FormatDebug, ShellTypeName, SpannedTypeName, ToDebug};
|
||||
pub use crate::utils::{did_you_mean, AbsoluteFile, AbsolutePath, RelativePath};
|
||||
pub use cli::cli;
|
||||
pub use data::base::{Primitive, Value};
|
||||
pub use data::config::{config_path, APP_INFO};
|
||||
pub use data::dict::{Dictionary, TaggedDictBuilder, TaggedListBuilder};
|
||||
pub use data::meta::{
|
||||
tag_for_tagged_list, HasFallibleSpan, HasSpan, Span, Spanned, SpannedItem, Tag, Tagged,
|
||||
TaggedItem,
|
||||
span_for_spanned_list, tag_for_tagged_list, HasFallibleSpan, HasSpan, Span, Spanned,
|
||||
SpannedItem, Tag, Tagged, TaggedItem,
|
||||
};
|
||||
pub use errors::{CoerceInto, ShellError};
|
||||
pub use num_traits::cast::ToPrimitive;
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod deserializer;
|
||||
pub(crate) mod hir;
|
||||
pub(crate) mod parse;
|
||||
|
51
src/parser/debug.rs
Normal file
51
src/parser/debug.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use crate::traits::ShellAnnotation;
|
||||
use pretty::{Render, RenderAnnotated};
|
||||
use std::io;
|
||||
use termcolor::WriteColor;
|
||||
|
||||
pub struct TermColored<'a, W> {
|
||||
color_stack: Vec<ShellAnnotation>,
|
||||
upstream: &'a mut W,
|
||||
}
|
||||
|
||||
impl<'a, W> TermColored<'a, W> {
|
||||
pub fn new(upstream: &'a mut W) -> TermColored<'a, W> {
|
||||
TermColored {
|
||||
color_stack: Vec::new(),
|
||||
upstream,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W> Render for TermColored<'a, W>
|
||||
where
|
||||
W: io::Write,
|
||||
{
|
||||
type Error = io::Error;
|
||||
|
||||
fn write_str(&mut self, s: &str) -> io::Result<usize> {
|
||||
self.upstream.write(s.as_bytes())
|
||||
}
|
||||
|
||||
fn write_str_all(&mut self, s: &str) -> io::Result<()> {
|
||||
self.upstream.write_all(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W> RenderAnnotated<ShellAnnotation> for TermColored<'a, W>
|
||||
where
|
||||
W: WriteColor,
|
||||
{
|
||||
fn push_annotation(&mut self, ann: &ShellAnnotation) -> Result<(), Self::Error> {
|
||||
self.color_stack.push(*ann);
|
||||
self.upstream.set_color(&(*ann).into())
|
||||
}
|
||||
|
||||
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
|
||||
self.color_stack.pop();
|
||||
match self.color_stack.last() {
|
||||
Some(previous) => self.upstream.set_color(&(*previous).into()),
|
||||
None => self.upstream.reset(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use crate::ColumnPath;
|
||||
use log::trace;
|
||||
use serde::de;
|
||||
use std::path::PathBuf;
|
||||
@ -97,7 +98,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||
item: Value::Primitive(Primitive::Nothing),
|
||||
..
|
||||
} => visitor.visit_bool(false),
|
||||
other => Err(ShellError::type_error("Boolean", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"Boolean",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn deserialize_i8<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
||||
@ -242,7 +246,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||
let de = SeqDeserializer::new(&mut self, items.into_iter());
|
||||
visitor.visit_seq(de)
|
||||
}
|
||||
(other, tag) => Err(ShellError::type_error("Vec", other.type_name().tagged(tag))),
|
||||
(other, tag) => Err(ShellError::type_error(
|
||||
"Vec",
|
||||
other.type_name().spanned(tag),
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn deserialize_tuple<V>(mut self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
|
||||
@ -263,7 +270,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||
}
|
||||
(other, tag) => Err(ShellError::type_error(
|
||||
"Tuple",
|
||||
other.type_name().tagged(tag),
|
||||
other.type_name().spanned(tag),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -328,7 +335,8 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||
let tagged_val_name = std::any::type_name::<Tagged<Value>>();
|
||||
|
||||
trace!(
|
||||
"type_name={} tagged_val_name={}",
|
||||
"name={} type_name={} tagged_val_name={}",
|
||||
name,
|
||||
type_name,
|
||||
tagged_val_name
|
||||
);
|
||||
@ -343,11 +351,33 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||
item: Value::Block(block),
|
||||
..
|
||||
} => block,
|
||||
other => return Err(ShellError::type_error("Block", other.tagged_type_name())),
|
||||
other => {
|
||||
return Err(ShellError::type_error(
|
||||
"Block",
|
||||
other.type_name().spanned(other.span()),
|
||||
))
|
||||
}
|
||||
};
|
||||
return visit::<value::Block, _>(block, name, fields, visitor);
|
||||
}
|
||||
|
||||
if name == "ColumnPath" {
|
||||
let path = match value.val {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::ColumnPath(path)),
|
||||
..
|
||||
} => path,
|
||||
other => {
|
||||
return Err(ShellError::type_error(
|
||||
"column path",
|
||||
other.type_name().spanned(other.span()),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
return visit::<ColumnPath, _>(path, name, fields, visitor);
|
||||
}
|
||||
|
||||
trace!("Extracting {:?} for {:?}", value.val, type_name);
|
||||
|
||||
let tag = value.val.tag();
|
||||
@ -376,7 +406,12 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||
..
|
||||
} => visit::<Tagged<String>, _>(string.tagged(tag), name, fields, visitor),
|
||||
|
||||
other => return Err(ShellError::type_error(name, other.tagged_type_name())),
|
||||
other => {
|
||||
return Err(ShellError::type_error(
|
||||
name,
|
||||
other.type_name().spanned(other.span()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
fn deserialize_enum<V>(
|
||||
|
@ -7,6 +7,8 @@ pub(crate) mod path;
|
||||
pub(crate) mod syntax_shape;
|
||||
pub(crate) mod tokens_iterator;
|
||||
|
||||
use crate::parser::hir::path::PathMember;
|
||||
use crate::parser::hir::syntax_shape::Member;
|
||||
use crate::parser::{registry, Operator, Unit};
|
||||
use crate::prelude::*;
|
||||
use derive_new::new;
|
||||
@ -178,24 +180,30 @@ impl Expression {
|
||||
RawExpression::Literal(Literal::String(inner.into())).spanned(outer.into())
|
||||
}
|
||||
|
||||
pub(crate) fn column_path(members: Vec<Member>, span: impl Into<Span>) -> Expression {
|
||||
RawExpression::Literal(Literal::ColumnPath(members)).spanned(span.into())
|
||||
}
|
||||
|
||||
pub(crate) fn path(
|
||||
head: Expression,
|
||||
tail: Vec<Spanned<impl Into<String>>>,
|
||||
tail: Vec<impl Into<PathMember>>,
|
||||
span: impl Into<Span>,
|
||||
) -> Expression {
|
||||
let tail = tail.into_iter().map(|t| t.map(|s| s.into())).collect();
|
||||
let tail = tail.into_iter().map(|t| t.into()).collect();
|
||||
RawExpression::Path(Box::new(Path::new(head, tail))).spanned(span.into())
|
||||
}
|
||||
|
||||
pub(crate) fn dot_member(head: Expression, next: Spanned<impl Into<String>>) -> Expression {
|
||||
pub(crate) fn dot_member(head: Expression, next: impl Into<PathMember>) -> Expression {
|
||||
let Spanned { item, span } = head;
|
||||
let next = next.into();
|
||||
|
||||
let new_span = head.span.until(next.span);
|
||||
|
||||
match item {
|
||||
RawExpression::Path(path) => {
|
||||
let (head, mut tail) = path.parts();
|
||||
|
||||
tail.push(next.map(|i| i.into()));
|
||||
tail.push(next);
|
||||
Expression::path(head, tail, new_span)
|
||||
}
|
||||
|
||||
@ -241,10 +249,6 @@ impl Expression {
|
||||
pub(crate) fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
|
||||
RawExpression::Variable(Variable::It(inner.into())).spanned(outer)
|
||||
}
|
||||
|
||||
pub(crate) fn tagged_type_name(&self) -> Tagged<&'static str> {
|
||||
self.item.type_name().tagged(self.span)
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for Spanned<RawExpression> {
|
||||
@ -301,6 +305,7 @@ pub enum Literal {
|
||||
Size(Number, Unit),
|
||||
String(Span),
|
||||
GlobPattern(String),
|
||||
ColumnPath(Vec<Member>),
|
||||
Bare,
|
||||
}
|
||||
|
||||
@ -318,6 +323,7 @@ impl std::fmt::Display for Tagged<&Literal> {
|
||||
Literal::Number(number) => write!(f, "{}", number),
|
||||
Literal::Size(number, unit) => write!(f, "{}{}", number, unit.as_str()),
|
||||
Literal::String(_) => write!(f, "String{{ {}..{} }}", span.start(), span.end()),
|
||||
Literal::ColumnPath(_) => write!(f, "ColumnPath"),
|
||||
Literal::GlobPattern(_) => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()),
|
||||
Literal::Bare => write!(f, "Bare{{ {}..{} }}", span.start(), span.end()),
|
||||
}
|
||||
@ -330,6 +336,15 @@ impl FormatDebug for Spanned<&Literal> {
|
||||
Literal::Number(..) => f.say_str("number", self.span.slice(source)),
|
||||
Literal::Size(..) => f.say_str("size", self.span.slice(source)),
|
||||
Literal::String(..) => f.say_str("string", self.span.slice(source)),
|
||||
Literal::ColumnPath(path) => f.say_block("column path", |f| {
|
||||
write!(f, "[ ")?;
|
||||
|
||||
for member in path {
|
||||
write!(f, "{} ", member.debug(source))?;
|
||||
}
|
||||
|
||||
write!(f, "]")
|
||||
}),
|
||||
Literal::GlobPattern(..) => f.say_str("glob", self.span.slice(source)),
|
||||
Literal::Bare => f.say_str("word", self.span.slice(source)),
|
||||
}
|
||||
@ -342,6 +357,7 @@ impl Literal {
|
||||
Literal::Number(..) => "number",
|
||||
Literal::Size(..) => "size",
|
||||
Literal::String(..) => "string",
|
||||
Literal::ColumnPath(..) => "column path",
|
||||
Literal::Bare => "string",
|
||||
Literal::GlobPattern(_) => "pattern",
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use crate::commands::classified::InternalCommand;
|
||||
use crate::commands::ClassifiedCommand;
|
||||
use crate::env::host::BasicHost;
|
||||
use crate::parser::hir::syntax_shape::*;
|
||||
use crate::parser::hir::TokensIterator;
|
||||
use crate::parser::hir::{self, named::NamedValue, NamedArguments};
|
||||
use crate::parser::hir::{
|
||||
self, named::NamedValue, path::PathMember, syntax_shape::*, NamedArguments,
|
||||
};
|
||||
use crate::parser::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b};
|
||||
use crate::parser::TokenNode;
|
||||
use crate::{HasSpan, Span, SpannedItem, Tag, Text};
|
||||
@ -28,7 +29,7 @@ fn test_parse_path() {
|
||||
let bare = tokens[2].expect_bare();
|
||||
hir::Expression::path(
|
||||
hir::Expression::it_variable(inner_var, outer_var),
|
||||
vec!["cpu".spanned(bare)],
|
||||
vec![PathMember::string("cpu", bare)],
|
||||
outer_var.until(bare),
|
||||
)
|
||||
},
|
||||
@ -50,7 +51,10 @@ fn test_parse_path() {
|
||||
|
||||
hir::Expression::path(
|
||||
hir::Expression::variable(inner_var, outer_var),
|
||||
vec!["amount".spanned(amount), "max ghz".spanned(outer_max_ghz)],
|
||||
vec![
|
||||
PathMember::string("amount", amount),
|
||||
PathMember::string("max ghz", outer_max_ghz),
|
||||
],
|
||||
outer_var.until(outer_max_ghz),
|
||||
)
|
||||
},
|
||||
@ -66,8 +70,6 @@ fn test_parse_command() {
|
||||
let bare = tokens[0].expect_bare();
|
||||
let pat = tokens[2].expect_pattern();
|
||||
|
||||
eprintln!("{:?} {:?} {:?}", bare, pat, bare.until(pat));
|
||||
|
||||
let mut map = IndexMap::new();
|
||||
map.insert("full".to_string(), NamedValue::AbsentSwitch);
|
||||
|
||||
|
@ -217,7 +217,7 @@ impl ExpandExpression for ExternalHeadShape {
|
||||
| AtomicToken::Pipeline { .. } => {
|
||||
return Err(ParseError::mismatch(
|
||||
"external command name",
|
||||
atom.tagged_type_name(),
|
||||
"pipeline".spanned(atom.span),
|
||||
))
|
||||
}
|
||||
AtomicToken::ExternalCommand { command } => {
|
||||
@ -293,7 +293,7 @@ impl ExpandExpression for ExternalContinuationShape {
|
||||
| AtomicToken::Pipeline { .. } => {
|
||||
return Err(ParseError::mismatch(
|
||||
"external argument",
|
||||
atom.tagged_type_name(),
|
||||
"pipeline".spanned(atom.span),
|
||||
))
|
||||
}
|
||||
}),
|
||||
|
@ -1,10 +1,107 @@
|
||||
use crate::parser::hir::Expression;
|
||||
use crate::prelude::*;
|
||||
use crate::traits::{DebugDocBuilder as b, PrettyDebug};
|
||||
use derive_new::new;
|
||||
use getset::{Getters, MutGetters};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
pub enum RawPathMember {
|
||||
String(String),
|
||||
Int(BigInt),
|
||||
}
|
||||
|
||||
pub type PathMember = Spanned<RawPathMember>;
|
||||
|
||||
impl PrettyDebug for &PathMember {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
match &self.item {
|
||||
RawPathMember::String(string) => b::primitive(format!("{:?}", string)),
|
||||
RawPathMember::Int(int) => b::primitive(format!("{}", int)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Hash, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Getters, Clone, new,
|
||||
)]
|
||||
pub struct ColumnPath {
|
||||
#[get = "pub"]
|
||||
members: Vec<PathMember>,
|
||||
}
|
||||
|
||||
impl ColumnPath {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &PathMember> {
|
||||
self.members.iter()
|
||||
}
|
||||
|
||||
pub fn split_last(&self) -> (&PathMember, &[PathMember]) {
|
||||
self.members.split_last().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for ColumnPath {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
let members: Vec<DebugDocBuilder> = self
|
||||
.members
|
||||
.iter()
|
||||
.map(|member| member.pretty_debug())
|
||||
.collect();
|
||||
|
||||
b::delimit(
|
||||
"(",
|
||||
b::description("path") + b::equals() + b::intersperse(members, b::space()),
|
||||
")",
|
||||
)
|
||||
.nest()
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for ColumnPath {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
self.members.fmt_debug(f, source)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFallibleSpan for ColumnPath {
|
||||
fn maybe_span(&self) -> Option<Span> {
|
||||
if self.members.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(span_for_spanned_list(self.members.iter().map(|m| m.span)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RawPathMember {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RawPathMember::String(string) => write!(f, "{}", string),
|
||||
RawPathMember::Int(int) => write!(f, "{}", int),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PathMember {
|
||||
pub fn string(string: impl Into<String>, span: impl Into<Span>) -> PathMember {
|
||||
RawPathMember::String(string.into()).spanned(span.into())
|
||||
}
|
||||
|
||||
pub fn int(int: impl Into<BigInt>, span: impl Into<Span>) -> PathMember {
|
||||
RawPathMember::Int(int.into()).spanned(span.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for PathMember {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
|
||||
match &self.item {
|
||||
RawPathMember::String(string) => f.say_str("member", &string),
|
||||
RawPathMember::Int(int) => f.say_block("member", |f| write!(f, "{}", int)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@ -23,7 +120,7 @@ use std::fmt;
|
||||
pub struct Path {
|
||||
head: Expression,
|
||||
#[get_mut = "pub(crate)"]
|
||||
tail: Vec<Spanned<String>>,
|
||||
tail: Vec<PathMember>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Path {
|
||||
@ -39,7 +136,7 @@ impl fmt::Display for Path {
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub(crate) fn parts(self) -> (Expression, Vec<Spanned<String>>) {
|
||||
pub(crate) fn parts(self) -> (Expression, Vec<PathMember>) {
|
||||
(self.head, self.tail)
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ pub(crate) use self::expression::string::StringShape;
|
||||
pub(crate) use self::expression::unit::UnitShape;
|
||||
pub(crate) use self::expression::variable_path::{
|
||||
ColorableDotShape, ColumnPathShape, DotShape, ExpressionContinuation,
|
||||
ExpressionContinuationShape, MemberShape, PathTailShape, VariablePathShape,
|
||||
ExpressionContinuationShape, Member, MemberShape, PathTailShape, VariablePathShape,
|
||||
};
|
||||
pub(crate) use self::expression::{continue_expression, AnyExpressionShape};
|
||||
pub(crate) use self::flat_shape::FlatShape;
|
||||
@ -138,15 +138,15 @@ impl FallibleColorSyntax for SyntaxShape {
|
||||
impl ExpandExpression for SyntaxShape {
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
SyntaxShape::Any => "any",
|
||||
SyntaxShape::Int => "integer",
|
||||
SyntaxShape::String => "string",
|
||||
SyntaxShape::Member => "column name",
|
||||
SyntaxShape::ColumnPath => "column path",
|
||||
SyntaxShape::Number => "number",
|
||||
SyntaxShape::Path => "file path",
|
||||
SyntaxShape::Pattern => "glob pattern",
|
||||
SyntaxShape::Block => "block",
|
||||
SyntaxShape::Any => "shape[any]",
|
||||
SyntaxShape::Int => "shape[integer]",
|
||||
SyntaxShape::String => "shape[string]",
|
||||
SyntaxShape::Member => "shape[column name]",
|
||||
SyntaxShape::ColumnPath => "shape[column path]",
|
||||
SyntaxShape::Number => "shape[number]",
|
||||
SyntaxShape::Path => "shape[file path]",
|
||||
SyntaxShape::Pattern => "shape[glob pattern]",
|
||||
SyntaxShape::Block => "shape[block]",
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,13 +165,12 @@ impl ExpandExpression for SyntaxShape {
|
||||
}
|
||||
SyntaxShape::ColumnPath => {
|
||||
let column_path = expand_syntax(&ColumnPathShape, token_nodes, context)?;
|
||||
|
||||
let Tagged { item: members, tag } = column_path.path();
|
||||
|
||||
Ok(hir::Expression::list(
|
||||
members.into_iter().map(|s| s.to_expr()).collect(),
|
||||
let Tagged {
|
||||
item: column_path,
|
||||
tag,
|
||||
))
|
||||
} = column_path;
|
||||
|
||||
Ok(hir::Expression::column_path(column_path, tag.span))
|
||||
}
|
||||
SyntaxShape::Number => expand_expr(&NumberShape, token_nodes, context),
|
||||
SyntaxShape::Path => expand_expr(&FilePathShape, token_nodes, context),
|
||||
@ -587,7 +586,7 @@ impl ExpandSyntax for BarePathShape {
|
||||
type Output = Span;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"shorthand path"
|
||||
"bare path"
|
||||
}
|
||||
|
||||
fn expand_syntax<'a, 'b>(
|
||||
@ -637,7 +636,7 @@ impl FallibleColorSyntax for BareShape {
|
||||
}
|
||||
|
||||
// otherwise, fail
|
||||
other => Err(ParseError::mismatch("word", other.tagged_type_name())),
|
||||
other => Err(ParseError::mismatch("word", other.spanned_type_name())),
|
||||
})
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
@ -666,7 +665,10 @@ impl FallibleColorSyntax for BareShape {
|
||||
}) => Ok(span),
|
||||
|
||||
// otherwise, fail
|
||||
other => Err(ParseError::mismatch("word", other.tagged_type_name())),
|
||||
other => Err(ParseError::mismatch(
|
||||
"word",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
})?;
|
||||
|
||||
token_nodes.color_shape((*input).spanned(*span));
|
||||
@ -698,7 +700,10 @@ impl ExpandSyntax for BareShape {
|
||||
Ok(span.spanned_string(context.source))
|
||||
}
|
||||
|
||||
other => Err(ParseError::mismatch("word", other.tagged_type_name())),
|
||||
other => Err(ParseError::mismatch(
|
||||
"word",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -986,7 +991,7 @@ impl FallibleColorSyntax for CommandHeadShape {
|
||||
|
||||
// Otherwise, we're not actually looking at a command
|
||||
_ => Err(ShellError::syntax_error(
|
||||
"No command at the head".tagged(atom.span),
|
||||
"No command at the head".spanned(atom.span),
|
||||
)),
|
||||
}
|
||||
})
|
||||
@ -1043,7 +1048,7 @@ impl FallibleColorSyntax for CommandHeadShape {
|
||||
|
||||
// Otherwise, we're not actually looking at a command
|
||||
_ => Err(ShellError::syntax_error(
|
||||
"No command at the head".tagged(atom.span),
|
||||
"No command at the head".spanned(atom.span),
|
||||
)),
|
||||
}
|
||||
})
|
||||
@ -1081,7 +1086,7 @@ impl ExpandSyntax for CommandHeadShape {
|
||||
_ => {
|
||||
return Err(ShellError::type_error(
|
||||
"command head2",
|
||||
token.type_name().tagged(token_span),
|
||||
token.type_name().spanned(token_span),
|
||||
))
|
||||
}
|
||||
})
|
||||
@ -1116,9 +1121,10 @@ impl ExpandSyntax for ClassifiedCommandShape {
|
||||
let head = expand_syntax(&CommandHeadShape, iterator, context)?;
|
||||
|
||||
match &head {
|
||||
CommandSignature::Expression(expr) => {
|
||||
Err(ParseError::mismatch("command", expr.tagged_type_name()))
|
||||
}
|
||||
CommandSignature::Expression(expr) => Err(ParseError::mismatch(
|
||||
"command",
|
||||
expr.type_name().spanned(expr.span),
|
||||
)),
|
||||
|
||||
// If the command starts with `^`, treat it as an external command no matter what
|
||||
CommandSignature::External(name) => {
|
||||
@ -1276,7 +1282,7 @@ impl ExpandExpression for InternalCommandHeadShape {
|
||||
node => {
|
||||
return Err(ParseError::mismatch(
|
||||
"command head",
|
||||
node.tagged_type_name(),
|
||||
node.type_name().spanned(node.span()),
|
||||
))
|
||||
}
|
||||
};
|
||||
@ -1294,7 +1300,7 @@ pub(crate) struct SingleError<'token> {
|
||||
|
||||
impl<'token> SingleError<'token> {
|
||||
pub(crate) fn error(&self) -> ParseError {
|
||||
ParseError::mismatch(self.expected, self.node.type_name().tagged(self.node.span))
|
||||
ParseError::mismatch(self.expected, self.node.type_name().spanned(self.node.span))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1313,7 +1319,10 @@ fn parse_single_node<'a, 'b, T>(
|
||||
},
|
||||
),
|
||||
|
||||
other => Err(ParseError::mismatch(expected, other.tagged_type_name())),
|
||||
other => Err(ParseError::mismatch(
|
||||
expected,
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -1334,7 +1343,12 @@ fn parse_single_node_skipping_ws<'a, 'b, T>(
|
||||
},
|
||||
)?,
|
||||
|
||||
other => return Err(ShellError::type_error(expected, other.tagged_type_name())),
|
||||
other => {
|
||||
return Err(ShellError::type_error(
|
||||
expected,
|
||||
other.type_name().spanned(other.span()),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
peeked.commit();
|
||||
@ -1429,7 +1443,12 @@ impl ExpandSyntax for WhitespaceShape {
|
||||
let span = match peeked.node {
|
||||
TokenNode::Whitespace(tag) => *tag,
|
||||
|
||||
other => return Err(ParseError::mismatch("whitespace", other.tagged_type_name())),
|
||||
other => {
|
||||
return Err(ParseError::mismatch(
|
||||
"whitespace",
|
||||
other.type_name().spanned(other.span()),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
peeked.commit();
|
||||
@ -1462,7 +1481,10 @@ impl<T: ExpandExpression> ExpandExpression for SpacedExpression<T> {
|
||||
expand_expr(&self.inner, token_nodes, context)
|
||||
}
|
||||
|
||||
other => Err(ParseError::mismatch("whitespace", other.tagged_type_name())),
|
||||
other => Err(ParseError::mismatch(
|
||||
"whitespace",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1590,7 +1612,7 @@ impl FallibleColorSyntax for SpaceShape {
|
||||
|
||||
other => Err(ShellError::type_error(
|
||||
"whitespace",
|
||||
other.tagged_type_name(),
|
||||
other.spanned_type_name(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -1622,7 +1644,7 @@ impl FallibleColorSyntax for SpaceShape {
|
||||
|
||||
other => Err(ShellError::type_error(
|
||||
"whitespace",
|
||||
other.tagged_type_name(),
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ use crate::parser::{
|
||||
},
|
||||
hir::tokens_iterator::TokensIterator,
|
||||
parse::token_tree::Delimiter,
|
||||
RawToken, TokenNode,
|
||||
};
|
||||
use crate::{Span, Spanned, SpannedItem};
|
||||
|
||||
@ -381,8 +380,11 @@ impl FallibleColorSyntax for ShorthandHeadShape {
|
||||
_context: &ExpandContext,
|
||||
shapes: &mut Vec<Spanned<FlatShape>>,
|
||||
) -> Result<(), ShellError> {
|
||||
use crate::parser::parse::token_tree::TokenNode;
|
||||
use crate::parser::parse::tokens::RawToken;
|
||||
|
||||
// A shorthand path must not be at EOF
|
||||
let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?;
|
||||
let peeked = token_nodes.peek_non_ws().not_eof("shorthand path head")?;
|
||||
|
||||
match peeked.node {
|
||||
// If the head of a shorthand path is a bare token, it expands to `$it.bare`
|
||||
@ -407,7 +409,7 @@ impl FallibleColorSyntax for ShorthandHeadShape {
|
||||
|
||||
other => Err(ShellError::type_error(
|
||||
"shorthand head",
|
||||
other.tagged_type_name(),
|
||||
other.spanned_type_name(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -427,7 +429,7 @@ impl FallibleColorSyntax for ShorthandHeadShape {
|
||||
shapes: &mut Vec<Spanned<FlatShape>>,
|
||||
) -> Result<(), ShellError> {
|
||||
// A shorthand path must not be at EOF
|
||||
let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?;
|
||||
let peeked = token_nodes.peek_non_ws().not_eof("shorthand path head")?;
|
||||
|
||||
match peeked.node {
|
||||
// If the head of a shorthand path is a bare token, it expands to `$it.bare`
|
||||
@ -468,56 +470,14 @@ impl ExpandExpression for ShorthandHeadShape {
|
||||
token_nodes: &'b mut TokensIterator<'a>,
|
||||
context: &ExpandContext,
|
||||
) -> Result<hir::Expression, ParseError> {
|
||||
// A shorthand path must not be at EOF
|
||||
let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?;
|
||||
|
||||
match peeked.node {
|
||||
// If the head of a shorthand path is a bare token, it expands to `$it.bare`
|
||||
TokenNode::Token(Spanned {
|
||||
item: RawToken::Bare,
|
||||
span,
|
||||
}) => {
|
||||
// Commit the peeked token
|
||||
peeked.commit();
|
||||
let head = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||
let head = head.to_path_member(context.source);
|
||||
|
||||
// Synthesize an `$it` expression
|
||||
let it = synthetic_it();
|
||||
let span = head.span;
|
||||
|
||||
// Make a path out of `$it` and the bare token as a member
|
||||
Ok(hir::Expression::path(
|
||||
it,
|
||||
vec![span.spanned_string(context.source)],
|
||||
*span,
|
||||
))
|
||||
}
|
||||
|
||||
// If the head of a shorthand path is a string, it expands to `$it."some string"`
|
||||
TokenNode::Token(Spanned {
|
||||
item: RawToken::String(inner),
|
||||
span: outer,
|
||||
}) => {
|
||||
// Commit the peeked token
|
||||
peeked.commit();
|
||||
|
||||
// Synthesize an `$it` expression
|
||||
let it = synthetic_it();
|
||||
|
||||
// Make a path out of `$it` and the bare token as a member
|
||||
Ok(hir::Expression::path(
|
||||
it,
|
||||
vec![inner.string(context.source).spanned(*outer)],
|
||||
*outer,
|
||||
))
|
||||
}
|
||||
|
||||
// Any other token is not a valid bare head
|
||||
other => {
|
||||
return Err(ParseError::mismatch(
|
||||
"shorthand path",
|
||||
other.tagged_type_name(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(hir::Expression::path(it, vec![head], span))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,7 +392,7 @@ impl FallibleColorSyntax for BareTailShape {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ShellError::syntax_error(
|
||||
"No tokens matched BareTailShape".tagged_unknown(),
|
||||
"No tokens matched BareTailShape".spanned_unknown(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -446,7 +446,7 @@ impl FallibleColorSyntax for BareTailShape {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ShellError::syntax_error(
|
||||
"No tokens matched BareTailShape".tagged_unknown(),
|
||||
"No tokens matched BareTailShape".spanned_unknown(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use crate::parser::{
|
||||
use crate::prelude::*;
|
||||
use crate::{Span, Spanned};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AtomicToken<'tokens> {
|
||||
Eof {
|
||||
span: Span,
|
||||
@ -83,6 +83,34 @@ pub enum AtomicToken<'tokens> {
|
||||
},
|
||||
}
|
||||
|
||||
impl<'tokens> ShellTypeName for AtomicToken<'tokens> {
|
||||
fn type_name(&self) -> &'static str {
|
||||
match &self {
|
||||
AtomicToken::Eof { .. } => "eof",
|
||||
AtomicToken::Error { .. } => "error",
|
||||
AtomicToken::Operator { .. } => "operator",
|
||||
AtomicToken::ShorthandFlag { .. } => "shorthand flag",
|
||||
AtomicToken::LonghandFlag { .. } => "flag",
|
||||
AtomicToken::Whitespace { .. } => "whitespace",
|
||||
AtomicToken::Dot { .. } => "dot",
|
||||
AtomicToken::Number { .. } => "number",
|
||||
AtomicToken::FilePath { .. } => "file path",
|
||||
AtomicToken::Size { .. } => "size",
|
||||
AtomicToken::String { .. } => "string",
|
||||
AtomicToken::ItVariable { .. } => "$it",
|
||||
AtomicToken::Variable { .. } => "variable",
|
||||
AtomicToken::ExternalCommand { .. } => "external command",
|
||||
AtomicToken::ExternalWord { .. } => "external word",
|
||||
AtomicToken::GlobPattern { .. } => "file pattern",
|
||||
AtomicToken::Word { .. } => "word",
|
||||
AtomicToken::SquareDelimited { .. } => "array literal",
|
||||
AtomicToken::ParenDelimited { .. } => "parenthesized expression",
|
||||
AtomicToken::BraceDelimited { .. } => "block",
|
||||
AtomicToken::Pipeline { .. } => "pipeline",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type SpannedAtomicToken<'tokens> = Spanned<AtomicToken<'tokens>>;
|
||||
|
||||
impl<'tokens> SpannedAtomicToken<'tokens> {
|
||||
@ -95,35 +123,38 @@ impl<'tokens> SpannedAtomicToken<'tokens> {
|
||||
AtomicToken::Eof { .. } => {
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
"eof atomic token".tagged(self.span),
|
||||
"eof atomic token".spanned(self.span),
|
||||
))
|
||||
}
|
||||
AtomicToken::Error { .. } => {
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
"eof atomic token".tagged(self.span),
|
||||
"eof atomic token".spanned(self.span),
|
||||
))
|
||||
}
|
||||
AtomicToken::Operator { .. } => {
|
||||
return Err(ParseError::mismatch(expected, "operator".tagged(self.span)))
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
"operator".spanned(self.span),
|
||||
))
|
||||
}
|
||||
AtomicToken::ShorthandFlag { .. } => {
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
"shorthand flag".tagged(self.span),
|
||||
"shorthand flag".spanned(self.span),
|
||||
))
|
||||
}
|
||||
AtomicToken::LonghandFlag { .. } => {
|
||||
return Err(ParseError::mismatch(expected, "flag".tagged(self.span)))
|
||||
return Err(ParseError::mismatch(expected, "flag".spanned(self.span)))
|
||||
}
|
||||
AtomicToken::Whitespace { .. } => {
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
"whitespace".tagged(self.span),
|
||||
"whitespace".spanned(self.span),
|
||||
))
|
||||
}
|
||||
AtomicToken::Dot { .. } => {
|
||||
return Err(ParseError::mismatch(expected, "dot".tagged(self.span)))
|
||||
return Err(ParseError::mismatch(expected, "dot".spanned(self.span)))
|
||||
}
|
||||
AtomicToken::Number { number } => {
|
||||
Expression::number(number.to_number(context.source), self.span)
|
||||
@ -281,6 +312,7 @@ pub struct ExpansionRule {
|
||||
pub(crate) allow_operator: bool,
|
||||
pub(crate) allow_eof: bool,
|
||||
pub(crate) treat_size_as_word: bool,
|
||||
pub(crate) separate_members: bool,
|
||||
pub(crate) commit_errors: bool,
|
||||
pub(crate) whitespace: WhitespaceHandling,
|
||||
}
|
||||
@ -293,6 +325,7 @@ impl ExpansionRule {
|
||||
allow_operator: false,
|
||||
allow_eof: false,
|
||||
treat_size_as_word: false,
|
||||
separate_members: false,
|
||||
commit_errors: false,
|
||||
whitespace: WhitespaceHandling::RejectWhitespace,
|
||||
}
|
||||
@ -307,6 +340,7 @@ impl ExpansionRule {
|
||||
allow_external_word: true,
|
||||
allow_operator: true,
|
||||
allow_eof: true,
|
||||
separate_members: false,
|
||||
treat_size_as_word: false,
|
||||
commit_errors: true,
|
||||
whitespace: WhitespaceHandling::AllowWhitespace,
|
||||
@ -355,6 +389,18 @@ impl ExpansionRule {
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn separate_members(mut self) -> ExpansionRule {
|
||||
self.separate_members = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn no_separate_members(mut self) -> ExpansionRule {
|
||||
self.separate_members = false;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn commit_errors(mut self) -> ExpansionRule {
|
||||
self.commit_errors = true;
|
||||
@ -374,9 +420,72 @@ impl ExpansionRule {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'content> FormatDebug for SpannedAtomicToken<'content> {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> std::fmt::Result {
|
||||
match &self.item {
|
||||
AtomicToken::Eof { .. } => f.say_str("atomic", "eof"),
|
||||
AtomicToken::Error { .. } => f.say_str("atomic", "error"),
|
||||
AtomicToken::Number { number } => {
|
||||
f.say_str("atomic", format!("{}", number.debug(source)))
|
||||
}
|
||||
AtomicToken::Size { number, unit } => f.say_str(
|
||||
"atomic size",
|
||||
format!("{}{}", number.debug(source), unit.debug(source)),
|
||||
),
|
||||
AtomicToken::String { body } => f.say_str("atomic string", body.slice(source)),
|
||||
AtomicToken::ItVariable { name } => f.say_str("atomic it", name.slice(source)),
|
||||
AtomicToken::Variable { name } => f.say_str("atomic variable", name.slice(source)),
|
||||
AtomicToken::ExternalCommand { command } => {
|
||||
f.say_str("atomic external command", command.slice(source))
|
||||
}
|
||||
AtomicToken::ExternalWord { text } => {
|
||||
f.say_str("atomic external word", text.slice(source))
|
||||
}
|
||||
AtomicToken::GlobPattern { pattern } => f.say_str("atomic glob", pattern.slice(source)),
|
||||
AtomicToken::FilePath { path } => f.say_str("atomic path", path.slice(source)),
|
||||
AtomicToken::Word { text } => f.say_str("word", text.slice(source)),
|
||||
AtomicToken::SquareDelimited { .. } => f.say_simple("atomic square"),
|
||||
AtomicToken::ParenDelimited { .. } => f.say_simple("atomic paren"),
|
||||
AtomicToken::BraceDelimited { .. } => f.say_simple("atomic brace"),
|
||||
AtomicToken::Pipeline { .. } => f.say_simple("atomic pipeline"),
|
||||
AtomicToken::ShorthandFlag { name } => {
|
||||
f.say_str("atomic shorthand", name.slice(source))
|
||||
}
|
||||
AtomicToken::LonghandFlag { name } => f.say_str("atomic longhand", name.slice(source)),
|
||||
AtomicToken::Dot { .. } => f.say_simple("atomic dot"),
|
||||
AtomicToken::Operator { text } => f.say_str("atomic operator", text.slice(source)),
|
||||
AtomicToken::Whitespace { text } => {
|
||||
f.say_str("atomic whitespace", &format!("{:?}", text.slice(source)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_atom<'me, 'content>(
|
||||
token_nodes: &'me mut TokensIterator<'content>,
|
||||
expected: &'static str,
|
||||
context: &ExpandContext,
|
||||
rule: ExpansionRule,
|
||||
) -> Result<SpannedAtomicToken<'content>, ParseError> {
|
||||
token_nodes.with_expand_tracer(|_, tracer| tracer.start("atom"));
|
||||
|
||||
let result = expand_atom_inner(token_nodes, expected, context, rule);
|
||||
|
||||
token_nodes.with_expand_tracer(|_, tracer| match &result {
|
||||
Ok(result) => {
|
||||
tracer.add_result(Box::new(format!("{}", result.debug(context.source))));
|
||||
tracer.success();
|
||||
}
|
||||
|
||||
Err(err) => tracer.failed(err),
|
||||
});
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// If the caller of expand_atom throws away the returned atomic token returned, it
|
||||
/// must use a checkpoint to roll it back.
|
||||
pub fn expand_atom<'me, 'content>(
|
||||
fn expand_atom_inner<'me, 'content>(
|
||||
token_nodes: &'me mut TokensIterator<'content>,
|
||||
expected: &'static str,
|
||||
context: &ExpandContext,
|
||||
@ -414,6 +523,30 @@ pub fn expand_atom<'me, 'content>(
|
||||
},
|
||||
}
|
||||
|
||||
match rule.separate_members {
|
||||
false => {}
|
||||
true => {
|
||||
let mut next = token_nodes.peek_any();
|
||||
|
||||
match next.node {
|
||||
Some(token) if token.is_word() => {
|
||||
next.commit();
|
||||
return Ok(AtomicToken::Word { text: token.span() }.spanned(token.span()));
|
||||
}
|
||||
|
||||
Some(token) if token.is_int() => {
|
||||
next.commit();
|
||||
return Ok(AtomicToken::Number {
|
||||
number: RawNumber::Int(token.span()),
|
||||
}
|
||||
.spanned(token.span()));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to parse the head of the stream as a bare path. A bare path includes
|
||||
// words as well as `.`s, connected together without whitespace.
|
||||
match expand_syntax(&BarePathShape, token_nodes, context) {
|
||||
@ -515,13 +648,7 @@ pub fn expand_atom<'me, 'content>(
|
||||
|
||||
// if whitespace is disallowed, return an error
|
||||
WhitespaceHandling::RejectWhitespace => {
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
"whitespace".tagged(Tag {
|
||||
span: *span,
|
||||
anchor: None,
|
||||
}),
|
||||
))
|
||||
return Err(ParseError::mismatch(expected, "whitespace".spanned(*span)))
|
||||
}
|
||||
},
|
||||
|
||||
@ -530,7 +657,8 @@ pub fn expand_atom<'me, 'content>(
|
||||
|
||||
peeked.commit();
|
||||
return Ok(AtomicToken::Error {
|
||||
error: ShellError::type_error("token", other.tagged_type_name()).spanned(span),
|
||||
error: ShellError::type_error("token", other.type_name().spanned(span))
|
||||
.spanned(span),
|
||||
}
|
||||
.spanned(span));
|
||||
}
|
||||
@ -547,20 +675,14 @@ pub fn expand_atom<'me, 'content>(
|
||||
RawToken::ExternalCommand(_) if !rule.allow_external_command => {
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
token.type_name().tagged(Tag {
|
||||
span: token_span,
|
||||
anchor: None,
|
||||
}),
|
||||
token.type_name().spanned(token_span),
|
||||
))
|
||||
}
|
||||
// rule.allow_external_word
|
||||
RawToken::ExternalWord if !rule.allow_external_word => {
|
||||
return Err(ParseError::mismatch(
|
||||
expected,
|
||||
"external word".tagged(Tag {
|
||||
span: token_span,
|
||||
anchor: None,
|
||||
}),
|
||||
"external word".spanned(token_span),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,7 @@ impl ExpandExpression for NumberShape {
|
||||
RawToken::ExternalWord => {
|
||||
return Err(ParseError::mismatch(
|
||||
"number",
|
||||
"syntax error".tagged(Tag {
|
||||
span: token_span,
|
||||
anchor: None,
|
||||
}),
|
||||
"syntax error".spanned(token_span),
|
||||
))
|
||||
}
|
||||
RawToken::Variable(tag) => hir::Expression::variable(tag, token_span),
|
||||
|
@ -29,7 +29,7 @@ impl FallibleColorSyntax for PatternShape {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
_ => Err(ShellError::type_error("pattern", atom.tagged_type_name())),
|
||||
_ => Err(ShellError::type_error("pattern", atom.spanned_type_name())),
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -59,7 +59,10 @@ impl FallibleColorSyntax for PatternShape {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
_ => Err(ShellError::type_error("pattern", atom.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"pattern",
|
||||
other.type_name().spanned(atom.span),
|
||||
)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ impl ExpandSyntax for UnitShape {
|
||||
let unit = unit_size(span.slice(context.source), *span);
|
||||
|
||||
let (_, (number, unit)) = match unit {
|
||||
Err(_) => return Err(ParseError::mismatch("unit", "word".tagged(Tag::unknown()))),
|
||||
Err(_) => return Err(ParseError::mismatch("unit", "word".spanned(*span))),
|
||||
Ok((number, unit)) => (number, unit),
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,15 @@
|
||||
use crate::parser::hir::path::PathMember;
|
||||
use crate::parser::hir::syntax_shape::{
|
||||
color_fallible_syntax, color_fallible_syntax_with, expand_atom, expand_expr, expand_syntax,
|
||||
parse_single_node, AnyExpressionShape, AtomicToken, BareShape, ExpandContext, ExpandExpression,
|
||||
ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ParseError, Peeked, SkipSyntax,
|
||||
StringShape, TestSyntax, WhitespaceShape,
|
||||
};
|
||||
use crate::parser::{hir, hir::Expression, hir::TokensIterator, Operator, RawToken};
|
||||
use crate::parser::{hir, hir::Expression, hir::TokensIterator, Operator, RawNumber, RawToken};
|
||||
use crate::prelude::*;
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use serde::Serialize;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct VariablePathShape;
|
||||
@ -32,7 +33,7 @@ impl ExpandExpression for VariablePathShape {
|
||||
let head = expand_expr(&VariableShape, token_nodes, context)?;
|
||||
let start = head.span;
|
||||
let mut end = start;
|
||||
let mut tail: Vec<Spanned<String>> = vec![];
|
||||
let mut tail: Vec<PathMember> = vec![];
|
||||
|
||||
loop {
|
||||
match DotShape.skip(token_nodes, context) {
|
||||
@ -40,8 +41,8 @@ impl ExpandExpression for VariablePathShape {
|
||||
Ok(_) => {}
|
||||
}
|
||||
|
||||
let syntax = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||
let member = syntax.to_spanned_string(context.source);
|
||||
let member = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||
let member = member.to_path_member(context.source);
|
||||
|
||||
end = member.span;
|
||||
tail.push(member);
|
||||
@ -206,8 +207,21 @@ impl FallibleColorSyntax for PathTailShape {
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for Spanned<Vec<PathMember>> {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
f.say_list(
|
||||
"path tail",
|
||||
&self.item,
|
||||
|f| write!(f, "["),
|
||||
|f, item| write!(f, "{}", item.debug(source)),
|
||||
|f| write!(f, " "),
|
||||
|f| write!(f, "]"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpandSyntax for PathTailShape {
|
||||
type Output = Spanned<Vec<Spanned<String>>>;
|
||||
type Output = Spanned<Vec<PathMember>>;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"path continuation"
|
||||
@ -219,7 +233,7 @@ impl ExpandSyntax for PathTailShape {
|
||||
context: &ExpandContext,
|
||||
) -> Result<Self::Output, ParseError> {
|
||||
let mut end: Option<Span> = None;
|
||||
let mut tail = vec![];
|
||||
let mut tail: Vec<PathMember> = vec![];
|
||||
|
||||
loop {
|
||||
match DotShape.skip(token_nodes, context) {
|
||||
@ -227,23 +241,17 @@ impl ExpandSyntax for PathTailShape {
|
||||
Ok(_) => {}
|
||||
}
|
||||
|
||||
let syntax = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||
let member = syntax.to_spanned_string(context.source);
|
||||
let member = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||
let member = member.to_path_member(context.source);
|
||||
end = Some(member.span);
|
||||
tail.push(member);
|
||||
}
|
||||
|
||||
match end {
|
||||
None => {
|
||||
return Err(ParseError::mismatch("path tail", {
|
||||
let typed_span = token_nodes.typed_span_at_cursor();
|
||||
|
||||
Tagged {
|
||||
tag: typed_span.span.into(),
|
||||
item: typed_span.item,
|
||||
}
|
||||
}))
|
||||
}
|
||||
None => Err(ParseError::mismatch(
|
||||
"path tail",
|
||||
token_nodes.typed_span_at_cursor(),
|
||||
)),
|
||||
|
||||
Some(end) => Ok(tail.spanned(end)),
|
||||
}
|
||||
@ -252,7 +260,7 @@ impl ExpandSyntax for PathTailShape {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExpressionContinuation {
|
||||
DotSuffix(Span, Spanned<String>),
|
||||
DotSuffix(Span, PathMember),
|
||||
InfixSuffix(Spanned<Operator>, Expression),
|
||||
}
|
||||
|
||||
@ -303,7 +311,7 @@ impl ExpandSyntax for ExpressionContinuationShape {
|
||||
// If a `.` was matched, it's a `Path`, and we expect a `Member` next
|
||||
Ok(dot) => {
|
||||
let syntax = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||
let member = syntax.to_spanned_string(context.source);
|
||||
let member = syntax.to_path_member(context.source);
|
||||
|
||||
Ok(ExpressionContinuation::DotSuffix(dot, member))
|
||||
}
|
||||
@ -487,7 +495,7 @@ impl FallibleColorSyntax for VariableShape {
|
||||
shapes.push(FlatShape::ItVariable.spanned(atom.span));
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ShellError::type_error("variable", atom.tagged_type_name())),
|
||||
_ => Err(ShellError::type_error("variable", atom.spanned_type_name())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -528,22 +536,44 @@ impl FallibleColorSyntax for VariableShape {
|
||||
token_nodes.color_shape(FlatShape::ItVariable.spanned(atom.span));
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ParseError::mismatch("variable", atom.tagged_type_name()).into()),
|
||||
_ => Err(ParseError::mismatch("variable", atom.type_name().spanned(atom.span)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
pub enum Member {
|
||||
String(/* outer */ Span, /* inner */ Span),
|
||||
Int(BigInt, Span),
|
||||
Bare(Span),
|
||||
}
|
||||
|
||||
impl ShellTypeName for Member {
|
||||
fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Member::String(_, _) => "string",
|
||||
Member::Int(_, _) => "integer",
|
||||
Member::Bare(_) => "word",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Member {
|
||||
pub fn to_path_member(&self, source: &Text) -> PathMember {
|
||||
match self {
|
||||
Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer),
|
||||
Member::Int(int, span) => PathMember::int(int.clone(), *span),
|
||||
Member::Bare(span) => PathMember::string(span.slice(source), *span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for Member {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
match self {
|
||||
Member::String(outer, _) => write!(f, "member ({})", outer.slice(source)),
|
||||
Member::Bare(bare) => write!(f, "member ({})", bare.slice(source)),
|
||||
Member::String(outer, _) => write!(f, "{}", outer.slice(source)),
|
||||
Member::Int(_, int) => write!(f, "{}", int.slice(source)),
|
||||
Member::Bare(bare) => write!(f, "{}", bare.slice(source)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -552,15 +582,17 @@ impl HasSpan for Member {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Member::String(outer, ..) => *outer,
|
||||
Member::Int(_, int) => *int,
|
||||
Member::Bare(name) => *name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Member {
|
||||
pub(crate) fn to_expr(&self) -> hir::Expression {
|
||||
pub fn to_expr(&self) -> hir::Expression {
|
||||
match self {
|
||||
Member::String(outer, inner) => hir::Expression::string(*inner, *outer),
|
||||
Member::Int(number, span) => hir::Expression::number(number.clone(), *span),
|
||||
Member::Bare(span) => hir::Expression::string(*span, *span),
|
||||
}
|
||||
}
|
||||
@ -568,26 +600,10 @@ impl Member {
|
||||
pub(crate) fn span(&self) -> Span {
|
||||
match self {
|
||||
Member::String(outer, _inner) => *outer,
|
||||
Member::Int(_, span) => *span,
|
||||
Member::Bare(span) => *span,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_spanned_string(&self, source: &str) -> Spanned<String> {
|
||||
match self {
|
||||
Member::String(outer, inner) => inner.string(source).spanned(*outer),
|
||||
Member::Bare(span) => span.spanned_string(source),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tagged_type_name(&self) -> Tagged<&'static str> {
|
||||
match self {
|
||||
Member::String(outer, _inner) => "string".tagged(outer),
|
||||
Member::Bare(span) => "word".tagged(Tag {
|
||||
span: *span,
|
||||
anchor: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ColumnPathState {
|
||||
@ -603,10 +619,10 @@ impl ColumnPathState {
|
||||
match self {
|
||||
ColumnPathState::Initial => ColumnPathState::LeadingDot(dot),
|
||||
ColumnPathState::LeadingDot(_) => {
|
||||
ColumnPathState::Error(ParseError::mismatch("column", "dot".tagged(dot)))
|
||||
ColumnPathState::Error(ParseError::mismatch("column", "dot".spanned(dot)))
|
||||
}
|
||||
ColumnPathState::Dot(..) => {
|
||||
ColumnPathState::Error(ParseError::mismatch("column", "dot".tagged(dot)))
|
||||
ColumnPathState::Error(ParseError::mismatch("column", "dot".spanned(dot)))
|
||||
}
|
||||
ColumnPathState::Member(tag, members) => ColumnPathState::Dot(tag, members, dot),
|
||||
ColumnPathState::Error(err) => ColumnPathState::Error(err),
|
||||
@ -626,9 +642,10 @@ impl ColumnPathState {
|
||||
tags
|
||||
})
|
||||
}
|
||||
ColumnPathState::Member(..) => {
|
||||
ColumnPathState::Error(ParseError::mismatch("column", member.tagged_type_name()))
|
||||
}
|
||||
ColumnPathState::Member(..) => ColumnPathState::Error(ParseError::mismatch(
|
||||
"column",
|
||||
member.type_name().spanned(member.span()),
|
||||
)),
|
||||
ColumnPathState::Error(err) => ColumnPathState::Error(err),
|
||||
}
|
||||
}
|
||||
@ -637,10 +654,10 @@ impl ColumnPathState {
|
||||
match self {
|
||||
ColumnPathState::Initial => Err(next.type_error("column path")),
|
||||
ColumnPathState::LeadingDot(dot) => {
|
||||
Err(ParseError::mismatch("column", "dot".tagged(dot)))
|
||||
Err(ParseError::mismatch("column", "dot".spanned(dot)))
|
||||
}
|
||||
ColumnPathState::Dot(_tag, _members, dot) => {
|
||||
Err(ParseError::mismatch("column", "dot".tagged(dot)))
|
||||
Err(ParseError::mismatch("column", "dot".spanned(dot)))
|
||||
}
|
||||
ColumnPathState::Member(tag, tags) => Ok(tags.tagged(tag)),
|
||||
ColumnPathState::Error(err) => Err(err),
|
||||
@ -655,14 +672,14 @@ pub fn expand_column_path<'a, 'b>(
|
||||
let mut state = ColumnPathState::Initial;
|
||||
|
||||
loop {
|
||||
let member = MemberShape.expand_syntax(token_nodes, context);
|
||||
let member = expand_syntax(&MemberShape, token_nodes, context);
|
||||
|
||||
match member {
|
||||
Err(_) => break,
|
||||
Ok(member) => state = state.member(member),
|
||||
}
|
||||
|
||||
let dot = DotShape.expand_syntax(token_nodes, context);
|
||||
let dot = expand_syntax(&DotShape, token_nodes, context);
|
||||
|
||||
match dot {
|
||||
Err(_) => break,
|
||||
@ -783,26 +800,8 @@ impl FormatDebug for Tagged<Vec<Member>> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Getters, new)]
|
||||
pub struct ColumnPath {
|
||||
#[get = "pub"]
|
||||
path: Tagged<Vec<Member>>,
|
||||
}
|
||||
|
||||
impl HasSpan for ColumnPath {
|
||||
fn span(&self) -> Span {
|
||||
self.path.tag.span
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatDebug for ColumnPath {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
f.say("column path", self.path.item.debug(source))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpandSyntax for ColumnPathShape {
|
||||
type Output = ColumnPath;
|
||||
type Output = Tagged<Vec<Member>>;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"column path"
|
||||
@ -813,7 +812,7 @@ impl ExpandSyntax for ColumnPathShape {
|
||||
token_nodes: &'b mut TokensIterator<'a>,
|
||||
context: &ExpandContext,
|
||||
) -> Result<Self::Output, ParseError> {
|
||||
Ok(ColumnPath::new(expand_column_path(token_nodes, context)?))
|
||||
Ok(expand_column_path(token_nodes, context)?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -888,6 +887,55 @@ impl FallibleColorSyntax for MemberShape {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct IntMemberShape;
|
||||
|
||||
impl ExpandSyntax for IntMemberShape {
|
||||
type Output = Member;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"integer member"
|
||||
}
|
||||
|
||||
fn expand_syntax<'a, 'b>(
|
||||
&self,
|
||||
token_nodes: &'b mut TokensIterator<'a>,
|
||||
context: &ExpandContext,
|
||||
) -> Result<Self::Output, ParseError> {
|
||||
token_nodes.atomic_parse(|token_nodes| {
|
||||
let next = expand_atom(
|
||||
token_nodes,
|
||||
"integer member",
|
||||
context,
|
||||
ExpansionRule::new().separate_members(),
|
||||
)?;
|
||||
|
||||
match next.item {
|
||||
AtomicToken::Number {
|
||||
number: RawNumber::Int(int),
|
||||
} => Ok(Member::Int(
|
||||
BigInt::from_str(int.slice(context.source)).unwrap(),
|
||||
int,
|
||||
)),
|
||||
|
||||
AtomicToken::Word { text } => {
|
||||
let int = BigInt::from_str(text.slice(context.source));
|
||||
|
||||
match int {
|
||||
Ok(int) => return Ok(Member::Int(int, text)),
|
||||
Err(_) => Err(ParseError::mismatch("integer member", "word".spanned(text))),
|
||||
}
|
||||
}
|
||||
|
||||
other => Err(ParseError::mismatch(
|
||||
"integer member",
|
||||
other.type_name().spanned(next.span),
|
||||
)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpandSyntax for MemberShape {
|
||||
type Output = Member;
|
||||
|
||||
@ -900,6 +948,10 @@ impl ExpandSyntax for MemberShape {
|
||||
token_nodes: &mut TokensIterator<'_>,
|
||||
context: &ExpandContext,
|
||||
) -> Result<Member, ParseError> {
|
||||
if let Ok(int) = expand_syntax(&IntMemberShape, token_nodes, context) {
|
||||
return Ok(int);
|
||||
}
|
||||
|
||||
let bare = BareShape.test(token_nodes, context);
|
||||
if let Some(peeked) = bare {
|
||||
let node = peeked.not_eof("column")?.commit();
|
||||
@ -956,7 +1008,7 @@ impl FallibleColorSyntax for ColorableDotShape {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
other => Err(ShellError::type_error("dot", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error("dot", other.spanned_type_name())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -985,7 +1037,10 @@ impl FallibleColorSyntax for ColorableDotShape {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
other => Err(ShellError::type_error("dot", other.tagged_type_name())),
|
||||
other => Err(ShellError::type_error(
|
||||
"dot",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1020,7 +1075,7 @@ impl ExpandSyntax for DotShape {
|
||||
_ => {
|
||||
return Err(ParseError::mismatch(
|
||||
"dot",
|
||||
token.type_name().tagged(token_span),
|
||||
token.type_name().spanned(token_span),
|
||||
))
|
||||
}
|
||||
})
|
||||
@ -1108,7 +1163,7 @@ impl FallibleColorSyntax for InfixShape {
|
||||
// Otherwise, it's not a match
|
||||
_ => Err(ParseError::mismatch(
|
||||
"infix operator",
|
||||
token.type_name().tagged(token_span),
|
||||
token.type_name().spanned(token_span),
|
||||
)),
|
||||
}
|
||||
},
|
||||
|
@ -150,7 +150,7 @@ impl<'content, 'me> PeekedNode<'content, 'me> {
|
||||
pub fn peek_error(node: &Option<&TokenNode>, eof_span: Span, expected: &'static str) -> ParseError {
|
||||
match node {
|
||||
None => ParseError::unexpected_eof(expected, eof_span),
|
||||
Some(node) => ParseError::mismatch(expected, node.tagged_type_name()),
|
||||
Some(node) => ParseError::mismatch(expected, node.type_name().spanned(node.span())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,7 +343,7 @@ impl<'content> TokensIterator<'content> {
|
||||
pub fn expand_frame<T>(
|
||||
&mut self,
|
||||
desc: &'static str,
|
||||
block: impl FnOnce(&mut TokensIterator) -> Result<T, ParseError>,
|
||||
block: impl FnOnce(&mut TokensIterator<'content>) -> Result<T, ParseError>,
|
||||
) -> Result<T, ParseError>
|
||||
where
|
||||
T: std::fmt::Debug + FormatDebug + Clone + HasFallibleSpan + 'static,
|
||||
@ -458,6 +458,34 @@ impl<'content> TokensIterator<'content> {
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
/// Use a checkpoint when you need to peek more than one token ahead, but can't be sure
|
||||
/// that you'll succeed.
|
||||
pub fn atomic_parse<'me, T>(
|
||||
&'me mut self,
|
||||
block: impl FnOnce(&mut TokensIterator<'content>) -> Result<T, ParseError>,
|
||||
) -> Result<T, ParseError> {
|
||||
let state = &mut self.state;
|
||||
|
||||
let index = state.index;
|
||||
#[cfg(coloring_in_tokens)]
|
||||
let shape_start = state.shapes.len();
|
||||
let seen = state.seen.clone();
|
||||
|
||||
let checkpoint = Checkpoint {
|
||||
iterator: self,
|
||||
index,
|
||||
seen,
|
||||
committed: false,
|
||||
#[cfg(coloring_in_tokens)]
|
||||
shape_start,
|
||||
};
|
||||
|
||||
let value = block(checkpoint.iterator)?;
|
||||
|
||||
checkpoint.commit();
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
#[cfg(coloring_in_tokens)]
|
||||
/// Use a checkpoint when you need to peek more than one token ahead, but can't be sure
|
||||
/// that you'll succeed.
|
||||
|
@ -164,6 +164,12 @@ impl Into<Number> for BigDecimal {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Number> for BigInt {
|
||||
fn into(self) -> Number {
|
||||
Number::Int(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[tracable_parser]
|
||||
pub fn number(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
||||
let (input, number) = raw_number(input)?;
|
||||
|
@ -147,7 +147,10 @@ impl TokenNode {
|
||||
item: RawToken::Variable(inner_span),
|
||||
span: outer_span,
|
||||
}) => Ok((*outer_span, *inner_span)),
|
||||
_ => Err(ShellError::type_error("variable", self.tagged_type_name())),
|
||||
_ => Err(ShellError::type_error(
|
||||
"variable",
|
||||
self.type_name().spanned(self.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,6 +204,26 @@ impl TokenNode {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_word(&self) -> bool {
|
||||
match self {
|
||||
TokenNode::Token(Spanned {
|
||||
item: RawToken::Bare,
|
||||
..
|
||||
}) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_int(&self) -> bool {
|
||||
match self {
|
||||
TokenNode::Token(Spanned {
|
||||
item: RawToken::Number(RawNumber::Int(_)),
|
||||
..
|
||||
}) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dot(&self) -> bool {
|
||||
match self {
|
||||
TokenNode::Token(Spanned {
|
||||
@ -250,7 +273,10 @@ impl TokenNode {
|
||||
pub fn as_pipeline(&self) -> Result<Pipeline, ParseError> {
|
||||
match self {
|
||||
TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()),
|
||||
other => Err(ParseError::mismatch("pipeline", other.tagged_type_name())),
|
||||
other => Err(ParseError::mismatch(
|
||||
"pipeline",
|
||||
other.type_name().spanned(other.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use crate::parser::{
|
||||
Flag,
|
||||
};
|
||||
use crate::traits::ToDebug;
|
||||
use crate::{Span, Spanned, Tag, Text};
|
||||
use crate::{Span, Spanned, SpannedItem, Text};
|
||||
use log::trace;
|
||||
|
||||
pub fn parse_command_tail(
|
||||
@ -39,9 +39,8 @@ pub fn parse_command_tail(
|
||||
|
||||
if tail.at_end() {
|
||||
return Err(ParseError::argument_error(
|
||||
config.name.clone(),
|
||||
config.name.clone().spanned(flag.span),
|
||||
ArgumentError::MissingValueForName(name.to_string()),
|
||||
flag.span,
|
||||
));
|
||||
}
|
||||
|
||||
@ -60,9 +59,8 @@ pub fn parse_command_tail(
|
||||
|
||||
if tail.at_end() {
|
||||
return Err(ParseError::argument_error(
|
||||
config.name.clone(),
|
||||
config.name.clone().spanned(flag.span),
|
||||
ArgumentError::MissingValueForName(name.to_string()),
|
||||
flag.span,
|
||||
));
|
||||
}
|
||||
|
||||
@ -96,12 +94,8 @@ pub fn parse_command_tail(
|
||||
PositionalType::Mandatory(..) => {
|
||||
if tail.at_end_possible_ws() {
|
||||
return Err(ParseError::argument_error(
|
||||
config.name.clone(),
|
||||
config.name.clone().spanned(command_span),
|
||||
ArgumentError::MissingMandatoryPositional(arg.0.name().to_string()),
|
||||
Tag {
|
||||
span: command_span,
|
||||
anchor: None,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -593,9 +587,8 @@ fn extract_mandatory(
|
||||
|
||||
match flag {
|
||||
None => Err(ParseError::argument_error(
|
||||
config.name.clone(),
|
||||
config.name.clone().spanned(span),
|
||||
ArgumentError::MissingMandatoryFlag(name.to_string()),
|
||||
span,
|
||||
)),
|
||||
|
||||
Some((pos, flag)) => {
|
||||
|
@ -7,7 +7,6 @@ use derive_new::new;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum NamedType {
|
||||
@ -178,63 +177,7 @@ impl EvaluatedArgs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(new)]
|
||||
pub struct DebugEvaluatedPositional<'a> {
|
||||
positional: &'a Option<Vec<Tagged<Value>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DebugEvaluatedPositional<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.positional {
|
||||
None => write!(f, "None"),
|
||||
Some(positional) => f
|
||||
.debug_list()
|
||||
.entries(positional.iter().map(|p| p.debug()))
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(new)]
|
||||
pub struct DebugEvaluatedNamed<'a> {
|
||||
named: &'a Option<IndexMap<String, Tagged<Value>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DebugEvaluatedNamed<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.named {
|
||||
None => write!(f, "None"),
|
||||
Some(named) => f
|
||||
.debug_map()
|
||||
.entries(named.iter().map(|(k, v)| (k, v.debug())))
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugEvaluatedArgs<'a> {
|
||||
args: &'a EvaluatedArgs,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DebugEvaluatedArgs<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut s = f.debug_struct("Args");
|
||||
|
||||
s.field(
|
||||
"positional",
|
||||
&DebugEvaluatedPositional::new(&self.args.positional),
|
||||
);
|
||||
s.field("named", &DebugEvaluatedNamed::new(&self.args.named));
|
||||
|
||||
s.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl EvaluatedArgs {
|
||||
pub fn debug(&self) -> DebugEvaluatedArgs<'_> {
|
||||
DebugEvaluatedArgs { args: self }
|
||||
}
|
||||
|
||||
pub fn nth(&self, pos: usize) -> Option<&Tagged<Value>> {
|
||||
match &self.positional {
|
||||
None => None,
|
||||
|
@ -143,8 +143,8 @@ fn send_response<T: Serialize>(result: T) {
|
||||
let response_raw = serde_json::to_string(&response);
|
||||
|
||||
match response_raw {
|
||||
Ok(response) => println!("{}", response),
|
||||
Err(err) => println!("{}", err),
|
||||
Ok(response) => outln!("{}", response),
|
||||
Err(err) => outln!("{}", err),
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crossterm::{cursor, terminal, Attribute, RawScreen};
|
||||
use nu::{
|
||||
serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature, Tagged, Value,
|
||||
outln, serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature,
|
||||
Tagged, Value,
|
||||
};
|
||||
use pretty_hex::*;
|
||||
|
||||
@ -47,7 +48,7 @@ fn view_binary(
|
||||
}
|
||||
#[cfg(not(feature = "rawkey"))]
|
||||
{
|
||||
println!("Interactive binary viewing currently requires the 'rawkey' feature");
|
||||
outln!("Interactive binary viewing currently requires the 'rawkey' feature");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@ -117,7 +118,7 @@ impl RenderContext {
|
||||
);
|
||||
}
|
||||
}
|
||||
println!("{}", Attribute::Reset);
|
||||
outln!("{}", Attribute::Reset);
|
||||
Ok(())
|
||||
}
|
||||
fn render_to_screen_hires(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -174,7 +175,7 @@ impl RenderContext {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
println!("{}", Attribute::Reset);
|
||||
outln!("{}", Attribute::Reset);
|
||||
Ok(())
|
||||
}
|
||||
pub fn flush(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -264,7 +265,7 @@ pub fn view_contents(
|
||||
|
||||
if raw_image_buffer.is_none() {
|
||||
//Not yet supported
|
||||
println!("{:?}", buffer.hex_dump());
|
||||
outln!("{:?}", buffer.hex_dump());
|
||||
return Ok(());
|
||||
}
|
||||
let raw_image_buffer = raw_image_buffer.unwrap();
|
||||
@ -322,7 +323,7 @@ pub fn view_contents(
|
||||
}
|
||||
_ => {
|
||||
//Not yet supported
|
||||
println!("{:?}", buffer.hex_dump());
|
||||
outln!("{:?}", buffer.hex_dump());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
use nu::{
|
||||
serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxShape,
|
||||
Tagged, Value,
|
||||
serve_plugin, CallInfo, ColumnPath, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError,
|
||||
ShellTypeName, Signature, SpannedItem, SyntaxShape, Tagged, TaggedItem, Value,
|
||||
};
|
||||
|
||||
pub type ColumnPath = Tagged<Vec<Tagged<Value>>>;
|
||||
|
||||
struct Edit {
|
||||
field: Option<ColumnPath>,
|
||||
field: Option<Tagged<ColumnPath>>,
|
||||
value: Option<Value>,
|
||||
}
|
||||
impl Edit {
|
||||
@ -21,7 +19,7 @@ impl Edit {
|
||||
let value_tag = value.tag();
|
||||
match (value.item, self.value.clone()) {
|
||||
(obj @ Value::Row(_), Some(v)) => match &self.field {
|
||||
Some(f) => match obj.replace_data_at_column_path(value_tag, &f, v) {
|
||||
Some(f) => match obj.tagged(value_tag).replace_data_at_column_path(&f, v) {
|
||||
Some(v) => return Ok(v),
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
@ -65,12 +63,17 @@ impl Plugin for Edit {
|
||||
if let Some(args) = call_info.args.positional {
|
||||
match &args[0] {
|
||||
table @ Tagged {
|
||||
item: Value::Table(_),
|
||||
item: Value::Primitive(Primitive::ColumnPath(_)),
|
||||
..
|
||||
} => {
|
||||
self.field = Some(table.as_column_path()?);
|
||||
}
|
||||
value => return Err(ShellError::type_error("table", value.tagged_type_name())),
|
||||
value => {
|
||||
return Err(ShellError::type_error(
|
||||
"table",
|
||||
value.type_name().spanned(value.span()),
|
||||
))
|
||||
}
|
||||
}
|
||||
match &args[1] {
|
||||
Tagged { item: v, .. } => {
|
||||
|
@ -2,8 +2,8 @@
|
||||
extern crate indexmap;
|
||||
|
||||
use nu::{
|
||||
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
|
||||
SyntaxShape, Tag, Tagged, TaggedItem, Value,
|
||||
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError,
|
||||
ShellTypeName, Signature, SpannedItem, SyntaxShape, Tag, Tagged, TaggedItem, Value,
|
||||
};
|
||||
|
||||
struct Embed {
|
||||
@ -42,7 +42,12 @@ impl Plugin for Embed {
|
||||
self.field = Some(s.clone());
|
||||
self.values = Vec::new();
|
||||
}
|
||||
value => return Err(ShellError::type_error("string", value.tagged_type_name())),
|
||||
value => {
|
||||
return Err(ShellError::type_error(
|
||||
"string",
|
||||
value.type_name().spanned(value.span()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu::{
|
||||
did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
|
||||
ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
|
||||
did_you_mean, serve_plugin, span_for_spanned_list, CallInfo, ColumnPath, Plugin, Primitive,
|
||||
ReturnSuccess, ReturnValue, ShellError, ShellTypeName, Signature, SpannedItem, SyntaxShape,
|
||||
Tagged, TaggedItem, Value,
|
||||
};
|
||||
|
||||
enum Action {
|
||||
@ -14,10 +15,8 @@ pub enum SemVerAction {
|
||||
Patch,
|
||||
}
|
||||
|
||||
pub type ColumnPath = Tagged<Vec<Tagged<Value>>>;
|
||||
|
||||
struct Inc {
|
||||
field: Option<ColumnPath>,
|
||||
field: Option<Tagged<ColumnPath>>,
|
||||
error: Option<String>,
|
||||
action: Option<Action>,
|
||||
}
|
||||
@ -89,7 +88,7 @@ impl Inc {
|
||||
} else {
|
||||
return Err(ShellError::type_error(
|
||||
"incrementable value",
|
||||
value.tagged_type_name(),
|
||||
value.type_name().spanned(value.span()),
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -98,48 +97,32 @@ impl Inc {
|
||||
Some(ref f) => {
|
||||
let fields = f.clone();
|
||||
|
||||
let replace_for = value.item.get_data_by_column_path(
|
||||
value.tag(),
|
||||
f,
|
||||
Box::new(move |(obj_source, column_path_tried)| {
|
||||
let replace_for = value.get_data_by_column_path(
|
||||
&f,
|
||||
Box::new(move |(obj_source, column_path_tried, _)| {
|
||||
match did_you_mean(&obj_source, &column_path_tried) {
|
||||
Some(suggestions) => {
|
||||
return ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", suggestions[0].1),
|
||||
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||
span_for_spanned_list(fields.iter().map(|p| p.span)),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
return ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
"row does not contain this column",
|
||||
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||
span_for_spanned_list(fields.iter().map(|p| p.span)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let replacement = match replace_for {
|
||||
Ok(got) => match got {
|
||||
Some(result) => self.inc(result.map(|x| x.clone()))?,
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"inc could not find field to replace",
|
||||
"column name",
|
||||
value.tag(),
|
||||
))
|
||||
}
|
||||
},
|
||||
Err(reason) => return Err(reason),
|
||||
};
|
||||
let got = replace_for?;
|
||||
let replacement = self.inc(got.map(|x| x.clone()))?;
|
||||
|
||||
match value.item.replace_data_at_column_path(
|
||||
value.tag(),
|
||||
&f,
|
||||
replacement.item.clone(),
|
||||
) {
|
||||
match value.replace_data_at_column_path(&f, replacement.item.clone()) {
|
||||
Some(v) => return Ok(v),
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
@ -156,7 +139,7 @@ impl Inc {
|
||||
},
|
||||
_ => Err(ShellError::type_error(
|
||||
"incrementable value",
|
||||
value.tagged_type_name(),
|
||||
value.type_name().spanned(value.span()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -188,12 +171,17 @@ impl Plugin for Inc {
|
||||
for arg in args {
|
||||
match arg {
|
||||
table @ Tagged {
|
||||
item: Value::Table(_),
|
||||
item: Value::Primitive(Primitive::ColumnPath(_)),
|
||||
..
|
||||
} => {
|
||||
self.field = Some(table.as_column_path()?);
|
||||
}
|
||||
value => return Err(ShellError::type_error("table", value.tagged_type_name())),
|
||||
value => {
|
||||
return Err(ShellError::type_error(
|
||||
"table",
|
||||
value.type_name().spanned(value.span()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,8 +217,8 @@ mod tests {
|
||||
use super::{Inc, SemVerAction};
|
||||
use indexmap::IndexMap;
|
||||
use nu::{
|
||||
CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, Tag, Tagged, TaggedDictBuilder,
|
||||
TaggedItem, Value,
|
||||
CallInfo, EvaluatedArgs, PathMember, Plugin, RawPathMember, ReturnSuccess, SpannedItem,
|
||||
Tag, Tagged, TaggedDictBuilder, TaggedItem, Value,
|
||||
};
|
||||
|
||||
struct CallStub {
|
||||
@ -255,13 +243,13 @@ mod tests {
|
||||
}
|
||||
|
||||
fn with_parameter(&mut self, name: &str) -> &mut Self {
|
||||
let fields: Vec<Tagged<Value>> = name
|
||||
let fields: Vec<PathMember> = name
|
||||
.split(".")
|
||||
.map(|s| Value::string(s.to_string()).tagged(Tag::unknown()))
|
||||
.map(|s| RawPathMember::String(s.to_string()).spanned_unknown())
|
||||
.collect();
|
||||
|
||||
self.positionals
|
||||
.push(Value::Table(fields).tagged(Tag::unknown()));
|
||||
.push(Value::column_path(fields).tagged_unknown());
|
||||
self
|
||||
}
|
||||
|
||||
@ -344,14 +332,13 @@ mod tests {
|
||||
.is_ok());
|
||||
|
||||
assert_eq!(
|
||||
plugin.field.map(|f| f
|
||||
.iter()
|
||||
.map(|f| match &f.item {
|
||||
Value::Primitive(Primitive::String(s)) => s.clone(),
|
||||
_ => panic!(""),
|
||||
})
|
||||
.collect()),
|
||||
Some(vec!["package".to_string(), "version".to_string()])
|
||||
plugin
|
||||
.field
|
||||
.map(|f| f.iter().map(|f| f.item.clone()).collect()),
|
||||
Some(vec![
|
||||
RawPathMember::String("package".to_string()),
|
||||
RawPathMember::String("version".to_string())
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,11 @@
|
||||
use itertools::Itertools;
|
||||
use nu::{
|
||||
serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxShape,
|
||||
Tagged, TaggedItem, Value,
|
||||
serve_plugin, CallInfo, ColumnPath, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError,
|
||||
ShellTypeName, Signature, SpannedItem, SyntaxShape, Tagged, Value,
|
||||
};
|
||||
|
||||
pub type ColumnPath = Vec<Tagged<Value>>;
|
||||
|
||||
struct Insert {
|
||||
field: Option<ColumnPath>,
|
||||
value: Option<Value>,
|
||||
value: Option<Tagged<Value>>,
|
||||
}
|
||||
impl Insert {
|
||||
fn new() -> Insert {
|
||||
@ -20,38 +17,19 @@ impl Insert {
|
||||
|
||||
fn insert(&self, value: Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
|
||||
let value_tag = value.tag();
|
||||
match (value.item, self.value.clone()) {
|
||||
(obj @ Value::Row(_), Some(v)) => match &self.field {
|
||||
Some(f) => match obj.insert_data_at_column_path(value_tag.clone(), f, v) {
|
||||
Some(v) => return Ok(v),
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
format!(
|
||||
"add could not find place to insert field {:?} {}",
|
||||
obj,
|
||||
f.iter()
|
||||
.map(|i| {
|
||||
match &i.item {
|
||||
Value::Primitive(primitive) => primitive.format(None),
|
||||
_ => String::from(""),
|
||||
}
|
||||
})
|
||||
.join(".")
|
||||
),
|
||||
"column name",
|
||||
&value_tag,
|
||||
))
|
||||
}
|
||||
|
||||
match (&value, &self.value, &self.field) {
|
||||
(
|
||||
obj @ Tagged {
|
||||
item: Value::Row(_),
|
||||
..
|
||||
},
|
||||
None => Err(ShellError::labeled_error(
|
||||
"add needs a column name when adding a value to a table",
|
||||
"column name",
|
||||
value_tag,
|
||||
)),
|
||||
},
|
||||
(value, _) => Err(ShellError::type_error(
|
||||
Some(v),
|
||||
Some(field),
|
||||
) => obj.clone().insert_data_at_column_path(field, v.clone()),
|
||||
(value, ..) => Err(ShellError::type_error(
|
||||
"row",
|
||||
value.type_name().tagged(value_tag),
|
||||
value.type_name().spanned(value_tag),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -78,16 +56,21 @@ impl Plugin for Insert {
|
||||
if let Some(args) = call_info.args.positional {
|
||||
match &args[0] {
|
||||
table @ Tagged {
|
||||
item: Value::Table(_),
|
||||
item: Value::Primitive(Primitive::ColumnPath(_)),
|
||||
..
|
||||
} => {
|
||||
self.field = Some(table.as_column_path()?.item);
|
||||
}
|
||||
|
||||
value => return Err(ShellError::type_error("table", value.tagged_type_name())),
|
||||
value => {
|
||||
return Err(ShellError::type_error(
|
||||
"table",
|
||||
value.type_name().spanned(value.span()),
|
||||
))
|
||||
}
|
||||
}
|
||||
match &args[1] {
|
||||
Tagged { item: v, .. } => {
|
||||
v @ Tagged { .. } => {
|
||||
self.value = Some(v.clone());
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu::{
|
||||
did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
|
||||
ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
|
||||
did_you_mean, serve_plugin, span_for_spanned_list, CallInfo, ColumnPath, Plugin, Primitive,
|
||||
ReturnSuccess, ReturnValue, ShellError, ShellTypeName, Signature, SyntaxShape, Tagged,
|
||||
TaggedItem, Value,
|
||||
};
|
||||
use std::cmp;
|
||||
|
||||
@ -12,10 +13,8 @@ enum Action {
|
||||
Substring(usize, usize),
|
||||
}
|
||||
|
||||
pub type ColumnPath = Tagged<Vec<Tagged<Value>>>;
|
||||
|
||||
struct Str {
|
||||
field: Option<ColumnPath>,
|
||||
field: Option<Tagged<ColumnPath>>,
|
||||
params: Option<Vec<String>>,
|
||||
error: Option<String>,
|
||||
action: Option<Action>,
|
||||
@ -62,7 +61,7 @@ impl Str {
|
||||
Ok(applied)
|
||||
}
|
||||
|
||||
fn for_field(&mut self, column_path: ColumnPath) {
|
||||
fn for_field(&mut self, column_path: Tagged<ColumnPath>) {
|
||||
self.field = Some(column_path);
|
||||
}
|
||||
|
||||
@ -130,55 +129,33 @@ impl Str {
|
||||
Some(ref f) => {
|
||||
let fields = f.clone();
|
||||
|
||||
let replace_for = value.item.get_data_by_column_path(
|
||||
value.tag(),
|
||||
f,
|
||||
Box::new(move |(obj_source, column_path_tried)| {
|
||||
let replace_for =
|
||||
value.get_data_by_column_path(
|
||||
&f,
|
||||
Box::new(move |(obj_source, column_path_tried, error)| {
|
||||
match did_you_mean(&obj_source, &column_path_tried) {
|
||||
Some(suggestions) => {
|
||||
return ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", suggestions[0].1),
|
||||
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
return ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
"row does not contain this column",
|
||||
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
||||
span_for_spanned_list(fields.iter().map(|p| p.span)),
|
||||
)
|
||||
}
|
||||
None => return error,
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let replacement = match replace_for {
|
||||
Ok(got) => match got {
|
||||
Some(result) => self.strutils(result.map(|x| x.clone()))?,
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"inc could not find field to replace",
|
||||
"column name",
|
||||
value.tag(),
|
||||
))
|
||||
}
|
||||
},
|
||||
Err(reason) => return Err(reason),
|
||||
};
|
||||
let got = replace_for?;
|
||||
let replacement = self.strutils(got.map(|x| x.clone()))?;
|
||||
|
||||
match value.item.replace_data_at_column_path(
|
||||
value.tag(),
|
||||
f,
|
||||
replacement.item.clone(),
|
||||
) {
|
||||
match value.replace_data_at_column_path(&f, replacement.item.clone()) {
|
||||
Some(v) => return Ok(v),
|
||||
None => {
|
||||
return Err(ShellError::type_error(
|
||||
None => Err(ShellError::labeled_error(
|
||||
"str could not find field to replace",
|
||||
"column name",
|
||||
value.tagged_type_name(),
|
||||
))
|
||||
}
|
||||
value.tag(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
None => Err(ShellError::untagged_runtime_error(format!(
|
||||
@ -189,7 +166,7 @@ impl Str {
|
||||
},
|
||||
_ => Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
value.type_name(),
|
||||
value.item.type_name(),
|
||||
value.tag,
|
||||
)),
|
||||
}
|
||||
@ -245,27 +222,9 @@ impl Plugin for Str {
|
||||
}
|
||||
|
||||
if let Some(possible_field) = args.nth(0) {
|
||||
match possible_field {
|
||||
string @ Tagged {
|
||||
item: Value::Primitive(Primitive::String(_)),
|
||||
..
|
||||
} => {
|
||||
self.for_field(string.as_column_path()?);
|
||||
}
|
||||
table @ Tagged {
|
||||
item: Value::Table(_),
|
||||
..
|
||||
} => {
|
||||
self.field = Some(table.as_column_path()?);
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unrecognized type in params",
|
||||
possible_field.type_name(),
|
||||
&possible_field.tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
let possible_field = possible_field.as_column_path()?;
|
||||
|
||||
self.for_field(possible_field);
|
||||
}
|
||||
for param in args.positional_iter() {
|
||||
match param {
|
||||
@ -303,8 +262,8 @@ mod tests {
|
||||
use super::{Action, Str};
|
||||
use indexmap::IndexMap;
|
||||
use nu::{
|
||||
CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, Tag, Tagged, TaggedDictBuilder,
|
||||
TaggedItem, Value,
|
||||
CallInfo, EvaluatedArgs, Plugin, Primitive, RawPathMember, ReturnSuccess, Tag, Tagged,
|
||||
TaggedDictBuilder, TaggedItem, Value,
|
||||
};
|
||||
use num_bigint::BigInt;
|
||||
|
||||
@ -419,14 +378,13 @@ mod tests {
|
||||
.is_ok());
|
||||
|
||||
assert_eq!(
|
||||
plugin.field.map(|f| f
|
||||
.iter()
|
||||
.map(|f| match &f.item {
|
||||
Value::Primitive(Primitive::String(s)) => s.clone(),
|
||||
_ => panic!(""),
|
||||
})
|
||||
.collect()),
|
||||
Some(vec!["package".to_string(), "description".to_string()])
|
||||
plugin
|
||||
.field
|
||||
.map(|f| f.iter().cloned().map(|f| f.item).collect()),
|
||||
Some(vec![
|
||||
RawPathMember::String("package".to_string()),
|
||||
RawPathMember::String("description".to_string())
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crossterm::{cursor, terminal, RawScreen};
|
||||
use crossterm::{InputEvent, KeyEvent};
|
||||
use nu::{
|
||||
serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature, Tagged, Value,
|
||||
outln, serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature,
|
||||
Tagged, Value,
|
||||
};
|
||||
|
||||
use syntect::easy::HighlightLines;
|
||||
@ -15,6 +16,7 @@ enum DrawCommand {
|
||||
DrawString(Style, String),
|
||||
NextLine,
|
||||
}
|
||||
|
||||
struct TextView;
|
||||
|
||||
impl TextView {
|
||||
@ -202,7 +204,7 @@ fn scroll_view_lines_if_needed(draw_commands: Vec<DrawCommand>, use_color_buffer
|
||||
}
|
||||
}
|
||||
|
||||
println!("");
|
||||
outln!("");
|
||||
}
|
||||
|
||||
fn scroll_view(s: &str) {
|
||||
|
@ -23,12 +23,13 @@ macro_rules! stream {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_stream {
|
||||
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
||||
(target: $target:tt, source: $source:expr, $desc:tt = $expr:expr) => {{
|
||||
if log::log_enabled!(target: $target, log::Level::Trace) {
|
||||
use futures::stream::StreamExt;
|
||||
let source = $source.clone();
|
||||
|
||||
let objects = $expr.values.inspect(|o| {
|
||||
trace!(target: $target, "{} = {:#?}", $desc, o.debug());
|
||||
let objects = $expr.values.inspect(move |o| {
|
||||
trace!(target: $target, "{} = {}", $desc, o.debug(&source));
|
||||
});
|
||||
|
||||
$crate::stream::InputStream::from_stream(objects.boxed())
|
||||
@ -57,6 +58,38 @@ macro_rules! trace_out_stream {
|
||||
}};
|
||||
}
|
||||
|
||||
// These macros exist to differentiate between intentional writing to stdout
|
||||
// and stray printlns left by accident
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! outln {
|
||||
($($tokens:tt)*) => { println!($($tokens)*) }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! errln {
|
||||
($($tokens:tt)*) => { eprintln!($($tokens)*) }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dict {
|
||||
($( $key:expr => $value:expr ),*) => {
|
||||
$crate::data::dict::TaggedDictBuilder::build(Tag::unknown(), |d| {
|
||||
$(
|
||||
d.insert($key, $value);
|
||||
)*
|
||||
})
|
||||
};
|
||||
|
||||
([tag] => $tag:expr, $( $key:expr => $value:expr ),*) => {
|
||||
$crate::data::dict::TaggedDictBuilder::build($tag, |d| {
|
||||
$(
|
||||
d.insert($key, $value);
|
||||
)*
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use crate::cli::MaybeOwned;
|
||||
pub(crate) use crate::commands::command::{
|
||||
CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext,
|
||||
@ -67,7 +100,7 @@ pub(crate) use crate::context::CommandRegistry;
|
||||
pub(crate) use crate::context::{AnchorLocation, Context};
|
||||
pub(crate) use crate::data::base as value;
|
||||
pub(crate) use crate::data::meta::{
|
||||
tag_for_tagged_list, HasFallibleSpan, HasSpan, Span, Spanned, SpannedItem, Tag, Tagged,
|
||||
span_for_spanned_list, HasFallibleSpan, HasSpan, Span, Spanned, SpannedItem, Tag, Tagged,
|
||||
TaggedItem,
|
||||
};
|
||||
pub(crate) use crate::data::types::ExtractType;
|
||||
@ -83,7 +116,10 @@ pub(crate) use crate::shell::help_shell::HelpShell;
|
||||
pub(crate) use crate::shell::shell_manager::ShellManager;
|
||||
pub(crate) use crate::shell::value_shell::ValueShell;
|
||||
pub(crate) use crate::stream::{InputStream, OutputStream};
|
||||
pub(crate) use crate::traits::{DebugFormatter, FormatDebug, HasTag, ToDebug};
|
||||
pub(crate) use crate::traits::{
|
||||
DebugDoc, DebugDocBuilder, DebugFormatter, FormatDebug, HasTag, PrettyDebug, PrettyType,
|
||||
ShellTypeName, SpannedTypeName, ToDebug,
|
||||
};
|
||||
pub(crate) use crate::Text;
|
||||
pub(crate) use async_stream::stream as async_stream;
|
||||
pub(crate) use bigdecimal::BigDecimal;
|
||||
|
@ -27,7 +27,11 @@ impl HelpShell {
|
||||
spec.insert("name", cmd);
|
||||
spec.insert(
|
||||
"description",
|
||||
value.get_data_by_key("usage").unwrap().as_string().unwrap(),
|
||||
value
|
||||
.get_data_by_key("usage".spanned_unknown())
|
||||
.unwrap()
|
||||
.as_string()
|
||||
.unwrap(),
|
||||
);
|
||||
spec.insert_tagged("details", value);
|
||||
|
||||
@ -70,7 +74,7 @@ impl HelpShell {
|
||||
for p in full_path.iter() {
|
||||
match p {
|
||||
x if x == sep => {}
|
||||
step => match viewed.get_data_by_key(step.to_str().unwrap()) {
|
||||
step => match viewed.get_data_by_key(step.to_str().unwrap().spanned_unknown()) {
|
||||
Some(v) => {
|
||||
viewed = v.clone();
|
||||
}
|
||||
|
@ -106,10 +106,10 @@ impl Highlighter for Helper {
|
||||
trace!(target: "nu::color_syntax", "{:#?}", tokens.color_tracer());
|
||||
|
||||
if log_enabled!(target: "nu::color_syntax", log::Level::Debug) {
|
||||
println!("");
|
||||
outln!("");
|
||||
ptree::print_tree(&tokens.color_tracer().clone().print(Text::from(line)))
|
||||
.unwrap();
|
||||
println!("");
|
||||
outln!("");
|
||||
}
|
||||
|
||||
for shape in shapes {
|
||||
|
@ -40,7 +40,7 @@ impl ValueShell {
|
||||
for p in full_path.iter() {
|
||||
match p {
|
||||
x if x == sep => {}
|
||||
step => match viewed.get_data_by_key(step.to_str().unwrap()) {
|
||||
step => match viewed.get_data_by_key(step.to_str().unwrap().spanned_unknown()) {
|
||||
Some(v) => {
|
||||
viewed = v.clone();
|
||||
}
|
||||
|
419
src/traits.rs
419
src/traits.rs
@ -1,6 +1,36 @@
|
||||
use crate::prelude::*;
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use pretty::{BoxAllocator, DocAllocator};
|
||||
use std::fmt::{self, Write};
|
||||
use std::hash::Hash;
|
||||
use termcolor::{Color, ColorSpec};
|
||||
|
||||
pub trait ShellTypeName {
|
||||
fn type_name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl<T: ShellTypeName> ShellTypeName for &T {
|
||||
fn type_name(&self) -> &'static str {
|
||||
(*self).type_name()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SpannedTypeName {
|
||||
fn spanned_type_name(&self) -> Spanned<&'static str>;
|
||||
}
|
||||
|
||||
impl<T: ShellTypeName> SpannedTypeName for Spanned<T> {
|
||||
fn spanned_type_name(&self) -> Spanned<&'static str> {
|
||||
self.item.type_name().spanned(self.span)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ShellTypeName> SpannedTypeName for Tagged<T> {
|
||||
fn spanned_type_name(&self) -> Spanned<&'static str> {
|
||||
self.item.type_name().spanned(self.tag.span)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Debuggable<'a, T: FormatDebug> {
|
||||
inner: &'a T,
|
||||
@ -13,7 +43,7 @@ impl FormatDebug for str {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToDebug> fmt::Display for Debuggable<'_, T> {
|
||||
impl<T: ToDebug> fmt::Debug for Debuggable<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.fmt_debug(
|
||||
&mut DebugFormatter::new(
|
||||
@ -26,11 +56,24 @@ impl<T: ToDebug> fmt::Display for Debuggable<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToDebug> fmt::Display for Debuggable<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.fmt_display(
|
||||
&mut DebugFormatter::new(
|
||||
f,
|
||||
ansi_term::Color::White.bold(),
|
||||
ansi_term::Color::Black.bold(),
|
||||
),
|
||||
self.source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasTag {
|
||||
fn tag(&self) -> Tag;
|
||||
}
|
||||
|
||||
#[derive(new)]
|
||||
#[derive(Getters, new)]
|
||||
pub struct DebugFormatter<'me, 'args> {
|
||||
formatter: &'me mut std::fmt::Formatter<'args>,
|
||||
style: ansi_term::Style,
|
||||
@ -38,6 +81,10 @@ pub struct DebugFormatter<'me, 'args> {
|
||||
}
|
||||
|
||||
impl<'me, 'args> DebugFormatter<'me, 'args> {
|
||||
pub fn say_simple(&mut self, kind: &str) -> std::fmt::Result {
|
||||
write!(self, "{}", self.style.paint(kind))
|
||||
}
|
||||
|
||||
pub fn say<'debuggable>(
|
||||
&mut self,
|
||||
kind: &str,
|
||||
@ -72,6 +119,40 @@ impl<'me, 'args> DebugFormatter<'me, 'args> {
|
||||
block(self)
|
||||
}
|
||||
|
||||
pub fn say_list<T, U: IntoIterator<Item = T>>(
|
||||
&mut self,
|
||||
kind: &str,
|
||||
list: U,
|
||||
open: impl Fn(&mut Self) -> std::fmt::Result,
|
||||
mut block: impl FnMut(&mut Self, &T) -> std::fmt::Result,
|
||||
interleave: impl Fn(&mut Self) -> std::fmt::Result,
|
||||
close: impl Fn(&mut Self) -> std::fmt::Result,
|
||||
) -> std::fmt::Result {
|
||||
write!(self, "{}", self.style.paint(kind))?;
|
||||
write!(self, "{}", self.default_style.paint(" "))?;
|
||||
open(self)?;
|
||||
write!(self, " ")?;
|
||||
|
||||
let mut list = list.into_iter();
|
||||
|
||||
let first = match list.next() {
|
||||
None => return Ok(()),
|
||||
Some(first) => first,
|
||||
};
|
||||
|
||||
block(self, &first)?;
|
||||
|
||||
for item in list {
|
||||
interleave(self)?;
|
||||
block(self, &item)?;
|
||||
}
|
||||
|
||||
write!(self, " ")?;
|
||||
close(self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn say_dict<'debuggable>(
|
||||
&mut self,
|
||||
kind: &str,
|
||||
@ -111,8 +192,342 @@ impl<'a, 'b> std::fmt::Write for DebugFormatter<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
|
||||
pub enum ShellStyle {
|
||||
Delimiter,
|
||||
Key,
|
||||
Value,
|
||||
Equals,
|
||||
Kind,
|
||||
Keyword,
|
||||
Primitive,
|
||||
Opaque,
|
||||
Description,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl From<ShellAnnotation> for ColorSpec {
|
||||
fn from(ann: ShellAnnotation) -> ColorSpec {
|
||||
match ann.style {
|
||||
ShellStyle::Delimiter => ColorSpec::new()
|
||||
.set_fg(Some(Color::White))
|
||||
.set_intense(false)
|
||||
.clone(),
|
||||
ShellStyle::Key => ColorSpec::new()
|
||||
.set_fg(Some(Color::Black))
|
||||
.set_intense(true)
|
||||
.clone(),
|
||||
ShellStyle::Value => ColorSpec::new()
|
||||
.set_fg(Some(Color::White))
|
||||
.set_intense(true)
|
||||
.clone(),
|
||||
ShellStyle::Equals => ColorSpec::new()
|
||||
.set_fg(Some(Color::Black))
|
||||
.set_intense(true)
|
||||
.clone(),
|
||||
ShellStyle::Kind => ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
|
||||
ShellStyle::Keyword => ColorSpec::new().set_fg(Some(Color::Magenta)).clone(),
|
||||
ShellStyle::Primitive => ColorSpec::new()
|
||||
.set_fg(Some(Color::Green))
|
||||
.set_intense(true)
|
||||
.clone(),
|
||||
ShellStyle::Opaque => ColorSpec::new()
|
||||
.set_fg(Some(Color::Yellow))
|
||||
.set_intense(true)
|
||||
.clone(),
|
||||
ShellStyle::Description => ColorSpec::new()
|
||||
.set_fg(Some(Color::Black))
|
||||
.set_intense(true)
|
||||
.clone(),
|
||||
ShellStyle::Error => ColorSpec::new()
|
||||
.set_fg(Some(Color::Red))
|
||||
.set_intense(true)
|
||||
.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Hash, new)]
|
||||
pub struct ShellAnnotation {
|
||||
style: ShellStyle,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ShellAnnotation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self.style)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellAnnotation {
|
||||
pub fn style(style: impl Into<ShellStyle>) -> ShellAnnotation {
|
||||
ShellAnnotation {
|
||||
style: style.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type PrettyDebugDoc =
|
||||
pretty::Doc<'static, pretty::BoxDoc<'static, ShellAnnotation>, ShellAnnotation>;
|
||||
|
||||
pub type PrettyDebugDocBuilder = pretty::DocBuilder<'static, pretty::BoxAllocator, ShellAnnotation>;
|
||||
|
||||
#[derive(Clone, new)]
|
||||
pub struct DebugDocBuilder {
|
||||
pub inner: PrettyDebugDocBuilder,
|
||||
}
|
||||
|
||||
impl PrettyDebug for DebugDocBuilder {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for DebugDocBuilder {
|
||||
type Output = DebugDocBuilder;
|
||||
|
||||
fn add(self, rhs: DebugDocBuilder) -> DebugDocBuilder {
|
||||
DebugDocBuilder::new(self.inner.append(rhs.inner))
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugDocBuilder {
|
||||
pub fn from_doc(doc: DebugDoc) -> DebugDocBuilder {
|
||||
DebugDocBuilder {
|
||||
inner: BoxAllocator.nil().append(doc),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blank() -> DebugDocBuilder {
|
||||
BoxAllocator.nil().into()
|
||||
}
|
||||
|
||||
pub fn delimiter(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Delimiter)
|
||||
}
|
||||
|
||||
pub fn key(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Key)
|
||||
}
|
||||
|
||||
pub fn value(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Value)
|
||||
}
|
||||
|
||||
pub fn as_value(self) -> DebugDocBuilder {
|
||||
self.inner
|
||||
.annotate(ShellAnnotation::style(ShellStyle::Value))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn equals() -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled("=", ShellStyle::Equals)
|
||||
}
|
||||
|
||||
pub fn kind(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Kind)
|
||||
}
|
||||
|
||||
pub fn as_kind(self) -> DebugDocBuilder {
|
||||
self.inner
|
||||
.annotate(ShellAnnotation::style(ShellStyle::Kind))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn keyword(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Keyword)
|
||||
}
|
||||
|
||||
pub fn primitive(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(format!("{}", string), ShellStyle::Primitive)
|
||||
}
|
||||
|
||||
pub fn opaque(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Opaque)
|
||||
}
|
||||
|
||||
pub fn description(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Description)
|
||||
}
|
||||
|
||||
pub fn error(string: impl std::fmt::Display) -> DebugDocBuilder {
|
||||
DebugDocBuilder::styled(string, ShellStyle::Error)
|
||||
}
|
||||
|
||||
pub fn delimit(start: &str, doc: DebugDocBuilder, end: &str) -> DebugDocBuilder {
|
||||
DebugDocBuilder::delimiter(start) + doc + DebugDocBuilder::delimiter(end)
|
||||
}
|
||||
|
||||
pub fn space() -> DebugDocBuilder {
|
||||
BoxAllocator.space().into()
|
||||
}
|
||||
|
||||
pub fn newline() -> DebugDocBuilder {
|
||||
BoxAllocator.newline().into()
|
||||
}
|
||||
|
||||
pub fn group(self) -> DebugDocBuilder {
|
||||
self.inner.group().into()
|
||||
}
|
||||
|
||||
pub fn nest(self) -> DebugDocBuilder {
|
||||
self.inner.nest(1).group().into()
|
||||
}
|
||||
|
||||
pub fn intersperse(
|
||||
list: impl IntoIterator<Item = DebugDocBuilder>,
|
||||
separator: DebugDocBuilder,
|
||||
) -> DebugDocBuilder {
|
||||
BoxAllocator.intersperse(list, separator).into()
|
||||
}
|
||||
|
||||
pub fn list(list: impl IntoIterator<Item = DebugDocBuilder>) -> DebugDocBuilder {
|
||||
let mut result: DebugDocBuilder = BoxAllocator.nil().into();
|
||||
|
||||
for item in list {
|
||||
result = result + item;
|
||||
}
|
||||
|
||||
result.into()
|
||||
}
|
||||
|
||||
fn styled(string: impl std::fmt::Display, style: ShellStyle) -> DebugDocBuilder {
|
||||
BoxAllocator
|
||||
.text(string.to_string())
|
||||
.annotate(ShellAnnotation::style(style))
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DebugDocBuilder {
|
||||
type Target = PrettyDebugDocBuilder;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, new)]
|
||||
pub struct DebugDoc {
|
||||
pub inner: PrettyDebugDoc,
|
||||
}
|
||||
|
||||
pub trait PrettyDebug {
|
||||
fn pretty_debug(&self) -> DebugDocBuilder;
|
||||
|
||||
fn to_doc(&self) -> DebugDoc {
|
||||
DebugDoc::new(self.pretty_doc())
|
||||
}
|
||||
|
||||
fn pretty_doc(&self) -> PrettyDebugDoc {
|
||||
let builder = self.pretty_debug();
|
||||
builder.inner.into()
|
||||
}
|
||||
|
||||
fn pretty_builder(&self) -> PrettyDebugDocBuilder {
|
||||
let doc = self.pretty_debug();
|
||||
doc.inner
|
||||
}
|
||||
|
||||
fn plain_string(&self, width: usize) -> String {
|
||||
let doc = self.pretty_doc();
|
||||
let mut buffer = termcolor::Buffer::no_color();
|
||||
|
||||
doc.render_raw(
|
||||
width,
|
||||
&mut crate::parser::debug::TermColored::new(&mut buffer),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
String::from_utf8_lossy(buffer.as_slice()).to_string()
|
||||
}
|
||||
|
||||
fn colored_string(&self, width: usize) -> String {
|
||||
let doc = self.pretty_doc();
|
||||
let mut buffer = termcolor::Buffer::ansi();
|
||||
|
||||
doc.render_raw(
|
||||
width,
|
||||
&mut crate::parser::debug::TermColored::new(&mut buffer),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
String::from_utf8_lossy(buffer.as_slice()).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<DebugDocBuilder> for PrettyDebugDocBuilder {
|
||||
fn into(self) -> DebugDocBuilder {
|
||||
DebugDocBuilder { inner: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DebugDoc {
|
||||
type Target = PrettyDebugDoc;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DebugDoc> for PrettyDebugDoc {
|
||||
fn from(input: DebugDoc) -> PrettyDebugDoc {
|
||||
input.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<PrettyDebugDoc> for DebugDocBuilder {
|
||||
fn into(self) -> PrettyDebugDoc {
|
||||
self.inner.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn hash_doc<H: std::hash::Hasher>(doc: &PrettyDebugDoc, state: &mut H) {
|
||||
match doc {
|
||||
pretty::Doc::Nil => 0u8.hash(state),
|
||||
pretty::Doc::Append(a, b) => {
|
||||
1u8.hash(state);
|
||||
hash_doc(&*a, state);
|
||||
hash_doc(&*b, state);
|
||||
}
|
||||
pretty::Doc::Group(a) => {
|
||||
2u8.hash(state);
|
||||
hash_doc(&*a, state);
|
||||
}
|
||||
pretty::Doc::Nest(a, b) => {
|
||||
3u8.hash(state);
|
||||
a.hash(state);
|
||||
hash_doc(&*b, state);
|
||||
}
|
||||
pretty::Doc::Space => 4u8.hash(state),
|
||||
pretty::Doc::Newline => 5u8.hash(state),
|
||||
pretty::Doc::Text(t) => {
|
||||
6u8.hash(state);
|
||||
t.hash(state);
|
||||
}
|
||||
pretty::Doc::Annotated(a, b) => {
|
||||
7u8.hash(state);
|
||||
a.hash(state);
|
||||
hash_doc(&*b, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for DebugDoc {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
hash_doc(&self.inner, state);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PrettyType {
|
||||
fn pretty_type(&self) -> DebugDocBuilder;
|
||||
}
|
||||
|
||||
pub trait FormatDebug: std::fmt::Debug {
|
||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result;
|
||||
|
||||
fn fmt_display(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||
self.fmt_debug(f, source)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToDebug: Sized + FormatDebug {
|
||||
|
17
src/utils.rs
17
src/utils.rs
@ -1,15 +1,16 @@
|
||||
use crate::data::meta::Tagged;
|
||||
use crate::data::Value;
|
||||
use crate::errors::ShellError;
|
||||
use crate::{PathMember, RawPathMember};
|
||||
use std::fmt;
|
||||
use std::ops::Div;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
pub fn did_you_mean(
|
||||
obj_source: &Value,
|
||||
field_tried: &Tagged<Value>,
|
||||
) -> Option<Vec<(usize, String)>> {
|
||||
let field_tried = field_tried.as_string().unwrap();
|
||||
pub fn did_you_mean(obj_source: &Value, field_tried: &PathMember) -> Option<Vec<(usize, String)>> {
|
||||
let field_tried = match &field_tried.item {
|
||||
RawPathMember::String(string) => string.clone(),
|
||||
RawPathMember::Int(int) => format!("{}", int),
|
||||
};
|
||||
|
||||
let possibilities = obj_source.data_descriptors();
|
||||
|
||||
@ -25,10 +26,10 @@ pub fn did_you_mean(
|
||||
|
||||
if possible_matches.len() > 0 {
|
||||
possible_matches.sort();
|
||||
return Some(possible_matches);
|
||||
}
|
||||
|
||||
Some(possible_matches)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AbsoluteFile {
|
||||
|
@ -203,9 +203,18 @@ fn errors_fetching_by_index_out_of_bounds() {
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.contains("Row not found"));
|
||||
assert!(actual.contains("There isn't a row indexed at '3'"));
|
||||
assert!(actual.contains("The table only has 3 rows (0..2)"))
|
||||
assert!(
|
||||
actual.contains("Row not found"),
|
||||
format!("actual: {:?}", actual)
|
||||
);
|
||||
assert!(
|
||||
actual.contains("There isn't a row indexed at 3"),
|
||||
format!("actual: {:?}", actual)
|
||||
);
|
||||
assert!(
|
||||
actual.contains("The table only has 3 rows (0 to 2)"),
|
||||
format!("actual: {:?}", actual)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@ -214,10 +223,13 @@ fn requires_at_least_one_column_member_path() {
|
||||
Playground::setup("get_test_9", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![EmptyFile("andres.txt")]);
|
||||
|
||||
let actual = nu_error!(
|
||||
cwd: dirs.test(), "ls | get"
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), "ls | get | get type | echo $it"
|
||||
);
|
||||
|
||||
assert!(actual.contains("requires member parameter"));
|
||||
assert_eq!(
|
||||
actual,
|
||||
"[row: name, type, size, created, accessed, modified]"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ fn open_can_parse_json() {
|
||||
fn open_can_parse_xml() {
|
||||
let actual = nu!(
|
||||
cwd: "tests/fixtures/formats",
|
||||
"open jonathan.xml | get rss.channel.item.link | echo $it"
|
||||
"open jonathan.xml | get rss.channel | get item | get link | echo $it"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -247,6 +247,80 @@ fn last_gets_last_row_when_no_amount_given() {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
Playground::setup("get_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"sample.toml",
|
||||
r#"
|
||||
nu_party_venue = "zion"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), h::pipeline(
|
||||
r#"
|
||||
open sample.toml
|
||||
| get nu_party_venue
|
||||
| echo $it
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual, "zion");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_more_than_one_member() {
|
||||
Playground::setup("get_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"sample.toml",
|
||||
r#"
|
||||
[[fortune_tellers]]
|
||||
name = "Andrés N. Robalino"
|
||||
arepas = 1
|
||||
broken_builds = 0
|
||||
|
||||
[[fortune_tellers]]
|
||||
name = "Jonathan Turner"
|
||||
arepas = 1
|
||||
broken_builds = 1
|
||||
|
||||
[[fortune_tellers]]
|
||||
name = "Yehuda Katz"
|
||||
arepas = 1
|
||||
broken_builds = 1
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), h::pipeline(
|
||||
r#"
|
||||
open sample.toml
|
||||
| get fortune_tellers
|
||||
| get arepas broken_builds
|
||||
| sum
|
||||
| echo $it
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual, "5");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_requires_at_least_one_member() {
|
||||
Playground::setup("first_test_3", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![EmptyFile("andres.txt")]);
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), "ls | get"
|
||||
);
|
||||
|
||||
assert!(actual.contains("[row: name"), format!("{:?}", actual));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines() {
|
||||
let actual = nu!(
|
||||
|
@ -525,8 +525,7 @@ fn can_convert_table_to_bson_and_back_into_table() {
|
||||
| to-bson
|
||||
| from-bson
|
||||
| get root
|
||||
| nth 1
|
||||
| get b
|
||||
| get 1.b
|
||||
| echo $it
|
||||
"#
|
||||
));
|
||||
|
Loading…
Reference in New Issue
Block a user