mirror of
https://github.com/nushell/nushell
synced 2024-12-26 04:53:09 +00:00
--no-edit
This commit is contained in:
parent
388fc24191
commit
cdb0eeafa2
84 changed files with 3927 additions and 1402 deletions
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)",
|
"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]]
|
[[package]]
|
||||||
name = "clipboard"
|
name = "clipboard"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -365,6 +376,21 @@ dependencies = [
|
||||||
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.1.4"
|
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)",
|
"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]]
|
[[package]]
|
||||||
name = "futures-core-preview"
|
name = "futures-core-preview"
|
||||||
version = "0.3.0-alpha.19"
|
version = "0.3.0-alpha.19"
|
||||||
|
@ -834,6 +865,17 @@ name = "futures-io-preview"
|
||||||
version = "0.3.0-alpha.19"
|
version = "0.3.0-alpha.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "futures-preview"
|
name = "futures-preview"
|
||||||
version = "0.3.0-alpha.19"
|
version = "0.3.0-alpha.19"
|
||||||
|
@ -852,11 +894,30 @@ name = "futures-sink-preview"
|
||||||
version = "0.3.0-alpha.19"
|
version = "0.3.0-alpha.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "futures-timer"
|
name = "futures-timer"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "futures-util-preview"
|
name = "futures-util-preview"
|
||||||
version = "0.3.0-alpha.19"
|
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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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-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-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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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-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_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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"trash 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1917,6 +1982,14 @@ name = "ppv-lite86"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "pretty-hex"
|
name = "pretty-hex"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -1956,6 +2029,21 @@ dependencies = [
|
||||||
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -2654,6 +2742,14 @@ dependencies = [
|
||||||
"wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -2731,6 +2827,11 @@ dependencies = [
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.11.2"
|
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 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 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 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 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 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 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 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 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 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 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"
|
"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 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 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-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-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-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-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-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-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-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-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 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"
|
"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 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 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 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-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_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 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 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 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 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"
|
"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 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 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 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 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 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"
|
"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.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 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 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 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 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"
|
"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"
|
strip-ansi-escapes = "0.1.0"
|
||||||
calamine = "0.16"
|
calamine = "0.16"
|
||||||
umask = "0.1"
|
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 }
|
neso = { version = "0.5.0", optional = true }
|
||||||
crossterm = { version = "0.10.2", 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 }
|
image = { version = "0.22.2", default_features = false, features = ["png_codec", "jpeg"], optional = true }
|
||||||
starship = { version = "0.26.4", optional = true}
|
starship = { version = "0.26.4", optional = true}
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["textview", "sys", "ps"]
|
default = ["textview", "sys", "ps"]
|
||||||
raw-key = ["rawkey", "neso"]
|
raw-key = ["rawkey", "neso"]
|
||||||
|
|
|
@ -4,21 +4,12 @@ command = "lalrpop"
|
||||||
args = ["src/parser/parser.lalrpop"]
|
args = ["src/parser/parser.lalrpop"]
|
||||||
|
|
||||||
[tasks.baseline]
|
[tasks.baseline]
|
||||||
dependencies = ["lalrpop"]
|
|
||||||
|
|
||||||
[tasks.build]
|
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["build"]
|
args = ["build", "--bins"]
|
||||||
dependencies = ["lalrpop"]
|
|
||||||
|
|
||||||
[tasks.run]
|
[tasks.run]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["run", "--release"]
|
args = ["run"]
|
||||||
dependencies = ["baseline"]
|
|
||||||
|
|
||||||
[tasks.release]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["build", "--release"]
|
|
||||||
dependencies = ["baseline"]
|
dependencies = ["baseline"]
|
||||||
|
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
|
|
58
src/cli.rs
58
src/cli.rs
|
@ -9,6 +9,7 @@ use crate::context::Context;
|
||||||
use crate::data::config;
|
use crate::data::config;
|
||||||
use crate::data::Value;
|
use crate::data::Value;
|
||||||
pub(crate) use crate::errors::ShellError;
|
pub(crate) use crate::errors::ShellError;
|
||||||
|
use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult};
|
||||||
#[cfg(not(feature = "starship-prompt"))]
|
#[cfg(not(feature = "starship-prompt"))]
|
||||||
use crate::git::current_branch;
|
use crate::git::current_branch;
|
||||||
use crate::parser::registry::Signature;
|
use crate::parser::registry::Signature;
|
||||||
|
@ -260,7 +261,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
whole_stream_command(Nth),
|
whole_stream_command(Nth),
|
||||||
whole_stream_command(Next),
|
whole_stream_command(Next),
|
||||||
whole_stream_command(Previous),
|
whole_stream_command(Previous),
|
||||||
whole_stream_command(Debug),
|
// whole_stream_command(Debug),
|
||||||
whole_stream_command(Shells),
|
whole_stream_command(Shells),
|
||||||
whole_stream_command(SplitColumn),
|
whole_stream_command(SplitColumn),
|
||||||
whole_stream_command(SplitRow),
|
whole_stream_command(SplitRow),
|
||||||
|
@ -325,7 +326,9 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
whole_stream_command(SplitBy),
|
whole_stream_command(SplitBy),
|
||||||
whole_stream_command(Table),
|
whole_stream_command(Table),
|
||||||
whole_stream_command(Version),
|
whole_stream_command(Version),
|
||||||
|
whole_stream_command(What),
|
||||||
whole_stream_command(Which),
|
whole_stream_command(Which),
|
||||||
|
whole_stream_command(DebugValue),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
|
@ -419,13 +422,47 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||||
let mut readline = Err(ReadlineError::Eof);
|
let mut readline = Err(ReadlineError::Eof);
|
||||||
while let Some(ref cmd) = initial_command {
|
while let Some(ref cmd) = initial_command {
|
||||||
readline = rl.readline_with_initial(&prompt, (&cmd, ""));
|
readline = rl.readline_with_initial(&prompt, (&cmd, ""));
|
||||||
initial_command = None;
|
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) => {
|
LineResult::Success(line) => {
|
||||||
rl.add_history_entry(line.clone());
|
rl.add_history_entry(line.clone());
|
||||||
let _ = rl.save_history(&History::path());
|
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 => {
|
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 => {
|
LineResult::Break => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -703,7 +731,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
||||||
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
||||||
Err(ReadlineError::Eof) => LineResult::Break,
|
Err(ReadlineError::Eof) => LineResult::Break,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("Error: {:?}", err);
|
outln!("Error: {:?}", err);
|
||||||
LineResult::Break
|
LineResult::Break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -725,9 +753,9 @@ fn classify_pipeline(
|
||||||
.map_err(|err| err.into());
|
.map_err(|err| err.into());
|
||||||
|
|
||||||
if log_enabled!(target: "nu::expand_syntax", log::Level::Debug) {
|
if log_enabled!(target: "nu::expand_syntax", log::Level::Debug) {
|
||||||
println!("");
|
outln!("");
|
||||||
ptree::print_tree(&iterator.expand_tracer().print(source.clone())).unwrap();
|
ptree::print_tree(&iterator.expand_tracer().print(source.clone())).unwrap();
|
||||||
println!("");
|
outln!("");
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub(crate) mod count;
|
||||||
pub(crate) mod cp;
|
pub(crate) mod cp;
|
||||||
pub(crate) mod date;
|
pub(crate) mod date;
|
||||||
pub(crate) mod debug;
|
pub(crate) mod debug;
|
||||||
|
pub(crate) mod debug_value;
|
||||||
pub(crate) mod echo;
|
pub(crate) mod echo;
|
||||||
pub(crate) mod enter;
|
pub(crate) mod enter;
|
||||||
pub(crate) mod env;
|
pub(crate) mod env;
|
||||||
|
@ -85,6 +86,7 @@ pub(crate) mod to_url;
|
||||||
pub(crate) mod to_yaml;
|
pub(crate) mod to_yaml;
|
||||||
pub(crate) mod trim;
|
pub(crate) mod trim;
|
||||||
pub(crate) mod version;
|
pub(crate) mod version;
|
||||||
|
pub(crate) mod what;
|
||||||
pub(crate) mod where_;
|
pub(crate) mod where_;
|
||||||
pub(crate) mod which_;
|
pub(crate) mod which_;
|
||||||
|
|
||||||
|
@ -101,7 +103,7 @@ pub(crate) use config::Config;
|
||||||
pub(crate) use count::Count;
|
pub(crate) use count::Count;
|
||||||
pub(crate) use cp::Cpy;
|
pub(crate) use cp::Cpy;
|
||||||
pub(crate) use date::Date;
|
pub(crate) use date::Date;
|
||||||
pub(crate) use debug::Debug;
|
pub(crate) use debug_value::DebugValue;
|
||||||
pub(crate) use echo::Echo;
|
pub(crate) use echo::Echo;
|
||||||
pub(crate) use enter::Enter;
|
pub(crate) use enter::Enter;
|
||||||
pub(crate) use env::Env;
|
pub(crate) use env::Env;
|
||||||
|
@ -173,5 +175,6 @@ pub(crate) use to_url::ToURL;
|
||||||
pub(crate) use to_yaml::ToYAML;
|
pub(crate) use to_yaml::ToYAML;
|
||||||
pub(crate) use trim::Trim;
|
pub(crate) use trim::Trim;
|
||||||
pub(crate) use version::Version;
|
pub(crate) use version::Version;
|
||||||
|
pub(crate) use what::What;
|
||||||
pub(crate) use where_::Where;
|
pub(crate) use where_::Where;
|
||||||
pub(crate) use which_::Which;
|
pub(crate) use which_::Which;
|
||||||
|
|
|
@ -46,7 +46,9 @@ pub fn autoview(
|
||||||
Ok(OutputStream::new(async_stream! {
|
Ok(OutputStream::new(async_stream! {
|
||||||
let mut output_stream: OutputStream = context.input.into();
|
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)) => {
|
Ok(Some(x)) => {
|
||||||
match output_stream.try_next().await {
|
match output_stream.try_next().await {
|
||||||
Ok(Some(y)) => {
|
Ok(Some(y)) => {
|
||||||
|
@ -91,7 +93,25 @@ pub fn autoview(
|
||||||
|
|
||||||
let raw = raw.clone();
|
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();
|
let mut named_args = NamedArguments::new();
|
||||||
named_args.insert_optional("start_number", Some(Expression::number(current_idx, Tag::unknown())));
|
named_args.insert_optional("start_number", Some(Expression::number(current_idx, Tag::unknown())));
|
||||||
command_args.call_info.args.named = Some(named_args);
|
command_args.call_info.args.named = Some(named_args);
|
||||||
|
@ -99,6 +119,7 @@ pub fn autoview(
|
||||||
let result = table.run(command_args, &context.commands);
|
let result = table.run(command_args, &context.commands);
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
|
|
||||||
|
|
||||||
if finished {
|
if finished {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,14 +141,14 @@ pub fn autoview(
|
||||||
let result = text.run(raw.with_input(stream.into()), &context.commands);
|
let result = text.run(raw.with_input(stream.into()), &context.commands);
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
} else {
|
} else {
|
||||||
println!("{}", s);
|
outln!("{}", s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Tagged {
|
Tagged {
|
||||||
item: Value::Primitive(Primitive::String(s)),
|
item: Value::Primitive(Primitive::String(s)),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
println!("{}", s);
|
outln!("{}", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tagged { item: Value::Primitive(Primitive::Binary(ref b)), .. } => {
|
Tagged { item: Value::Primitive(Primitive::Binary(ref b)), .. } => {
|
||||||
|
@ -138,7 +159,7 @@ pub fn autoview(
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
} else {
|
} else {
|
||||||
use pretty_hex::*;
|
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);
|
let result = table.run(raw.with_input(stream.into()), &context.commands);
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
} else {
|
} 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));
|
trace!(target: "nu::run::internal", "{}", self.args.debug(&source));
|
||||||
}
|
}
|
||||||
|
|
||||||
let objects: InputStream =
|
let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", source: source, "input" = input.objects);
|
||||||
trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects);
|
|
||||||
|
|
||||||
let command = context.expect_command(&self.name);
|
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 result = result.values;
|
||||||
let mut context = context.clone();
|
let mut context = context.clone();
|
||||||
|
|
||||||
let stream = async_stream! {
|
let stream = async_stream! {
|
||||||
|
let mut soft_errs: Vec<ShellError> = vec![];
|
||||||
|
let mut yielded = false;
|
||||||
|
|
||||||
while let Some(item) = result.next().await {
|
while let Some(item) = result.next().await {
|
||||||
match item {
|
match item {
|
||||||
Ok(ReturnSuccess::Action(action)) => match action {
|
Ok(ReturnSuccess::Action(action)) => match action {
|
||||||
|
@ -192,6 +194,10 @@ impl InternalCommand {
|
||||||
context.shell_manager.set_path(path);
|
context.shell_manager.set_path(path);
|
||||||
}
|
}
|
||||||
CommandAction::Exit => std::process::exit(0), // TODO: save history.txt
|
CommandAction::Exit => std::process::exit(0), // TODO: save history.txt
|
||||||
|
CommandAction::Error(err) => {
|
||||||
|
context.error(err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
CommandAction::EnterHelpShell(value) => {
|
CommandAction::EnterHelpShell(value) => {
|
||||||
match value {
|
match value {
|
||||||
Tagged {
|
Tagged {
|
||||||
|
@ -237,11 +243,28 @@ impl InternalCommand {
|
||||||
},
|
},
|
||||||
|
|
||||||
Ok(ReturnSuccess::Value(v)) => {
|
Ok(ReturnSuccess::Value(v)) => {
|
||||||
|
yielded = true;
|
||||||
yield Ok(v);
|
yield Ok(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(x) => {
|
Ok(ReturnSuccess::DebugValue(v)) => {
|
||||||
yield Ok(Value::Error(x).tagged_unknown());
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl CallInfo {
|
||||||
#[derive(Getters)]
|
#[derive(Getters)]
|
||||||
#[get = "pub(crate)"]
|
#[get = "pub(crate)"]
|
||||||
pub struct CommandArgs {
|
pub struct CommandArgs {
|
||||||
pub host: Arc<Mutex<dyn Host>>,
|
pub host: Arc<Mutex<Box<dyn Host>>>,
|
||||||
pub ctrl_c: Arc<AtomicBool>,
|
pub ctrl_c: Arc<AtomicBool>,
|
||||||
pub shell_manager: ShellManager,
|
pub shell_manager: ShellManager,
|
||||||
pub call_info: UnevaluatedCallInfo,
|
pub call_info: UnevaluatedCallInfo,
|
||||||
|
@ -78,7 +78,7 @@ pub struct CommandArgs {
|
||||||
#[derive(Getters, Clone)]
|
#[derive(Getters, Clone)]
|
||||||
#[get = "pub(crate)"]
|
#[get = "pub(crate)"]
|
||||||
pub struct RawCommandArgs {
|
pub struct RawCommandArgs {
|
||||||
pub host: Arc<Mutex<dyn Host>>,
|
pub host: Arc<Mutex<Box<dyn Host>>>,
|
||||||
pub ctrl_c: Arc<AtomicBool>,
|
pub ctrl_c: Arc<AtomicBool>,
|
||||||
pub shell_manager: ShellManager,
|
pub shell_manager: ShellManager,
|
||||||
pub call_info: UnevaluatedCallInfo,
|
pub call_info: UnevaluatedCallInfo,
|
||||||
|
@ -94,6 +94,10 @@ impl RawCommandArgs {
|
||||||
input: input.into(),
|
input: input.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn source(&self) -> Text {
|
||||||
|
self.call_info.source.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for CommandArgs {
|
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,
|
self,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
callback: fn(T, RunnableContext) -> Result<OutputStream, ShellError>,
|
callback: fn(T, RunnableContext) -> Result<O, ShellError>,
|
||||||
) -> Result<RunnableArgs<T>, ShellError> {
|
) -> Result<RunnableArgs<T, O>, ShellError> {
|
||||||
let shell_manager = self.shell_manager.clone();
|
let shell_manager = self.shell_manager.clone();
|
||||||
let host = self.host.clone();
|
let host = self.host.clone();
|
||||||
|
let source = self.source();
|
||||||
let ctrl_c = self.ctrl_c.clone();
|
let ctrl_c = self.ctrl_c.clone();
|
||||||
let args = self.evaluate_once(registry)?;
|
let args = self.evaluate_once(registry)?;
|
||||||
let call_info = args.call_info.clone();
|
let call_info = args.call_info.clone();
|
||||||
|
@ -147,6 +156,7 @@ impl CommandArgs {
|
||||||
context: RunnableContext {
|
context: RunnableContext {
|
||||||
input,
|
input,
|
||||||
commands: registry.clone(),
|
commands: registry.clone(),
|
||||||
|
source,
|
||||||
shell_manager,
|
shell_manager,
|
||||||
name: name_tag,
|
name: name_tag,
|
||||||
host,
|
host,
|
||||||
|
@ -170,6 +180,7 @@ impl CommandArgs {
|
||||||
|
|
||||||
let shell_manager = self.shell_manager.clone();
|
let shell_manager = self.shell_manager.clone();
|
||||||
let host = self.host.clone();
|
let host = self.host.clone();
|
||||||
|
let source = self.source();
|
||||||
let ctrl_c = self.ctrl_c.clone();
|
let ctrl_c = self.ctrl_c.clone();
|
||||||
let args = self.evaluate_once(registry)?;
|
let args = self.evaluate_once(registry)?;
|
||||||
let call_info = args.call_info.clone();
|
let call_info = args.call_info.clone();
|
||||||
|
@ -183,6 +194,7 @@ impl CommandArgs {
|
||||||
context: RunnableContext {
|
context: RunnableContext {
|
||||||
input,
|
input,
|
||||||
commands: registry.clone(),
|
commands: registry.clone(),
|
||||||
|
source,
|
||||||
shell_manager,
|
shell_manager,
|
||||||
name: name_tag,
|
name: name_tag,
|
||||||
host,
|
host,
|
||||||
|
@ -208,7 +220,8 @@ impl RunnablePerItemContext {
|
||||||
pub struct RunnableContext {
|
pub struct RunnableContext {
|
||||||
pub input: InputStream,
|
pub input: InputStream,
|
||||||
pub shell_manager: ShellManager,
|
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 ctrl_c: Arc<AtomicBool>,
|
||||||
pub commands: CommandRegistry,
|
pub commands: CommandRegistry,
|
||||||
pub name: Tag,
|
pub name: Tag,
|
||||||
|
@ -232,15 +245,15 @@ impl<T> RunnablePerItemArgs<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RunnableArgs<T> {
|
pub struct RunnableArgs<T, O: ToOutputStream> {
|
||||||
args: T,
|
args: T,
|
||||||
context: RunnableContext,
|
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> {
|
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 {
|
pub enum CommandAction {
|
||||||
ChangePath(String),
|
ChangePath(String),
|
||||||
Exit,
|
Exit,
|
||||||
|
Error(ShellError),
|
||||||
EnterShell(String),
|
EnterShell(String),
|
||||||
EnterValueShell(Tagged<Value>),
|
EnterValueShell(Tagged<Value>),
|
||||||
EnterHelpShell(Tagged<Value>),
|
EnterHelpShell(Tagged<Value>),
|
||||||
|
@ -396,16 +410,17 @@ pub enum CommandAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatDebug for 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 {
|
match self {
|
||||||
CommandAction::ChangePath(s) => write!(f, "action:change-path={}", s),
|
CommandAction::ChangePath(s) => write!(f, "action:change-path={}", s),
|
||||||
CommandAction::Exit => write!(f, "action:exit"),
|
CommandAction::Exit => write!(f, "action:exit"),
|
||||||
|
CommandAction::Error(_) => write!(f, "action:error"),
|
||||||
CommandAction::EnterShell(s) => write!(f, "action:enter-shell={}", s),
|
CommandAction::EnterShell(s) => write!(f, "action:enter-shell={}", s),
|
||||||
CommandAction::EnterValueShell(t) => {
|
CommandAction::EnterValueShell(t) => {
|
||||||
write!(f, "action:enter-value-shell={:?}", t.debug())
|
write!(f, "action:enter-value-shell={}", t.debug(source))
|
||||||
}
|
}
|
||||||
CommandAction::EnterHelpShell(t) => {
|
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::PreviousShell => write!(f, "action:previous-shell"),
|
||||||
CommandAction::NextShell => write!(f, "action:next-shell"),
|
CommandAction::NextShell => write!(f, "action:next-shell"),
|
||||||
|
@ -417,6 +432,7 @@ impl FormatDebug for CommandAction {
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ReturnSuccess {
|
pub enum ReturnSuccess {
|
||||||
Value(Tagged<Value>),
|
Value(Tagged<Value>),
|
||||||
|
DebugValue(Tagged<Value>),
|
||||||
Action(CommandAction),
|
Action(CommandAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,7 +442,8 @@ impl FormatDebug for ReturnValue {
|
||||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Err(err) => write!(f, "{}", err.debug(source)),
|
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)),
|
Ok(ReturnSuccess::Action(a)) => write!(f, "{}", a.debug(source)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,6 +464,10 @@ impl ReturnSuccess {
|
||||||
Ok(ReturnSuccess::Value(input.into()))
|
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 {
|
pub fn action(input: CommandAction) -> ReturnValue {
|
||||||
Ok(ReturnSuccess::Action(input))
|
Ok(ReturnSuccess::Action(input))
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub fn debug(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStr
|
||||||
Ok(input
|
Ok(input
|
||||||
.values
|
.values
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
println!("{:?}", v);
|
outln!("{:?}", v);
|
||||||
ReturnSuccess::value(v)
|
ReturnSuccess::value(v)
|
||||||
})
|
})
|
||||||
.to_output_stream())
|
.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<dyn Fn(Tagged<Value>, Tag) -> Option<Tagged<Value>> + 'static> {
|
||||||
Box::new(move |value: Tagged<Value>, tag| match key {
|
Box::new(move |value: Tagged<Value>, tag| match key {
|
||||||
Some(ref key_given) => {
|
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))
|
Some(item.clone().tagged(tag))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub(crate) fn format(input: Vec<Value>, host: &mut dyn Host) {
|
||||||
crate::format::print_view(&view, &mut *host);
|
crate::format::print_view(&view, &mut *host);
|
||||||
|
|
||||||
if last != i {
|
if last != i {
|
||||||
println!("");
|
outln!("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
|
use crate::data::base::shape::Shapes;
|
||||||
use crate::data::Value;
|
use crate::data::Value;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::utils::did_you_mean;
|
use crate::utils::did_you_mean;
|
||||||
|
use crate::ColumnPath;
|
||||||
|
use futures_util::pin_mut;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
pub struct Get;
|
pub struct Get;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GetArgs {
|
pub struct GetArgs {
|
||||||
member: ColumnPath,
|
|
||||||
rest: Vec<ColumnPath>,
|
rest: Vec<ColumnPath>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,16 +21,10 @@ impl WholeStreamCommand for Get {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("get")
|
Signature::build("get").rest(
|
||||||
.required(
|
SyntaxShape::ColumnPath,
|
||||||
"member",
|
"optionally return additional data by path",
|
||||||
SyntaxShape::ColumnPath,
|
)
|
||||||
"the path to the data to get",
|
|
||||||
)
|
|
||||||
.rest(
|
|
||||||
SyntaxShape::ColumnPath,
|
|
||||||
"optionally return additional data by path",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
|
@ -44,8 +40,6 @@ impl WholeStreamCommand for Get {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ColumnPath = Vec<Tagged<Value>>;
|
|
||||||
|
|
||||||
pub fn get_column_path(
|
pub fn get_column_path(
|
||||||
path: &ColumnPath,
|
path: &ColumnPath,
|
||||||
obj: &Tagged<Value>,
|
obj: &Tagged<Value>,
|
||||||
|
@ -53,85 +47,52 @@ pub fn get_column_path(
|
||||||
let fields = path.clone();
|
let fields = path.clone();
|
||||||
|
|
||||||
let value = obj.get_data_by_column_path(
|
let value = obj.get_data_by_column_path(
|
||||||
obj.tag(),
|
|
||||||
path,
|
path,
|
||||||
Box::new(move |(obj_source, column_path_tried)| {
|
Box::new(move |(obj_source, column_path_tried, error)| {
|
||||||
match obj_source {
|
match obj_source {
|
||||||
Value::Table(rows) => {
|
Value::Table(rows) => {
|
||||||
let total = rows.len();
|
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(),
|
Some(last_field) => last_field.span(),
|
||||||
None => column_path_tried.tag(),
|
None => column_path_tried.span(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return ShellError::labeled_error_with_secondary(
|
return ShellError::labeled_error_with_secondary(
|
||||||
"Row not found",
|
"Row not found",
|
||||||
format!(
|
format!("There isn't a row indexed at {}", **column_path_tried),
|
||||||
"There isn't a row indexed at '{}'",
|
column_path_tried.span(),
|
||||||
match &*column_path_tried {
|
if total == 1 {
|
||||||
Value::Primitive(primitive) => primitive.format(None),
|
format!("The table only has 1 row")
|
||||||
_ => String::from(""),
|
} else {
|
||||||
}
|
format!("The table only has {} rows (0 to {})", total, total - 1)
|
||||||
),
|
},
|
||||||
column_path_tried.tag(),
|
|
||||||
format!("The table only has {} rows (0..{})", total, total - 1),
|
|
||||||
end_tag,
|
end_tag,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
match &column_path_tried {
|
match did_you_mean(&obj_source, column_path_tried) {
|
||||||
Tagged {
|
Some(suggestions) => {
|
||||||
item: Value::Primitive(Primitive::Int(index)),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
return ShellError::labeled_error(
|
return ShellError::labeled_error(
|
||||||
"No rows available",
|
"Unknown column",
|
||||||
format!(
|
format!("did you mean '{}'?", suggestions[0].1),
|
||||||
"Not a table. Perhaps you meant to get the column '{}' instead?",
|
span_for_spanned_list(fields.members().iter().map(|p| p.span())),
|
||||||
index
|
|
||||||
),
|
|
||||||
column_path_tried.tag(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => match did_you_mean(&obj_source, &column_path_tried) {
|
None => {}
|
||||||
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())),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = match value {
|
let res = match value {
|
||||||
Ok(fetched) => match fetched {
|
Ok(Tagged { item: v, tag }) => Ok((v.clone()).tagged(&tag)),
|
||||||
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)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err(reason) => Err(reason),
|
Err(reason) => Err(reason),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -139,54 +100,69 @@ pub fn get_column_path(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(
|
pub fn get(
|
||||||
GetArgs {
|
GetArgs { rest: mut fields }: GetArgs,
|
||||||
member,
|
|
||||||
rest: fields,
|
|
||||||
}: GetArgs,
|
|
||||||
RunnableContext { input, .. }: RunnableContext,
|
RunnableContext { input, .. }: RunnableContext,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
trace!("get {:?} {:?}", member, fields);
|
if fields.len() == 0 {
|
||||||
|
let stream = async_stream! {
|
||||||
|
let values = input.values;
|
||||||
|
pin_mut!(values);
|
||||||
|
|
||||||
let stream = input
|
let mut shapes = Shapes::new();
|
||||||
.values
|
let mut index = 0;
|
||||||
.map(move |item| {
|
|
||||||
let mut result = VecDeque::new();
|
|
||||||
|
|
||||||
let member = vec![member.clone()];
|
while let Some(row) = values.next().await {
|
||||||
|
shapes.add(&row.item, index);
|
||||||
let column_paths = vec![&member, &fields]
|
index += 1;
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.collect::<Vec<&ColumnPath>>();
|
|
||||||
|
|
||||||
for path in column_paths {
|
|
||||||
let res = get_column_path(&path, &item);
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(got) => match got {
|
|
||||||
Tagged {
|
|
||||||
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),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
other => result
|
|
||||||
.push_back(ReturnSuccess::value((*other).clone().tagged(&item.tag))),
|
|
||||||
},
|
|
||||||
Err(reason) => result.push_back(Err(reason)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result
|
|
||||||
})
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
Ok(stream.to_output_stream())
|
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| {
|
||||||
|
let mut result = VecDeque::new();
|
||||||
|
|
||||||
|
let member = vec![member.clone()];
|
||||||
|
|
||||||
|
let column_paths = vec![&member, &fields]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<&ColumnPath>>();
|
||||||
|
|
||||||
|
for path in column_paths {
|
||||||
|
let res = get_column_path(&path, &item);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(got) => match got {
|
||||||
|
Tagged {
|
||||||
|
item: Value::Table(rows),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
for item in rows {
|
||||||
|
result.push_back(ReturnSuccess::value(item.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => result.push_back(ReturnSuccess::value(other.clone())),
|
||||||
|
},
|
||||||
|
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();
|
let mut groups = indexmap::IndexMap::new();
|
||||||
|
|
||||||
for value in values {
|
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() {
|
if group_key.is_none() {
|
||||||
let possibilities = value.data_descriptors();
|
let possibilities = value.data_descriptors();
|
||||||
|
|
|
@ -44,7 +44,11 @@ impl PerItemCommand for Help {
|
||||||
short_desc.insert("name", cmd);
|
short_desc.insert("name", cmd);
|
||||||
short_desc.insert(
|
short_desc.insert(
|
||||||
"description",
|
"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()));
|
help.push_back(ReturnSuccess::value(short_desc.into_tagged_value()));
|
||||||
|
|
|
@ -88,7 +88,8 @@ pub fn histogram(
|
||||||
for percentage in start.into_iter() {
|
for percentage in start.into_iter() {
|
||||||
|
|
||||||
let mut fact = TaggedDictBuilder::new(&name);
|
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() {
|
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>());
|
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 tag = args.name_tag();
|
||||||
let input = args.input;
|
let input = args.input;
|
||||||
|
|
||||||
let input: InputStream = trace_stream!(target: "nu::trace_stream::lines", "input" = input);
|
|
||||||
|
|
||||||
let stream = input
|
let stream = input
|
||||||
.values
|
.values
|
||||||
.map(move |v| match v.item {
|
.map(move |v| match v.item {
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result<OutputStream,
|
||||||
if args.header_row {
|
if args.header_row {
|
||||||
for i in input.clone() {
|
for i in input.clone() {
|
||||||
if let Some(desc) = descs.get(0) {
|
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) => {
|
Some(x) => {
|
||||||
if let Ok(s) = x.as_string() {
|
if let Ok(s) = x.as_string() {
|
||||||
headers.push(s);
|
headers.push(s);
|
||||||
|
@ -115,7 +115,7 @@ pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result<OutputStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in input.clone() {
|
for i in input.clone() {
|
||||||
match i.get_data_by_key(&desc) {
|
match i.get_data_by_key(desc[..].spanned_unknown()) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
dict.insert_tagged(headers[column_num].clone(), x.clone());
|
dict.insert_tagged(headers[column_num].clone(), x.clone());
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,7 +237,7 @@ pub fn filter_plugin(
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let mut result = VecDeque::new();
|
let mut result = VecDeque::new();
|
||||||
result.push_back(Err(ShellError::untagged_runtime_error(format!(
|
result.push_back(Err(ShellError::untagged_runtime_error(format!(
|
||||||
"Error while processing filter response: {:?} {}",
|
"Error while processing filter response: {:?}\n== input ==\n{}",
|
||||||
e, input
|
e, input
|
||||||
))));
|
))));
|
||||||
result
|
result
|
||||||
|
|
|
@ -40,7 +40,7 @@ fn sort_by(
|
||||||
|
|
||||||
let calc_key = |item: &Tagged<Value>| {
|
let calc_key = |item: &Tagged<Value>| {
|
||||||
rest.iter()
|
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>>>>()
|
.collect::<Vec<Option<Tagged<Value>>>>()
|
||||||
};
|
};
|
||||||
vec.sort_by_cached_key(calc_key);
|
vec.sort_by_cached_key(calc_key);
|
||||||
|
|
|
@ -108,7 +108,7 @@ pub fn split(
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::type_error(
|
return Err(ShellError::type_error(
|
||||||
"a table value",
|
"a table value",
|
||||||
other.tagged_type_name(),
|
other.spanned_type_name(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ pub fn split(
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::type_error(
|
return Err(ShellError::type_error(
|
||||||
"a table value",
|
"a table value",
|
||||||
group.tagged_type_name(),
|
group.spanned_type_name(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ pub fn split(
|
||||||
ref other => {
|
ref other => {
|
||||||
return Err(ShellError::type_error(
|
return Err(ShellError::type_error(
|
||||||
"a table value",
|
"a table value",
|
||||||
other.tagged_type_name(),
|
other.spanned_type_name(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ pub fn split(
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::type_error(
|
return Err(ShellError::type_error(
|
||||||
"a table value",
|
"a table value",
|
||||||
value.tagged_type_name(),
|
value.spanned_type_name(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,8 @@ fn t_sort_by(
|
||||||
};
|
};
|
||||||
|
|
||||||
if show_columns {
|
if show_columns {
|
||||||
for label in columns_sorted(column_grouped_by_name, &values[0], &name).iter() {
|
for label in columns_sorted(column_grouped_by_name, &values[0], &name).into_iter() {
|
||||||
yield ReturnSuccess::value(label.clone());
|
yield ReturnSuccess::value(Value::string(label.item).tagged(label.tag));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match t_sort(column_grouped_by_name, None, &values[0], name) {
|
match t_sort(column_grouped_by_name, None, &values[0], name) {
|
||||||
|
@ -82,7 +82,7 @@ pub fn columns_sorted(
|
||||||
_group_by_name: Option<String>,
|
_group_by_name: Option<String>,
|
||||||
value: &Tagged<Value>,
|
value: &Tagged<Value>,
|
||||||
tag: impl Into<Tag>,
|
tag: impl Into<Tag>,
|
||||||
) -> Vec<Tagged<Value>> {
|
) -> Vec<Tagged<String>> {
|
||||||
let origin_tag = tag.into();
|
let origin_tag = tag.into();
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
|
@ -110,22 +110,20 @@ pub fn columns_sorted(
|
||||||
|
|
||||||
keys.sort();
|
keys.sort();
|
||||||
|
|
||||||
let keys: Vec<Value> = keys
|
let keys: Vec<String> = keys
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|k| {
|
.map(|k| match k {
|
||||||
Value::string(match k {
|
Tagged {
|
||||||
Tagged {
|
item: Value::Primitive(Primitive::Date(d)),
|
||||||
item: Value::Primitive(Primitive::Date(d)),
|
..
|
||||||
..
|
} => format!("{}", d.format("%B %d-%Y")),
|
||||||
} => format!("{}", d.format("%B %d-%Y")),
|
_ => k.as_string().unwrap(),
|
||||||
_ => k.as_string().unwrap(),
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
keys.into_iter().map(|k| k.tagged(&origin_tag)).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 {
|
match group_by_name {
|
||||||
Some(column_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 {
|
match split_by_name {
|
||||||
None => {
|
None => {
|
||||||
|
@ -147,70 +146,41 @@ pub fn t_sort(
|
||||||
dataset.insert_tagged("default", value.clone());
|
dataset.insert_tagged("default", value.clone());
|
||||||
let dataset = dataset.into_tagged_value();
|
let dataset = dataset.into_tagged_value();
|
||||||
|
|
||||||
let split_labels = match &dataset {
|
let split_labels: Vec<Tagged<String>> = match &dataset {
|
||||||
Tagged {
|
Tagged {
|
||||||
item: Value::Row(rows),
|
item: Value::Row(rows),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut keys: Vec<Tagged<Value>> = rows
|
let mut keys: Vec<Tagged<String>> = rows
|
||||||
.entries
|
.entries
|
||||||
.keys()
|
.keys()
|
||||||
.map(|s| s.as_ref())
|
.map(|k| k.clone().tagged_unknown())
|
||||||
.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()
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
keys.sort();
|
keys.sort();
|
||||||
|
|
||||||
let keys: Vec<Value> = keys
|
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()
|
|
||||||
}
|
}
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let results: Vec<Vec<Tagged<Value>>> = split_labels
|
let results: Vec<Vec<Tagged<Value>>> = split_labels
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|split| {
|
.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
|
sorted_labels
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|label| {
|
.map(|label| match &groups {
|
||||||
let label = label.as_string().unwrap();
|
Some(Tagged {
|
||||||
|
item: Value::Row(dict),
|
||||||
match groups {
|
..
|
||||||
Some(Tagged {
|
}) => dict
|
||||||
item: Value::Row(dict),
|
.get_data_by_key(label.borrow_spanned())
|
||||||
..
|
.unwrap()
|
||||||
}) => dict.get_data_by_key(&label).unwrap().clone(),
|
.clone(),
|
||||||
_ => Value::Table(vec![]).tagged(&origin_tag),
|
_ => Value::Table(vec![]).tagged(&origin_tag),
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
|
@ -299,9 +269,9 @@ mod tests {
|
||||||
Tag::unknown()
|
Tag::unknown()
|
||||||
),
|
),
|
||||||
vec![
|
vec![
|
||||||
string("August 23-2019"),
|
format!("August 23-2019").tagged_unknown(),
|
||||||
string("September 24-2019"),
|
format!("September 24-2019").tagged_unknown(),
|
||||||
string("October 10-2019")
|
format!("October 10-2019").tagged_unknown()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::data::{Dictionary, Primitive, Value};
|
use crate::data::{Dictionary, Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::RawPathMember;
|
||||||
use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document};
|
use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document};
|
||||||
use std::convert::TryInto;
|
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::Nothing) => Bson::Null,
|
||||||
Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()),
|
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::Pattern(p)) => Bson::String(p.clone()),
|
||||||
Value::Primitive(Primitive::Path(s)) => Bson::String(s.display().to_string()),
|
Value::Primitive(Primitive::Path(s)) => Bson::String(s.display().to_string()),
|
||||||
Value::Table(l) => Bson::Array(
|
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(
|
_ => Err(ShellError::type_error(
|
||||||
"bson binary",
|
"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::data::{Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use csv::WriterBuilder;
|
use csv::WriterBuilder;
|
||||||
|
use indexmap::{indexset, IndexSet};
|
||||||
|
|
||||||
fn from_value_to_delimited_string(
|
fn from_value_to_delimited_string(
|
||||||
tagged_value: &Tagged<Value>,
|
tagged_value: &Tagged<Value>,
|
||||||
|
@ -46,15 +47,16 @@ fn from_value_to_delimited_string(
|
||||||
.from_writer(vec![]);
|
.from_writer(vec![]);
|
||||||
|
|
||||||
let merged_descriptors = merge_descriptors(&list);
|
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.");
|
.expect("can not write.");
|
||||||
|
|
||||||
for l in list {
|
for l in list {
|
||||||
let mut row = vec![];
|
let mut row = vec![];
|
||||||
for desc in &merged_descriptors {
|
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) => {
|
Some(s) => {
|
||||||
row.push(to_string_tagged_value(s)?);
|
row.push(to_string_tagged_value(&s)?);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
row.push(String::new());
|
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> {
|
fn merge_descriptors(values: &[Tagged<Value>]) -> Vec<Spanned<String>> {
|
||||||
let mut ret = vec![];
|
let mut ret: Vec<Spanned<String>> = vec![];
|
||||||
|
let mut seen: IndexSet<String> = indexset! {};
|
||||||
for value in values {
|
for value in values {
|
||||||
for desc in value.data_descriptors() {
|
for desc in value.data_descriptors() {
|
||||||
if !ret.contains(&desc) {
|
if !seen.contains(&desc[..]) {
|
||||||
ret.push(desc);
|
seen.insert(desc.clone());
|
||||||
|
ret.push(desc.spanned(value.tag.span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::data::{Primitive, Value};
|
use crate::data::{Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::RawPathMember;
|
||||||
|
|
||||||
pub struct ToJSON;
|
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::Nothing) => serde_json::Value::Null,
|
||||||
Value::Primitive(Primitive::Pattern(s)) => serde_json::Value::String(s.clone()),
|
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::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::Primitive(Primitive::Path(s)) => serde_json::Value::String(s.display().to_string()),
|
||||||
|
|
||||||
Value::Table(l) => serde_json::Value::Array(json_list(l)?),
|
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::Date(d) => format!("'{}'", d),
|
||||||
Primitive::Path(p) => format!("'{}'", p.display().to_string().replace("'", "''")),
|
Primitive::Path(p) => format!("'{}'", p.display().to_string().replace("'", "''")),
|
||||||
Primitive::Binary(u) => format!("x'{}'", encode(u)),
|
Primitive::Binary(u) => format!("x'{}'", encode(u)),
|
||||||
Primitive::BeginningOfStream => "NULL".into(),
|
Primitive::BeginningOfStream | Primitive::EndOfStream | Primitive::ColumnPath(_) => {
|
||||||
Primitive::EndOfStream => "NULL".into(),
|
"NULL".into()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => "NULL".into(),
|
_ => "NULL".into(),
|
||||||
}
|
}
|
||||||
|
@ -179,9 +180,9 @@ fn sqlite_input_stream_to_bytes(
|
||||||
{
|
{
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}", create);
|
outln!("{}", create);
|
||||||
println!("{}", insert);
|
outln!("{}", insert);
|
||||||
println!("{:?}", e);
|
outln!("{:?}", e);
|
||||||
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
|
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::data::{Primitive, Value};
|
use crate::data::{Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::RawPathMember;
|
||||||
|
|
||||||
pub struct ToTOML;
|
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::Pattern(s)) => toml::Value::String(s.clone()),
|
||||||
Value::Primitive(Primitive::String(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::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::Table(l) => toml::Value::Array(collect_values(l)?),
|
||||||
Value::Error(e) => return Err(e.clone()),
|
Value::Error(e) => return Err(e.clone()),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::data::{Primitive, Value};
|
use crate::data::{Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::RawPathMember;
|
||||||
|
|
||||||
pub struct ToYAML;
|
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::Nothing) => serde_yaml::Value::Null,
|
||||||
Value::Primitive(Primitive::Pattern(s)) => serde_yaml::Value::String(s.clone()),
|
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::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::Primitive(Primitive::Path(s)) => serde_yaml::Value::String(s.display().to_string()),
|
||||||
|
|
||||||
Value::Table(l) => {
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
registry: CommandRegistry,
|
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 ctrl_c: Arc<AtomicBool>,
|
||||||
pub(crate) shell_manager: ShellManager,
|
pub(crate) shell_manager: ShellManager,
|
||||||
}
|
}
|
||||||
|
@ -85,18 +86,69 @@ impl Context {
|
||||||
let registry = CommandRegistry::new();
|
let registry = CommandRegistry::new();
|
||||||
Ok(Context {
|
Ok(Context {
|
||||||
registry: registry.clone(),
|
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)),
|
ctrl_c: Arc::new(AtomicBool::new(false)),
|
||||||
shell_manager: ShellManager::basic(registry)?,
|
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();
|
let mut host = self.host.lock().unwrap();
|
||||||
|
|
||||||
block(&mut *host)
|
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>>) {
|
pub fn add_commands(&mut self, commands: Vec<Arc<Command>>) {
|
||||||
for command in commands {
|
for command in commands {
|
||||||
self.registry.insert(command.name().to_string(), command);
|
self.registry.insert(command.name().to_string(), command);
|
||||||
|
|
554
src/data/base.rs
554
src/data/base.rs
|
@ -1,7 +1,13 @@
|
||||||
|
mod debug;
|
||||||
|
mod property_get;
|
||||||
|
pub(crate) mod shape;
|
||||||
|
|
||||||
use crate::context::CommandRegistry;
|
use crate::context::CommandRegistry;
|
||||||
|
use crate::data::base::shape::{Column, InlineShape, TypeShape};
|
||||||
use crate::data::TaggedDictBuilder;
|
use crate::data::TaggedDictBuilder;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
use crate::evaluate::{evaluate_baseline_expr, Scope};
|
use crate::evaluate::{evaluate_baseline_expr, Scope};
|
||||||
|
use crate::parser::hir::path::{ColumnPath, PathMember};
|
||||||
use crate::parser::{hir, Operator};
|
use crate::parser::{hir, Operator};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::Text;
|
use crate::Text;
|
||||||
|
@ -11,7 +17,6 @@ use derive_new::new;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
@ -76,6 +81,7 @@ pub enum Primitive {
|
||||||
Decimal(BigDecimal),
|
Decimal(BigDecimal),
|
||||||
Bytes(u64),
|
Bytes(u64),
|
||||||
String(String),
|
String(String),
|
||||||
|
ColumnPath(ColumnPath),
|
||||||
Pattern(String),
|
Pattern(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Date(DateTime<Utc>),
|
Date(DateTime<Utc>),
|
||||||
|
@ -89,6 +95,27 @@ pub enum Primitive {
|
||||||
EndOfStream,
|
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 {
|
impl From<BigDecimal> for Primitive {
|
||||||
fn from(decimal: BigDecimal) -> Primitive {
|
fn from(decimal: BigDecimal) -> Primitive {
|
||||||
Primitive::Decimal(decimal)
|
Primitive::Decimal(decimal)
|
||||||
|
@ -102,47 +129,6 @@ impl From<f64> for Primitive {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn number(number: impl Into<Number>) -> Primitive {
|
||||||
let number = number.into();
|
let number = number.into();
|
||||||
|
|
||||||
|
@ -177,6 +163,24 @@ impl Primitive {
|
||||||
Primitive::Decimal(decimal) => format!("{}", decimal),
|
Primitive::Decimal(decimal) => format!("{}", decimal),
|
||||||
Primitive::Pattern(s) => format!("{}", s),
|
Primitive::Pattern(s) => format!("{}", s),
|
||||||
Primitive::String(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) {
|
Primitive::Boolean(b) => match (b, field_name) {
|
||||||
(true, None) => format!("Yes"),
|
(true, None) => format!("Yes"),
|
||||||
(false, None) => format!("No"),
|
(false, None) => format!("No"),
|
||||||
|
@ -220,7 +224,7 @@ pub struct Operation {
|
||||||
pub(crate) right: Value,
|
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 struct Block {
|
||||||
pub(crate) expressions: Vec<hir::Expression>,
|
pub(crate) expressions: Vec<hir::Expression>,
|
||||||
pub(crate) source: Text,
|
pub(crate) source: Text,
|
||||||
|
@ -270,6 +274,18 @@ pub enum Value {
|
||||||
Block(Block),
|
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 {
|
impl Into<Value> for Number {
|
||||||
fn into(self) -> Value {
|
fn into(self) -> Value {
|
||||||
match self {
|
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> {
|
impl Tagged<Value> {
|
||||||
pub fn tagged_type_name(&self) -> Tagged<String> {
|
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())
|
name.tagged(self.tag())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,7 +326,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for Block {
|
||||||
Value::Block(block) => Ok(block.clone()),
|
Value::Block(block) => Ok(block.clone()),
|
||||||
v => Err(ShellError::type_error(
|
v => Err(ShellError::type_error(
|
||||||
"Block",
|
"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(
|
v => Err(ShellError::type_error(
|
||||||
"Integer",
|
"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()),
|
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
|
||||||
v => Err(ShellError::type_error(
|
v => Err(ShellError::type_error(
|
||||||
"String",
|
"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()),
|
Value::Primitive(Primitive::Binary(b)) => Ok(b.clone()),
|
||||||
v => Err(ShellError::type_error(
|
v => Err(ShellError::type_error(
|
||||||
"Binary",
|
"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),
|
Value::Row(d) => Ok(d),
|
||||||
v => Err(ShellError::type_error(
|
v => Err(ShellError::type_error(
|
||||||
"Dictionary",
|
"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),
|
Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present),
|
||||||
v => Err(ShellError::type_error(
|
v => Err(ShellError::type_error(
|
||||||
"Boolean",
|
"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 {
|
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> {
|
pub fn data_descriptors(&self) -> Vec<String> {
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(_) => vec![],
|
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> {
|
pub fn get_data(&self, desc: &String) -> MaybeOwned<'_, Value> {
|
||||||
match self {
|
match self {
|
||||||
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
|
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
|
||||||
|
@ -690,23 +439,18 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn format_leaf(&self, desc: Option<&String>) -> String {
|
pub(crate) fn format_type(&self, width: usize) -> String {
|
||||||
match self {
|
TypeShape::from_value(self).colored_string(width)
|
||||||
Value::Primitive(p) => p.format(desc),
|
}
|
||||||
Value::Block(b) => itertools::join(
|
|
||||||
b.expressions
|
pub(crate) fn format_leaf(&self) -> DebugDocBuilder {
|
||||||
.iter()
|
InlineShape::from_value(self).format().pretty_debug()
|
||||||
.map(|e| e.span.slice(&b.source).to_string()),
|
}
|
||||||
"; ",
|
|
||||||
),
|
pub(crate) fn format_for_column(&self, column: impl Into<Column>) -> DebugDocBuilder {
|
||||||
Value::Row(_) => format!("[table: 1 row]"),
|
InlineShape::from_value(self)
|
||||||
Value::Table(l) => format!(
|
.format_for_column(column)
|
||||||
"[table: {} {}]",
|
.pretty_debug()
|
||||||
l.len(),
|
|
||||||
if l.len() == 1 { "row" } else { "rows" }
|
|
||||||
),
|
|
||||||
Value::Error(_) => format!("[error]"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn style_leaf(&self) -> &'static str {
|
pub(crate) fn style_leaf(&self) -> &'static str {
|
||||||
|
@ -720,7 +464,7 @@ impl Value {
|
||||||
&self,
|
&self,
|
||||||
operator: &Operator,
|
operator: &Operator,
|
||||||
other: &Value,
|
other: &Value,
|
||||||
) -> Result<bool, (String, String)> {
|
) -> Result<bool, (&'static str, &'static str)> {
|
||||||
match operator {
|
match operator {
|
||||||
_ => {
|
_ => {
|
||||||
let coerced = coerce_compare(self, other)?;
|
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)]
|
#[allow(unused)]
|
||||||
pub fn row(entries: IndexMap<String, Tagged<Value>>) -> Value {
|
pub fn row(entries: IndexMap<String, Tagged<Value>>) -> Value {
|
||||||
Value::Row(entries.into())
|
Value::Row(entries.into())
|
||||||
|
@ -766,6 +524,16 @@ impl Value {
|
||||||
Value::Primitive(Primitive::String(s.into()))
|
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 {
|
pub fn pattern(s: impl Into<String>) -> Value {
|
||||||
Value::Primitive(Primitive::String(s.into()))
|
Value::Primitive(Primitive::String(s.into()))
|
||||||
}
|
}
|
||||||
|
@ -778,10 +546,6 @@ impl Value {
|
||||||
Value::Primitive(Primitive::Bytes(s.into()))
|
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 {
|
pub fn decimal(s: impl Into<BigDecimal>) -> Value {
|
||||||
Value::Primitive(Primitive::Decimal(s.into()))
|
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()),
|
Value::Primitive(Primitive::String(path_str)) => Ok(PathBuf::from(&path_str).clone()),
|
||||||
other => Err(ShellError::type_error(
|
other => Err(ShellError::type_error(
|
||||||
"Path",
|
"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) {
|
match (left, right) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => coerce_compare_primitive(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(
|
fn coerce_compare_primitive(
|
||||||
left: &Primitive,
|
left: &Primitive,
|
||||||
right: &Primitive,
|
right: &Primitive,
|
||||||
) -> Result<CompareValues, (String, String)> {
|
) -> Result<CompareValues, (&'static str, &'static str)> {
|
||||||
use Primitive::*;
|
use Primitive::*;
|
||||||
|
|
||||||
Ok(match (left, right) {
|
Ok(match (left, right) {
|
||||||
|
@ -941,16 +708,19 @@ fn coerce_compare_primitive(
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::data::meta::*;
|
use crate::data::meta::*;
|
||||||
|
use crate::parser::hir::path::PathMember;
|
||||||
|
use crate::ColumnPath as ColumnPathValue;
|
||||||
use crate::ShellError;
|
use crate::ShellError;
|
||||||
use crate::Value;
|
use crate::Value;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use num_bigint::BigInt;
|
||||||
|
|
||||||
fn string(input: impl Into<String>) -> Tagged<Value> {
|
fn string(input: impl Into<String>) -> Tagged<Value> {
|
||||||
Value::string(input.into()).tagged_unknown()
|
Value::string(input.into()).tagged_unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number(n: i64) -> Tagged<Value> {
|
fn int(input: impl Into<BigInt>) -> Tagged<Value> {
|
||||||
Value::number(n).tagged_unknown()
|
Value::int(input.into()).tagged_unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn row(entries: IndexMap<String, Tagged<Value>>) -> Tagged<Value> {
|
fn row(entries: IndexMap<String, Tagged<Value>>) -> Tagged<Value> {
|
||||||
|
@ -961,12 +731,16 @@ mod tests {
|
||||||
Value::table(list).tagged_unknown()
|
Value::table(list).tagged_unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_callback() -> impl FnOnce((Value, Tagged<Value>)) -> ShellError {
|
fn error_callback(
|
||||||
move |(_obj_source, _column_path_tried)| ShellError::unimplemented("will never be called.")
|
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>> {
|
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<ColumnPathValue> {
|
||||||
table(paths).as_column_path().unwrap().item
|
table(&paths.iter().cloned().collect())
|
||||||
|
.as_column_path()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -976,7 +750,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*row.get_data_by_key("amigos").unwrap(),
|
row.get_data_by_key("amigos".spanned_unknown()).unwrap(),
|
||||||
table(&vec![
|
table(&vec![
|
||||||
string("andres"),
|
string("andres"),
|
||||||
string("jonathan"),
|
string("jonathan"),
|
||||||
|
@ -1000,17 +774,17 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
**value
|
*value
|
||||||
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
.tagged(tag)
|
||||||
.unwrap()
|
.get_data_by_column_path(&field_path, Box::new(error_callback("package.version")))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
version
|
version
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn column_path_that_contains_just_a_number_gets_a_row_from_a_table() {
|
fn gets_first_matching_field_from_rows_with_same_field_inside_a_table() {
|
||||||
let field_path = column_path(&vec![string("package"), string("authors"), number(0)]);
|
let field_path = column_path(&vec![string("package"), string("authors"), string("name")]);
|
||||||
|
|
||||||
let (_, tag) = string("Andrés N. Robalino").into_parts();
|
let (_, tag) = string("Andrés N. Robalino").into_parts();
|
||||||
|
|
||||||
|
@ -1027,9 +801,43 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
**value
|
value
|
||||||
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
.tagged(tag)
|
||||||
.unwrap()
|
.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(),
|
.unwrap(),
|
||||||
Value::row(indexmap! {
|
Value::row(indexmap! {
|
||||||
"name".into() => string("Andrés N. Robalino")
|
"name".into() => string("Andrés N. Robalino")
|
||||||
|
@ -1056,9 +864,12 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
**value
|
*value
|
||||||
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
|
.tagged(tag)
|
||||||
.unwrap()
|
.get_data_by_column_path(
|
||||||
|
&field_path,
|
||||||
|
Box::new(error_callback("package.authors.\"0\""))
|
||||||
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Value::row(indexmap! {
|
Value::row(indexmap! {
|
||||||
"name".into() => string("Andrés N. Robalino")
|
"name".into() => string("Andrés N. Robalino")
|
||||||
|
@ -1081,7 +892,8 @@ mod tests {
|
||||||
let (replacement, tag) = string("jonas").into_parts();
|
let (replacement, tag) = string("jonas").into_parts();
|
||||||
|
|
||||||
let actual = sample
|
let actual = sample
|
||||||
.replace_data_at_column_path(tag, &field_path, replacement)
|
.tagged(tag)
|
||||||
|
.replace_data_at_column_path(&field_path, replacement)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(actual, row(indexmap! {"amigos".into() => string("jonas")}));
|
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 (replacement, tag) = table(&vec![string("yehuda::jonathan::andres")]).into_parts();
|
||||||
|
|
||||||
let actual = sample
|
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();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1159,7 +972,8 @@ mod tests {
|
||||||
.into_parts();
|
.into_parts();
|
||||||
|
|
||||||
let actual = sample
|
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();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
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),
|
Value::Row(Dictionary { entries }) => Ok(entries),
|
||||||
other => Err(ShellError::type_error(
|
other => Err(ShellError::type_error(
|
||||||
"Dictionary",
|
"Dictionary",
|
||||||
other.type_name().tagged(&tag),
|
other.type_name().spanned(tag.span),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,69 @@
|
||||||
use crate::data::{Primitive, Value};
|
use crate::data::{Primitive, Value};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::traits::{DebugDocBuilder as b, PrettyDebug};
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
use getset::Getters;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use pretty::{BoxAllocator, DocAllocator};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::{Ordering, PartialOrd};
|
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 {
|
pub struct Dictionary {
|
||||||
|
#[get = "pub"]
|
||||||
pub entries: IndexMap<String, Tagged<Value>>,
|
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 {
|
impl PartialOrd for Dictionary {
|
||||||
fn partial_cmp(&self, other: &Dictionary) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Dictionary) -> Option<Ordering> {
|
||||||
let this: Vec<&String> = self.entries.keys().collect();
|
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>> {
|
pub fn keys(&self) -> impl Iterator<Item = &String> {
|
||||||
match self
|
self.entries.keys()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_data_by_key(&self, name: Spanned<&str>) -> Option<Tagged<Value>> {
|
||||||
|
let result = self
|
||||||
.entries
|
.entries
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(desc_name, _)| *desc_name == name)
|
.find(|(desc_name, _)| *desc_name == name.item)?
|
||||||
{
|
.1;
|
||||||
Some((_, v)) => Some(v),
|
|
||||||
None => None,
|
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>> {
|
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 {
|
pub(crate) fn insert_data_at_key(&mut self, name: &str, value: Tagged<Value>) {
|
||||||
let mut debug = f.debug_struct("Dictionary");
|
self.entries.insert(name.to_string(), value);
|
||||||
|
|
||||||
for (desc, value) in self.entries.iter() {
|
|
||||||
debug.field(desc, &value.debug());
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
pub fn with_capacity(tag: impl Into<Tag>, n: usize) -> TaggedDictBuilder {
|
||||||
TaggedDictBuilder {
|
TaggedDictBuilder {
|
||||||
tag: tag.into(),
|
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 {
|
pub trait SpannedItem: Sized {
|
||||||
fn spanned(self, span: impl Into<Span>) -> Spanned<Self> {
|
fn spanned(self, span: impl Into<Span>) -> Spanned<Self> {
|
||||||
Spanned {
|
Spanned {
|
||||||
|
@ -53,6 +68,17 @@ pub struct Tagged<T> {
|
||||||
pub item: 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> {
|
impl<T> HasTag for Tagged<T> {
|
||||||
fn tag(&self) -> Tag {
|
fn tag(&self) -> Tag {
|
||||||
self.tag.clone()
|
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 {
|
pub fn tag(&self) -> Tag {
|
||||||
self.tag.clone()
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
start: usize,
|
start: usize,
|
||||||
|
|
|
@ -27,7 +27,10 @@ impl ExtractType for bool {
|
||||||
item: Value::Primitive(Primitive::Nothing),
|
item: Value::Primitive(Primitive::Nothing),
|
||||||
..
|
..
|
||||||
} => Ok(false),
|
} => 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)),
|
item: Value::Primitive(Primitive::Path(p)),
|
||||||
..
|
..
|
||||||
} => Ok(p.clone()),
|
} => 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)),
|
item: Value::Primitive(Primitive::Int(int)),
|
||||||
..
|
..
|
||||||
} => Ok(int.tagged(&value.tag).coerce_into("converting to i64")?),
|
} => 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)),
|
item: Value::Primitive(Primitive::Int(int)),
|
||||||
..
|
..
|
||||||
} => Ok(int.tagged(&value.tag).coerce_into("converting to u64")?),
|
} => 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)),
|
item: Value::Primitive(Primitive::String(string)),
|
||||||
..
|
..
|
||||||
} => Ok(string.clone()),
|
} => 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 stdout(&mut self, out: &str);
|
||||||
fn stderr(&mut self, out: &str);
|
fn stderr(&mut self, out: &str);
|
||||||
|
|
||||||
|
fn width(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Host for Box<dyn Host> {
|
impl Host for Box<dyn Host> {
|
||||||
|
@ -37,6 +39,10 @@ impl Host for Box<dyn Host> {
|
||||||
fn err_termcolor(&self) -> termcolor::StandardStream {
|
fn err_termcolor(&self) -> termcolor::StandardStream {
|
||||||
(**self).err_termcolor()
|
(**self).err_termcolor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn width(&self) -> usize {
|
||||||
|
(**self).width()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -53,24 +59,29 @@ impl Host for BasicHost {
|
||||||
|
|
||||||
fn stdout(&mut self, out: &str) {
|
fn stdout(&mut self, out: &str) {
|
||||||
match out {
|
match out {
|
||||||
"\n" => println!(""),
|
"\n" => outln!(""),
|
||||||
other => println!("{}", other),
|
other => outln!("{}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stderr(&mut self, out: &str) {
|
fn stderr(&mut self, out: &str) {
|
||||||
match out {
|
match out {
|
||||||
"\n" => eprintln!(""),
|
"\n" => errln!(""),
|
||||||
other => eprintln!("{}", other),
|
other => errln!("{}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn out_termcolor(&self) -> termcolor::StandardStream {
|
fn out_termcolor(&self) -> termcolor::StandardStream {
|
||||||
termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto)
|
termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn err_termcolor(&self) -> termcolor::StandardStream {
|
fn err_termcolor(&self) -> termcolor::StandardStream {
|
||||||
termcolor::StandardStream::stderr(termcolor::ColorChoice::Auto)
|
termcolor::StandardStream::stderr(termcolor::ColorChoice::Auto)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn width(&self) -> usize {
|
||||||
|
std::cmp::max(textwrap::termwidth(), 20)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_unexpected<T>(
|
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 language_reporting::{Diagnostic, Label, Severity};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
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 {
|
pub enum Description {
|
||||||
Source(Tagged<String>),
|
Source(Spanned<String>),
|
||||||
Synthetic(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 {
|
impl Description {
|
||||||
fn into_label(self) -> Result<Label<Span>, String> {
|
fn into_label(self) -> Result<Label<Span>, String> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -20,72 +27,53 @@ impl Description {
|
||||||
Description::Synthetic(s) => Err(s),
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ParseErrorReason {
|
pub enum ParseErrorReason {
|
||||||
Eof {
|
Eof {
|
||||||
expected: &'static str,
|
expected: &'static str,
|
||||||
|
span: Span,
|
||||||
},
|
},
|
||||||
Mismatch {
|
Mismatch {
|
||||||
expected: &'static str,
|
expected: &'static str,
|
||||||
actual: Tagged<String>,
|
actual: Spanned<String>,
|
||||||
},
|
},
|
||||||
ArgumentError {
|
ArgumentError {
|
||||||
command: String,
|
command: Spanned<String>,
|
||||||
error: ArgumentError,
|
error: ArgumentError,
|
||||||
tag: Tag,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParseError {
|
pub struct ParseError {
|
||||||
reason: ParseErrorReason,
|
reason: ParseErrorReason,
|
||||||
tag: Tag,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseError {
|
impl ParseError {
|
||||||
pub fn unexpected_eof(expected: &'static str, span: Span) -> ParseError {
|
pub fn unexpected_eof(expected: &'static str, span: Span) -> ParseError {
|
||||||
ParseError {
|
ParseError {
|
||||||
reason: ParseErrorReason::Eof { expected },
|
reason: ParseErrorReason::Eof { expected, span },
|
||||||
tag: span.into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mismatch(expected: &'static str, actual: Tagged<impl Into<String>>) -> ParseError {
|
pub fn mismatch(expected: &'static str, actual: Spanned<impl Into<String>>) -> ParseError {
|
||||||
let Tagged { tag, item } = actual;
|
let Spanned { span, item } = actual;
|
||||||
|
|
||||||
ParseError {
|
ParseError {
|
||||||
reason: ParseErrorReason::Mismatch {
|
reason: ParseErrorReason::Mismatch {
|
||||||
expected,
|
expected,
|
||||||
actual: item.into().tagged(tag.clone()),
|
actual: item.into().spanned(span),
|
||||||
},
|
},
|
||||||
tag,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn argument_error(
|
pub fn argument_error(command: Spanned<impl Into<String>>, kind: ArgumentError) -> ParseError {
|
||||||
command: impl Into<String>,
|
|
||||||
kind: ArgumentError,
|
|
||||||
tag: impl Into<Tag>,
|
|
||||||
) -> ParseError {
|
|
||||||
let tag = tag.into();
|
|
||||||
|
|
||||||
ParseError {
|
ParseError {
|
||||||
reason: ParseErrorReason::ArgumentError {
|
reason: ParseErrorReason::ArgumentError {
|
||||||
command: command.into(),
|
command: command.item.into().spanned(command.span),
|
||||||
error: kind,
|
error: kind,
|
||||||
tag: tag.clone(),
|
|
||||||
},
|
},
|
||||||
tag: tag.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,20 +81,18 @@ impl ParseError {
|
||||||
impl From<ParseError> for ShellError {
|
impl From<ParseError> for ShellError {
|
||||||
fn from(error: ParseError) -> ShellError {
|
fn from(error: ParseError) -> ShellError {
|
||||||
match error.reason {
|
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 } => {
|
ParseErrorReason::Mismatch { actual, expected } => {
|
||||||
ShellError::type_error(expected, actual.clone())
|
ShellError::type_error(expected, actual.clone())
|
||||||
}
|
}
|
||||||
ParseErrorReason::ArgumentError {
|
ParseErrorReason::ArgumentError { command, error } => {
|
||||||
command,
|
ShellError::argument_error(command, error)
|
||||||
error,
|
}
|
||||||
tag,
|
|
||||||
} => ShellError::argument_error(command, error, tag),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Clone, Ord, Hash, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum ArgumentError {
|
pub enum ArgumentError {
|
||||||
MissingMandatoryFlag(String),
|
MissingMandatoryFlag(String),
|
||||||
MissingMandatoryPositional(String),
|
MissingMandatoryPositional(String),
|
||||||
|
@ -114,19 +100,12 @@ pub enum ArgumentError {
|
||||||
InvalidExternalWord,
|
InvalidExternalWord,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize, Hash)]
|
||||||
pub struct ShellError {
|
pub struct ShellError {
|
||||||
error: ProximateShellError,
|
error: ProximateShellError,
|
||||||
cause: Option<Box<ProximateShellError>>,
|
cause: Option<Box<ProximateShellError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShellError {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn tag(&self) -> Option<Tag> {
|
|
||||||
self.error.tag()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormatDebug for ShellError {
|
impl FormatDebug for ShellError {
|
||||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||||
self.error.fmt_debug(f, source)
|
self.error.fmt_debug(f, source)
|
||||||
|
@ -145,7 +124,7 @@ impl serde::de::Error for ShellError {
|
||||||
impl ShellError {
|
impl ShellError {
|
||||||
pub fn type_error(
|
pub fn type_error(
|
||||||
expected: impl Into<String>,
|
expected: impl Into<String>,
|
||||||
actual: Tagged<impl Into<String>>,
|
actual: Spanned<impl Into<String>>,
|
||||||
) -> ShellError {
|
) -> ShellError {
|
||||||
ProximateShellError::TypeError {
|
ProximateShellError::TypeError {
|
||||||
expected: expected.into(),
|
expected: expected.into(),
|
||||||
|
@ -154,6 +133,28 @@ impl ShellError {
|
||||||
.start()
|
.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 {
|
pub fn untagged_runtime_error(error: impl Into<String>) -> ShellError {
|
||||||
ProximateShellError::UntaggedRuntimeError {
|
ProximateShellError::UntaggedRuntimeError {
|
||||||
reason: error.into(),
|
reason: error.into(),
|
||||||
|
@ -161,10 +162,10 @@ impl ShellError {
|
||||||
.start()
|
.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 {
|
ProximateShellError::UnexpectedEof {
|
||||||
expected: expected.into(),
|
expected: expected.into(),
|
||||||
tag: tag.into(),
|
span: span.into(),
|
||||||
}
|
}
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
|
@ -172,34 +173,26 @@ impl ShellError {
|
||||||
pub(crate) fn range_error(
|
pub(crate) fn range_error(
|
||||||
expected: impl Into<ExpectedRange>,
|
expected: impl Into<ExpectedRange>,
|
||||||
actual: &Tagged<impl fmt::Debug>,
|
actual: &Tagged<impl fmt::Debug>,
|
||||||
operation: String,
|
operation: impl Into<String>,
|
||||||
) -> ShellError {
|
) -> ShellError {
|
||||||
ProximateShellError::RangeError {
|
ProximateShellError::RangeError {
|
||||||
kind: expected.into(),
|
kind: expected.into(),
|
||||||
actual_kind: format!("{:?}", actual.item).tagged(actual.tag()),
|
actual_kind: format!("{:?}", actual.item).spanned(actual.span()),
|
||||||
operation,
|
operation: operation.into(),
|
||||||
}
|
}
|
||||||
.start()
|
.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 {
|
ProximateShellError::SyntaxError {
|
||||||
problem: problem.map(|p| p.into()),
|
problem: problem.map(|p| p.into()),
|
||||||
}
|
}
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn invalid_command(problem: impl Into<Tag>) -> ShellError {
|
|
||||||
ProximateShellError::InvalidCommand {
|
|
||||||
command: problem.into(),
|
|
||||||
}
|
|
||||||
.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn coerce_error(
|
pub(crate) fn coerce_error(
|
||||||
left: Tagged<impl Into<String>>,
|
left: Spanned<impl Into<String>>,
|
||||||
right: Tagged<impl Into<String>>,
|
right: Spanned<impl Into<String>>,
|
||||||
) -> ShellError {
|
) -> ShellError {
|
||||||
ProximateShellError::CoerceError {
|
ProximateShellError::CoerceError {
|
||||||
left: left.map(|l| l.into()),
|
left: left.map(|l| l.into()),
|
||||||
|
@ -208,23 +201,21 @@ impl ShellError {
|
||||||
.start()
|
.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 {
|
ProximateShellError::MissingValue {
|
||||||
tag,
|
span,
|
||||||
reason: reason.into(),
|
reason: reason.into(),
|
||||||
}
|
}
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn argument_error(
|
pub(crate) fn argument_error(
|
||||||
command: impl Into<String>,
|
command: Spanned<impl Into<String>>,
|
||||||
kind: ArgumentError,
|
kind: ArgumentError,
|
||||||
tag: impl Into<Tag>,
|
|
||||||
) -> ShellError {
|
) -> ShellError {
|
||||||
ProximateShellError::ArgumentError {
|
ProximateShellError::ArgumentError {
|
||||||
command: command.into(),
|
command: command.map(|c| c.into()),
|
||||||
error: kind,
|
error: kind,
|
||||||
tag: tag.into(),
|
|
||||||
}
|
}
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
|
@ -262,18 +253,14 @@ impl ShellError {
|
||||||
|
|
||||||
pub(crate) fn to_diagnostic(self) -> Diagnostic<Span> {
|
pub(crate) fn to_diagnostic(self) -> Diagnostic<Span> {
|
||||||
match self.error {
|
match self.error {
|
||||||
ProximateShellError::InvalidCommand { command } => {
|
ProximateShellError::MissingValue { span, reason } => {
|
||||||
Diagnostic::new(Severity::Error, "Invalid command")
|
|
||||||
.with_label(Label::new_primary(command.span))
|
|
||||||
}
|
|
||||||
ProximateShellError::MissingValue { tag, reason } => {
|
|
||||||
let mut d = Diagnostic::new(
|
let mut d = Diagnostic::new(
|
||||||
Severity::Bug,
|
Severity::Bug,
|
||||||
format!("Internal Error (missing value) :: {}", reason),
|
format!("Internal Error (missing value) :: {}", reason),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(tag) = tag {
|
if let Some(span) = span {
|
||||||
d = d.with_label(Label::new_primary(tag.span));
|
d = d.with_label(Label::new_primary(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
d
|
d
|
||||||
|
@ -281,80 +268,79 @@ impl ShellError {
|
||||||
ProximateShellError::ArgumentError {
|
ProximateShellError::ArgumentError {
|
||||||
command,
|
command,
|
||||||
error,
|
error,
|
||||||
tag,
|
|
||||||
} => match error {
|
} => match error {
|
||||||
ArgumentError::InvalidExternalWord => Diagnostic::new(
|
ArgumentError::InvalidExternalWord => Diagnostic::new(
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
format!("Invalid bare word for Nu command (did you intend to invoke an external command?)"))
|
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(
|
ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new(
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
format!(
|
format!(
|
||||||
"{} requires {}{}",
|
"{} requires {}{}",
|
||||||
Color::Cyan.paint(command),
|
Color::Cyan.paint(&command.item),
|
||||||
Color::Black.bold().paint("--"),
|
Color::Black.bold().paint("--"),
|
||||||
Color::Black.bold().paint(name)
|
Color::Black.bold().paint(name)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.with_label(Label::new_primary(tag.span)),
|
.with_label(Label::new_primary(command.span)),
|
||||||
ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new(
|
ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new(
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
format!(
|
format!(
|
||||||
"{} requires {} parameter",
|
"{} requires {} parameter",
|
||||||
Color::Cyan.paint(command),
|
Color::Cyan.paint(&command.item),
|
||||||
Color::Green.bold().paint(name.clone())
|
Color::Green.bold().paint(name.clone())
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.with_label(
|
.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(
|
ArgumentError::MissingValueForName(name) => Diagnostic::new(
|
||||||
Severity::Error,
|
Severity::Error,
|
||||||
format!(
|
format!(
|
||||||
"{} is missing value for flag {}{}",
|
"{} is missing value for flag {}{}",
|
||||||
Color::Cyan.paint(command),
|
Color::Cyan.paint(&command.item),
|
||||||
Color::Black.bold().paint("--"),
|
Color::Black.bold().paint("--"),
|
||||||
Color::Black.bold().paint(name)
|
Color::Black.bold().paint(name)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.with_label(Label::new_primary(tag.span)),
|
.with_label(Label::new_primary(command.span)),
|
||||||
},
|
},
|
||||||
ProximateShellError::TypeError {
|
ProximateShellError::TypeError {
|
||||||
expected,
|
expected,
|
||||||
actual:
|
actual:
|
||||||
Tagged {
|
Spanned {
|
||||||
item: Some(actual),
|
item: Some(actual),
|
||||||
tag,
|
span,
|
||||||
},
|
},
|
||||||
} => Diagnostic::new(Severity::Error, "Type Error").with_label(
|
} => Diagnostic::new(Severity::Error, "Type Error").with_label(
|
||||||
Label::new_primary(tag.span)
|
Label::new_primary(span)
|
||||||
.with_message(format!("Expected {}, found {}", expected, actual)),
|
.with_message(format!("Expected {}, found {}", expected, actual)),
|
||||||
),
|
),
|
||||||
ProximateShellError::TypeError {
|
ProximateShellError::TypeError {
|
||||||
expected,
|
expected,
|
||||||
actual:
|
actual:
|
||||||
Tagged {
|
Spanned {
|
||||||
item: None,
|
item: None,
|
||||||
tag
|
span
|
||||||
},
|
},
|
||||||
} => Diagnostic::new(Severity::Error, "Type Error")
|
} => 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 {
|
ProximateShellError::UnexpectedEof {
|
||||||
expected, tag
|
expected, span
|
||||||
} => Diagnostic::new(Severity::Error, format!("Unexpected end of input"))
|
} => 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 {
|
ProximateShellError::RangeError {
|
||||||
kind,
|
kind,
|
||||||
operation,
|
operation,
|
||||||
actual_kind:
|
actual_kind:
|
||||||
Tagged {
|
Spanned {
|
||||||
item,
|
item,
|
||||||
tag
|
span
|
||||||
},
|
},
|
||||||
} => Diagnostic::new(Severity::Error, "Range Error").with_label(
|
} => 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",
|
"Expected to convert {} to {} while {}, but it was out of range",
|
||||||
item,
|
item,
|
||||||
kind.desc(),
|
kind.desc(),
|
||||||
|
@ -364,12 +350,12 @@ impl ShellError {
|
||||||
|
|
||||||
ProximateShellError::SyntaxError {
|
ProximateShellError::SyntaxError {
|
||||||
problem:
|
problem:
|
||||||
Tagged {
|
Spanned {
|
||||||
tag,
|
span,
|
||||||
item
|
item
|
||||||
},
|
},
|
||||||
} => Diagnostic::new(Severity::Error, "Syntax Error")
|
} => 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, .. } => {
|
ProximateShellError::MissingProperty { subpath, expr, .. } => {
|
||||||
let subpath = subpath.into_label();
|
let subpath = subpath.into_label();
|
||||||
|
@ -389,11 +375,26 @@ impl ShellError {
|
||||||
diag
|
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::Diagnostic(diag) => diag.diagnostic,
|
||||||
ProximateShellError::CoerceError { left, right } => {
|
ProximateShellError::CoerceError { left, right } => {
|
||||||
Diagnostic::new(Severity::Error, "Coercion error")
|
Diagnostic::new(Severity::Error, "Coercion error")
|
||||||
.with_label(Label::new_primary(left.tag().span).with_message(left.item))
|
.with_label(Label::new_primary(left.span).with_message(left.item))
|
||||||
.with_label(Label::new_secondary(right.tag().span).with_message(right.item))
|
.with_label(Label::new_secondary(right.span).with_message(right.item))
|
||||||
}
|
}
|
||||||
|
|
||||||
ProximateShellError::UntaggedRuntimeError { reason } => Diagnostic::new(Severity::Error, format!("Error: {}", reason))
|
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 {
|
pub enum ExpectedRange {
|
||||||
I8,
|
I8,
|
||||||
I16,
|
I16,
|
||||||
|
@ -461,12 +462,24 @@ pub enum ExpectedRange {
|
||||||
U128,
|
U128,
|
||||||
F32,
|
F32,
|
||||||
F64,
|
F64,
|
||||||
|
Usize,
|
||||||
|
Size,
|
||||||
BigInt,
|
BigInt,
|
||||||
BigDecimal,
|
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 {
|
impl ExpectedRange {
|
||||||
fn desc(&self) -> &'static str {
|
fn desc(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
ExpectedRange::I8 => "an 8-bit signed integer",
|
ExpectedRange::I8 => "an 8-bit signed integer",
|
||||||
ExpectedRange::I16 => "a 16-bit signed integer",
|
ExpectedRange::I16 => "a 16-bit signed integer",
|
||||||
|
@ -480,51 +493,54 @@ impl ExpectedRange {
|
||||||
ExpectedRange::U128 => "a 128-bit unsigned integer",
|
ExpectedRange::U128 => "a 128-bit unsigned integer",
|
||||||
ExpectedRange::F32 => "a 32-bit float",
|
ExpectedRange::F32 => "a 32-bit float",
|
||||||
ExpectedRange::F64 => "a 64-bit float",
|
ExpectedRange::F64 => "a 64-bit float",
|
||||||
|
ExpectedRange::Usize => "an list index",
|
||||||
|
ExpectedRange::Size => "a list offset",
|
||||||
ExpectedRange::BigDecimal => "a decimal",
|
ExpectedRange::BigDecimal => "a decimal",
|
||||||
ExpectedRange::BigInt => "an integer",
|
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 {
|
pub enum ProximateShellError {
|
||||||
SyntaxError {
|
SyntaxError {
|
||||||
problem: Tagged<String>,
|
problem: Spanned<String>,
|
||||||
},
|
},
|
||||||
UnexpectedEof {
|
UnexpectedEof {
|
||||||
expected: String,
|
expected: String,
|
||||||
tag: Tag,
|
span: Span,
|
||||||
},
|
|
||||||
InvalidCommand {
|
|
||||||
command: Tag,
|
|
||||||
},
|
},
|
||||||
TypeError {
|
TypeError {
|
||||||
expected: String,
|
expected: String,
|
||||||
actual: Tagged<Option<String>>,
|
actual: Spanned<Option<String>>,
|
||||||
},
|
},
|
||||||
MissingProperty {
|
MissingProperty {
|
||||||
subpath: Description,
|
subpath: Description,
|
||||||
expr: Description,
|
expr: Description,
|
||||||
tag: Tag,
|
},
|
||||||
|
InvalidIntegerIndex {
|
||||||
|
subpath: Description,
|
||||||
|
integer: Span,
|
||||||
},
|
},
|
||||||
MissingValue {
|
MissingValue {
|
||||||
tag: Option<Tag>,
|
span: Option<Span>,
|
||||||
reason: String,
|
reason: String,
|
||||||
},
|
},
|
||||||
ArgumentError {
|
ArgumentError {
|
||||||
command: String,
|
command: Spanned<String>,
|
||||||
error: ArgumentError,
|
error: ArgumentError,
|
||||||
tag: Tag,
|
|
||||||
},
|
},
|
||||||
RangeError {
|
RangeError {
|
||||||
kind: ExpectedRange,
|
kind: ExpectedRange,
|
||||||
actual_kind: Tagged<String>,
|
actual_kind: Spanned<String>,
|
||||||
operation: String,
|
operation: String,
|
||||||
},
|
},
|
||||||
Diagnostic(ShellDiagnostic),
|
Diagnostic(ShellDiagnostic),
|
||||||
CoerceError {
|
CoerceError {
|
||||||
left: Tagged<String>,
|
left: Spanned<String>,
|
||||||
right: Tagged<String>,
|
right: Spanned<String>,
|
||||||
},
|
},
|
||||||
UntaggedRuntimeError {
|
UntaggedRuntimeError {
|
||||||
reason: String,
|
reason: String,
|
||||||
|
@ -539,21 +555,22 @@ impl ProximateShellError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn tag(&self) -> Option<Tag> {
|
// pub(crate) fn tag(&self) -> Option<Tag> {
|
||||||
Some(match self {
|
// Some(match self {
|
||||||
ProximateShellError::SyntaxError { problem } => problem.tag(),
|
// ProximateShellError::SyntaxError { problem } => problem.tag(),
|
||||||
ProximateShellError::UnexpectedEof { tag, .. } => tag.clone(),
|
// ProximateShellError::UnexpectedEof { tag, .. } => tag.clone(),
|
||||||
ProximateShellError::InvalidCommand { command } => command.clone(),
|
// ProximateShellError::InvalidCommand { command } => command.clone(),
|
||||||
ProximateShellError::TypeError { actual, .. } => actual.tag.clone(),
|
// ProximateShellError::TypeError { actual, .. } => actual.tag.clone(),
|
||||||
ProximateShellError::MissingProperty { tag, .. } => tag.clone(),
|
// ProximateShellError::MissingProperty { tag, .. } => tag.clone(),
|
||||||
ProximateShellError::MissingValue { tag, .. } => return tag.clone(),
|
// ProximateShellError::MissingValue { tag, .. } => return tag.clone(),
|
||||||
ProximateShellError::ArgumentError { tag, .. } => tag.clone(),
|
// ProximateShellError::ArgumentError { tag, .. } => tag.clone(),
|
||||||
ProximateShellError::RangeError { actual_kind, .. } => actual_kind.tag.clone(),
|
// ProximateShellError::RangeError { actual_kind, .. } => actual_kind.tag.clone(),
|
||||||
ProximateShellError::Diagnostic(..) => return None,
|
// ProximateShellError::InvalidIntegerIndex { integer, .. } => integer.into(),
|
||||||
ProximateShellError::UntaggedRuntimeError { .. } => return None,
|
// ProximateShellError::Diagnostic(..) => return None,
|
||||||
ProximateShellError::CoerceError { left, right } => left.tag.until(&right.tag),
|
// ProximateShellError::UntaggedRuntimeError { .. } => return None,
|
||||||
})
|
// ProximateShellError::CoerceError { left, right } => left.tag.until(&right.tag),
|
||||||
}
|
// })
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatDebug for ProximateShellError {
|
impl FormatDebug for ProximateShellError {
|
||||||
|
@ -568,6 +585,23 @@ pub struct ShellDiagnostic {
|
||||||
pub(crate) diagnostic: Diagnostic<Span>,
|
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 {
|
impl PartialEq for ShellDiagnostic {
|
||||||
fn eq(&self, _other: &ShellDiagnostic) -> bool {
|
fn eq(&self, _other: &ShellDiagnostic) -> bool {
|
||||||
false
|
false
|
||||||
|
@ -598,10 +632,10 @@ impl std::fmt::Display for ShellError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match &self.error {
|
match &self.error {
|
||||||
ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"),
|
ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"),
|
||||||
ProximateShellError::InvalidCommand { .. } => write!(f, "InvalidCommand"),
|
|
||||||
ProximateShellError::TypeError { .. } => write!(f, "TypeError"),
|
ProximateShellError::TypeError { .. } => write!(f, "TypeError"),
|
||||||
ProximateShellError::UnexpectedEof { .. } => write!(f, "UnexpectedEof"),
|
ProximateShellError::UnexpectedEof { .. } => write!(f, "UnexpectedEof"),
|
||||||
ProximateShellError::RangeError { .. } => write!(f, "RangeError"),
|
ProximateShellError::RangeError { .. } => write!(f, "RangeError"),
|
||||||
|
ProximateShellError::InvalidIntegerIndex { .. } => write!(f, "InvalidIntegerIndex"),
|
||||||
ProximateShellError::SyntaxError { .. } => write!(f, "SyntaxError"),
|
ProximateShellError::SyntaxError { .. } => write!(f, "SyntaxError"),
|
||||||
ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
|
ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"),
|
||||||
ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"),
|
ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::data::base::Block;
|
use crate::data::base::Block;
|
||||||
use crate::errors::ArgumentError;
|
use crate::errors::ArgumentError;
|
||||||
|
use crate::parser::hir::path::{ColumnPath, RawPathMember};
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
hir::{self, Expression, RawExpression},
|
hir::{self, Expression, RawExpression},
|
||||||
CommandRegistry, Text,
|
CommandRegistry, Text,
|
||||||
|
@ -62,9 +63,8 @@ pub(crate) fn evaluate_baseline_expr(
|
||||||
match &expr.item {
|
match &expr.item {
|
||||||
RawExpression::Literal(literal) => Ok(evaluate_literal(literal.tagged(tag), source)),
|
RawExpression::Literal(literal) => Ok(evaluate_literal(literal.tagged(tag), source)),
|
||||||
RawExpression::ExternalWord => Err(ShellError::argument_error(
|
RawExpression::ExternalWord => Err(ShellError::argument_error(
|
||||||
"Invalid external word",
|
"Invalid external word".spanned(tag.span),
|
||||||
ArgumentError::InvalidExternalWord,
|
ArgumentError::InvalidExternalWord,
|
||||||
tag,
|
|
||||||
)),
|
)),
|
||||||
RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(tag)),
|
RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(tag)),
|
||||||
RawExpression::Synthetic(hir::Synthetic::String(s)) => {
|
RawExpression::Synthetic(hir::Synthetic::String(s)) => {
|
||||||
|
@ -82,14 +82,8 @@ pub(crate) fn evaluate_baseline_expr(
|
||||||
match left.compare(binary.op(), &*right) {
|
match left.compare(binary.op(), &*right) {
|
||||||
Ok(result) => Ok(Value::boolean(result).tagged(tag)),
|
Ok(result) => Ok(Value::boolean(result).tagged(tag)),
|
||||||
Err((left_type, right_type)) => Err(ShellError::coerce_error(
|
Err((left_type, right_type)) => Err(ShellError::coerce_error(
|
||||||
left_type.tagged(Tag {
|
left_type.spanned(binary.left().span),
|
||||||
span: binary.left().span,
|
right_type.spanned(binary.right().span),
|
||||||
anchor: None,
|
|
||||||
}),
|
|
||||||
right_type.tagged(Tag {
|
|
||||||
span: binary.right().span,
|
|
||||||
anchor: None,
|
|
||||||
}),
|
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,35 +104,33 @@ pub(crate) fn evaluate_baseline_expr(
|
||||||
let value = evaluate_baseline_expr(path.head(), registry, scope, source)?;
|
let value = evaluate_baseline_expr(path.head(), registry, scope, source)?;
|
||||||
let mut item = value;
|
let mut item = value;
|
||||||
|
|
||||||
for name in path.tail() {
|
for member in path.tail() {
|
||||||
let next = item.get_data_by_key(name);
|
let next = item.get_data_by_member(member);
|
||||||
|
|
||||||
match next {
|
match next {
|
||||||
None => {
|
Err(err) => {
|
||||||
let possibilities = item.data_descriptors();
|
let possibilities = item.data_descriptors();
|
||||||
|
|
||||||
let mut possible_matches: Vec<_> = possibilities
|
if let RawPathMember::String(name) = &member.item {
|
||||||
.iter()
|
let mut possible_matches: Vec<_> = possibilities
|
||||||
.map(|x| (natural::distance::levenshtein_distance(x, &name), x))
|
.iter()
|
||||||
.collect();
|
.map(|x| (natural::distance::levenshtein_distance(x, &name), x))
|
||||||
|
.collect();
|
||||||
|
|
||||||
possible_matches.sort();
|
possible_matches.sort();
|
||||||
|
|
||||||
if possible_matches.len() > 0 {
|
if possible_matches.len() > 0 {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
format!("did you mean '{}'?", possible_matches[0].1),
|
format!("did you mean '{}'?", possible_matches[0].1),
|
||||||
&tag,
|
&tag,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(err);
|
||||||
"Unknown column",
|
}
|
||||||
"row does not have this column",
|
|
||||||
&tag,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(next) => {
|
Ok(next) => {
|
||||||
item = next.clone().item.tagged(&tag);
|
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> {
|
fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged<Value> {
|
||||||
let result = match literal.item {
|
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::Number(int) => int.into(),
|
||||||
hir::Literal::Size(int, unit) => unit.compute(int),
|
hir::Literal::Size(int, unit) => unit.compute(int),
|
||||||
hir::Literal::String(tag) => Value::string(tag.slice(source)),
|
hir::Literal::String(tag) => Value::string(tag.slice(source)),
|
||||||
|
@ -212,10 +212,12 @@ fn evaluate_external(
|
||||||
_source: &Text,
|
_source: &Text,
|
||||||
) -> Result<Tagged<Value>, ShellError> {
|
) -> Result<Tagged<Value>, ShellError> {
|
||||||
Err(ShellError::syntax_error(
|
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> {
|
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 {
|
for desc in descs {
|
||||||
let value = value.get_data(&desc);
|
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))
|
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();
|
let max_name_size: usize = self.entries.iter().map(|(n, _)| n.len()).max().unwrap();
|
||||||
|
|
||||||
for (name, value) in &self.entries {
|
for (name, value) in &self.entries {
|
||||||
println!("{:width$} : {}", name, value, width = max_name_size)
|
outln!("{:width$} : {}", name, value, width = max_name_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl RenderView for GenericView<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
b @ Value::Block(_) => {
|
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));
|
let view = EntriesView::from_value(&Value::string(printed));
|
||||||
view.render_view(host)?;
|
view.render_view(host)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::data::Value;
|
use crate::data::Value;
|
||||||
use crate::format::RenderView;
|
use crate::format::RenderView;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::traits::{DebugDocBuilder, DebugDocBuilder as b, PrettyDebug};
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use textwrap::fill;
|
use textwrap::fill;
|
||||||
|
|
||||||
|
@ -57,63 +58,33 @@ impl TableView {
|
||||||
let mut entries = vec![];
|
let mut entries = vec![];
|
||||||
|
|
||||||
for (idx, value) in values.iter().enumerate() {
|
for (idx, value) in values.iter().enumerate() {
|
||||||
// let mut row: Vec<(String, &'static str)> = match value {
|
let mut row: Vec<(DebugDocBuilder, &'static str)> = match value {
|
||||||
// Tagged {
|
Tagged {
|
||||||
// item: Value::Row(..),
|
item: Value::Row(..),
|
||||||
// ..
|
..
|
||||||
// } => headers
|
} => headers
|
||||||
// .iter()
|
.iter()
|
||||||
// .enumerate()
|
.enumerate()
|
||||||
// .map(|(i, d)| {
|
.map(|(i, d)| {
|
||||||
// let data = value.get_data(d);
|
let data = value.get_data(d);
|
||||||
// return (
|
return (
|
||||||
// data.borrow().format_leaf(Some(&headers[i])),
|
data.borrow().format_for_column(&headers[i]),
|
||||||
// data.borrow().style_leaf(),
|
data.borrow().style_leaf(),
|
||||||
// );
|
);
|
||||||
// })
|
})
|
||||||
// .collect(),
|
.collect(),
|
||||||
// x => vec![(x.format_leaf(None), x.style_leaf())],
|
x => vec![(x.format_leaf(), x.style_leaf())],
|
||||||
// };
|
};
|
||||||
|
|
||||||
let mut row: Vec<(String, &'static str)> = 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])),
|
|
||||||
data.borrow().style_leaf(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
Value::nothing().format_leaf(None),
|
|
||||||
Value::nothing().style_leaf(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if values.len() > 1 {
|
if values.len() > 1 {
|
||||||
// Indices are black, bold, right-aligned:
|
// 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);
|
entries.push(row);
|
||||||
|
@ -125,10 +96,13 @@ impl TableView {
|
||||||
headers.insert(0, format!("#"));
|
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() {
|
for head in 0..headers.len() {
|
||||||
let mut current_col_max = 0;
|
let mut current_col_max = 0;
|
||||||
for row in 0..values.len() {
|
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 {
|
if value_length > current_col_max {
|
||||||
current_col_max = value_length;
|
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
|
// Make sure we have enough space for the columns we have
|
||||||
let max_num_of_columns = termwidth / 10;
|
let max_num_of_columns = termwidth / 10;
|
||||||
|
|
||||||
|
@ -155,7 +126,7 @@ impl TableView {
|
||||||
|
|
||||||
headers.push("...".to_string());
|
headers.push("...".to_string());
|
||||||
for row in 0..entries.len() {
|
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
|
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
|
// Wrap cells as needed
|
||||||
for head in 0..headers.len() {
|
for head in 0..headers.len() {
|
||||||
if max_per_column[head] > max_naive_column_width {
|
if max_per_column[head] > max_naive_column_width {
|
||||||
headers[head] = fill(&headers[head], max_column_width);
|
if true_width(&headers[head]) > max_column_width {
|
||||||
for row in 0..entries.len() {
|
headers[head] = fill(&headers[head], max_column_width);
|
||||||
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 {
|
impl RenderView for TableView {
|
||||||
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
|
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> {
|
||||||
if self.entries.len() == 0 {
|
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 errors;
|
||||||
mod evaluate;
|
mod evaluate;
|
||||||
mod format;
|
mod format;
|
||||||
|
mod fuzzysearch;
|
||||||
mod git;
|
mod git;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod plugin;
|
mod plugin;
|
||||||
|
@ -26,18 +27,19 @@ mod utils;
|
||||||
pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
|
pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
|
||||||
pub use crate::context::AnchorLocation;
|
pub use crate::context::AnchorLocation;
|
||||||
pub use crate::env::host::BasicHost;
|
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::hir::SyntaxShape;
|
||||||
pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
|
pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
|
||||||
pub use crate::plugin::{serve_plugin, Plugin};
|
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 crate::utils::{did_you_mean, AbsoluteFile, AbsolutePath, RelativePath};
|
||||||
pub use cli::cli;
|
pub use cli::cli;
|
||||||
pub use data::base::{Primitive, Value};
|
pub use data::base::{Primitive, Value};
|
||||||
pub use data::config::{config_path, APP_INFO};
|
pub use data::config::{config_path, APP_INFO};
|
||||||
pub use data::dict::{Dictionary, TaggedDictBuilder, TaggedListBuilder};
|
pub use data::dict::{Dictionary, TaggedDictBuilder, TaggedListBuilder};
|
||||||
pub use data::meta::{
|
pub use data::meta::{
|
||||||
tag_for_tagged_list, HasFallibleSpan, HasSpan, Span, Spanned, SpannedItem, Tag, Tagged,
|
span_for_spanned_list, tag_for_tagged_list, HasFallibleSpan, HasSpan, Span, Spanned,
|
||||||
TaggedItem,
|
SpannedItem, Tag, Tagged, TaggedItem,
|
||||||
};
|
};
|
||||||
pub use errors::{CoerceInto, ShellError};
|
pub use errors::{CoerceInto, ShellError};
|
||||||
pub use num_traits::cast::ToPrimitive;
|
pub use num_traits::cast::ToPrimitive;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub(crate) mod debug;
|
||||||
pub(crate) mod deserializer;
|
pub(crate) mod deserializer;
|
||||||
pub(crate) mod hir;
|
pub(crate) mod hir;
|
||||||
pub(crate) mod parse;
|
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::prelude::*;
|
||||||
|
use crate::ColumnPath;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use serde::de;
|
use serde::de;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -97,7 +98,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||||
item: Value::Primitive(Primitive::Nothing),
|
item: Value::Primitive(Primitive::Nothing),
|
||||||
..
|
..
|
||||||
} => visitor.visit_bool(false),
|
} => 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>
|
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());
|
let de = SeqDeserializer::new(&mut self, items.into_iter());
|
||||||
visitor.visit_seq(de)
|
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>
|
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(
|
(other, tag) => Err(ShellError::type_error(
|
||||||
"Tuple",
|
"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>>();
|
let tagged_val_name = std::any::type_name::<Tagged<Value>>();
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"type_name={} tagged_val_name={}",
|
"name={} type_name={} tagged_val_name={}",
|
||||||
|
name,
|
||||||
type_name,
|
type_name,
|
||||||
tagged_val_name
|
tagged_val_name
|
||||||
);
|
);
|
||||||
|
@ -343,11 +351,33 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
|
||||||
item: Value::Block(block),
|
item: Value::Block(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);
|
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);
|
trace!("Extracting {:?} for {:?}", value.val, type_name);
|
||||||
|
|
||||||
let tag = value.val.tag();
|
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),
|
} => 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>(
|
fn deserialize_enum<V>(
|
||||||
|
|
|
@ -7,6 +7,8 @@ pub(crate) mod path;
|
||||||
pub(crate) mod syntax_shape;
|
pub(crate) mod syntax_shape;
|
||||||
pub(crate) mod tokens_iterator;
|
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::parser::{registry, Operator, Unit};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
@ -178,24 +180,30 @@ impl Expression {
|
||||||
RawExpression::Literal(Literal::String(inner.into())).spanned(outer.into())
|
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(
|
pub(crate) fn path(
|
||||||
head: Expression,
|
head: Expression,
|
||||||
tail: Vec<Spanned<impl Into<String>>>,
|
tail: Vec<impl Into<PathMember>>,
|
||||||
span: impl Into<Span>,
|
span: impl Into<Span>,
|
||||||
) -> Expression {
|
) -> 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())
|
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 Spanned { item, span } = head;
|
||||||
|
let next = next.into();
|
||||||
|
|
||||||
let new_span = head.span.until(next.span);
|
let new_span = head.span.until(next.span);
|
||||||
|
|
||||||
match item {
|
match item {
|
||||||
RawExpression::Path(path) => {
|
RawExpression::Path(path) => {
|
||||||
let (head, mut tail) = path.parts();
|
let (head, mut tail) = path.parts();
|
||||||
|
|
||||||
tail.push(next.map(|i| i.into()));
|
tail.push(next);
|
||||||
Expression::path(head, tail, new_span)
|
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 {
|
pub(crate) fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
|
||||||
RawExpression::Variable(Variable::It(inner.into())).spanned(outer)
|
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> {
|
impl FormatDebug for Spanned<RawExpression> {
|
||||||
|
@ -301,6 +305,7 @@ pub enum Literal {
|
||||||
Size(Number, Unit),
|
Size(Number, Unit),
|
||||||
String(Span),
|
String(Span),
|
||||||
GlobPattern(String),
|
GlobPattern(String),
|
||||||
|
ColumnPath(Vec<Member>),
|
||||||
Bare,
|
Bare,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,6 +323,7 @@ impl std::fmt::Display for Tagged<&Literal> {
|
||||||
Literal::Number(number) => write!(f, "{}", number),
|
Literal::Number(number) => write!(f, "{}", number),
|
||||||
Literal::Size(number, unit) => write!(f, "{}{}", number, unit.as_str()),
|
Literal::Size(number, unit) => write!(f, "{}{}", number, unit.as_str()),
|
||||||
Literal::String(_) => write!(f, "String{{ {}..{} }}", span.start(), span.end()),
|
Literal::String(_) => write!(f, "String{{ {}..{} }}", span.start(), span.end()),
|
||||||
|
Literal::ColumnPath(_) => write!(f, "ColumnPath"),
|
||||||
Literal::GlobPattern(_) => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()),
|
Literal::GlobPattern(_) => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()),
|
||||||
Literal::Bare => write!(f, "Bare{{ {}..{} }}", 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::Number(..) => f.say_str("number", self.span.slice(source)),
|
||||||
Literal::Size(..) => f.say_str("size", 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::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::GlobPattern(..) => f.say_str("glob", self.span.slice(source)),
|
||||||
Literal::Bare => f.say_str("word", self.span.slice(source)),
|
Literal::Bare => f.say_str("word", self.span.slice(source)),
|
||||||
}
|
}
|
||||||
|
@ -342,6 +357,7 @@ impl Literal {
|
||||||
Literal::Number(..) => "number",
|
Literal::Number(..) => "number",
|
||||||
Literal::Size(..) => "size",
|
Literal::Size(..) => "size",
|
||||||
Literal::String(..) => "string",
|
Literal::String(..) => "string",
|
||||||
|
Literal::ColumnPath(..) => "column path",
|
||||||
Literal::Bare => "string",
|
Literal::Bare => "string",
|
||||||
Literal::GlobPattern(_) => "pattern",
|
Literal::GlobPattern(_) => "pattern",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::commands::classified::InternalCommand;
|
use crate::commands::classified::InternalCommand;
|
||||||
use crate::commands::ClassifiedCommand;
|
use crate::commands::ClassifiedCommand;
|
||||||
use crate::env::host::BasicHost;
|
use crate::env::host::BasicHost;
|
||||||
use crate::parser::hir::syntax_shape::*;
|
|
||||||
use crate::parser::hir::TokensIterator;
|
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::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b};
|
||||||
use crate::parser::TokenNode;
|
use crate::parser::TokenNode;
|
||||||
use crate::{HasSpan, Span, SpannedItem, Tag, Text};
|
use crate::{HasSpan, Span, SpannedItem, Tag, Text};
|
||||||
|
@ -28,7 +29,7 @@ fn test_parse_path() {
|
||||||
let bare = tokens[2].expect_bare();
|
let bare = tokens[2].expect_bare();
|
||||||
hir::Expression::path(
|
hir::Expression::path(
|
||||||
hir::Expression::it_variable(inner_var, outer_var),
|
hir::Expression::it_variable(inner_var, outer_var),
|
||||||
vec!["cpu".spanned(bare)],
|
vec![PathMember::string("cpu", bare)],
|
||||||
outer_var.until(bare),
|
outer_var.until(bare),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -50,7 +51,10 @@ fn test_parse_path() {
|
||||||
|
|
||||||
hir::Expression::path(
|
hir::Expression::path(
|
||||||
hir::Expression::variable(inner_var, outer_var),
|
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),
|
outer_var.until(outer_max_ghz),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -66,8 +70,6 @@ fn test_parse_command() {
|
||||||
let bare = tokens[0].expect_bare();
|
let bare = tokens[0].expect_bare();
|
||||||
let pat = tokens[2].expect_pattern();
|
let pat = tokens[2].expect_pattern();
|
||||||
|
|
||||||
eprintln!("{:?} {:?} {:?}", bare, pat, bare.until(pat));
|
|
||||||
|
|
||||||
let mut map = IndexMap::new();
|
let mut map = IndexMap::new();
|
||||||
map.insert("full".to_string(), NamedValue::AbsentSwitch);
|
map.insert("full".to_string(), NamedValue::AbsentSwitch);
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,7 @@ impl ExpandExpression for ExternalHeadShape {
|
||||||
| AtomicToken::Pipeline { .. } => {
|
| AtomicToken::Pipeline { .. } => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
"external command name",
|
"external command name",
|
||||||
atom.tagged_type_name(),
|
"pipeline".spanned(atom.span),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
AtomicToken::ExternalCommand { command } => {
|
AtomicToken::ExternalCommand { command } => {
|
||||||
|
@ -293,7 +293,7 @@ impl ExpandExpression for ExternalContinuationShape {
|
||||||
| AtomicToken::Pipeline { .. } => {
|
| AtomicToken::Pipeline { .. } => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
"external argument",
|
"external argument",
|
||||||
atom.tagged_type_name(),
|
"pipeline".spanned(atom.span),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,10 +1,107 @@
|
||||||
use crate::parser::hir::Expression;
|
use crate::parser::hir::Expression;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::traits::{DebugDocBuilder as b, PrettyDebug};
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use getset::{Getters, MutGetters};
|
use getset::{Getters, MutGetters};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
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(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
|
@ -23,7 +120,7 @@ use std::fmt;
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
head: Expression,
|
head: Expression,
|
||||||
#[get_mut = "pub(crate)"]
|
#[get_mut = "pub(crate)"]
|
||||||
tail: Vec<Spanned<String>>,
|
tail: Vec<PathMember>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Path {
|
impl fmt::Display for Path {
|
||||||
|
@ -39,7 +136,7 @@ impl fmt::Display for Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
pub(crate) fn parts(self) -> (Expression, Vec<Spanned<String>>) {
|
pub(crate) fn parts(self) -> (Expression, Vec<PathMember>) {
|
||||||
(self.head, self.tail)
|
(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::unit::UnitShape;
|
||||||
pub(crate) use self::expression::variable_path::{
|
pub(crate) use self::expression::variable_path::{
|
||||||
ColorableDotShape, ColumnPathShape, DotShape, ExpressionContinuation,
|
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::expression::{continue_expression, AnyExpressionShape};
|
||||||
pub(crate) use self::flat_shape::FlatShape;
|
pub(crate) use self::flat_shape::FlatShape;
|
||||||
|
@ -138,15 +138,15 @@ impl FallibleColorSyntax for SyntaxShape {
|
||||||
impl ExpandExpression for SyntaxShape {
|
impl ExpandExpression for SyntaxShape {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
SyntaxShape::Any => "any",
|
SyntaxShape::Any => "shape[any]",
|
||||||
SyntaxShape::Int => "integer",
|
SyntaxShape::Int => "shape[integer]",
|
||||||
SyntaxShape::String => "string",
|
SyntaxShape::String => "shape[string]",
|
||||||
SyntaxShape::Member => "column name",
|
SyntaxShape::Member => "shape[column name]",
|
||||||
SyntaxShape::ColumnPath => "column path",
|
SyntaxShape::ColumnPath => "shape[column path]",
|
||||||
SyntaxShape::Number => "number",
|
SyntaxShape::Number => "shape[number]",
|
||||||
SyntaxShape::Path => "file path",
|
SyntaxShape::Path => "shape[file path]",
|
||||||
SyntaxShape::Pattern => "glob pattern",
|
SyntaxShape::Pattern => "shape[glob pattern]",
|
||||||
SyntaxShape::Block => "block",
|
SyntaxShape::Block => "shape[block]",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,13 +165,12 @@ impl ExpandExpression for SyntaxShape {
|
||||||
}
|
}
|
||||||
SyntaxShape::ColumnPath => {
|
SyntaxShape::ColumnPath => {
|
||||||
let column_path = expand_syntax(&ColumnPathShape, token_nodes, context)?;
|
let column_path = expand_syntax(&ColumnPathShape, token_nodes, context)?;
|
||||||
|
let Tagged {
|
||||||
let Tagged { item: members, tag } = column_path.path();
|
item: column_path,
|
||||||
|
|
||||||
Ok(hir::Expression::list(
|
|
||||||
members.into_iter().map(|s| s.to_expr()).collect(),
|
|
||||||
tag,
|
tag,
|
||||||
))
|
} = column_path;
|
||||||
|
|
||||||
|
Ok(hir::Expression::column_path(column_path, tag.span))
|
||||||
}
|
}
|
||||||
SyntaxShape::Number => expand_expr(&NumberShape, token_nodes, context),
|
SyntaxShape::Number => expand_expr(&NumberShape, token_nodes, context),
|
||||||
SyntaxShape::Path => expand_expr(&FilePathShape, token_nodes, context),
|
SyntaxShape::Path => expand_expr(&FilePathShape, token_nodes, context),
|
||||||
|
@ -587,7 +586,7 @@ impl ExpandSyntax for BarePathShape {
|
||||||
type Output = Span;
|
type Output = Span;
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"shorthand path"
|
"bare path"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_syntax<'a, 'b>(
|
fn expand_syntax<'a, 'b>(
|
||||||
|
@ -637,7 +636,7 @@ impl FallibleColorSyntax for BareShape {
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, fail
|
// 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())
|
.map_err(|err| err.into())
|
||||||
}
|
}
|
||||||
|
@ -666,7 +665,10 @@ impl FallibleColorSyntax for BareShape {
|
||||||
}) => Ok(span),
|
}) => Ok(span),
|
||||||
|
|
||||||
// otherwise, fail
|
// 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));
|
token_nodes.color_shape((*input).spanned(*span));
|
||||||
|
@ -698,7 +700,10 @@ impl ExpandSyntax for BareShape {
|
||||||
Ok(span.spanned_string(context.source))
|
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
|
// Otherwise, we're not actually looking at a command
|
||||||
_ => Err(ShellError::syntax_error(
|
_ => 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
|
// Otherwise, we're not actually looking at a command
|
||||||
_ => Err(ShellError::syntax_error(
|
_ => 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(
|
return Err(ShellError::type_error(
|
||||||
"command head2",
|
"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)?;
|
let head = expand_syntax(&CommandHeadShape, iterator, context)?;
|
||||||
|
|
||||||
match &head {
|
match &head {
|
||||||
CommandSignature::Expression(expr) => {
|
CommandSignature::Expression(expr) => Err(ParseError::mismatch(
|
||||||
Err(ParseError::mismatch("command", expr.tagged_type_name()))
|
"command",
|
||||||
}
|
expr.type_name().spanned(expr.span),
|
||||||
|
)),
|
||||||
|
|
||||||
// If the command starts with `^`, treat it as an external command no matter what
|
// If the command starts with `^`, treat it as an external command no matter what
|
||||||
CommandSignature::External(name) => {
|
CommandSignature::External(name) => {
|
||||||
|
@ -1276,7 +1282,7 @@ impl ExpandExpression for InternalCommandHeadShape {
|
||||||
node => {
|
node => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
"command head",
|
"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> {
|
impl<'token> SingleError<'token> {
|
||||||
pub(crate) fn error(&self) -> ParseError {
|
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();
|
peeked.commit();
|
||||||
|
@ -1429,7 +1443,12 @@ impl ExpandSyntax for WhitespaceShape {
|
||||||
let span = match peeked.node {
|
let span = match peeked.node {
|
||||||
TokenNode::Whitespace(tag) => *tag,
|
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();
|
peeked.commit();
|
||||||
|
@ -1462,7 +1481,10 @@ impl<T: ExpandExpression> ExpandExpression for SpacedExpression<T> {
|
||||||
expand_expr(&self.inner, token_nodes, context)
|
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(
|
other => Err(ShellError::type_error(
|
||||||
"whitespace",
|
"whitespace",
|
||||||
other.tagged_type_name(),
|
other.spanned_type_name(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1622,7 +1644,7 @@ impl FallibleColorSyntax for SpaceShape {
|
||||||
|
|
||||||
other => Err(ShellError::type_error(
|
other => Err(ShellError::type_error(
|
||||||
"whitespace",
|
"whitespace",
|
||||||
other.tagged_type_name(),
|
other.type_name().spanned(other.span()),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ use crate::parser::{
|
||||||
},
|
},
|
||||||
hir::tokens_iterator::TokensIterator,
|
hir::tokens_iterator::TokensIterator,
|
||||||
parse::token_tree::Delimiter,
|
parse::token_tree::Delimiter,
|
||||||
RawToken, TokenNode,
|
|
||||||
};
|
};
|
||||||
use crate::{Span, Spanned, SpannedItem};
|
use crate::{Span, Spanned, SpannedItem};
|
||||||
|
|
||||||
|
@ -381,8 +380,11 @@ impl FallibleColorSyntax for ShorthandHeadShape {
|
||||||
_context: &ExpandContext,
|
_context: &ExpandContext,
|
||||||
shapes: &mut Vec<Spanned<FlatShape>>,
|
shapes: &mut Vec<Spanned<FlatShape>>,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
|
use crate::parser::parse::token_tree::TokenNode;
|
||||||
|
use crate::parser::parse::tokens::RawToken;
|
||||||
|
|
||||||
// A shorthand path must not be at EOF
|
// 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 {
|
match peeked.node {
|
||||||
// If the head of a shorthand path is a bare token, it expands to `$it.bare`
|
// 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(
|
other => Err(ShellError::type_error(
|
||||||
"shorthand head",
|
"shorthand head",
|
||||||
other.tagged_type_name(),
|
other.spanned_type_name(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,7 +429,7 @@ impl FallibleColorSyntax for ShorthandHeadShape {
|
||||||
shapes: &mut Vec<Spanned<FlatShape>>,
|
shapes: &mut Vec<Spanned<FlatShape>>,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
// A shorthand path must not be at EOF
|
// 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 {
|
match peeked.node {
|
||||||
// If the head of a shorthand path is a bare token, it expands to `$it.bare`
|
// 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>,
|
token_nodes: &'b mut TokensIterator<'a>,
|
||||||
context: &ExpandContext,
|
context: &ExpandContext,
|
||||||
) -> Result<hir::Expression, ParseError> {
|
) -> Result<hir::Expression, ParseError> {
|
||||||
// A shorthand path must not be at EOF
|
let head = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||||
let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?;
|
let head = head.to_path_member(context.source);
|
||||||
|
|
||||||
match peeked.node {
|
// Synthesize an `$it` expression
|
||||||
// If the head of a shorthand path is a bare token, it expands to `$it.bare`
|
let it = synthetic_it();
|
||||||
TokenNode::Token(Spanned {
|
let span = head.span;
|
||||||
item: RawToken::Bare,
|
|
||||||
span,
|
|
||||||
}) => {
|
|
||||||
// Commit the peeked token
|
|
||||||
peeked.commit();
|
|
||||||
|
|
||||||
// Synthesize an `$it` expression
|
Ok(hir::Expression::path(it, vec![head], span))
|
||||||
let it = synthetic_it();
|
|
||||||
|
|
||||||
// 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(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -392,7 +392,7 @@ impl FallibleColorSyntax for BareTailShape {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::syntax_error(
|
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(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::syntax_error(
|
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::prelude::*;
|
||||||
use crate::{Span, Spanned};
|
use crate::{Span, Spanned};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AtomicToken<'tokens> {
|
pub enum AtomicToken<'tokens> {
|
||||||
Eof {
|
Eof {
|
||||||
span: Span,
|
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>>;
|
pub type SpannedAtomicToken<'tokens> = Spanned<AtomicToken<'tokens>>;
|
||||||
|
|
||||||
impl<'tokens> SpannedAtomicToken<'tokens> {
|
impl<'tokens> SpannedAtomicToken<'tokens> {
|
||||||
|
@ -95,35 +123,38 @@ impl<'tokens> SpannedAtomicToken<'tokens> {
|
||||||
AtomicToken::Eof { .. } => {
|
AtomicToken::Eof { .. } => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
expected,
|
expected,
|
||||||
"eof atomic token".tagged(self.span),
|
"eof atomic token".spanned(self.span),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
AtomicToken::Error { .. } => {
|
AtomicToken::Error { .. } => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
expected,
|
expected,
|
||||||
"eof atomic token".tagged(self.span),
|
"eof atomic token".spanned(self.span),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
AtomicToken::Operator { .. } => {
|
AtomicToken::Operator { .. } => {
|
||||||
return Err(ParseError::mismatch(expected, "operator".tagged(self.span)))
|
return Err(ParseError::mismatch(
|
||||||
|
expected,
|
||||||
|
"operator".spanned(self.span),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
AtomicToken::ShorthandFlag { .. } => {
|
AtomicToken::ShorthandFlag { .. } => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
expected,
|
expected,
|
||||||
"shorthand flag".tagged(self.span),
|
"shorthand flag".spanned(self.span),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
AtomicToken::LonghandFlag { .. } => {
|
AtomicToken::LonghandFlag { .. } => {
|
||||||
return Err(ParseError::mismatch(expected, "flag".tagged(self.span)))
|
return Err(ParseError::mismatch(expected, "flag".spanned(self.span)))
|
||||||
}
|
}
|
||||||
AtomicToken::Whitespace { .. } => {
|
AtomicToken::Whitespace { .. } => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
expected,
|
expected,
|
||||||
"whitespace".tagged(self.span),
|
"whitespace".spanned(self.span),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
AtomicToken::Dot { .. } => {
|
AtomicToken::Dot { .. } => {
|
||||||
return Err(ParseError::mismatch(expected, "dot".tagged(self.span)))
|
return Err(ParseError::mismatch(expected, "dot".spanned(self.span)))
|
||||||
}
|
}
|
||||||
AtomicToken::Number { number } => {
|
AtomicToken::Number { number } => {
|
||||||
Expression::number(number.to_number(context.source), self.span)
|
Expression::number(number.to_number(context.source), self.span)
|
||||||
|
@ -281,6 +312,7 @@ pub struct ExpansionRule {
|
||||||
pub(crate) allow_operator: bool,
|
pub(crate) allow_operator: bool,
|
||||||
pub(crate) allow_eof: bool,
|
pub(crate) allow_eof: bool,
|
||||||
pub(crate) treat_size_as_word: bool,
|
pub(crate) treat_size_as_word: bool,
|
||||||
|
pub(crate) separate_members: bool,
|
||||||
pub(crate) commit_errors: bool,
|
pub(crate) commit_errors: bool,
|
||||||
pub(crate) whitespace: WhitespaceHandling,
|
pub(crate) whitespace: WhitespaceHandling,
|
||||||
}
|
}
|
||||||
|
@ -293,6 +325,7 @@ impl ExpansionRule {
|
||||||
allow_operator: false,
|
allow_operator: false,
|
||||||
allow_eof: false,
|
allow_eof: false,
|
||||||
treat_size_as_word: false,
|
treat_size_as_word: false,
|
||||||
|
separate_members: false,
|
||||||
commit_errors: false,
|
commit_errors: false,
|
||||||
whitespace: WhitespaceHandling::RejectWhitespace,
|
whitespace: WhitespaceHandling::RejectWhitespace,
|
||||||
}
|
}
|
||||||
|
@ -307,6 +340,7 @@ impl ExpansionRule {
|
||||||
allow_external_word: true,
|
allow_external_word: true,
|
||||||
allow_operator: true,
|
allow_operator: true,
|
||||||
allow_eof: true,
|
allow_eof: true,
|
||||||
|
separate_members: false,
|
||||||
treat_size_as_word: false,
|
treat_size_as_word: false,
|
||||||
commit_errors: true,
|
commit_errors: true,
|
||||||
whitespace: WhitespaceHandling::AllowWhitespace,
|
whitespace: WhitespaceHandling::AllowWhitespace,
|
||||||
|
@ -355,6 +389,18 @@ impl ExpansionRule {
|
||||||
self
|
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)]
|
#[allow(unused)]
|
||||||
pub fn commit_errors(mut self) -> ExpansionRule {
|
pub fn commit_errors(mut self) -> ExpansionRule {
|
||||||
self.commit_errors = true;
|
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
|
/// If the caller of expand_atom throws away the returned atomic token returned, it
|
||||||
/// must use a checkpoint to roll it back.
|
/// 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>,
|
token_nodes: &'me mut TokensIterator<'content>,
|
||||||
expected: &'static str,
|
expected: &'static str,
|
||||||
context: &ExpandContext,
|
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
|
// 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.
|
// words as well as `.`s, connected together without whitespace.
|
||||||
match expand_syntax(&BarePathShape, token_nodes, context) {
|
match expand_syntax(&BarePathShape, token_nodes, context) {
|
||||||
|
@ -515,13 +648,7 @@ pub fn expand_atom<'me, 'content>(
|
||||||
|
|
||||||
// if whitespace is disallowed, return an error
|
// if whitespace is disallowed, return an error
|
||||||
WhitespaceHandling::RejectWhitespace => {
|
WhitespaceHandling::RejectWhitespace => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(expected, "whitespace".spanned(*span)))
|
||||||
expected,
|
|
||||||
"whitespace".tagged(Tag {
|
|
||||||
span: *span,
|
|
||||||
anchor: None,
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -530,7 +657,8 @@ pub fn expand_atom<'me, 'content>(
|
||||||
|
|
||||||
peeked.commit();
|
peeked.commit();
|
||||||
return Ok(AtomicToken::Error {
|
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));
|
.spanned(span));
|
||||||
}
|
}
|
||||||
|
@ -547,20 +675,14 @@ pub fn expand_atom<'me, 'content>(
|
||||||
RawToken::ExternalCommand(_) if !rule.allow_external_command => {
|
RawToken::ExternalCommand(_) if !rule.allow_external_command => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
expected,
|
expected,
|
||||||
token.type_name().tagged(Tag {
|
token.type_name().spanned(token_span),
|
||||||
span: token_span,
|
|
||||||
anchor: None,
|
|
||||||
}),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// rule.allow_external_word
|
// rule.allow_external_word
|
||||||
RawToken::ExternalWord if !rule.allow_external_word => {
|
RawToken::ExternalWord if !rule.allow_external_word => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
expected,
|
expected,
|
||||||
"external word".tagged(Tag {
|
"external word".spanned(token_span),
|
||||||
span: token_span,
|
|
||||||
anchor: None,
|
|
||||||
}),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,7 @@ impl ExpandExpression for NumberShape {
|
||||||
RawToken::ExternalWord => {
|
RawToken::ExternalWord => {
|
||||||
return Err(ParseError::mismatch(
|
return Err(ParseError::mismatch(
|
||||||
"number",
|
"number",
|
||||||
"syntax error".tagged(Tag {
|
"syntax error".spanned(token_span),
|
||||||
span: token_span,
|
|
||||||
anchor: None,
|
|
||||||
}),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
RawToken::Variable(tag) => hir::Expression::variable(tag, token_span),
|
RawToken::Variable(tag) => hir::Expression::variable(tag, token_span),
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl FallibleColorSyntax for PatternShape {
|
||||||
Ok(())
|
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(())
|
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 unit = unit_size(span.slice(context.source), *span);
|
||||||
|
|
||||||
let (_, (number, unit)) = match unit {
|
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),
|
Ok((number, unit)) => (number, unit),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
|
use crate::parser::hir::path::PathMember;
|
||||||
use crate::parser::hir::syntax_shape::{
|
use crate::parser::hir::syntax_shape::{
|
||||||
color_fallible_syntax, color_fallible_syntax_with, expand_atom, expand_expr, expand_syntax,
|
color_fallible_syntax, color_fallible_syntax_with, expand_atom, expand_expr, expand_syntax,
|
||||||
parse_single_node, AnyExpressionShape, AtomicToken, BareShape, ExpandContext, ExpandExpression,
|
parse_single_node, AnyExpressionShape, AtomicToken, BareShape, ExpandContext, ExpandExpression,
|
||||||
ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ParseError, Peeked, SkipSyntax,
|
ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ParseError, Peeked, SkipSyntax,
|
||||||
StringShape, TestSyntax, WhitespaceShape,
|
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 crate::prelude::*;
|
||||||
use derive_new::new;
|
use serde::Serialize;
|
||||||
use getset::Getters;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct VariablePathShape;
|
pub struct VariablePathShape;
|
||||||
|
@ -32,7 +33,7 @@ impl ExpandExpression for VariablePathShape {
|
||||||
let head = expand_expr(&VariableShape, token_nodes, context)?;
|
let head = expand_expr(&VariableShape, token_nodes, context)?;
|
||||||
let start = head.span;
|
let start = head.span;
|
||||||
let mut end = start;
|
let mut end = start;
|
||||||
let mut tail: Vec<Spanned<String>> = vec![];
|
let mut tail: Vec<PathMember> = vec![];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match DotShape.skip(token_nodes, context) {
|
match DotShape.skip(token_nodes, context) {
|
||||||
|
@ -40,8 +41,8 @@ impl ExpandExpression for VariablePathShape {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let syntax = expand_syntax(&MemberShape, token_nodes, context)?;
|
let member = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||||
let member = syntax.to_spanned_string(context.source);
|
let member = member.to_path_member(context.source);
|
||||||
|
|
||||||
end = member.span;
|
end = member.span;
|
||||||
tail.push(member);
|
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 {
|
impl ExpandSyntax for PathTailShape {
|
||||||
type Output = Spanned<Vec<Spanned<String>>>;
|
type Output = Spanned<Vec<PathMember>>;
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"path continuation"
|
"path continuation"
|
||||||
|
@ -219,7 +233,7 @@ impl ExpandSyntax for PathTailShape {
|
||||||
context: &ExpandContext,
|
context: &ExpandContext,
|
||||||
) -> Result<Self::Output, ParseError> {
|
) -> Result<Self::Output, ParseError> {
|
||||||
let mut end: Option<Span> = None;
|
let mut end: Option<Span> = None;
|
||||||
let mut tail = vec![];
|
let mut tail: Vec<PathMember> = vec![];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match DotShape.skip(token_nodes, context) {
|
match DotShape.skip(token_nodes, context) {
|
||||||
|
@ -227,23 +241,17 @@ impl ExpandSyntax for PathTailShape {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let syntax = expand_syntax(&MemberShape, token_nodes, context)?;
|
let member = expand_syntax(&MemberShape, token_nodes, context)?;
|
||||||
let member = syntax.to_spanned_string(context.source);
|
let member = member.to_path_member(context.source);
|
||||||
end = Some(member.span);
|
end = Some(member.span);
|
||||||
tail.push(member);
|
tail.push(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
match end {
|
match end {
|
||||||
None => {
|
None => Err(ParseError::mismatch(
|
||||||
return Err(ParseError::mismatch("path tail", {
|
"path tail",
|
||||||
let typed_span = token_nodes.typed_span_at_cursor();
|
token_nodes.typed_span_at_cursor(),
|
||||||
|
)),
|
||||||
Tagged {
|
|
||||||
tag: typed_span.span.into(),
|
|
||||||
item: typed_span.item,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(end) => Ok(tail.spanned(end)),
|
Some(end) => Ok(tail.spanned(end)),
|
||||||
}
|
}
|
||||||
|
@ -252,7 +260,7 @@ impl ExpandSyntax for PathTailShape {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ExpressionContinuation {
|
pub enum ExpressionContinuation {
|
||||||
DotSuffix(Span, Spanned<String>),
|
DotSuffix(Span, PathMember),
|
||||||
InfixSuffix(Spanned<Operator>, Expression),
|
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
|
// If a `.` was matched, it's a `Path`, and we expect a `Member` next
|
||||||
Ok(dot) => {
|
Ok(dot) => {
|
||||||
let syntax = expand_syntax(&MemberShape, token_nodes, context)?;
|
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))
|
Ok(ExpressionContinuation::DotSuffix(dot, member))
|
||||||
}
|
}
|
||||||
|
@ -487,7 +495,7 @@ impl FallibleColorSyntax for VariableShape {
|
||||||
shapes.push(FlatShape::ItVariable.spanned(atom.span));
|
shapes.push(FlatShape::ItVariable.spanned(atom.span));
|
||||||
Ok(())
|
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));
|
token_nodes.color_shape(FlatShape::ItVariable.spanned(atom.span));
|
||||||
Ok(())
|
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 {
|
pub enum Member {
|
||||||
String(/* outer */ Span, /* inner */ Span),
|
String(/* outer */ Span, /* inner */ Span),
|
||||||
|
Int(BigInt, Span),
|
||||||
Bare(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 {
|
impl FormatDebug for Member {
|
||||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Member::String(outer, _) => write!(f, "member ({})", outer.slice(source)),
|
Member::String(outer, _) => write!(f, "{}", outer.slice(source)),
|
||||||
Member::Bare(bare) => write!(f, "member ({})", bare.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 {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Member::String(outer, ..) => *outer,
|
Member::String(outer, ..) => *outer,
|
||||||
|
Member::Int(_, int) => *int,
|
||||||
Member::Bare(name) => *name,
|
Member::Bare(name) => *name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Member {
|
impl Member {
|
||||||
pub(crate) fn to_expr(&self) -> hir::Expression {
|
pub fn to_expr(&self) -> hir::Expression {
|
||||||
match self {
|
match self {
|
||||||
Member::String(outer, inner) => hir::Expression::string(*inner, *outer),
|
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),
|
Member::Bare(span) => hir::Expression::string(*span, *span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,26 +600,10 @@ impl Member {
|
||||||
pub(crate) fn span(&self) -> Span {
|
pub(crate) fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Member::String(outer, _inner) => *outer,
|
Member::String(outer, _inner) => *outer,
|
||||||
|
Member::Int(_, span) => *span,
|
||||||
Member::Bare(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 {
|
enum ColumnPathState {
|
||||||
|
@ -603,10 +619,10 @@ impl ColumnPathState {
|
||||||
match self {
|
match self {
|
||||||
ColumnPathState::Initial => ColumnPathState::LeadingDot(dot),
|
ColumnPathState::Initial => ColumnPathState::LeadingDot(dot),
|
||||||
ColumnPathState::LeadingDot(_) => {
|
ColumnPathState::LeadingDot(_) => {
|
||||||
ColumnPathState::Error(ParseError::mismatch("column", "dot".tagged(dot)))
|
ColumnPathState::Error(ParseError::mismatch("column", "dot".spanned(dot)))
|
||||||
}
|
}
|
||||||
ColumnPathState::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::Member(tag, members) => ColumnPathState::Dot(tag, members, dot),
|
||||||
ColumnPathState::Error(err) => ColumnPathState::Error(err),
|
ColumnPathState::Error(err) => ColumnPathState::Error(err),
|
||||||
|
@ -626,9 +642,10 @@ impl ColumnPathState {
|
||||||
tags
|
tags
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ColumnPathState::Member(..) => {
|
ColumnPathState::Member(..) => ColumnPathState::Error(ParseError::mismatch(
|
||||||
ColumnPathState::Error(ParseError::mismatch("column", member.tagged_type_name()))
|
"column",
|
||||||
}
|
member.type_name().spanned(member.span()),
|
||||||
|
)),
|
||||||
ColumnPathState::Error(err) => ColumnPathState::Error(err),
|
ColumnPathState::Error(err) => ColumnPathState::Error(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -637,10 +654,10 @@ impl ColumnPathState {
|
||||||
match self {
|
match self {
|
||||||
ColumnPathState::Initial => Err(next.type_error("column path")),
|
ColumnPathState::Initial => Err(next.type_error("column path")),
|
||||||
ColumnPathState::LeadingDot(dot) => {
|
ColumnPathState::LeadingDot(dot) => {
|
||||||
Err(ParseError::mismatch("column", "dot".tagged(dot)))
|
Err(ParseError::mismatch("column", "dot".spanned(dot)))
|
||||||
}
|
}
|
||||||
ColumnPathState::Dot(_tag, _members, 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::Member(tag, tags) => Ok(tags.tagged(tag)),
|
||||||
ColumnPathState::Error(err) => Err(err),
|
ColumnPathState::Error(err) => Err(err),
|
||||||
|
@ -655,14 +672,14 @@ pub fn expand_column_path<'a, 'b>(
|
||||||
let mut state = ColumnPathState::Initial;
|
let mut state = ColumnPathState::Initial;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let member = MemberShape.expand_syntax(token_nodes, context);
|
let member = expand_syntax(&MemberShape, token_nodes, context);
|
||||||
|
|
||||||
match member {
|
match member {
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
Ok(member) => state = state.member(member),
|
Ok(member) => state = state.member(member),
|
||||||
}
|
}
|
||||||
|
|
||||||
let dot = DotShape.expand_syntax(token_nodes, context);
|
let dot = expand_syntax(&DotShape, token_nodes, context);
|
||||||
|
|
||||||
match dot {
|
match dot {
|
||||||
Err(_) => break,
|
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 {
|
impl ExpandSyntax for ColumnPathShape {
|
||||||
type Output = ColumnPath;
|
type Output = Tagged<Vec<Member>>;
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"column path"
|
"column path"
|
||||||
|
@ -813,7 +812,7 @@ impl ExpandSyntax for ColumnPathShape {
|
||||||
token_nodes: &'b mut TokensIterator<'a>,
|
token_nodes: &'b mut TokensIterator<'a>,
|
||||||
context: &ExpandContext,
|
context: &ExpandContext,
|
||||||
) -> Result<Self::Output, ParseError> {
|
) -> 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 {
|
impl ExpandSyntax for MemberShape {
|
||||||
type Output = Member;
|
type Output = Member;
|
||||||
|
|
||||||
|
@ -900,6 +948,10 @@ impl ExpandSyntax for MemberShape {
|
||||||
token_nodes: &mut TokensIterator<'_>,
|
token_nodes: &mut TokensIterator<'_>,
|
||||||
context: &ExpandContext,
|
context: &ExpandContext,
|
||||||
) -> Result<Member, ParseError> {
|
) -> Result<Member, ParseError> {
|
||||||
|
if let Ok(int) = expand_syntax(&IntMemberShape, token_nodes, context) {
|
||||||
|
return Ok(int);
|
||||||
|
}
|
||||||
|
|
||||||
let bare = BareShape.test(token_nodes, context);
|
let bare = BareShape.test(token_nodes, context);
|
||||||
if let Some(peeked) = bare {
|
if let Some(peeked) = bare {
|
||||||
let node = peeked.not_eof("column")?.commit();
|
let node = peeked.not_eof("column")?.commit();
|
||||||
|
@ -956,7 +1008,7 @@ impl FallibleColorSyntax for ColorableDotShape {
|
||||||
Ok(())
|
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(())
|
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(
|
return Err(ParseError::mismatch(
|
||||||
"dot",
|
"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
|
// Otherwise, it's not a match
|
||||||
_ => Err(ParseError::mismatch(
|
_ => Err(ParseError::mismatch(
|
||||||
"infix operator",
|
"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 {
|
pub fn peek_error(node: &Option<&TokenNode>, eof_span: Span, expected: &'static str) -> ParseError {
|
||||||
match node {
|
match node {
|
||||||
None => ParseError::unexpected_eof(expected, eof_span),
|
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>(
|
pub fn expand_frame<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
desc: &'static str,
|
desc: &'static str,
|
||||||
block: impl FnOnce(&mut TokensIterator) -> Result<T, ParseError>,
|
block: impl FnOnce(&mut TokensIterator<'content>) -> Result<T, ParseError>,
|
||||||
) -> Result<T, ParseError>
|
) -> Result<T, ParseError>
|
||||||
where
|
where
|
||||||
T: std::fmt::Debug + FormatDebug + Clone + HasFallibleSpan + 'static,
|
T: std::fmt::Debug + FormatDebug + Clone + HasFallibleSpan + 'static,
|
||||||
|
@ -458,6 +458,34 @@ impl<'content> TokensIterator<'content> {
|
||||||
return Ok(value);
|
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)]
|
#[cfg(coloring_in_tokens)]
|
||||||
/// Use a checkpoint when you need to peek more than one token ahead, but can't be sure
|
/// Use a checkpoint when you need to peek more than one token ahead, but can't be sure
|
||||||
/// that you'll succeed.
|
/// 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]
|
#[tracable_parser]
|
||||||
pub fn number(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
pub fn number(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
||||||
let (input, number) = raw_number(input)?;
|
let (input, number) = raw_number(input)?;
|
||||||
|
|
|
@ -147,7 +147,10 @@ impl TokenNode {
|
||||||
item: RawToken::Variable(inner_span),
|
item: RawToken::Variable(inner_span),
|
||||||
span: outer_span,
|
span: outer_span,
|
||||||
}) => Ok((*outer_span, *inner_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 {
|
pub fn is_dot(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
TokenNode::Token(Spanned {
|
TokenNode::Token(Spanned {
|
||||||
|
@ -250,7 +273,10 @@ impl TokenNode {
|
||||||
pub fn as_pipeline(&self) -> Result<Pipeline, ParseError> {
|
pub fn as_pipeline(&self) -> Result<Pipeline, ParseError> {
|
||||||
match self {
|
match self {
|
||||||
TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()),
|
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,
|
Flag,
|
||||||
};
|
};
|
||||||
use crate::traits::ToDebug;
|
use crate::traits::ToDebug;
|
||||||
use crate::{Span, Spanned, Tag, Text};
|
use crate::{Span, Spanned, SpannedItem, Text};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
pub fn parse_command_tail(
|
pub fn parse_command_tail(
|
||||||
|
@ -39,9 +39,8 @@ pub fn parse_command_tail(
|
||||||
|
|
||||||
if tail.at_end() {
|
if tail.at_end() {
|
||||||
return Err(ParseError::argument_error(
|
return Err(ParseError::argument_error(
|
||||||
config.name.clone(),
|
config.name.clone().spanned(flag.span),
|
||||||
ArgumentError::MissingValueForName(name.to_string()),
|
ArgumentError::MissingValueForName(name.to_string()),
|
||||||
flag.span,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,9 +59,8 @@ pub fn parse_command_tail(
|
||||||
|
|
||||||
if tail.at_end() {
|
if tail.at_end() {
|
||||||
return Err(ParseError::argument_error(
|
return Err(ParseError::argument_error(
|
||||||
config.name.clone(),
|
config.name.clone().spanned(flag.span),
|
||||||
ArgumentError::MissingValueForName(name.to_string()),
|
ArgumentError::MissingValueForName(name.to_string()),
|
||||||
flag.span,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,12 +94,8 @@ pub fn parse_command_tail(
|
||||||
PositionalType::Mandatory(..) => {
|
PositionalType::Mandatory(..) => {
|
||||||
if tail.at_end_possible_ws() {
|
if tail.at_end_possible_ws() {
|
||||||
return Err(ParseError::argument_error(
|
return Err(ParseError::argument_error(
|
||||||
config.name.clone(),
|
config.name.clone().spanned(command_span),
|
||||||
ArgumentError::MissingMandatoryPositional(arg.0.name().to_string()),
|
ArgumentError::MissingMandatoryPositional(arg.0.name().to_string()),
|
||||||
Tag {
|
|
||||||
span: command_span,
|
|
||||||
anchor: None,
|
|
||||||
},
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,9 +587,8 @@ fn extract_mandatory(
|
||||||
|
|
||||||
match flag {
|
match flag {
|
||||||
None => Err(ParseError::argument_error(
|
None => Err(ParseError::argument_error(
|
||||||
config.name.clone(),
|
config.name.clone().spanned(span),
|
||||||
ArgumentError::MissingMandatoryFlag(name.to_string()),
|
ArgumentError::MissingMandatoryFlag(name.to_string()),
|
||||||
span,
|
|
||||||
)),
|
)),
|
||||||
|
|
||||||
Some((pos, flag)) => {
|
Some((pos, flag)) => {
|
||||||
|
|
|
@ -7,7 +7,6 @@ use derive_new::new;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub enum NamedType {
|
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 {
|
impl EvaluatedArgs {
|
||||||
pub fn debug(&self) -> DebugEvaluatedArgs<'_> {
|
|
||||||
DebugEvaluatedArgs { args: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nth(&self, pos: usize) -> Option<&Tagged<Value>> {
|
pub fn nth(&self, pos: usize) -> Option<&Tagged<Value>> {
|
||||||
match &self.positional {
|
match &self.positional {
|
||||||
None => None,
|
None => None,
|
||||||
|
|
|
@ -143,8 +143,8 @@ fn send_response<T: Serialize>(result: T) {
|
||||||
let response_raw = serde_json::to_string(&response);
|
let response_raw = serde_json::to_string(&response);
|
||||||
|
|
||||||
match response_raw {
|
match response_raw {
|
||||||
Ok(response) => println!("{}", response),
|
Ok(response) => outln!("{}", response),
|
||||||
Err(err) => println!("{}", err),
|
Err(err) => outln!("{}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crossterm::{cursor, terminal, Attribute, RawScreen};
|
use crossterm::{cursor, terminal, Attribute, RawScreen};
|
||||||
use nu::{
|
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::*;
|
use pretty_hex::*;
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ fn view_binary(
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "rawkey"))]
|
#[cfg(not(feature = "rawkey"))]
|
||||||
{
|
{
|
||||||
println!("Interactive binary viewing currently requires the 'rawkey' feature");
|
outln!("Interactive binary viewing currently requires the 'rawkey' feature");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,7 @@ impl RenderContext {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("{}", Attribute::Reset);
|
outln!("{}", Attribute::Reset);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn render_to_screen_hires(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn flush(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
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() {
|
if raw_image_buffer.is_none() {
|
||||||
//Not yet supported
|
//Not yet supported
|
||||||
println!("{:?}", buffer.hex_dump());
|
outln!("{:?}", buffer.hex_dump());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let raw_image_buffer = raw_image_buffer.unwrap();
|
let raw_image_buffer = raw_image_buffer.unwrap();
|
||||||
|
@ -322,7 +323,7 @@ pub fn view_contents(
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
//Not yet supported
|
//Not yet supported
|
||||||
println!("{:?}", buffer.hex_dump());
|
outln!("{:?}", buffer.hex_dump());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use nu::{
|
use nu::{
|
||||||
serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxShape,
|
serve_plugin, CallInfo, ColumnPath, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError,
|
||||||
Tagged, Value,
|
ShellTypeName, Signature, SpannedItem, SyntaxShape, Tagged, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type ColumnPath = Tagged<Vec<Tagged<Value>>>;
|
|
||||||
|
|
||||||
struct Edit {
|
struct Edit {
|
||||||
field: Option<ColumnPath>,
|
field: Option<Tagged<ColumnPath>>,
|
||||||
value: Option<Value>,
|
value: Option<Value>,
|
||||||
}
|
}
|
||||||
impl Edit {
|
impl Edit {
|
||||||
|
@ -21,7 +19,7 @@ impl Edit {
|
||||||
let value_tag = value.tag();
|
let value_tag = value.tag();
|
||||||
match (value.item, self.value.clone()) {
|
match (value.item, self.value.clone()) {
|
||||||
(obj @ Value::Row(_), Some(v)) => match &self.field {
|
(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),
|
Some(v) => return Ok(v),
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
|
@ -65,12 +63,17 @@ impl Plugin for Edit {
|
||||||
if let Some(args) = call_info.args.positional {
|
if let Some(args) = call_info.args.positional {
|
||||||
match &args[0] {
|
match &args[0] {
|
||||||
table @ Tagged {
|
table @ Tagged {
|
||||||
item: Value::Table(_),
|
item: Value::Primitive(Primitive::ColumnPath(_)),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.field = Some(table.as_column_path()?);
|
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] {
|
match &args[1] {
|
||||||
Tagged { item: v, .. } => {
|
Tagged { item: v, .. } => {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
extern crate indexmap;
|
extern crate indexmap;
|
||||||
|
|
||||||
use nu::{
|
use nu::{
|
||||||
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
|
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError,
|
||||||
SyntaxShape, Tag, Tagged, TaggedItem, Value,
|
ShellTypeName, Signature, SpannedItem, SyntaxShape, Tag, Tagged, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Embed {
|
struct Embed {
|
||||||
|
@ -42,7 +42,12 @@ impl Plugin for Embed {
|
||||||
self.field = Some(s.clone());
|
self.field = Some(s.clone());
|
||||||
self.values = Vec::new();
|
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::{
|
use nu::{
|
||||||
did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
|
did_you_mean, serve_plugin, span_for_spanned_list, CallInfo, ColumnPath, Plugin, Primitive,
|
||||||
ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
|
ReturnSuccess, ReturnValue, ShellError, ShellTypeName, Signature, SpannedItem, SyntaxShape,
|
||||||
|
Tagged, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
|
@ -14,10 +15,8 @@ pub enum SemVerAction {
|
||||||
Patch,
|
Patch,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ColumnPath = Tagged<Vec<Tagged<Value>>>;
|
|
||||||
|
|
||||||
struct Inc {
|
struct Inc {
|
||||||
field: Option<ColumnPath>,
|
field: Option<Tagged<ColumnPath>>,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
action: Option<Action>,
|
action: Option<Action>,
|
||||||
}
|
}
|
||||||
|
@ -89,7 +88,7 @@ impl Inc {
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::type_error(
|
return Err(ShellError::type_error(
|
||||||
"incrementable value",
|
"incrementable value",
|
||||||
value.tagged_type_name(),
|
value.type_name().spanned(value.span()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,48 +97,32 @@ impl Inc {
|
||||||
Some(ref f) => {
|
Some(ref f) => {
|
||||||
let fields = f.clone();
|
let fields = f.clone();
|
||||||
|
|
||||||
let replace_for = value.item.get_data_by_column_path(
|
let replace_for = value.get_data_by_column_path(
|
||||||
value.tag(),
|
&f,
|
||||||
f,
|
Box::new(move |(obj_source, column_path_tried, _)| {
|
||||||
Box::new(move |(obj_source, column_path_tried)| {
|
|
||||||
match did_you_mean(&obj_source, &column_path_tried) {
|
match did_you_mean(&obj_source, &column_path_tried) {
|
||||||
Some(suggestions) => {
|
Some(suggestions) => {
|
||||||
return ShellError::labeled_error(
|
return ShellError::labeled_error(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
format!("did you mean '{}'?", suggestions[0].1),
|
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 => {
|
None => {
|
||||||
return ShellError::labeled_error(
|
return ShellError::labeled_error(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
"row does not contain this 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 {
|
let got = replace_for?;
|
||||||
Ok(got) => match got {
|
let replacement = self.inc(got.map(|x| x.clone()))?;
|
||||||
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),
|
|
||||||
};
|
|
||||||
|
|
||||||
match value.item.replace_data_at_column_path(
|
match value.replace_data_at_column_path(&f, replacement.item.clone()) {
|
||||||
value.tag(),
|
|
||||||
&f,
|
|
||||||
replacement.item.clone(),
|
|
||||||
) {
|
|
||||||
Some(v) => return Ok(v),
|
Some(v) => return Ok(v),
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
|
@ -156,7 +139,7 @@ impl Inc {
|
||||||
},
|
},
|
||||||
_ => Err(ShellError::type_error(
|
_ => Err(ShellError::type_error(
|
||||||
"incrementable value",
|
"incrementable value",
|
||||||
value.tagged_type_name(),
|
value.type_name().spanned(value.span()),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,12 +171,17 @@ impl Plugin for Inc {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
match arg {
|
match arg {
|
||||||
table @ Tagged {
|
table @ Tagged {
|
||||||
item: Value::Table(_),
|
item: Value::Primitive(Primitive::ColumnPath(_)),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.field = Some(table.as_column_path()?);
|
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 super::{Inc, SemVerAction};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu::{
|
use nu::{
|
||||||
CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, Tag, Tagged, TaggedDictBuilder,
|
CallInfo, EvaluatedArgs, PathMember, Plugin, RawPathMember, ReturnSuccess, SpannedItem,
|
||||||
TaggedItem, Value,
|
Tag, Tagged, TaggedDictBuilder, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CallStub {
|
struct CallStub {
|
||||||
|
@ -255,13 +243,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_parameter(&mut self, name: &str) -> &mut Self {
|
fn with_parameter(&mut self, name: &str) -> &mut Self {
|
||||||
let fields: Vec<Tagged<Value>> = name
|
let fields: Vec<PathMember> = name
|
||||||
.split(".")
|
.split(".")
|
||||||
.map(|s| Value::string(s.to_string()).tagged(Tag::unknown()))
|
.map(|s| RawPathMember::String(s.to_string()).spanned_unknown())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.positionals
|
self.positionals
|
||||||
.push(Value::Table(fields).tagged(Tag::unknown()));
|
.push(Value::column_path(fields).tagged_unknown());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,14 +332,13 @@ mod tests {
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
plugin.field.map(|f| f
|
plugin
|
||||||
.iter()
|
.field
|
||||||
.map(|f| match &f.item {
|
.map(|f| f.iter().map(|f| f.item.clone()).collect()),
|
||||||
Value::Primitive(Primitive::String(s)) => s.clone(),
|
Some(vec![
|
||||||
_ => panic!(""),
|
RawPathMember::String("package".to_string()),
|
||||||
})
|
RawPathMember::String("version".to_string())
|
||||||
.collect()),
|
])
|
||||||
Some(vec!["package".to_string(), "version".to_string()])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use itertools::Itertools;
|
|
||||||
use nu::{
|
use nu::{
|
||||||
serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxShape,
|
serve_plugin, CallInfo, ColumnPath, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError,
|
||||||
Tagged, TaggedItem, Value,
|
ShellTypeName, Signature, SpannedItem, SyntaxShape, Tagged, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type ColumnPath = Vec<Tagged<Value>>;
|
|
||||||
|
|
||||||
struct Insert {
|
struct Insert {
|
||||||
field: Option<ColumnPath>,
|
field: Option<ColumnPath>,
|
||||||
value: Option<Value>,
|
value: Option<Tagged<Value>>,
|
||||||
}
|
}
|
||||||
impl Insert {
|
impl Insert {
|
||||||
fn new() -> Insert {
|
fn new() -> Insert {
|
||||||
|
@ -20,38 +17,19 @@ impl Insert {
|
||||||
|
|
||||||
fn insert(&self, value: Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
|
fn insert(&self, value: Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
|
||||||
let value_tag = value.tag();
|
let value_tag = value.tag();
|
||||||
match (value.item, self.value.clone()) {
|
|
||||||
(obj @ Value::Row(_), Some(v)) => match &self.field {
|
match (&value, &self.value, &self.field) {
|
||||||
Some(f) => match obj.insert_data_at_column_path(value_tag.clone(), f, v) {
|
(
|
||||||
Some(v) => return Ok(v),
|
obj @ Tagged {
|
||||||
None => {
|
item: Value::Row(_),
|
||||||
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,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None => Err(ShellError::labeled_error(
|
Some(v),
|
||||||
"add needs a column name when adding a value to a table",
|
Some(field),
|
||||||
"column name",
|
) => obj.clone().insert_data_at_column_path(field, v.clone()),
|
||||||
value_tag,
|
(value, ..) => Err(ShellError::type_error(
|
||||||
)),
|
|
||||||
},
|
|
||||||
(value, _) => Err(ShellError::type_error(
|
|
||||||
"row",
|
"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 {
|
if let Some(args) = call_info.args.positional {
|
||||||
match &args[0] {
|
match &args[0] {
|
||||||
table @ Tagged {
|
table @ Tagged {
|
||||||
item: Value::Table(_),
|
item: Value::Primitive(Primitive::ColumnPath(_)),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.field = Some(table.as_column_path()?.item);
|
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] {
|
match &args[1] {
|
||||||
Tagged { item: v, .. } => {
|
v @ Tagged { .. } => {
|
||||||
self.value = Some(v.clone());
|
self.value = Some(v.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use nu::{
|
use nu::{
|
||||||
did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
|
did_you_mean, serve_plugin, span_for_spanned_list, CallInfo, ColumnPath, Plugin, Primitive,
|
||||||
ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
|
ReturnSuccess, ReturnValue, ShellError, ShellTypeName, Signature, SyntaxShape, Tagged,
|
||||||
|
TaggedItem, Value,
|
||||||
};
|
};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
|
@ -12,10 +13,8 @@ enum Action {
|
||||||
Substring(usize, usize),
|
Substring(usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ColumnPath = Tagged<Vec<Tagged<Value>>>;
|
|
||||||
|
|
||||||
struct Str {
|
struct Str {
|
||||||
field: Option<ColumnPath>,
|
field: Option<Tagged<ColumnPath>>,
|
||||||
params: Option<Vec<String>>,
|
params: Option<Vec<String>>,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
action: Option<Action>,
|
action: Option<Action>,
|
||||||
|
@ -62,7 +61,7 @@ impl Str {
|
||||||
Ok(applied)
|
Ok(applied)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_field(&mut self, column_path: ColumnPath) {
|
fn for_field(&mut self, column_path: Tagged<ColumnPath>) {
|
||||||
self.field = Some(column_path);
|
self.field = Some(column_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,55 +129,33 @@ impl Str {
|
||||||
Some(ref f) => {
|
Some(ref f) => {
|
||||||
let fields = f.clone();
|
let fields = f.clone();
|
||||||
|
|
||||||
let replace_for = value.item.get_data_by_column_path(
|
let replace_for =
|
||||||
value.tag(),
|
value.get_data_by_column_path(
|
||||||
f,
|
&f,
|
||||||
Box::new(move |(obj_source, column_path_tried)| {
|
Box::new(move |(obj_source, column_path_tried, error)| {
|
||||||
match did_you_mean(&obj_source, &column_path_tried) {
|
match did_you_mean(&obj_source, &column_path_tried) {
|
||||||
Some(suggestions) => {
|
Some(suggestions) => {
|
||||||
return ShellError::labeled_error(
|
return ShellError::labeled_error(
|
||||||
"Unknown column",
|
"Unknown column",
|
||||||
format!("did you mean '{}'?", suggestions[0].1),
|
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 error,
|
||||||
}
|
}
|
||||||
None => {
|
}),
|
||||||
return ShellError::labeled_error(
|
);
|
||||||
"Unknown column",
|
|
||||||
"row does not contain this column",
|
|
||||||
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let replacement = match replace_for {
|
let got = replace_for?;
|
||||||
Ok(got) => match got {
|
let replacement = self.strutils(got.map(|x| x.clone()))?;
|
||||||
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),
|
|
||||||
};
|
|
||||||
|
|
||||||
match value.item.replace_data_at_column_path(
|
match value.replace_data_at_column_path(&f, replacement.item.clone()) {
|
||||||
value.tag(),
|
|
||||||
f,
|
|
||||||
replacement.item.clone(),
|
|
||||||
) {
|
|
||||||
Some(v) => return Ok(v),
|
Some(v) => return Ok(v),
|
||||||
None => {
|
None => Err(ShellError::labeled_error(
|
||||||
return Err(ShellError::type_error(
|
"str could not find field to replace",
|
||||||
"column name",
|
"column name",
|
||||||
value.tagged_type_name(),
|
value.tag(),
|
||||||
))
|
)),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Err(ShellError::untagged_runtime_error(format!(
|
None => Err(ShellError::untagged_runtime_error(format!(
|
||||||
|
@ -189,7 +166,7 @@ impl Str {
|
||||||
},
|
},
|
||||||
_ => Err(ShellError::labeled_error(
|
_ => Err(ShellError::labeled_error(
|
||||||
"Unrecognized type in stream",
|
"Unrecognized type in stream",
|
||||||
value.type_name(),
|
value.item.type_name(),
|
||||||
value.tag,
|
value.tag,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
@ -245,27 +222,9 @@ impl Plugin for Str {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(possible_field) = args.nth(0) {
|
if let Some(possible_field) = args.nth(0) {
|
||||||
match possible_field {
|
let possible_field = possible_field.as_column_path()?;
|
||||||
string @ Tagged {
|
|
||||||
item: Value::Primitive(Primitive::String(_)),
|
self.for_field(possible_field);
|
||||||
..
|
|
||||||
} => {
|
|
||||||
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,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for param in args.positional_iter() {
|
for param in args.positional_iter() {
|
||||||
match param {
|
match param {
|
||||||
|
@ -303,8 +262,8 @@ mod tests {
|
||||||
use super::{Action, Str};
|
use super::{Action, Str};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu::{
|
use nu::{
|
||||||
CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, Tag, Tagged, TaggedDictBuilder,
|
CallInfo, EvaluatedArgs, Plugin, Primitive, RawPathMember, ReturnSuccess, Tag, Tagged,
|
||||||
TaggedItem, Value,
|
TaggedDictBuilder, TaggedItem, Value,
|
||||||
};
|
};
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
|
|
||||||
|
@ -419,14 +378,13 @@ mod tests {
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
plugin.field.map(|f| f
|
plugin
|
||||||
.iter()
|
.field
|
||||||
.map(|f| match &f.item {
|
.map(|f| f.iter().cloned().map(|f| f.item).collect()),
|
||||||
Value::Primitive(Primitive::String(s)) => s.clone(),
|
Some(vec![
|
||||||
_ => panic!(""),
|
RawPathMember::String("package".to_string()),
|
||||||
})
|
RawPathMember::String("description".to_string())
|
||||||
.collect()),
|
])
|
||||||
Some(vec!["package".to_string(), "description".to_string()])
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crossterm::{cursor, terminal, RawScreen};
|
use crossterm::{cursor, terminal, RawScreen};
|
||||||
use crossterm::{InputEvent, KeyEvent};
|
use crossterm::{InputEvent, KeyEvent};
|
||||||
use nu::{
|
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;
|
use syntect::easy::HighlightLines;
|
||||||
|
@ -15,6 +16,7 @@ enum DrawCommand {
|
||||||
DrawString(Style, String),
|
DrawString(Style, String),
|
||||||
NextLine,
|
NextLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextView;
|
struct TextView;
|
||||||
|
|
||||||
impl 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) {
|
fn scroll_view(s: &str) {
|
||||||
|
|
|
@ -23,12 +23,13 @@ macro_rules! stream {
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! trace_stream {
|
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) {
|
if log::log_enabled!(target: $target, log::Level::Trace) {
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
let source = $source.clone();
|
||||||
|
|
||||||
let objects = $expr.values.inspect(|o| {
|
let objects = $expr.values.inspect(move |o| {
|
||||||
trace!(target: $target, "{} = {:#?}", $desc, o.debug());
|
trace!(target: $target, "{} = {}", $desc, o.debug(&source));
|
||||||
});
|
});
|
||||||
|
|
||||||
$crate::stream::InputStream::from_stream(objects.boxed())
|
$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::cli::MaybeOwned;
|
||||||
pub(crate) use crate::commands::command::{
|
pub(crate) use crate::commands::command::{
|
||||||
CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext,
|
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::context::{AnchorLocation, Context};
|
||||||
pub(crate) use crate::data::base as value;
|
pub(crate) use crate::data::base as value;
|
||||||
pub(crate) use crate::data::meta::{
|
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,
|
TaggedItem,
|
||||||
};
|
};
|
||||||
pub(crate) use crate::data::types::ExtractType;
|
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::shell_manager::ShellManager;
|
||||||
pub(crate) use crate::shell::value_shell::ValueShell;
|
pub(crate) use crate::shell::value_shell::ValueShell;
|
||||||
pub(crate) use crate::stream::{InputStream, OutputStream};
|
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 crate::Text;
|
||||||
pub(crate) use async_stream::stream as async_stream;
|
pub(crate) use async_stream::stream as async_stream;
|
||||||
pub(crate) use bigdecimal::BigDecimal;
|
pub(crate) use bigdecimal::BigDecimal;
|
||||||
|
|
|
@ -27,7 +27,11 @@ impl HelpShell {
|
||||||
spec.insert("name", cmd);
|
spec.insert("name", cmd);
|
||||||
spec.insert(
|
spec.insert(
|
||||||
"description",
|
"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);
|
spec.insert_tagged("details", value);
|
||||||
|
|
||||||
|
@ -70,7 +74,7 @@ impl HelpShell {
|
||||||
for p in full_path.iter() {
|
for p in full_path.iter() {
|
||||||
match p {
|
match p {
|
||||||
x if x == sep => {}
|
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) => {
|
Some(v) => {
|
||||||
viewed = v.clone();
|
viewed = v.clone();
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,10 +106,10 @@ impl Highlighter for Helper {
|
||||||
trace!(target: "nu::color_syntax", "{:#?}", tokens.color_tracer());
|
trace!(target: "nu::color_syntax", "{:#?}", tokens.color_tracer());
|
||||||
|
|
||||||
if log_enabled!(target: "nu::color_syntax", log::Level::Debug) {
|
if log_enabled!(target: "nu::color_syntax", log::Level::Debug) {
|
||||||
println!("");
|
outln!("");
|
||||||
ptree::print_tree(&tokens.color_tracer().clone().print(Text::from(line)))
|
ptree::print_tree(&tokens.color_tracer().clone().print(Text::from(line)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("");
|
outln!("");
|
||||||
}
|
}
|
||||||
|
|
||||||
for shape in shapes {
|
for shape in shapes {
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl ValueShell {
|
||||||
for p in full_path.iter() {
|
for p in full_path.iter() {
|
||||||
match p {
|
match p {
|
||||||
x if x == sep => {}
|
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) => {
|
Some(v) => {
|
||||||
viewed = v.clone();
|
viewed = v.clone();
|
||||||
}
|
}
|
||||||
|
|
419
src/traits.rs
419
src/traits.rs
|
@ -1,6 +1,36 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
use getset::Getters;
|
||||||
|
use pretty::{BoxAllocator, DocAllocator};
|
||||||
use std::fmt::{self, Write};
|
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> {
|
pub struct Debuggable<'a, T: FormatDebug> {
|
||||||
inner: &'a T,
|
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 {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.inner.fmt_debug(
|
self.inner.fmt_debug(
|
||||||
&mut DebugFormatter::new(
|
&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 {
|
pub trait HasTag {
|
||||||
fn tag(&self) -> Tag;
|
fn tag(&self) -> Tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(Getters, new)]
|
||||||
pub struct DebugFormatter<'me, 'args> {
|
pub struct DebugFormatter<'me, 'args> {
|
||||||
formatter: &'me mut std::fmt::Formatter<'args>,
|
formatter: &'me mut std::fmt::Formatter<'args>,
|
||||||
style: ansi_term::Style,
|
style: ansi_term::Style,
|
||||||
|
@ -38,6 +81,10 @@ pub struct DebugFormatter<'me, 'args> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'me, 'args> 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>(
|
pub fn say<'debuggable>(
|
||||||
&mut self,
|
&mut self,
|
||||||
kind: &str,
|
kind: &str,
|
||||||
|
@ -72,6 +119,40 @@ impl<'me, 'args> DebugFormatter<'me, 'args> {
|
||||||
block(self)
|
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>(
|
pub fn say_dict<'debuggable>(
|
||||||
&mut self,
|
&mut self,
|
||||||
kind: &str,
|
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 {
|
pub trait FormatDebug: std::fmt::Debug {
|
||||||
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result;
|
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 {
|
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::meta::Tagged;
|
||||||
use crate::data::Value;
|
use crate::data::Value;
|
||||||
use crate::errors::ShellError;
|
use crate::errors::ShellError;
|
||||||
|
use crate::{PathMember, RawPathMember};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Div;
|
use std::ops::Div;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
pub fn did_you_mean(
|
pub fn did_you_mean(obj_source: &Value, field_tried: &PathMember) -> Option<Vec<(usize, String)>> {
|
||||||
obj_source: &Value,
|
let field_tried = match &field_tried.item {
|
||||||
field_tried: &Tagged<Value>,
|
RawPathMember::String(string) => string.clone(),
|
||||||
) -> Option<Vec<(usize, String)>> {
|
RawPathMember::Int(int) => format!("{}", int),
|
||||||
let field_tried = field_tried.as_string().unwrap();
|
};
|
||||||
|
|
||||||
let possibilities = obj_source.data_descriptors();
|
let possibilities = obj_source.data_descriptors();
|
||||||
|
|
||||||
|
@ -25,10 +26,10 @@ pub fn did_you_mean(
|
||||||
|
|
||||||
if possible_matches.len() > 0 {
|
if possible_matches.len() > 0 {
|
||||||
possible_matches.sort();
|
possible_matches.sort();
|
||||||
return Some(possible_matches);
|
Some(possible_matches)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AbsoluteFile {
|
pub struct AbsoluteFile {
|
||||||
|
|
|
@ -203,9 +203,18 @@ fn errors_fetching_by_index_out_of_bounds() {
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(actual.contains("Row not found"));
|
assert!(
|
||||||
assert!(actual.contains("There isn't a row indexed at '3'"));
|
actual.contains("Row not found"),
|
||||||
assert!(actual.contains("The table only has 3 rows (0..2)"))
|
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| {
|
Playground::setup("get_test_9", |dirs, sandbox| {
|
||||||
sandbox.with_files(vec![EmptyFile("andres.txt")]);
|
sandbox.with_files(vec![EmptyFile("andres.txt")]);
|
||||||
|
|
||||||
let actual = nu_error!(
|
let actual = nu!(
|
||||||
cwd: dirs.test(), "ls | get"
|
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() {
|
fn open_can_parse_xml() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: "tests/fixtures/formats",
|
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!(
|
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]
|
#[test]
|
||||||
fn lines() {
|
fn lines() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
|
|
|
@ -525,8 +525,7 @@ fn can_convert_table_to_bson_and_back_into_table() {
|
||||||
| to-bson
|
| to-bson
|
||||||
| from-bson
|
| from-bson
|
||||||
| get root
|
| get root
|
||||||
| nth 1
|
| get 1.b
|
||||||
| get b
|
|
||||||
| echo $it
|
| echo $it
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
Loading…
Reference in a new issue