diff --git a/members/braces/src/lib.rs b/members/braces/src/lib.rs index 8b4b9a0979bf4cf7c379b8fd025e9a619912b054..8b24cc4919d8757f70d959cd0ee238279208f5f1 100644 --- a/members/braces/src/lib.rs +++ b/members/braces/src/lib.rs @@ -20,11 +20,8 @@ pub fn expand<'a>( let multiple_brace_expand = MultipleBraceExpand::new(tokens, expanders); Box::new(multiple_brace_expand) } else if expanders.len() == 1 { - let single_brace_expand = SingleBraceExpand { - elements: expanders[0].iter().map(|element| *element), - tokens, - loop_count: 0, - }; + let single_brace_expand = + SingleBraceExpand { elements: expanders[0].iter().cloned(), tokens, loop_count: 0 }; Box::new(single_brace_expand) } else { Box::new(::std::iter::empty()) diff --git a/members/builtins/src/calc.rs b/members/builtins/src/calc.rs index f1ae0e8eeabbba44371f9233e03c505ed5706694..18ff32ca701f8e148ddce32e1f487d1320513968 100644 --- a/members/builtins/src/calc.rs +++ b/members/builtins/src/calc.rs @@ -2,7 +2,7 @@ use calculate::{eval, eval_polish, CalcError, Value}; use small; use std::io::{self, Write}; -const REPL_GUIDE: &'static str = r#"ion-calc +const REPL_GUIDE: &str = r#"ion-calc Type in expressions to have them evaluated. Type "help" for help."#; @@ -26,10 +26,10 @@ SPECIAL EXPRESSIONS exits the program NOTATIONS - infix notation + infix notation e.g. 3 * 4 + 5 - polish notation + polish notation e.g. + * 3 4 5 EXAMPLES diff --git a/members/lexers/src/assignments/mod.rs b/members/lexers/src/assignments/mod.rs index 0aa783e19e1ba4b1a17496aa80c2456e1c4a3d37..3006f23fda89e750d396b63b664255a1813ac71b 100644 --- a/members/lexers/src/assignments/mod.rs +++ b/members/lexers/src/assignments/mod.rs @@ -10,9 +10,7 @@ pub use self::{ /// Given an valid assignment expression, this will split it into `keys`, /// `operator`, `values`. -pub fn assignment_lexer<'a>( - statement: &'a str, -) -> (Option<&'a str>, Option<Operator>, Option<&'a str>) { +pub fn assignment_lexer(statement: &str) -> (Option<&str>, Option<Operator>, Option<&str>) { let statement = statement.trim(); if statement.is_empty() { return (None, None, None); @@ -20,29 +18,27 @@ pub fn assignment_lexer<'a>( let (mut read, mut start) = (0, 0); let as_bytes = statement.as_bytes(); - let mut bytes = statement.bytes(); + let mut bytes = statement.bytes().peekable(); let mut operator = None; while let Some(byte) = bytes.next() { + operator = Some(Operator::Equal); if b'=' == byte { - operator = Some(Operator::Equal); - if as_bytes.get(read + 1).is_none() { - return (Some(&statement[..read].trim()), operator, None); + if bytes.peek().is_none() { + return (Some(&statement[..read].trim()), Some(Operator::Equal), None); } start = read; read += 1; break; - } else { - match find_operator(as_bytes, read) { - None => (), - Some((op, found)) => { - operator = Some(op); - start = read; - read = found; - break; - } - } } + + if let Some((op, found)) = find_operator(as_bytes, read) { + operator = Some(op); + start = read; + read = found; + break; + } + read += 1; } @@ -62,7 +58,7 @@ fn find_operator(bytes: &[u8], read: usize) -> Option<(Operator, usize)> { } else if bytes[read + 1] == b'=' { Operator::parse_single(bytes[read]).map(|op| (op, read + 2)) } else if bytes[read + 2] == b'=' { - Operator::parse_double(&bytes[read..read + 2]).map(|op| (op, read + 3)) + Operator::parse_double(&bytes[read..=read + 1]).map(|op| (op, read + 3)) } else { None } diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs index cc2e1e9051563d8fe335aa3cd0ac8dd82dcfbbe1..a8ebd74d0d071ffe096a58859927b06de5bd9c3c 100644 --- a/src/lib/shell/assignments.rs +++ b/src/lib/shell/assignments.rs @@ -8,7 +8,6 @@ use itoa; use lexers::assignments::{Operator, Primitive}; use parser::assignments::*; use shell::{history::ShellHistory, variables::VariableType}; -use small; use std::{ env, ffi::OsStr, @@ -16,43 +15,42 @@ use std::{ io::{self, BufWriter, Write}, mem, os::unix::ffi::OsStrExt, + result::Result, str, }; use types; -fn list_vars(shell: &Shell) { +fn list_vars(shell: &Shell) -> Result<(), io::Error> { let stdout = io::stdout(); let mut buffer = BufWriter::new(stdout.lock()); - // Small function for formatting and append an array entry to a string buffer. - fn print_array<W: Write>(buffer: &mut W, key: &str, array: &[small::String]) { - let _ = buffer.write([key, " = [ "].concat().as_bytes()); - if array.len() > 1 { - let mut vars = array.iter(); - if let Some(ref var) = vars.next() { - let _ = buffer.write(["'", var, "', "].concat().as_bytes()); - vars.for_each(|var| { - let _ = buffer.write(["'", var, "' "].concat().as_bytes()); - }); - } - let _ = buffer.write(b"]\n"); - } else if array.len() == 1 { - let _ = buffer.write(["'", &array[0], "' ]\n"].concat().as_bytes()); - } else { - let _ = buffer.write("]\n".as_bytes()); - } - } - // Write all the string variables to the buffer. - let _ = buffer.write(b"# String Variables\n"); + let _ = buffer.write(b"# String Variables\n")?; for (key, val) in shell.variables.string_vars() { - let _ = buffer.write([key, " = ", val.as_str(), "\n"].concat().as_bytes()); + writeln!(buffer, "{} = {}", key, val)?; } // Then immediately follow that with a list of array variables. - let _ = buffer.write(b"\n# Array Variables\n"); + let _ = buffer.write(b"\n# Array Variables\n")?; for (key, val) in shell.variables.arrays() { - print_array(&mut buffer, &key, &**val) + write!(buffer, "{} = [ ", key)?; + let mut vars = val.iter(); + if let Some(ref var) = vars.next() { + write!(buffer, "'{}' ", var)?; + vars.map(|var| write!(buffer, ", '{}' ", var)).collect::<Result<Vec<_>, _>>()?; + } + writeln!(buffer, "]")?; + } + Ok(()) +} + +fn arithmetic_op(operator: Operator, value: f64) -> Result<Box<dyn Fn(f64) -> f64>, String> { + match operator { + Operator::Add => Ok(Box::new(move |src| src + value)), + Operator::Divide => Ok(Box::new(move |src| src / value)), + Operator::Subtract => Ok(Box::new(move |src| src - value)), + Operator::Multiply => Ok(Box::new(move |src| src * value)), + _ => Err("operator does not work on arrays".to_string()), } } @@ -83,80 +81,66 @@ impl VariableStore for Shell { let stdout = io::stdout(); let mut stdout = stdout.lock(); for (key, val) in env::vars() { - let _ = writeln!(stdout, "{} =\"{}\"", key, val); + let _ = writeln!(stdout, "{} = \"{}\"", key, val); } return SUCCESS; } }; for action in actions { - match action { - Ok(Action::UpdateArray(key, Operator::Equal, expression)) => { - match value_check(self, &expression, &key.kind) { - Ok(VariableType::Array(values)) => env::set_var(key.name, values.join(" ")), - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; - } - _ => { - eprintln!( - "ion: assignment error: export of type '{}' is not supported", - key.kind - ); - return FAILURE; - } - } - } - Ok(Action::UpdateArray(..)) => { - eprintln!( - "ion: arithmetic operators on array expressions aren't supported yet." - ); - return FAILURE; + let err = action.map_err(|e| e.to_string()).and_then(|act| match act { + Action::UpdateArray(key, Operator::Equal, expression) => { + value_check(self, &expression, &key.kind) + .map_err(|e| e.to_string()) + .and_then(|val| match val { + VariableType::Array(values) => { + env::set_var(key.name, values.join(" ")); + Ok(()) + } + _ => Err(format!("export of type '{}' is not supported", key.kind)), + }) + .map_err(|why| format!("{}: {}", key.name, why)) } - Ok(Action::UpdateString(key, operator, expression)) => { + Action::UpdateArray(..) => Err("arithmetic operators on array expressions aren't \ + supported yet." + .to_string()), + Action::UpdateString(key, operator, expression) => { match value_check(self, &expression, &key.kind) { Ok(VariableType::Str(value)) => { let key_name: &str = &key.name; - let lhs: types::Str = self + let lhs = self .variables .get::<types::Str>(key_name) .unwrap_or_else(|| "0".into()); - let result = math(&lhs, &key.kind, operator, &value, |value| { - let str_value = unsafe { str::from_utf8_unchecked(value) }; + math(&lhs, &key.kind, operator, &value, |value| { + let mut str_value = + unsafe { str::from_utf8_unchecked(value) }.to_string(); + if key_name == "PATH" && str_value.find('~').is_some() { - let final_value = str_value.replace( + str_value = str_value.replace( "~", env::var("HOME") .as_ref() .map(|s| s.as_str()) .unwrap_or("~"), - ); - env::set_var( - key_name, - &OsStr::from_bytes(final_value.as_bytes()), ) - } else { - env::set_var(key_name, &OsStr::from_bytes(value)) } - }); - if let Err(why) = result { - eprintln!("ion: assignment error: {}", why); - return FAILURE; - } - } - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; + env::set_var(key_name, &OsStr::from_bytes(str_value.as_bytes())); + Ok(()) + }) + .map_err(|e| e.to_string()) } + Err(why) => Err(format!("{}: {}", key.name, why)), _ => unreachable!(), } } - Err(why) => { - eprintln!("ion: assignment error: {}", why); - return FAILURE; - } + }); + + if let Err(why) = err { + eprintln!("ion: assignment error: {}", why); + return FAILURE; } } @@ -167,7 +151,7 @@ impl VariableStore for Shell { let mut collected: HashMap<&str, VariableType> = HashMap::new(); let (actions_step1, actions_step2) = match action { LocalAction::List => { - list_vars(&self); + let _ = list_vars(&self); return SUCCESS; } LocalAction::Assign(ref keys, op, ref vals) => { @@ -175,371 +159,231 @@ impl VariableStore for Shell { } }; for action in actions_step1 { - match action { - Ok(Action::UpdateArray(key, operator, expression)) => { - match operator { - Operator::Equal | Operator::OptionalEqual => { - match value_check(self, &expression, &key.kind) { - Ok(VariableType::Array(values)) => { - // When we changed the HISTORY_IGNORE variable, update the - // ignore patterns. This happens first because `set_array` - // consumes 'values' - if key.name == "HISTORY_IGNORE" { - self.update_ignore_patterns(&values); - } - collected.insert(key.name, VariableType::Array(values)); - } - Ok(VariableType::Str(value)) => { - collected.insert(key.name, VariableType::Str(value)); - } - Ok(VariableType::HashMap(hmap)) => { - collected.insert(key.name, VariableType::HashMap(hmap)); - } - Ok(VariableType::BTreeMap(bmap)) => { - collected.insert(key.name, VariableType::BTreeMap(bmap)); - } - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; - } - _ => (), - } - } - Operator::Concatenate => match value_check(self, &expression, &key.kind) { - Ok(VariableType::Array(values)) => { - match self.variables.get_mut(key.name) { - Some(VariableType::Array(ref mut array)) => { - array.extend(values); - } - None => { - eprintln!( - "ion: assignment error: {}: cannot concatenate \ - non-array variable", - key.name - ); - return FAILURE; + let err = action.map_err(|e| format!("{}", e)).and_then(|act| match act { + Action::UpdateArray(key, operator, expression) => { + let right_hand_side = value_check(self, &expression, &key.kind); + + right_hand_side.map_err(|why| format!("{}: {}", key.name, why)).and_then( + |val| { + if [Operator::Equal, Operator::OptionalEqual].contains(&operator) { + // When we changed the HISTORY_IGNORE variable, update the + // ignore patterns. This happens first because `set_array` + // consumes 'values' + if key.name == "HISTORY_IGNORE" { + if let VariableType::Array(values) = &val { + self.update_ignore_patterns(values); } - _ => (), } + collected.insert(key.name, val); + return Ok(()); } - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; - } - _ => (), - }, - Operator::ConcatenateHead => { - match value_check(self, &expression, &key.kind) { - Ok(VariableType::Array(values)) => { - match self.variables.get_mut(key.name) { - Some(VariableType::Array(ref mut array)) => { - for (index, value) in values.into_iter().enumerate() { - array.insert(index, value); + + let left_hand_side = self + .variables + .get_mut(key.name) + .ok_or_else(|| "cannot concatenate non-array variable".to_string()); + if let VariableType::Array(values) = val { + left_hand_side.map(|v| { + if let VariableType::Array(ref mut array) = v { + match operator { + Operator::Concatenate => array.extend(values), + Operator::ConcatenateHead => values + .into_iter() + .rev() + .for_each(|value| array.insert(0, value)), + Operator::Filter => { + array.retain(|item| !values.contains(item)) } + _ => {} } - None => { - eprintln!( - "ion: assignment error: {}: cannot head \ - concatenate non-array variable", - key.name - ); - return FAILURE; - } - _ => (), } - } - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; - } - _ => (), - } - } - Operator::Filter => match value_check(self, &expression, &key.kind) { - Ok(VariableType::Array(values)) => { - match self.variables.get_mut(key.name) { - Some(VariableType::Array(ref mut array)) => { - *array = array - .iter() - .filter(|item| { - values.iter().all(|value| *item != value) - }) - .cloned() - .collect(); - } - None => { - eprintln!( - "ion: assignment error: {}: cannot head concatenate \ - non-array variable", - key.name - ); - return FAILURE; - } - _ => (), - } - } - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; + }) + } else { + Ok(()) } - _ => (), }, - _ => (), - } + ) } - Ok(Action::UpdateString(key, operator, expression)) => { + Action::UpdateString(key, operator, expression) => { if ["HOME", "HOST", "PWD", "MWD", "SWD", "?"].contains(&key.name) { - eprintln!("ion: not allowed to set {}", key.name); - return FAILURE; + return Err(format!("not allowed to set {}", key.name)); } - match value_check(self, &expression, &key.kind) { - Ok(VariableType::Str(value)) => { - match operator { - Operator::Equal | Operator::OptionalEqual => { - collected.insert(key.name, VariableType::Str(value)); - continue; - } - Operator::Concatenate => { - match self.variables.get_mut(key.name) { - Some(VariableType::Array(ref mut array)) => { - array.push(value); - } - None => { - eprintln!( - "ion: assignment error: {}: cannot concatenate \ - non-array variable", - key.name - ); - return FAILURE; - } - _ => (), - } - continue; - } - Operator::ConcatenateHead => { - match self.variables.get_mut(key.name) { - Some(VariableType::Array(ref mut array)) => { - array.insert(0, value); - } - None => { - eprintln!( - "ion: assignment error: {}: cannot head \ - concatenate non-array variable", - key.name - ); - return FAILURE; - } - _ => (), - } - continue; - } - Operator::Filter => { - match self.variables.get_mut(key.name) { - Some(VariableType::Array(ref mut array)) => { - *array = array - .iter() - .filter(move |item| **item != value) - .cloned() - .collect(); - } - None => { - eprintln!( - "ion: assignment error: {}: cannot head \ - concatenate non-array variable", - key.name - ); - return FAILURE; - } - _ => (), - } - continue; - } - _ => (), + let right_hand_side = value_check(self, &expression, &key.kind); + + let left_hand_side = self.variables.get_mut(key.name).ok_or_else(|| { + format!("{}: cannot concatenate non-array variable", key.name) + }); + right_hand_side.map_err(|why| format!("{}: {}", key.name, why)).and_then( + |val| { + if [Operator::Equal, Operator::OptionalEqual].contains(&operator) { + collected.insert(key.name, val); + return Ok(()); } - match self.variables.get_mut(key.name) { - Some(VariableType::Str(lhs)) => { - let result = math(&lhs, &key.kind, operator, &value, |value| { - collected.insert( - key.name, - VariableType::Str( - unsafe { str::from_utf8_unchecked(value) }.into(), - ), - ); - }); - if let Err(why) = result { - eprintln!("ion: assignment error: {}", why); - return FAILURE; - } - } - Some(VariableType::Array(array)) => { - let value = match value.parse::<f64>() { - Ok(n) => n, - Err(_) => { - eprintln!( - "ion: assignment error: value is not a float" + left_hand_side.and_then(|v| match val { + VariableType::Str(value) => match v { + VariableType::Str(lhs) => { + math(&lhs, &key.kind, operator, &value, |value| { + collected.insert( + key.name, + VariableType::Str( + unsafe { str::from_utf8_unchecked(value) } + .into(), + ), ); - return FAILURE; + Ok(()) + }) + .map_err(|e| format!("{}", e)) + } + VariableType::Array(ref mut array) => match operator { + Operator::Concatenate => { + array.push(value); + Ok(()) } - }; - - let action: Box<dyn Fn(f64) -> f64> = match operator { - Operator::Add => Box::new(|src| src + value), - Operator::Divide => Box::new(|src| src / value), - Operator::Subtract => Box::new(|src| src - value), - Operator::Multiply => Box::new(|src| src * value), - _ => { - eprintln!( - "ion: assignment error: operator does not work on \ - arrays" - ); - return FAILURE; + Operator::ConcatenateHead => { + array.insert(0, value); + Ok(()) } - }; - - for element in array { - match element.parse::<f64>() { - Ok(number) => { - *element = action(number).to_string().into() - } - Err(_) => { - eprintln!( - "ion: assignment error: {} is not a float", - element - ); - return FAILURE; - } + Operator::Filter => { + array.retain(|item| item != &value); + Ok(()) } - } - } - _ => { - eprintln!( - "ion: assignment error: type does not support this \ - operator" - ); - return FAILURE; - } - } - } - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; - } - _ => unreachable!(), - } - } - Err(why) => { - eprintln!("ion: assignment error: {}", why); - return FAILURE; + _ => value + .parse::<f64>() + .map_err(|_| format!("`{}` is not a float", value)) + .and_then(|value| arithmetic_op(operator, value)) + .and_then(|action| { + array + .iter_mut() + .map(|element| { + element + .parse::<f64>() + .map_err(|_| { + format!( + "`{}` is not a float", + element + ) + }) + .map(|n| action(n)) + .map(|result| { + *element = result.to_string().into() + }) + }) + .find(|e| e.is_err()) + .unwrap_or(Ok(())) + }), + }, + _ => Err("type does not support this operator".to_string()), + }, + _ => unreachable!(), + }) + }, + ) } + }); + + if let Err(why) = err { + eprintln!("ion: assignment error: {}", why); + return FAILURE; } } for action in actions_step2 { - match action { - Ok(Action::UpdateArray(key, operator, ..)) => { + match action.unwrap() { + Action::UpdateArray(key, operator, ..) => { if operator == Operator::OptionalEqual && self.variables.get_ref(key.name).is_some() { continue; } - match collected.remove(key.name) { - hmap @ Some(VariableType::HashMap(_)) => { - if let Primitive::HashMap(_) = key.kind { - self.variables.set(key.name, hmap.unwrap()); - } else if let Primitive::Indexed(..) = key.kind { - eprintln!("ion: cannot insert hmap into index"); - return FAILURE; + + let err = collected + .remove(key.name) + .map(|var| match (&var, &key.kind) { + (VariableType::HashMap(_), Primitive::Indexed(..)) => { + Err("cannot insert hmap into index".to_string()) } - } - bmap @ Some(VariableType::BTreeMap(_)) => { - if let Primitive::BTreeMap(_) = key.kind { - self.variables.set(key.name, bmap.unwrap()); - } else if let Primitive::Indexed(..) = key.kind { - eprintln!("ion: cannot insert bmap into index"); - return FAILURE; + (VariableType::BTreeMap(_), Primitive::Indexed(..)) => { + Err("cannot insert bmap into index".to_string()) } - } - array @ Some(VariableType::Array(_)) => { - if let Primitive::Indexed(..) = key.kind { - eprintln!("ion: multi-dimensional arrays are not yet supported"); - return FAILURE; - } else { - self.variables.set(key.name, array.unwrap()); + (VariableType::Array(_), Primitive::Indexed(..)) => { + Err("multi-dimensional arrays are not yet supported".to_string()) } - } - Some(VariableType::Str(value)) => { - if let Primitive::Indexed(ref index_value, ref index_kind) = key.kind { - match value_check(self, index_value, index_kind) { - Ok(VariableType::Str(ref index)) => match self - .variables - .get_mut(key.name) - { - Some(VariableType::HashMap(hmap)) => { - hmap.insert(index.clone(), VariableType::Str(value)); - } - Some(VariableType::BTreeMap(bmap)) => { - bmap.insert(index.clone(), VariableType::Str(value)); - } - Some(VariableType::Array(array)) => { - let index_num = match index.parse::<usize>() { - Ok(num) => num, - Err(_) => { - eprintln!( - "ion: index variable does not contain a \ + (VariableType::HashMap(_), Primitive::HashMap(_)) + | (VariableType::BTreeMap(_), Primitive::BTreeMap(_)) + | (VariableType::Array(_), _) => { + self.variables.set(key.name, var); + Ok(()) + } + ( + VariableType::Str(_), + Primitive::Indexed(ref index_value, ref index_kind), + ) => value_check(self, index_value, index_kind) + .map_err(|why| format!("assignment error: {}: {}", key.name, why)) + .and_then(|val| match val { + VariableType::Str(ref index) => { + match self.variables.get_mut(key.name) { + Some(VariableType::HashMap(hmap)) => { + hmap.insert(index.clone(), var); + Ok(()) + } + Some(VariableType::BTreeMap(bmap)) => { + bmap.insert(index.clone(), var); + Ok(()) + } + Some(VariableType::Array(array)) => index + .parse::<usize>() + .map_err(|_| { + format!( + "index variable does not contain a \ numeric value: {}", index - ); - return FAILURE; - } - }; - if let Some(val) = array.get_mut(index_num) { - *val = value; - } + ) + }) + .map(|index_num| { + if let (Some(val), VariableType::Str(value)) = + (array.get_mut(index_num), var) + { + *val = value; + } + }), + _ => Ok(()), } - _ => (), - }, - Ok(VariableType::Array(_)) => { - eprintln!("ion: index variable cannot be an array"); - return FAILURE; } - Ok(VariableType::HashMap(_)) => { - eprintln!("ion: index variable cannot be a hmap"); - return FAILURE; + VariableType::Array(_) => { + Err("index variable cannot be an array".to_string()) } - Ok(VariableType::BTreeMap(_)) => { - eprintln!("ion: index variable cannot be a bmap"); - return FAILURE; + VariableType::HashMap(_) => { + Err("index variable cannot be a hmap".to_string()) } - Err(why) => { - eprintln!("ion: assignment error: {}: {}", key.name, why); - return FAILURE; + VariableType::BTreeMap(_) => { + Err("index variable cannot be a bmap".to_string()) } - _ => (), - } - } - } - _ => (), + _ => Ok(()), + }), + _ => Ok(()), + }) + .unwrap_or(Ok(())); + + if let Err(why) = err { + eprintln!("ion: {}", why); + return FAILURE; } } - Ok(Action::UpdateString(key, operator, ..)) => { + Action::UpdateString(key, operator, ..) => { if operator == Operator::OptionalEqual && self.variables.get_ref(key.name).is_some() { continue; } match collected.remove(key.name) { - str_ @ Some(VariableType::Str(_)) => { - self.variables.set(key.name, str_.unwrap()); - } - array @ Some(VariableType::Array(_)) => { - self.variables.set(key.name, array.unwrap()); + Some(var @ VariableType::Str(_)) | Some(var @ VariableType::Array(_)) => { + self.variables.set(key.name, var); } _ => (), } } - _ => unreachable!(), } } @@ -601,13 +445,16 @@ fn parse_i64<F: Fn(i64, i64) -> Option<i64>>( } } -fn write_integer<F: FnMut(&[u8])>(integer: i64, mut func: F) { +fn write_integer<F: FnMut(&[u8]) -> Result<(), MathError>>( + integer: i64, + mut func: F, +) -> Result<(), MathError> { let mut buffer: [u8; 20] = unsafe { mem::uninitialized() }; let capacity = itoa::write(&mut buffer[..], integer).unwrap(); - func(&buffer[..capacity]); + func(&buffer[..capacity]) } -fn math<'a, F: FnMut(&[u8])>( +fn math<'a, F: FnMut(&[u8]) -> Result<(), MathError>>( lhs: &str, key: &Primitive, operator: Operator, @@ -615,72 +462,64 @@ fn math<'a, F: FnMut(&[u8])>( mut writefn: F, ) -> Result<(), MathError> { match operator { - Operator::Add => { - if Primitive::Any == *key || Primitive::Float == *key { - writefn(parse_f64(lhs, value, |lhs, rhs| lhs + rhs)?.to_string().as_bytes()); - } else if let Primitive::Integer = key { - write_integer(parse_i64(lhs, value, |lhs, rhs| Some(lhs + rhs))?, writefn); - } else { - return Err(MathError::Unsupported); + Operator::Add => match key { + Primitive::Any | Primitive::Float => { + writefn(parse_f64(lhs, value, |lhs, rhs| lhs + rhs)?.to_string().as_bytes()) } - } - Operator::Divide => { - if Primitive::Any == *key || Primitive::Float == *key || Primitive::Integer == *key { - writefn(parse_f64(lhs, value, |lhs, rhs| lhs / rhs)?.to_string().as_bytes()); - } else { - return Err(MathError::Unsupported); + Primitive::Integer => { + write_integer(parse_i64(lhs, value, |lhs, rhs| Some(lhs + rhs))?, writefn) } - } - Operator::IntegerDivide => { - if Primitive::Any == *key || Primitive::Float == *key { - write_integer( - parse_i64(lhs, value, |lhs, rhs| { - // We want to make sure we don't divide by zero, so instead, we give them a None as a result to signify that we were unable to calculate the result. - if rhs == 0 { - None - } else { - Some(lhs / rhs) - } - })?, - writefn, - ); - } else { - return Err(MathError::Unsupported); + _ => Err(MathError::Unsupported), + }, + Operator::Divide => match key { + Primitive::Any | Primitive::Float | Primitive::Integer => { + writefn(parse_f64(lhs, value, |lhs, rhs| lhs / rhs)?.to_string().as_bytes()) } - } - Operator::Subtract => { - if Primitive::Any == *key || Primitive::Float == *key { - writefn(parse_f64(lhs, value, |lhs, rhs| lhs - rhs)?.to_string().as_bytes()); - } else if let Primitive::Integer = key { - write_integer(parse_i64(lhs, value, |lhs, rhs| Some(lhs - rhs))?, writefn); - } else { - return Err(MathError::Unsupported); + _ => Err(MathError::Unsupported), + }, + Operator::IntegerDivide => match key { + Primitive::Any | Primitive::Float => write_integer( + parse_i64(lhs, value, |lhs, rhs| { + // We want to make sure we don't divide by zero, so instead, we give them a None + // as a result to signify that we were unable to calculate the result. + if rhs == 0 { + None + } else { + Some(lhs / rhs) + } + })?, + writefn, + ), + _ => Err(MathError::Unsupported), + }, + Operator::Subtract => match key { + Primitive::Any | Primitive::Float => { + writefn(parse_f64(lhs, value, |lhs, rhs| lhs - rhs)?.to_string().as_bytes()) } - } - Operator::Multiply => { - if Primitive::Any == *key || Primitive::Float == *key { - writefn(parse_f64(lhs, value, |lhs, rhs| lhs * rhs)?.to_string().as_bytes()); - } else if let Primitive::Integer = key { - write_integer(parse_i64(lhs, value, |lhs, rhs| Some(lhs * rhs))?, writefn); - } else { - return Err(MathError::Unsupported); + Primitive::Integer => { + write_integer(parse_i64(lhs, value, |lhs, rhs| Some(lhs - rhs))?, writefn) } - } - Operator::Exponent => { - if Primitive::Any == *key || Primitive::Float == *key { - writefn(parse_f64(lhs, value, |lhs, rhs| lhs.powf(rhs))?.to_string().as_bytes()); - } else if let Primitive::Integer = key { - write_integer( - parse_i64(lhs, value, |lhs, rhs| Some(lhs.pow(rhs as u32)))?, - writefn, - ); - } else { - return Err(MathError::Unsupported); + _ => Err(MathError::Unsupported), + }, + Operator::Multiply => match key { + Primitive::Any | Primitive::Float => { + writefn(parse_f64(lhs, value, |lhs, rhs| lhs * rhs)?.to_string().as_bytes()) } - } + Primitive::Integer => { + write_integer(parse_i64(lhs, value, |lhs, rhs| Some(lhs * rhs))?, writefn) + } + _ => Err(MathError::Unsupported), + }, + Operator::Exponent => match key { + Primitive::Any | Primitive::Float => { + writefn(parse_f64(lhs, value, |lhs, rhs| lhs.powf(rhs))?.to_string().as_bytes()) + } + Primitive::Integer => { + write_integer(parse_i64(lhs, value, |lhs, rhs| Some(lhs.pow(rhs as u32)))?, writefn) + } + _ => Err(MathError::Unsupported), + }, Operator::Equal => writefn(value.as_bytes()), - _ => return Err(MathError::Unsupported), - }; - - Ok(()) + _ => Err(MathError::Unsupported), + } }