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:
Jonathan Turner 2021-04-08 20:15:36 +12:00 committed by GitHub
parent 5fcf11fcb0
commit 09a1f5acb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 442 additions and 224 deletions

5
Cargo.lock generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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