Improve signature infrastructure

The `config` command uses different kinds of named arguments, which
illustrates how it works.
This commit is contained in:
Yehuda Katz 2019-05-31 22:50:16 -07:00
parent 9df8a261ab
commit 69effbc9e7
36 changed files with 673 additions and 208 deletions

77
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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),
]);
}

View File

@ -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;

View File

@ -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(),

View File

@ -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 {

View File

@ -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

View File

@ -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
View 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")))
}

View File

@ -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())
}

View File

@ -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

View File

@ -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

View File

@ -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)?;

View File

@ -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;

View File

@ -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<_>>();

View File

@ -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 {

View File

@ -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;

View File

@ -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(),

View File

@ -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| {

View File

@ -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
View File

@ -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 {

View File

@ -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(),
})
}
}

View File

@ -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

View File

@ -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![];

View File

@ -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};

View File

@ -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
View 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),
}
}

View File

@ -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()
}
}

View File

@ -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
View 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)
}
}

View File

@ -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,
}

View File

@ -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;

View File

@ -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,

View File

@ -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 = {

View File

@ -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<

View File

@ -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;
}