mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
Begin migration away from arg serialization (#3281)
* initial implementation * Move a few commands over to new arg system * Fix char also
This commit is contained in:
parent
5fcf11fcb0
commit
09a1f5acb9
20 changed files with 442 additions and 224 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -3373,7 +3373,9 @@ dependencies = [
|
|||
"ansi_term 0.12.1",
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"bigdecimal",
|
||||
"bytes 0.5.6",
|
||||
"chrono",
|
||||
"codespan-reporting",
|
||||
"derive-new",
|
||||
"dirs-next",
|
||||
|
@ -3401,6 +3403,9 @@ dependencies = [
|
|||
"nu-stream",
|
||||
"nu-test-support",
|
||||
"nu-value-ext",
|
||||
"num-bigint 0.3.2",
|
||||
"num-format",
|
||||
"num-traits 0.2.14",
|
||||
"parking_lot 0.11.1",
|
||||
"rayon",
|
||||
"serde 1.0.124",
|
||||
|
|
|
@ -7,13 +7,6 @@ use nu_source::Tagged;
|
|||
|
||||
pub struct Command;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AnsiArgs {
|
||||
code: Value,
|
||||
escape: Option<Tagged<String>>,
|
||||
osc: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Command {
|
||||
fn name(&self) -> &str {
|
||||
"ansi"
|
||||
|
@ -120,9 +113,14 @@ Format: #
|
|||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (AnsiArgs { code, escape, osc }, _) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let code: Option<Result<Tagged<String>, ShellError>> = args.opt(0);
|
||||
let escape: Option<Result<Tagged<String>, ShellError>> = args.get_flag("escape");
|
||||
let osc: Option<Result<Tagged<String>, ShellError>> = args.get_flag("osc");
|
||||
|
||||
if let Some(e) = escape {
|
||||
let e = e?;
|
||||
let esc_vec: Vec<char> = e.item.chars().collect();
|
||||
if esc_vec[0] == '\\' {
|
||||
return Err(ShellError::labeled_error(
|
||||
|
@ -138,6 +136,7 @@ Format: #
|
|||
}
|
||||
|
||||
if let Some(o) = osc {
|
||||
let o = o?;
|
||||
let osc_vec: Vec<char> = o.item.chars().collect();
|
||||
if osc_vec[0] == '\\' {
|
||||
return Err(ShellError::labeled_error(
|
||||
|
@ -155,25 +154,33 @@ Format: #
|
|||
)));
|
||||
}
|
||||
|
||||
let code_string = code.as_string()?;
|
||||
let ansi_code = str_to_ansi(code_string);
|
||||
if let Some(code) = code {
|
||||
let code = code?;
|
||||
let ansi_code = str_to_ansi(&code.item);
|
||||
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
)))
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown ansi code",
|
||||
"unknown ansi code",
|
||||
code.tag(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown ansi code",
|
||||
"unknown ansi code",
|
||||
code.tag(),
|
||||
"Expected ansi code",
|
||||
"expect ansi code",
|
||||
args.call_info.name_tag.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn str_to_ansi(s: String) -> Option<String> {
|
||||
match s.as_str() {
|
||||
pub fn str_to_ansi(s: &str) -> Option<String> {
|
||||
match s {
|
||||
"g" | "green" => Some(Color::Green.prefix().to_string()),
|
||||
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
|
||||
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
|
||||
|
|
|
@ -5,11 +5,6 @@ use nu_data::value::format_leaf;
|
|||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BuildStringArgs {
|
||||
rest: Vec<Value>,
|
||||
}
|
||||
|
||||
pub struct BuildString;
|
||||
|
||||
impl WholeStreamCommand for BuildString {
|
||||
|
@ -28,7 +23,8 @@ impl WholeStreamCommand for BuildString {
|
|||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (BuildStringArgs { rest }, _) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let mut output_string = String::new();
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ pub fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
let mut selected_year: i32 = current_year;
|
||||
let mut current_day_option: Option<u32> = Some(current_day);
|
||||
|
||||
let month_range = if let Some(full_year_value) = args.get("full-year") {
|
||||
let month_range = if let Some(full_year_value) = args.call_info.args.get("full-year") {
|
||||
if let Ok(year_u64) = full_year_value.as_u64() {
|
||||
selected_year = year_u64 as i32;
|
||||
|
||||
|
@ -209,7 +209,7 @@ fn add_month_to_table(
|
|||
|
||||
let month_helper = match month_helper_result {
|
||||
Ok(month_helper) => month_helper,
|
||||
Err(()) => match args.get("full-year") {
|
||||
Err(()) => match args.call_info.args.get("full-year") {
|
||||
Some(full_year_value) => {
|
||||
return Err(get_invalid_year_shell_error(&full_year_value.tag()))
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ fn add_month_to_table(
|
|||
|
||||
let mut week_start_day = days_of_the_week[0].to_string();
|
||||
|
||||
if let Some(week_start_value) = args.get("week-start") {
|
||||
if let Some(week_start_value) = args.call_info.args.get("week-start") {
|
||||
if let Ok(day) = week_start_value.as_string() {
|
||||
if days_of_the_week.contains(&day.as_str()) {
|
||||
week_start_day = day;
|
||||
|
@ -264,10 +264,10 @@ fn add_month_to_table(
|
|||
let mut day_number: u32 = 1;
|
||||
let day_limit: u32 = total_start_offset + month_helper.number_of_days_in_month;
|
||||
|
||||
let should_show_year_column = args.has("year");
|
||||
let should_show_quarter_column = args.has("quarter");
|
||||
let should_show_month_column = args.has("month");
|
||||
let should_show_month_names = args.has("month-names");
|
||||
let should_show_year_column = args.has_flag("year");
|
||||
let should_show_quarter_column = args.has_flag("quarter");
|
||||
let should_show_month_column = args.has_flag("month");
|
||||
let should_show_month_names = args.has_flag("month-names");
|
||||
|
||||
while day_number <= day_limit {
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_engine::{FromValue, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Char;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CharArgs {
|
||||
name: Tagged<String>,
|
||||
rest: Vec<Tagged<String>>,
|
||||
unicode: bool,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Char {
|
||||
fn name(&self) -> &str {
|
||||
"char"
|
||||
|
@ -65,14 +58,11 @@ impl WholeStreamCommand for Char {
|
|||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (
|
||||
CharArgs {
|
||||
name,
|
||||
rest,
|
||||
unicode,
|
||||
},
|
||||
_,
|
||||
) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let name: Tagged<String> = args.req(0)?;
|
||||
let rest: Vec<Value> = args.rest(1)?;
|
||||
let unicode = args.has_flag("unicode");
|
||||
|
||||
if unicode {
|
||||
if !rest.is_empty() {
|
||||
|
@ -86,6 +76,7 @@ impl WholeStreamCommand for Char {
|
|||
}
|
||||
// Get the rest of the bytes
|
||||
for byte_part in rest {
|
||||
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
|
||||
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
|
||||
match decoded_char {
|
||||
Ok(ch) => multi_byte.push(ch),
|
||||
|
|
|
@ -6,16 +6,9 @@ use nu_errors::ShellError;
|
|||
use nu_protocol::{
|
||||
hir::CapturedBlock, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Each;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EachArgs {
|
||||
block: CapturedBlock,
|
||||
numbered: Tagged<bool>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Each {
|
||||
fn name(&self) -> &str {
|
||||
"each"
|
||||
|
@ -110,12 +103,16 @@ pub(crate) fn make_indexed_item(index: usize, item: Value) -> Value {
|
|||
|
||||
fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
let args = raw_args.evaluate_once()?;
|
||||
|
||||
let (each_args, input): (EachArgs, _) = raw_args.process()?;
|
||||
let block = Arc::new(Box::new(each_args.block));
|
||||
let block: CapturedBlock = args.req(0)?;
|
||||
let numbered: bool = args.has_flag("numbered");
|
||||
|
||||
if each_args.numbered.item {
|
||||
Ok(input
|
||||
let block = Arc::new(Box::new(block));
|
||||
|
||||
if numbered {
|
||||
Ok(args
|
||||
.input
|
||||
.enumerate()
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
|
@ -130,7 +127,8 @@ fn each(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
.flatten()
|
||||
.to_output_stream())
|
||||
} else {
|
||||
Ok(input
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |input| {
|
||||
let block = block.clone();
|
||||
let context = context.clone();
|
||||
|
|
|
@ -8,11 +8,6 @@ use nu_protocol::{
|
|||
|
||||
pub struct Echo;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EchoArgs {
|
||||
pub rest: Vec<Value>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Echo {
|
||||
fn name(&self) -> &str {
|
||||
"echo"
|
||||
|
@ -47,9 +42,10 @@ impl WholeStreamCommand for Echo {
|
|||
}
|
||||
|
||||
fn echo(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (args, _): (EchoArgs, _) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
let rest: Vec<Value> = args.rest(0)?;
|
||||
|
||||
let stream = args.rest.into_iter().map(|i| match i.as_string() {
|
||||
let stream = rest.into_iter().map(|i| match i.as_string() {
|
||||
Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(s).into_value(i.tag.clone()),
|
||||
))),
|
||||
|
|
|
@ -9,13 +9,6 @@ use nu_protocol::{
|
|||
|
||||
pub struct If;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IfArgs {
|
||||
condition: CapturedBlock,
|
||||
then_case: CapturedBlock,
|
||||
else_case: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for If {
|
||||
fn name(&self) -> &str {
|
||||
"if"
|
||||
|
@ -67,14 +60,12 @@ fn if_command(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
let tag = raw_args.call_info.name_tag.clone();
|
||||
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
||||
|
||||
let (
|
||||
IfArgs {
|
||||
condition,
|
||||
then_case,
|
||||
else_case,
|
||||
},
|
||||
input,
|
||||
) = raw_args.process()?;
|
||||
let args = raw_args.evaluate_once()?;
|
||||
let condition: CapturedBlock = args.req(0)?;
|
||||
let then_case: CapturedBlock = args.req(1)?;
|
||||
let else_case: CapturedBlock = args.req(2)?;
|
||||
let input = args.input;
|
||||
|
||||
let cond = {
|
||||
if condition.block.block.len() != 1 {
|
||||
return Err(ShellError::labeled_error(
|
||||
|
|
|
@ -7,13 +7,6 @@ use nu_source::Tagged;
|
|||
|
||||
pub struct Let;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LetArgs {
|
||||
pub name: Tagged<String>,
|
||||
pub equals: Tagged<String>,
|
||||
pub rhs: CapturedBlock,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for Let {
|
||||
fn name(&self) -> &str {
|
||||
"let"
|
||||
|
@ -46,8 +39,11 @@ impl WholeStreamCommand for Let {
|
|||
pub fn letcmd(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let args = args.evaluate_once()?;
|
||||
|
||||
let (LetArgs { name, rhs, .. }, _) = args.process()?;
|
||||
//let (LetArgs { name, rhs, .. }, _) = args.process()?;
|
||||
let name: Tagged<String> = args.req(0)?;
|
||||
let rhs: CapturedBlock = args.req(2)?;
|
||||
|
||||
let (expr, captured) = {
|
||||
if rhs.block.block.len() != 1 {
|
||||
|
|
|
@ -111,99 +111,6 @@ impl Iterator for SleepIterator {
|
|||
}
|
||||
}
|
||||
|
||||
// struct SleepHandler {
|
||||
// shared_state: Arc<Mutex<SharedState>>,
|
||||
// }
|
||||
|
||||
// impl SleepHandler {
|
||||
// /// Create a new `SleepHandler` which will complete after the provided
|
||||
// /// timeout and check for Ctrl+C periodically.
|
||||
// pub fn new(duration: Duration, ctrl_c: Arc<AtomicBool>) -> Self {
|
||||
// let shared_state = Arc::new(Mutex::new(SharedState {
|
||||
// done: false,
|
||||
// waker: None,
|
||||
// }));
|
||||
|
||||
// // Spawn the main sleep thread
|
||||
// let thread_shared_state = shared_state.clone();
|
||||
// thread::spawn(move || {
|
||||
// thread::sleep(duration);
|
||||
// let mut shared_state = thread_shared_state.lock();
|
||||
// // Signal that the timer has completed and wake up the last
|
||||
// // task on which the future was polled, if one exists.
|
||||
// if !shared_state.done {
|
||||
// shared_state.done = true;
|
||||
// if let Some(waker) = shared_state.waker.take() {
|
||||
// waker.wake()
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Spawn the Ctrl+C-watching polling thread
|
||||
// let thread_shared_state = shared_state.clone();
|
||||
// thread::spawn(move || {
|
||||
// loop {
|
||||
// {
|
||||
// let mut shared_state = thread_shared_state.lock();
|
||||
// // exit if the main thread is done
|
||||
// if shared_state.done {
|
||||
// return;
|
||||
// }
|
||||
// // finish the future prematurely if Ctrl+C has been pressed
|
||||
// if ctrl_c.load(Ordering::SeqCst) {
|
||||
// shared_state.done = true;
|
||||
// if let Some(waker) = shared_state.waker.take() {
|
||||
// waker.wake()
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// // sleep for a short time
|
||||
// thread::sleep(CTRL_C_CHECK_INTERVAL);
|
||||
// }
|
||||
// });
|
||||
|
||||
// SleepHandler { shared_state }
|
||||
// }
|
||||
// }
|
||||
|
||||
// struct SharedState {
|
||||
// done: bool,
|
||||
// }
|
||||
|
||||
// impl Iterator for SleepHandler {
|
||||
// type Item = ();
|
||||
|
||||
// fn next(&mut self) -> Option<Self::Item> {
|
||||
// let mut shared_state = self.shared_state.lock();
|
||||
// loop {
|
||||
// if shared_state.done {
|
||||
// return None;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// impl Future for SleepHandler {
|
||||
// type Output = ();
|
||||
|
||||
// fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||
// // Look at the shared state to see if the timer has already completed.
|
||||
// Poll::Ready(())
|
||||
// } else {
|
||||
// // Set the waker if necessary
|
||||
// if shared_state
|
||||
// .waker
|
||||
// .as_ref()
|
||||
// .map(|waker| !waker.will_wake(&cx.waker()))
|
||||
// .unwrap_or(true)
|
||||
// {
|
||||
// shared_state.waker = Some(cx.waker().clone());
|
||||
// }
|
||||
// Poll::Pending
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Sleep;
|
||||
|
|
|
@ -6,11 +6,6 @@ use nu_source::Tagged;
|
|||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SubCommandArgs {
|
||||
separator: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str collect"
|
||||
|
@ -43,8 +38,15 @@ impl WholeStreamCommand for SubCommand {
|
|||
|
||||
pub fn collect(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let (SubCommandArgs { separator }, input) = args.process()?;
|
||||
let separator = separator.map(|tagged| tagged.item).unwrap_or_default();
|
||||
//let (SubCommandArgs { separator }, input) = args.process()?;
|
||||
let args = args.evaluate_once()?;
|
||||
let separator: Option<Result<Tagged<String>, ShellError>> = args.opt(0);
|
||||
let input = args.input;
|
||||
let separator = if let Some(separator) = separator {
|
||||
separator?.item
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
|
||||
let strings: Vec<Result<String, ShellError>> = input.map(|value| value.as_string()).collect();
|
||||
let strings: Result<Vec<_>, _> = strings.into_iter().collect::<Result<_, _>>();
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::primitive::get_color_config;
|
|||
use nu_data::value::{format_leaf, style_leaf};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_table::{draw_table, Alignment, StyledString, TextStyle};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
@ -169,22 +169,10 @@ fn table(configuration: TableConfiguration, args: CommandArgs) -> Result<OutputS
|
|||
// config.toml.... yet.
|
||||
let color_hm = get_color_config();
|
||||
|
||||
let mut start_number = match args.get("start_number") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(i)),
|
||||
..
|
||||
}) => {
|
||||
if let Some(num) = i.to_usize() {
|
||||
num
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a row number",
|
||||
"expected a row number",
|
||||
&args.args.call_info.name_tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => 0,
|
||||
let mut start_number = if let Some(f) = args.get_flag("start_number") {
|
||||
f?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let mut delay_slot = None;
|
||||
|
|
|
@ -55,7 +55,7 @@ impl WholeStreamCommand for Uniq {
|
|||
|
||||
fn uniq(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once()?;
|
||||
let should_show_count = args.has("count");
|
||||
let should_show_count = args.has_flag("count");
|
||||
let input = args.input;
|
||||
let uniq_values = {
|
||||
let mut counter = IndexMap::<nu_protocol::Value, usize>::new();
|
||||
|
|
|
@ -24,7 +24,9 @@ dyn-clone = "1.0.4"
|
|||
ansi_term = "0.12.1"
|
||||
async-recursion = "0.3.2"
|
||||
async-trait = "0.1.42"
|
||||
bigdecimal = "0.2.0"
|
||||
bytes = "0.5.6"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
derive-new = "0.5.8"
|
||||
dirs-next = { version = "2.0.0", optional = true }
|
||||
dunce = "1.0.1"
|
||||
|
@ -39,6 +41,9 @@ glob = "0.3.0"
|
|||
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
log = "0.4.14"
|
||||
num-bigint = { version = "0.3.1", features = ["serde"] }
|
||||
num-format = "0.4.0"
|
||||
num-traits = "0.2.14"
|
||||
parking_lot = "0.11.1"
|
||||
rayon = "1.5.0"
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::env::host::Host;
|
|||
use crate::evaluate::scope::Scope;
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use crate::shell::shell_manager::ShellManager;
|
||||
use crate::FromValue;
|
||||
use crate::{call_info::UnevaluatedCallInfo, config_holder::ConfigHolder};
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
|
@ -171,11 +172,44 @@ impl EvaluatedCommandArgs {
|
|||
.ok_or_else(|| ShellError::unimplemented("Better error: expect_nth"))
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<&Value> {
|
||||
self.call_info.args.get(name)
|
||||
pub fn get_flag<T: FromValue>(&self, name: &str) -> Option<Result<T, ShellError>> {
|
||||
self.call_info
|
||||
.args
|
||||
.get(name)
|
||||
.map(|x| FromValue::from_value(x))
|
||||
}
|
||||
|
||||
pub fn has(&self, name: &str) -> bool {
|
||||
pub fn has_flag(&self, name: &str) -> bool {
|
||||
self.call_info.args.has(name)
|
||||
}
|
||||
|
||||
pub fn req<T: FromValue>(&self, pos: usize) -> Result<T, ShellError> {
|
||||
if let Some(v) = self.nth(pos) {
|
||||
FromValue::from_value(v)
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Position beyond end of command arguments",
|
||||
"can't access beyond end of command arguments",
|
||||
self.call_info.name_tag.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn opt<T: FromValue>(&self, pos: usize) -> Option<Result<T, ShellError>> {
|
||||
if let Some(v) = self.nth(pos) {
|
||||
Some(FromValue::from_value(v))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rest<T: FromValue>(&self, starting_pos: usize) -> Result<Vec<T>, ShellError> {
|
||||
let mut output = vec![];
|
||||
|
||||
for val in self.call_info.args.positional_iter().skip(starting_pos) {
|
||||
output.push(FromValue::from_value(val)?);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
|
281
crates/nu-engine/src/from_value.rs
Normal file
281
crates/nu-engine/src/from_value.rs
Normal file
|
@ -0,0 +1,281 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, ColumnPath, Dictionary, Primitive, Range, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::{Tagged, TaggedItem};
|
||||
|
||||
pub trait FromValue: Sized {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError>;
|
||||
}
|
||||
|
||||
impl FromValue for Value {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
Ok(v.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for num_bigint::BigInt {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(i)),
|
||||
..
|
||||
}
|
||||
| Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Filesize(i)),
|
||||
..
|
||||
}
|
||||
| Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Duration(i)),
|
||||
..
|
||||
} => Ok(i.clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to integer",
|
||||
"can't convert to integer",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for u64 {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
v.as_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for usize {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
v.as_usize()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for i64 {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
v.as_i64()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for i32 {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
v.as_i32()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for bigdecimal::BigDecimal {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Decimal(d)),
|
||||
..
|
||||
} => Ok(d.clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to decimal",
|
||||
"can't convert to decimal",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for String {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s)),
|
||||
..
|
||||
} => Ok(s.clone()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::GlobPattern(s)),
|
||||
..
|
||||
} => Ok(s.clone()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(p)),
|
||||
..
|
||||
} => Ok(p.to_string_lossy().to_string()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to string",
|
||||
"can't convert to string",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Tagged<String> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
let tag = v.tag.clone();
|
||||
v.as_string().map(|s| s.tagged(tag))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for PathBuf {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s)),
|
||||
..
|
||||
} => Ok(PathBuf::from(s)),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(p)),
|
||||
..
|
||||
} => Ok(p.clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to filepath",
|
||||
"can't convert to filepath",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for ColumnPath {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::ColumnPath(c)),
|
||||
..
|
||||
} => Ok(c.clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to column path",
|
||||
"can't convert to column path",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for bool {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Boolean(b)),
|
||||
..
|
||||
} => Ok(*b),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to boolean",
|
||||
"can't convert to boolean",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Tagged<bool> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Boolean(b)),
|
||||
tag,
|
||||
} => Ok((*b).tagged(tag)),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to boolean",
|
||||
"can't convert to boolean",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for DateTime<FixedOffset> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Date(d)),
|
||||
..
|
||||
} => Ok(*d),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to date",
|
||||
"can't convert to date",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Range {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Range(r)),
|
||||
..
|
||||
} => Ok((**r).clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to range",
|
||||
"can't convert to range",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Vec<u8> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Binary(b)),
|
||||
..
|
||||
} => Ok(b.clone()),
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s)),
|
||||
..
|
||||
} => Ok(s.bytes().collect()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to binary data",
|
||||
"can't convert to binary data",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Dictionary {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Row(r),
|
||||
..
|
||||
} => Ok(r.clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to row",
|
||||
"can't convert to row",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for CapturedBlock {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Block(b),
|
||||
..
|
||||
} => Ok((**b).clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to block",
|
||||
"can't convert to block",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Vec<Value> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value {
|
||||
value: UntaggedValue::Table(t),
|
||||
..
|
||||
} => Ok(t.clone()),
|
||||
Value { tag, .. } => Err(ShellError::labeled_error(
|
||||
"Can't convert to table",
|
||||
"can't convert to table",
|
||||
tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ mod evaluate;
|
|||
mod evaluation_context;
|
||||
mod example;
|
||||
pub mod filesystem;
|
||||
mod from_value;
|
||||
mod maybe_text_codec;
|
||||
pub mod plugin;
|
||||
mod print;
|
||||
|
@ -36,6 +37,7 @@ pub use crate::example::Example;
|
|||
pub use crate::filesystem::dir_info::{DirBuilder, DirInfo, FileInfo};
|
||||
pub use crate::filesystem::filesystem_shell::FilesystemShell;
|
||||
pub use crate::filesystem::path;
|
||||
pub use crate::from_value::FromValue;
|
||||
pub use crate::maybe_text_codec::{BufCodecReader, MaybeTextCodec, StringOrBinary};
|
||||
pub use crate::print::maybe_print_errors;
|
||||
pub use crate::runnable_context::RunnableContext;
|
||||
|
|
|
@ -71,19 +71,6 @@ impl Default for MaybeTextCodec {
|
|||
}
|
||||
}
|
||||
|
||||
// impl MaybeTextCodec {
|
||||
// fn encode(&mut self, item: StringOrBinary, mut dst: &mut [u8]) {
|
||||
// match item {
|
||||
// StringOrBinary::String(s) => {
|
||||
// dst.put(s.as_bytes());
|
||||
// }
|
||||
// StringOrBinary::Binary(b) => {
|
||||
// dst.put(Bytes::from(b));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl MaybeTextCodec {
|
||||
pub fn decode(&mut self, src: &[u8]) -> Result<Option<StringOrBinary>, ShellError> {
|
||||
if src.is_empty() {
|
||||
|
|
|
@ -429,6 +429,14 @@ impl Value {
|
|||
matches!(&self.value, UntaggedValue::Primitive(_))
|
||||
}
|
||||
|
||||
/// View the Value as unsigned size, if possible
|
||||
pub fn as_usize(&self) -> Result<usize, ShellError> {
|
||||
match &self.value {
|
||||
UntaggedValue::Primitive(primitive) => primitive.as_usize(self.tag.span),
|
||||
_ => Err(ShellError::type_error("integer", self.spanned_type_name())),
|
||||
}
|
||||
}
|
||||
|
||||
/// View the Value as unsigned 64-bit, if possible
|
||||
pub fn as_u64(&self) -> Result<u64, ShellError> {
|
||||
match &self.value {
|
||||
|
|
|
@ -60,6 +60,30 @@ pub enum Primitive {
|
|||
}
|
||||
|
||||
impl Primitive {
|
||||
/// Converts a primitive value to a u64, if possible. Uses a span to build an error if the conversion isn't possible.
|
||||
pub fn as_usize(&self, span: Span) -> Result<usize, ShellError> {
|
||||
match self {
|
||||
Primitive::Int(int) => int.to_usize().ok_or_else(|| {
|
||||
ShellError::range_error(
|
||||
ExpectedRange::U64,
|
||||
&format!("{}", int).spanned(span),
|
||||
"converting an integer into an unsigned 64-bit integer",
|
||||
)
|
||||
}),
|
||||
Primitive::Decimal(decimal) => decimal.to_usize().ok_or_else(|| {
|
||||
ShellError::range_error(
|
||||
ExpectedRange::U64,
|
||||
&format!("{}", decimal).spanned(span),
|
||||
"converting a decimal into an unsigned 64-bit integer",
|
||||
)
|
||||
}),
|
||||
other => Err(ShellError::type_error(
|
||||
"number",
|
||||
other.type_name().spanned(span),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a primitive value to a u64, if possible. Uses a span to build an error if the conversion isn't possible.
|
||||
pub fn as_u64(&self, span: Span) -> Result<u64, ShellError> {
|
||||
match self {
|
||||
|
|
Loading…
Reference in a new issue