forked from extern/nushell
Improve signature infrastructure
The `config` command uses different kinds of named arguments, which illustrates how it works.
This commit is contained in:
parent
9df8a261ab
commit
69effbc9e7
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -29,6 +29,17 @@ dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "app_dirs"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "0.3.11"
|
||||
@ -169,6 +180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -832,6 +844,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "indexmap"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iovec"
|
||||
@ -842,6 +857,11 @@ dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-match"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.7.11"
|
||||
@ -1053,6 +1073,7 @@ name = "nu"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byte-unit 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1089,6 +1110,8 @@ dependencies = [
|
||||
"sysinfo 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml-query 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1160,6 +1183,15 @@ name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ole32-sys"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
version = "4.3.2"
|
||||
@ -1648,6 +1680,15 @@ name = "shell-words"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "shell32-sys"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.9"
|
||||
@ -1903,6 +1944,30 @@ dependencies = [
|
||||
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml-query"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"is-match 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml-query_derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml-query_derive"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-util"
|
||||
version = "0.1.3"
|
||||
@ -2020,6 +2085,11 @@ dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "xi-unicode"
|
||||
version = "0.1.0"
|
||||
@ -2043,6 +2113,7 @@ dependencies = [
|
||||
"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
|
||||
"checksum ansi_colours 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0f302a81afc6a7f4350c04f0ba7cfab529cc009bca3324b3fb5764e6add8b6"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d"
|
||||
"checksum arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841"
|
||||
"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392"
|
||||
"checksum array-macro 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c4ff37a25fb442a1fecfd399be0dde685558bca30fb998420532889a36852d2"
|
||||
@ -2135,6 +2206,7 @@ dependencies = [
|
||||
"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
|
||||
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
|
||||
"checksum is-match 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7e5b386aef33a1c677be65237cb9d32c3f3ef56bd035949710c4bb13083eb053"
|
||||
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
|
||||
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
@ -2169,6 +2241,7 @@ dependencies = [
|
||||
"checksum num-traits 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d9c79c952a4a139f44a0fe205c4ee66ce239c0e6ce72cd935f5f7e2f717549dd"
|
||||
"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba"
|
||||
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
|
||||
"checksum onig 4.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a646989adad8a19f49be2090374712931c3a59835cb5277b4530f48b417f26e7"
|
||||
"checksum onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388410bf5fa341f10e58e6db3975f4bea1ac30247dd79d37a9e5ced3cb4cc3b0"
|
||||
"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
|
||||
@ -2224,6 +2297,7 @@ dependencies = [
|
||||
"checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f"
|
||||
"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
|
||||
"checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a"
|
||||
"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c"
|
||||
"checksum signal-hook 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "72ab58f1fda436857e6337dcb6a5aaa34f16c5ddc87b3a8b6ef7a212f90b9c5a"
|
||||
"checksum signal-hook-registry 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cded4ffa32146722ec54ab1f16320568465aa922aa9ab4708129599740da85d7"
|
||||
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
@ -2252,6 +2326,8 @@ dependencies = [
|
||||
"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926"
|
||||
"checksum tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72558af20be886ea124595ea0f806dd5703b8958e4705429dd58b3d8231f72f2"
|
||||
"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039"
|
||||
"checksum toml-query 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a24369a1894ac8224efcfd567c3d141aea360292f49888e7ec7dcc316527aebb"
|
||||
"checksum toml-query_derive 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c99ca245ec273c7e75c8ee58f47b882d0146f3c2c8495158082c6671e8b5335"
|
||||
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
||||
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
@ -2271,6 +2347,7 @@ dependencies = [
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
|
||||
"checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a"
|
||||
"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
|
||||
"checksum xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12ea8eda4b1eb72f02d148402e23832d56a33f55d8c1b2d5bcdde91d79d47cb1"
|
||||
"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"
|
||||
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
|
||||
|
@ -11,7 +11,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
rustyline = "4.1.0"
|
||||
sysinfo = "0.8.4"
|
||||
chrono = "0.4.6"
|
||||
chrono = { version = "0.4.6", features = ["serde"] }
|
||||
chrono-tz = "0.5.1"
|
||||
derive-new = "0.5.6"
|
||||
prettytable-rs = "0.8.0"
|
||||
@ -21,7 +21,7 @@ conch-parser = "0.1.1"
|
||||
nom = "5.0.0-beta1"
|
||||
subprocess = "0.1.18"
|
||||
dunce = "1.0.0"
|
||||
indexmap = "1.0.2"
|
||||
indexmap = { version = "1.0.2", features = ["serde-1"] }
|
||||
chrono-humanize = "0.0.11"
|
||||
byte-unit = "2.1.0"
|
||||
ordered-float = "1.0.2"
|
||||
@ -44,6 +44,9 @@ getset = "0.0.7"
|
||||
logos = "0.10.0-rc2"
|
||||
logos-derive = "0.10.0-rc2"
|
||||
language-reporting = "0.3.0"
|
||||
app_dirs = "1.2.1"
|
||||
toml = "0.5.1"
|
||||
toml-query = "0.9.0"
|
||||
|
||||
[dependencies.pancurses]
|
||||
version = "0.16"
|
||||
|
@ -60,6 +60,7 @@ pub async fn cli() -> Result<(), Box<Error>> {
|
||||
command("to-array", to_array::to_array),
|
||||
command("to-json", to_json::to_json),
|
||||
Arc::new(Where),
|
||||
Arc::new(Config),
|
||||
command("sort-by", sort_by::sort_by),
|
||||
]);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ crate mod cd;
|
||||
crate mod classified;
|
||||
crate mod column;
|
||||
crate mod command;
|
||||
crate mod config;
|
||||
crate mod from_json;
|
||||
crate mod ls;
|
||||
crate mod open;
|
||||
@ -22,5 +23,6 @@ crate mod view;
|
||||
crate mod where_;
|
||||
|
||||
crate use command::command;
|
||||
crate use config::Config;
|
||||
crate use to_array::stream_to_array;
|
||||
crate use where_::Where;
|
||||
|
@ -3,7 +3,7 @@ use crate::prelude::*;
|
||||
use std::env;
|
||||
|
||||
pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let target = match args.args.first() {
|
||||
let target = match args.positional.first() {
|
||||
// TODO: This needs better infra
|
||||
None => return Err(ShellError::string(format!("cd must take one arg"))),
|
||||
Some(v) => v.as_string()?.clone(),
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::parser::registry::Args;
|
||||
use crate::prelude::*;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures_codec::{Decoder, Encoder, Framed};
|
||||
@ -80,7 +81,7 @@ crate enum ClassifiedCommand {
|
||||
|
||||
crate struct InternalCommand {
|
||||
crate command: Arc<dyn Command>,
|
||||
crate args: Vec<Value>,
|
||||
crate args: Args,
|
||||
}
|
||||
|
||||
impl InternalCommand {
|
||||
|
@ -4,11 +4,11 @@ use crate::object::Value;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn column(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if args.args.is_empty() {
|
||||
if args.positional.is_empty() {
|
||||
return Err(ShellError::string("select requires a field"));
|
||||
}
|
||||
|
||||
let fields: Result<Vec<String>, _> = args.args.iter().map(|a| a.as_string()).collect();
|
||||
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();
|
||||
let fields = fields?;
|
||||
|
||||
let objects = args
|
||||
|
@ -7,20 +7,22 @@ use std::path::PathBuf;
|
||||
pub struct CommandArgs {
|
||||
pub host: Arc<Mutex<dyn Host + Send>>,
|
||||
pub env: Arc<Mutex<Environment>>,
|
||||
pub args: Vec<Value>,
|
||||
pub positional: Vec<Value>,
|
||||
pub named: indexmap::IndexMap<String, Value>,
|
||||
pub input: InputStream,
|
||||
}
|
||||
|
||||
impl CommandArgs {
|
||||
crate fn from_context(
|
||||
ctx: &'caller mut Context,
|
||||
args: Vec<Value>,
|
||||
positional: Vec<Value>,
|
||||
input: InputStream,
|
||||
) -> CommandArgs {
|
||||
CommandArgs {
|
||||
host: ctx.host.clone(),
|
||||
env: ctx.env.clone(),
|
||||
args,
|
||||
positional,
|
||||
named: indexmap::IndexMap::default(),
|
||||
input,
|
||||
}
|
||||
}
|
||||
|
117
src/commands/config.rs
Normal file
117
src/commands/config.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::config;
|
||||
use crate::object::Value;
|
||||
use crate::parser::registry::{NamedType, NamedValue};
|
||||
use crate::parser::CommandConfig;
|
||||
use crate::prelude::*;
|
||||
use indexmap::IndexMap;
|
||||
use log::trace;
|
||||
|
||||
pub struct Config;
|
||||
|
||||
impl Command for Config {
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
config(args)
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
"config"
|
||||
}
|
||||
|
||||
fn config(&self) -> CommandConfig {
|
||||
let mut named: IndexMap<String, NamedType> = IndexMap::new();
|
||||
named.insert("set".to_string(), NamedType::Optional(NamedValue::Tuple));
|
||||
named.insert("get".to_string(), NamedType::Optional(NamedValue::Single));
|
||||
named.insert("clear".to_string(), NamedType::Switch);
|
||||
|
||||
named.insert(
|
||||
"remove".to_string(),
|
||||
NamedType::Optional(NamedValue::Single),
|
||||
);
|
||||
|
||||
CommandConfig {
|
||||
name: self.name().to_string(),
|
||||
mandatory_positional: vec![],
|
||||
optional_positional: vec![],
|
||||
rest_positional: false,
|
||||
named,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mut result = crate::object::config::config()?;
|
||||
|
||||
trace!("{:#?}", args.positional);
|
||||
trace!("{:#?}", args.named);
|
||||
|
||||
if let Some(v) = args.named.get("get") {
|
||||
let key = v.as_string()?;
|
||||
let value = result
|
||||
.get(&key)
|
||||
.ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?;
|
||||
|
||||
return Ok(
|
||||
futures::stream::once(futures::future::ready(ReturnValue::Value(value.clone())))
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(v) = args.named.get("set") {
|
||||
if let Ok((key, value)) = v.as_pair() {
|
||||
result.insert(key.as_string()?, value.clone());
|
||||
|
||||
config::write_config(&result)?;
|
||||
|
||||
return Ok(
|
||||
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object(
|
||||
result.into(),
|
||||
))))
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_) = args.named.get("clear") {
|
||||
result.clear();
|
||||
|
||||
config::write_config(&result)?;
|
||||
|
||||
return Ok(
|
||||
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object(
|
||||
result.into(),
|
||||
))))
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(v) = args.named.get("remove") {
|
||||
let key = v.as_string()?;
|
||||
|
||||
if result.contains_key(&key) {
|
||||
result.remove(&key);
|
||||
} else {
|
||||
return Err(ShellError::string(&format!(
|
||||
"{} does not exist in config",
|
||||
key
|
||||
)));
|
||||
}
|
||||
|
||||
return Ok(
|
||||
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object(
|
||||
result.into(),
|
||||
))))
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
|
||||
if args.positional.len() == 0 {
|
||||
return Ok(
|
||||
futures::stream::once(futures::future::ready(ReturnValue::Value(Value::Object(
|
||||
result.into(),
|
||||
))))
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
|
||||
Err(ShellError::string(format!("Unimplemented")))
|
||||
}
|
@ -6,7 +6,7 @@ use std::path::PathBuf;
|
||||
pub fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
|
||||
let mut full_path = PathBuf::from(cwd);
|
||||
match &args.args[0] {
|
||||
match &args.positional[0] {
|
||||
Value::Primitive(Primitive::String(s)) => full_path.push(s),
|
||||
_ => {}
|
||||
}
|
||||
@ -14,7 +14,9 @@ pub fn open(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let contents = std::fs::read_to_string(&full_path).unwrap();
|
||||
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(ReturnValue::Value(Value::Primitive(Primitive::String(contents))));
|
||||
stream.push_back(ReturnValue::Value(Value::Primitive(Primitive::String(
|
||||
contents,
|
||||
))));
|
||||
|
||||
Ok(stream.boxed())
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ use crate::object::Value;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if args.args.is_empty() {
|
||||
if args.positional.is_empty() {
|
||||
return Err(ShellError::string("select requires a field"));
|
||||
}
|
||||
|
||||
let fields: Result<Vec<String>, _> = args.args.iter().map(|a| a.as_string()).collect();
|
||||
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();
|
||||
let fields = fields?;
|
||||
|
||||
let stream = args
|
||||
|
@ -8,7 +8,10 @@ fn get_member(path: &str, obj: &Value) -> Option<Value> {
|
||||
match current.get_data_by_key(p) {
|
||||
Some(v) => current = v,
|
||||
None => {
|
||||
return Some(Value::Error(Box::new(ShellError::string(format!("Object field name not found: {}", p)))))
|
||||
return Some(Value::Error(Box::new(ShellError::string(format!(
|
||||
"Object field name not found: {}",
|
||||
p
|
||||
)))))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,11 +20,11 @@ fn get_member(path: &str, obj: &Value) -> Option<Value> {
|
||||
}
|
||||
|
||||
pub fn select(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if args.args.is_empty() {
|
||||
if args.positional.is_empty() {
|
||||
return Err(ShellError::string("select requires a field"));
|
||||
}
|
||||
|
||||
let fields: Result<Vec<String>, _> = args.args.iter().map(|a| a.as_string()).collect();
|
||||
let fields: Result<Vec<String>, _> = args.positional.iter().map(|a| a.as_string()).collect();
|
||||
let fields = fields?;
|
||||
|
||||
let stream = args
|
||||
|
@ -6,7 +6,7 @@ use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
pub fn size(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if args.args.is_empty() {
|
||||
if args.positional.is_empty() {
|
||||
return Err(ShellError::string("size requires at least one file"));
|
||||
}
|
||||
let cwd = args.env.lock().unwrap().cwd().to_path_buf();
|
||||
@ -14,7 +14,7 @@ pub fn size(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mut contents = String::new();
|
||||
|
||||
let mut list = VecDeque::new();
|
||||
for name in args.args {
|
||||
for name in args.positional {
|
||||
let name = name.as_string()?;
|
||||
let path = cwd.join(&name);
|
||||
let mut file = File::open(path)?;
|
||||
|
@ -2,7 +2,7 @@ use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn skip(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let amount = args.args[0].as_i64()?;
|
||||
let amount = args.positional[0].as_i64()?;
|
||||
|
||||
let input = args.input;
|
||||
|
||||
|
@ -2,7 +2,7 @@ use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn sort_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let fields: Result<Vec<_>, _> = args.args.iter().map(|a| a.as_string()).collect();
|
||||
let fields: Result<Vec<_>, _> = args.positional.iter().map(|a| a.as_string()).collect();
|
||||
let fields = fields?;
|
||||
|
||||
let output = args.input.collect::<Vec<_>>();
|
||||
|
@ -7,7 +7,7 @@ use log::debug;
|
||||
|
||||
pub fn split_column(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
let args = args.args;
|
||||
let args = args.positional;
|
||||
|
||||
Ok(input
|
||||
.map(move |v| match v {
|
||||
|
@ -4,7 +4,7 @@ use crate::prelude::*;
|
||||
// TODO: "Amount remaining" wrapper
|
||||
|
||||
pub fn take(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let amount = args.args[0].as_i64()?;
|
||||
let amount = args.positional[0].as_i64()?;
|
||||
|
||||
let input = args.input;
|
||||
|
||||
|
@ -3,7 +3,7 @@ use crate::prelude::*;
|
||||
use prettyprint::PrettyPrinter;
|
||||
|
||||
pub fn view(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let target = match args.args.first() {
|
||||
let target = match args.positional.first() {
|
||||
// TODO: This needs better infra
|
||||
None => return Err(ShellError::string(format!("cat must take one arg"))),
|
||||
Some(v) => v.as_string()?.clone(),
|
||||
|
@ -25,11 +25,11 @@ impl Command for Where {
|
||||
}
|
||||
|
||||
pub fn r#where(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if args.args.is_empty() {
|
||||
if args.positional.is_empty() {
|
||||
return Err(ShellError::string("select requires a field"));
|
||||
}
|
||||
|
||||
let block = args.args[0].as_block()?;
|
||||
let block = args.positional[0].as_block()?;
|
||||
let input = args.input;
|
||||
|
||||
let objects = input.filter_map(move |item| {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::parser::{CommandConfig, CommandRegistry};
|
||||
use crate::parser::{Args, CommandConfig, CommandRegistry};
|
||||
use crate::prelude::*;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
@ -47,13 +47,14 @@ impl Context {
|
||||
crate fn run_command(
|
||||
&mut self,
|
||||
command: Arc<dyn Command>,
|
||||
arg_list: Vec<Value>,
|
||||
args: Args,
|
||||
input: InputStream,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let command_args = CommandArgs {
|
||||
host: self.host.clone(),
|
||||
env: self.env.clone(),
|
||||
args: arg_list,
|
||||
positional: args.positional,
|
||||
named: args.named,
|
||||
input,
|
||||
};
|
||||
|
||||
|
4
src/env/host.rs
vendored
4
src/env/host.rs
vendored
@ -1,7 +1,8 @@
|
||||
use crate::prelude::*;
|
||||
use language_reporting::termcolor;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Host {
|
||||
pub trait Host: Debug {
|
||||
fn out_terminal(&self) -> Box<term::StdoutTerminal>;
|
||||
fn err_terminal(&self) -> Box<term::StderrTerminal>;
|
||||
|
||||
@ -38,6 +39,7 @@ impl Host for Box<dyn Host> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
crate struct BasicHost;
|
||||
|
||||
impl Host for BasicHost {
|
||||
|
@ -3,10 +3,10 @@ use crate::parser::lexer::{Span, SpannedToken};
|
||||
use crate::prelude::*;
|
||||
use derive_new::new;
|
||||
use language_reporting::Diagnostic;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde_derive::Serialize;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum ShellError {
|
||||
String(StringError),
|
||||
Diagnostic(ShellDiagnostic, String),
|
||||
@ -85,7 +85,20 @@ impl Serialize for ShellDiagnostic {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone, Serialize)]
|
||||
impl Deserialize<'de> for ShellDiagnostic {
|
||||
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(ShellDiagnostic {
|
||||
diagnostic: Diagnostic::new(
|
||||
language_reporting::Severity::Error,
|
||||
"deserialize not implemented for ShellDiagnostic",
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone, Serialize, Deserialize)]
|
||||
pub struct StringError {
|
||||
title: String,
|
||||
error: Value,
|
||||
@ -137,3 +150,12 @@ impl std::convert::From<nom::Err<(&str, nom::error::ErrorKind)>> for ShellError
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<toml::ser::Error> for ShellError {
|
||||
fn from(input: toml::ser::Error) -> ShellError {
|
||||
ShellError::String(StringError {
|
||||
title: format!("{:?}", input),
|
||||
error: Value::nothing(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,6 @@ fn evaluate_reference(r: &ast::Variable, scope: &Scope) -> Result<Value, ShellEr
|
||||
|
||||
match r {
|
||||
It => Ok(scope.it.copy()),
|
||||
True => Ok(Value::boolean(true)),
|
||||
False => Ok(Value::boolean(false)),
|
||||
Other(s) => Err(ShellError::string(&format!(
|
||||
"Unimplemented variable reference: {}",
|
||||
s
|
||||
|
@ -24,6 +24,10 @@ impl TableView {
|
||||
let item = &values[0];
|
||||
let descs = item.data_descriptors();
|
||||
|
||||
if descs.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let headers: Vec<String> = descs.iter().map(|d| d.name.display().to_string()).collect();
|
||||
|
||||
let mut entries = vec![];
|
||||
|
@ -1,8 +1,10 @@
|
||||
crate mod base;
|
||||
crate mod config;
|
||||
crate mod desc;
|
||||
crate mod dict;
|
||||
crate mod files;
|
||||
crate mod process;
|
||||
crate mod serialization;
|
||||
crate mod types;
|
||||
|
||||
crate use base::{Primitive, Value};
|
||||
|
@ -10,12 +10,27 @@ use derive_new::new;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde_derive::Serialize;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
type OF64 = OrderedFloat<f64>;
|
||||
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, new)]
|
||||
pub struct OF64 {
|
||||
crate inner: OrderedFloat<f64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
impl OF64 {
|
||||
crate fn into_inner(&self) -> f64 {
|
||||
self.inner.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for OF64 {
|
||||
fn from(float: f64) -> Self {
|
||||
OF64::new(OrderedFloat(float))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize)]
|
||||
pub enum Primitive {
|
||||
Nothing,
|
||||
Int(i64),
|
||||
@ -35,7 +50,7 @@ impl Serialize for Primitive {
|
||||
match self {
|
||||
Primitive::Nothing => serializer.serialize_i32(0),
|
||||
Primitive::Int(i) => serializer.serialize_i64(*i),
|
||||
Primitive::Float(f) => serializer.serialize_f64(f.into_inner()),
|
||||
Primitive::Float(OF64 { inner: f }) => serializer.serialize_f64(f.into_inner()),
|
||||
Primitive::Bytes(b) => serializer.serialize_u128(*b),
|
||||
Primitive::String(ref s) => serializer.serialize_str(s),
|
||||
Primitive::Boolean(b) => serializer.serialize_bool(*b),
|
||||
@ -63,7 +78,7 @@ impl Primitive {
|
||||
}
|
||||
}
|
||||
Primitive::Int(i) => format!("{}", i),
|
||||
Primitive::Float(f) => format!("{:.*}", 2, f.into_inner()),
|
||||
Primitive::Float(OF64 { inner: f }) => format!("{:.*}", 2, f.into_inner()),
|
||||
Primitive::String(s) => format!("{}", s),
|
||||
Primitive::Boolean(b) => match (b, field_name) {
|
||||
(true, None) => format!("Yes"),
|
||||
@ -97,6 +112,17 @@ impl Serialize for Block {
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize<'de> for Block {
|
||||
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Block::new(ast::Expression::Leaf(ast::Leaf::String(
|
||||
format!("Unserializable block"),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub fn invoke(&self, value: &Value) -> Result<Value, ShellError> {
|
||||
let scope = Scope::new(value.copy());
|
||||
@ -115,21 +141,6 @@ pub enum Value {
|
||||
Error(Box<ShellError>),
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Value::Primitive(p) => p.serialize(serializer),
|
||||
Value::Object(o) => o.serialize(serializer),
|
||||
Value::List(l) => l.serialize(serializer),
|
||||
Value::Block(b) => b.serialize(serializer),
|
||||
Value::Error(e) => e.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
crate fn data_descriptors(&self) -> Vec<DataDescriptor> {
|
||||
match self {
|
||||
@ -207,6 +218,24 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn is_string(&self, expected: &str) -> bool {
|
||||
match self {
|
||||
Value::Primitive(Primitive::String(s)) if s == expected => true,
|
||||
other => false,
|
||||
}
|
||||
}
|
||||
|
||||
crate fn as_pair(&self) -> Result<(Value, Value), ShellError> {
|
||||
match self {
|
||||
Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())),
|
||||
other => Err(ShellError::string(format!(
|
||||
"Expected pair, got {:?}",
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn as_string(&self) -> Result<String, ShellError> {
|
||||
match self {
|
||||
Value::Primitive(Primitive::String(s)) => Ok(s.to_string()),
|
||||
@ -280,8 +309,7 @@ impl Value {
|
||||
Value::Primitive(Primitive::Float(s.into()))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn bool(s: impl Into<bool>) -> Value {
|
||||
crate fn boolean(s: impl Into<bool>) -> Value {
|
||||
Value::Primitive(Primitive::Boolean(s.into()))
|
||||
}
|
||||
|
||||
@ -289,6 +317,16 @@ impl Value {
|
||||
Value::Primitive(Primitive::Date(s.into()))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn date_from_str(s: &str) -> Result<Value, ShellError> {
|
||||
let date = DateTime::parse_from_rfc3339(s)
|
||||
.map_err(|err| ShellError::string(&format!("Date parse error: {}", err)))?;
|
||||
|
||||
let date = date.with_timezone(&chrono::offset::Utc);
|
||||
|
||||
Ok(Value::Primitive(Primitive::Date(date)))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
crate fn system_date_result(s: Result<SystemTime, std::io::Error>) -> Value {
|
||||
match s {
|
||||
@ -297,10 +335,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
crate fn boolean(s: impl Into<bool>) -> Value {
|
||||
Value::Primitive(Primitive::Boolean(s.into()))
|
||||
}
|
||||
|
||||
crate fn nothing() -> Value {
|
||||
Value::Primitive(Primitive::Nothing)
|
||||
}
|
||||
|
60
src/object/config.rs
Normal file
60
src/object/config.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
use app_dirs::*;
|
||||
use indexmap::IndexMap;
|
||||
use log::trace;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
const APP_INFO: AppInfo = AppInfo {
|
||||
name: "nu",
|
||||
author: "nu shell developers",
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct Config {
|
||||
#[serde(flatten)]
|
||||
extra: IndexMap<String, Value>,
|
||||
}
|
||||
|
||||
crate fn write_config(map: &IndexMap<String, Value>) -> Result<(), ShellError> {
|
||||
let location = app_root(AppDataType::UserConfig, &APP_INFO)
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?;
|
||||
|
||||
let filename = location.join("config.toml");
|
||||
touch(&filename)?;
|
||||
|
||||
let contents = toml::to_string(&Config { extra: map.clone() })?;
|
||||
|
||||
fs::write(&filename, &contents)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
crate fn config() -> Result<IndexMap<String, Value>, ShellError> {
|
||||
let location = app_root(AppDataType::UserConfig, &APP_INFO)
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?;
|
||||
|
||||
let filename = location.join("config.toml");
|
||||
touch(&filename)?;
|
||||
|
||||
trace!("config file = {}", filename.display());
|
||||
|
||||
let contents = fs::read_to_string(filename)
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?;
|
||||
|
||||
let parsed: Config = toml::from_str(&contents)
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?;
|
||||
|
||||
Ok(parsed.extra)
|
||||
}
|
||||
|
||||
// A simple implementation of `% touch path` (ignores existing files)
|
||||
fn touch(path: &Path) -> io::Result<()> {
|
||||
match OpenOptions::new().create(true).write(true).open(path) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
use crate::object::types::{AnyShell, Type};
|
||||
use crate::object::types::Type;
|
||||
use derive_new::new;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
pub enum DescriptorName {
|
||||
String(String),
|
||||
ValueOf,
|
||||
@ -31,23 +31,11 @@ impl DescriptorName {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, new)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash, new)]
|
||||
pub struct DataDescriptor {
|
||||
crate name: DescriptorName,
|
||||
crate readonly: bool,
|
||||
crate ty: Box<dyn Type>,
|
||||
}
|
||||
|
||||
impl Serialize for DataDescriptor {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self.name {
|
||||
DescriptorName::String(ref s) => serializer.serialize_str(s),
|
||||
DescriptorName::ValueOf => serializer.serialize_str("value_of")
|
||||
}
|
||||
}
|
||||
crate ty: Type,
|
||||
}
|
||||
|
||||
impl From<&str> for DataDescriptor {
|
||||
@ -55,7 +43,7 @@ impl From<&str> for DataDescriptor {
|
||||
DataDescriptor {
|
||||
name: DescriptorName::String(input.to_string()),
|
||||
readonly: true,
|
||||
ty: Box::new(AnyShell),
|
||||
ty: Type::Any,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,49 +53,23 @@ impl From<String> for DataDescriptor {
|
||||
DataDescriptor {
|
||||
name: DescriptorName::String(input),
|
||||
readonly: true,
|
||||
ty: Box::new(AnyShell),
|
||||
ty: Type::Any,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for DataDescriptor {
|
||||
fn eq(&self, other: &DataDescriptor) -> bool {
|
||||
self.name == other.name && self.readonly == other.readonly && self.ty.equal(&*other.ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for DataDescriptor {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
self.readonly.hash(state);
|
||||
self.ty.id().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for DataDescriptor {}
|
||||
|
||||
impl DescriptorName {
|
||||
crate fn for_string_name(name: impl Into<String>) -> DescriptorName {
|
||||
DescriptorName::String(name.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for DataDescriptor {
|
||||
fn clone(&self) -> DataDescriptor {
|
||||
DataDescriptor {
|
||||
name: self.name.clone(),
|
||||
readonly: self.readonly,
|
||||
ty: self.ty.copy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataDescriptor {
|
||||
crate fn value_of() -> DataDescriptor {
|
||||
DataDescriptor {
|
||||
name: DescriptorName::ValueOf,
|
||||
readonly: true,
|
||||
ty: Box::new(AnyShell),
|
||||
ty: Type::Any,
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +77,7 @@ impl DataDescriptor {
|
||||
DataDescriptor {
|
||||
name: name.into(),
|
||||
readonly: true,
|
||||
ty: Box::new(AnyShell),
|
||||
ty: Type::Any,
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,10 +86,6 @@ impl DataDescriptor {
|
||||
}
|
||||
|
||||
crate fn copy(&self) -> DataDescriptor {
|
||||
DataDescriptor {
|
||||
name: self.name.clone(),
|
||||
readonly: self.readonly,
|
||||
ty: self.ty.copy(),
|
||||
}
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
@ -2,28 +2,16 @@ use crate::prelude::*;
|
||||
|
||||
use crate::object::DataDescriptor;
|
||||
use crate::object::{Primitive, Value};
|
||||
use derive_new::new;
|
||||
use indexmap::IndexMap;
|
||||
use serde::ser::{Serialize, Serializer, SerializeMap};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Clone)]
|
||||
#[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, new)]
|
||||
pub struct Dictionary {
|
||||
entries: IndexMap<DataDescriptor, Value>,
|
||||
}
|
||||
|
||||
impl Serialize for Dictionary {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(Some(self.entries.len()))?;
|
||||
for (k, v) in &self.entries {
|
||||
map.serialize_entry(&k, &v)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Dictionary {
|
||||
// TODO: FIXME
|
||||
fn partial_cmp(&self, _other: &Dictionary) -> Option<Ordering> {
|
||||
@ -31,6 +19,18 @@ impl PartialOrd for Dictionary {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IndexMap<String, Value>> for Dictionary {
|
||||
fn from(input: IndexMap<String, Value>) -> Dictionary {
|
||||
let mut out = IndexMap::default();
|
||||
|
||||
for (key, value) in input {
|
||||
out.insert(DataDescriptor::for_string_name(key), value);
|
||||
}
|
||||
|
||||
Dictionary::new(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Dictionary {
|
||||
// TODO: FIXME
|
||||
fn cmp(&self, _other: &Dictionary) -> Ordering {
|
||||
|
111
src/object/serialization.rs
Normal file
111
src/object/serialization.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use crate::object::base::OF64;
|
||||
use crate::prelude::*;
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
use serde::de::{self, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
|
||||
struct OF64Visitor;
|
||||
|
||||
impl Visitor<'_> for OF64Visitor {
|
||||
type Value = OF64;
|
||||
|
||||
fn visit_f64<E>(self, value: f64) -> Result<OF64, E> {
|
||||
Ok(OF64::new(OrderedFloat(value)))
|
||||
}
|
||||
|
||||
fn visit_f32<E>(self, value: f32) -> Result<OF64, E> {
|
||||
Ok(OF64::new(OrderedFloat(value as f64)))
|
||||
}
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a float")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for OF64 {
|
||||
fn deserialize<D>(deserializer: D) -> Result<OF64, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_f64(OF64Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Value::Primitive(p) => p.serialize(serializer),
|
||||
Value::Object(o) => o.serialize(serializer),
|
||||
Value::List(l) => l.serialize(serializer),
|
||||
Value::Block(b) => b.serialize(serializer),
|
||||
Value::Error(e) => e.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ValueVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ValueVisitor {
|
||||
type Value = Value;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a shell value")
|
||||
}
|
||||
|
||||
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::int(value))
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::int(value))
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::int(value))
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
// TODO: Handle overflow better
|
||||
Ok(Value::int(value as i64))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::string(value))
|
||||
}
|
||||
|
||||
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::boolean(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Value {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(ValueVisitor)
|
||||
}
|
||||
}
|
@ -1,30 +1,7 @@
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use derive_new::new;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
pub trait Type: Debug + Send {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn equal(&self, other: &dyn Type) -> bool;
|
||||
fn id(&self) -> u64;
|
||||
fn copy(&self) -> Box<Type>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct AnyShell;
|
||||
|
||||
impl Type for AnyShell {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
|
||||
fn equal(&self, other: &dyn Type) -> bool {
|
||||
other.as_any().is::<AnyShell>()
|
||||
}
|
||||
|
||||
fn id(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn copy(&self) -> Box<Type> {
|
||||
Box::new(AnyShell)
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash, new)]
|
||||
pub enum Type {
|
||||
Any,
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ crate mod registry;
|
||||
crate mod span;
|
||||
|
||||
crate use ast::{ParsedCommand, Pipeline};
|
||||
crate use registry::{CommandConfig, CommandRegistry};
|
||||
crate use registry::{Args, CommandConfig, CommandRegistry};
|
||||
|
||||
use crate::errors::ShellError;
|
||||
use lexer::Lexer;
|
||||
|
@ -85,6 +85,13 @@ impl Expression {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
crate fn is_flag(&self, value: &str) -> bool {
|
||||
match self {
|
||||
Expression::Flag(Flag::Longhand(f)) if value == f => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, new)]
|
||||
@ -151,17 +158,22 @@ impl Path {
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Variable {
|
||||
It,
|
||||
True,
|
||||
False,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl Variable {
|
||||
crate fn from_str(input: &str) -> Expression {
|
||||
match input {
|
||||
"it" => Expression::VariableReference(Variable::It),
|
||||
"true" => Expression::Leaf(Leaf::Boolean(true)),
|
||||
"false" => Expression::Leaf(Leaf::Boolean(false)),
|
||||
other => Expression::VariableReference(Variable::Other(other.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn print(&self) -> String {
|
||||
match self {
|
||||
Variable::It => format!("$it"),
|
||||
Variable::True => format!("$true"),
|
||||
Variable::False => format!("$false"),
|
||||
Variable::Other(s) => format!("${}", s),
|
||||
}
|
||||
}
|
||||
@ -171,18 +183,6 @@ impl Variable {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Variable {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
|
||||
Ok(match input {
|
||||
"it" => Variable::It,
|
||||
"true" => Variable::True,
|
||||
"false" => Variable::False,
|
||||
other => Variable::Other(other.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct BarePath {
|
||||
head: String,
|
||||
|
@ -65,8 +65,8 @@ Expr: Expression = {
|
||||
<PathHead>
|
||||
}
|
||||
|
||||
Variable: Variable = {
|
||||
"$" <"variable"> => Variable::from_str(<>.as_slice()).unwrap(),
|
||||
Variable: Expression = {
|
||||
"$" <"variable"> => Variable::from_str(<>.as_slice()),
|
||||
}
|
||||
|
||||
Member: String = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// auto-generated: "lalrpop 0.17.0"
|
||||
// sha256: efaf89a1d956869b47a3f5daff048341c19934c68ad5e1ed9fe8e5c4222d2
|
||||
// sha256: 327a2eaaded6615e365add5d44719ae0dd3217f5b0fc3ba130f052328c2bd439
|
||||
#![allow(unused)]
|
||||
use std::str::FromStr;
|
||||
use crate::parser::ast::*;
|
||||
@ -41,7 +41,6 @@ mod __parse__Pipeline {
|
||||
Variant10(i64),
|
||||
Variant11(Operator),
|
||||
Variant12(Pipeline),
|
||||
Variant13(Variable),
|
||||
}
|
||||
const __ACTION: &'static [i8] = &[
|
||||
// State 0
|
||||
@ -1364,17 +1363,6 @@ mod __parse__Pipeline {
|
||||
_ => panic!("symbol type mismatch")
|
||||
}
|
||||
}
|
||||
fn __pop_Variant13<
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, Variable, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::Variant13(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
}
|
||||
}
|
||||
fn __pop_Variant10<
|
||||
'input,
|
||||
>(
|
||||
@ -2022,7 +2010,7 @@ mod __parse__Pipeline {
|
||||
) -> (usize, usize)
|
||||
{
|
||||
// Leaf = Variable => ActionFn(9);
|
||||
let __sym0 = __pop_Variant13(__symbols);
|
||||
let __sym0 = __pop_Variant6(__symbols);
|
||||
let __start = __sym0.0.clone();
|
||||
let __end = __sym0.2.clone();
|
||||
let __nt = super::__action9::<>(__sym0);
|
||||
@ -2393,7 +2381,7 @@ mod __parse__Pipeline {
|
||||
let __start = __sym0.0.clone();
|
||||
let __end = __sym1.2.clone();
|
||||
let __nt = super::__action25::<>(__sym0, __sym1);
|
||||
__symbols.push((__start, __Symbol::Variant13(__nt), __end));
|
||||
__symbols.push((__start, __Symbol::Variant6(__nt), __end));
|
||||
(2, 25)
|
||||
}
|
||||
pub(crate) fn __reduce52<
|
||||
@ -2522,7 +2510,7 @@ fn __action8<
|
||||
fn __action9<
|
||||
'input,
|
||||
>(
|
||||
(_, __0, _): (usize, Variable, usize),
|
||||
(_, __0, _): (usize, Expression, usize),
|
||||
) -> Expression
|
||||
{
|
||||
Expression::VariableReference(__0)
|
||||
@ -2679,9 +2667,9 @@ fn __action25<
|
||||
>(
|
||||
(_, _, _): (usize, SpannedToken<'input>, usize),
|
||||
(_, __0, _): (usize, SpannedToken<'input>, usize),
|
||||
) -> Variable
|
||||
) -> Expression
|
||||
{
|
||||
Variable::from_str(__0.as_slice()).unwrap()
|
||||
Variable::from_str(__0.as_slice())
|
||||
}
|
||||
|
||||
fn __action26<
|
||||
|
@ -5,10 +5,21 @@ use indexmap::IndexMap;
|
||||
#[allow(unused)]
|
||||
#[derive(Debug)]
|
||||
pub enum NamedType {
|
||||
Switch(String),
|
||||
Single(String),
|
||||
Array(String),
|
||||
Block(String),
|
||||
Switch,
|
||||
Mandatory(NamedValue),
|
||||
Optional(NamedValue),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NamedValue {
|
||||
Single,
|
||||
Tuple,
|
||||
|
||||
#[allow(unused)]
|
||||
Block,
|
||||
|
||||
#[allow(unused)]
|
||||
Array,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -62,13 +73,55 @@ pub struct CommandConfig {
|
||||
crate named: IndexMap<String, NamedType>,
|
||||
}
|
||||
|
||||
pub struct Args {
|
||||
pub positional: Vec<Value>,
|
||||
pub named: IndexMap<String, Value>,
|
||||
}
|
||||
|
||||
impl CommandConfig {
|
||||
crate fn evaluate_args(
|
||||
&self,
|
||||
mut args: impl Iterator<Item = &'expr ast::Expression>,
|
||||
args: impl Iterator<Item = &'expr ast::Expression>,
|
||||
scope: &Scope,
|
||||
) -> Result<Vec<Value>, ShellError> {
|
||||
let mut results: Vec<Value> = vec![];
|
||||
) -> Result<Args, ShellError> {
|
||||
let mut positional: Vec<Value> = vec![];
|
||||
let mut named: IndexMap<String, Value> = IndexMap::default();
|
||||
|
||||
let mut args: Vec<ast::Expression> = args.cloned().collect();
|
||||
|
||||
for (key, ty) in self.named.iter() {
|
||||
let index = args.iter().position(|a| a.is_flag(&key));
|
||||
|
||||
match (index, ty) {
|
||||
(Some(i), NamedType::Switch) => {
|
||||
args.remove(i);
|
||||
named.insert(key.clone(), Value::boolean(true));
|
||||
}
|
||||
|
||||
(None, NamedType::Switch) => {}
|
||||
|
||||
(Some(i), NamedType::Optional(v)) => {
|
||||
args.remove(i);
|
||||
named.insert(key.clone(), extract_named(&mut args, i, v)?);
|
||||
}
|
||||
|
||||
(None, NamedType::Optional(_)) => {}
|
||||
|
||||
(Some(i), NamedType::Mandatory(v)) => {
|
||||
args.remove(i);
|
||||
named.insert(key.clone(), extract_named(&mut args, i, v)?);
|
||||
}
|
||||
|
||||
(None, NamedType::Mandatory(_)) => {
|
||||
return Err(ShellError::string(&format!(
|
||||
"Expected mandatory argument {}, but it was missing",
|
||||
key
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut args = args.into_iter();
|
||||
|
||||
for param in &self.mandatory_positional {
|
||||
let arg = args.next();
|
||||
@ -84,21 +137,25 @@ impl CommandConfig {
|
||||
Some(arg) => param.evaluate(arg.clone(), scope)?,
|
||||
};
|
||||
|
||||
results.push(value);
|
||||
positional.push(value);
|
||||
}
|
||||
|
||||
if self.rest_positional {
|
||||
let rest: Result<Vec<Value>, _> =
|
||||
args.map(|i| evaluate_expr(i, &Scope::empty())).collect();
|
||||
results.extend(rest?);
|
||||
args.map(|i| evaluate_expr(&i, &Scope::empty())).collect();
|
||||
positional.extend(rest?);
|
||||
} else {
|
||||
match args.next() {
|
||||
None => {}
|
||||
Some(_) => return Err(ShellError::string("Too many arguments")),
|
||||
let rest: Vec<ast::Expression> = args.collect();
|
||||
|
||||
if rest.len() > 0 {
|
||||
return Err(ShellError::string(&format!(
|
||||
"Too many arguments, extras: {:?}",
|
||||
rest
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
Ok(Args { positional, named })
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -107,6 +164,49 @@ impl CommandConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_named(
|
||||
v: &mut Vec<ast::Expression>,
|
||||
position: usize,
|
||||
ty: &NamedValue,
|
||||
) -> Result<Value, ShellError> {
|
||||
match ty {
|
||||
NamedValue::Single => {
|
||||
let expr = v.remove(position);
|
||||
expect_simple_expr(expr)
|
||||
}
|
||||
|
||||
NamedValue::Tuple => {
|
||||
let expr = v.remove(position);
|
||||
let next = v.remove(position);
|
||||
|
||||
let list = vec![expect_simple_expr(expr)?, expect_simple_expr(next)?];
|
||||
Ok(Value::List(list))
|
||||
}
|
||||
|
||||
other => Err(ShellError::string(&format!(
|
||||
"Unimplemented named argument {:?}",
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_simple_expr(expr: ast::Expression) -> Result<Value, ShellError> {
|
||||
match expr {
|
||||
ast::Expression::Leaf(l) => Ok(match l {
|
||||
ast::Leaf::Bare(s) => Value::string(s.to_string()),
|
||||
ast::Leaf::String(s) => Value::string(s),
|
||||
ast::Leaf::Boolean(b) => Value::boolean(b),
|
||||
ast::Leaf::Int(i) => Value::int(i),
|
||||
}),
|
||||
|
||||
// TODO: Diagnostic
|
||||
other => Err(ShellError::string(&format!(
|
||||
"Expected a value, found {}",
|
||||
other.print()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CommandRegistry {
|
||||
fn get(&self, name: &str) -> CommandConfig;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user