From c6d76f81ea71ecf7948a5e404283e7116d4c9ad3 Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Fri, 27 Oct 2017 20:50:35 -0400
Subject: [PATCH] Refactor Assignments Module Severely trims down on LOC for
 performing assignments.

---
 src/main.rs              |   6 +-
 src/shell/assignments.rs | 427 ++++++++-------------------------------
 src/shell/mod.rs         |   4 +-
 src/shell/signals.rs     |   2 +-
 4 files changed, 92 insertions(+), 347 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 7fd5a282..66ebe740 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -52,10 +52,10 @@ use std::sync::atomic::Ordering;
 
 extern "C" fn handler(signal: i32) {
     let signal = match signal {
-        sys::SIGINT  => signals::SIGINT,
-        sys::SIGHUP  => signals::SIGHUP,
+        sys::SIGINT => signals::SIGINT,
+        sys::SIGHUP => signals::SIGHUP,
         sys::SIGTERM => signals::SIGTERM,
-        _ => unreachable!()
+        _ => unreachable!(),
     };
 
     signals::PENDING.store(signal, Ordering::SeqCst);
diff --git a/src/shell/assignments.rs b/src/shell/assignments.rs
index 50029b3c..82c181f1 100644
--- a/src/shell/assignments.rs
+++ b/src/shell/assignments.rs
@@ -1,10 +1,13 @@
-use std::env;
-use std::io::{self, Write};
-
 use super::Shell;
 use super::status::*;
 use parser::assignments::*;
 use shell::history::ShellHistory;
+use std::borrow::Cow;
+use std::env;
+use std::ffi::OsStr;
+use std::fmt::{self, Display};
+use std::io::{self, Write};
+use std::os::unix::ffi::OsStrExt;
 use types::{ArrayVariableContext, VariableContext};
 
 fn print_vars(list: &VariableContext) {
@@ -91,8 +94,13 @@ impl VariableStore for Shell {
 
                             match value_check(self, &expression, key.kind) {
                                 Ok(ReturnValue::Str(value)) => {
-                                    if !integer_math(self, key, operator, &value) {
-                                        return FAILURE;
+                                    let lhs = self.variables.get_var_or_empty(&key.name);
+                                    match math(&lhs, key.kind, operator, &value) {
+                                        Ok(value) => self.variables.set_var(&key.name, &value),
+                                        Err(why) => {
+                                            eprintln!("ion: assignment error: {}", why);
+                                            return FAILURE;
+                                        }
                                     }
                                 }
                                 Err(why) => {
@@ -147,8 +155,16 @@ impl VariableStore for Shell {
                     Ok(Action::UpdateString(key, operator, expression)) => {
                         match value_check(self, &expression, key.kind) {
                             Ok(ReturnValue::Str(value)) => {
-                                if !integer_math_export(&self, key, operator, &value) {
-                                    return FAILURE;
+                                let lhs = self.variables.get_var_or_empty(&key.name);
+                                match math(&lhs, key.kind, operator, &value) {
+                                    Ok(value) => {
+                                        let value = OsStr::from_bytes(&value.as_bytes());
+                                        env::set_var(&key.name, &value)
+                                    }
+                                    Err(why) => {
+                                        eprintln!("ion: assignment error: {}", why);
+                                        return FAILURE;
+                                    }
                                 }
                             }
                             Err(why) => {
@@ -190,356 +206,85 @@ impl VariableStore for Shell {
     }
 }
 
-// NOTE: Here there be excessively long functions.
+enum MathError {
+    RHS,
+    LHS,
+    Unsupported,
+}
 
-fn integer_math(shell: &mut Shell, key: Key, operator: Operator, value: &str) -> bool {
-    match operator {
-        Operator::Add => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs + rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs + rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
-        },
-        Operator::Divide => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs / rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs / rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
-        },
-        Operator::IntegerDivide => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs / rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
-        },
-        Operator::Subtract => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs - rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs - rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
-        },
-        Operator::Multiply => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs * rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs * rhs).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
-        },
-        Operator::Exponent => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs.powf(rhs)).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<u32>() {
-                    Ok(rhs) => {
-                        let value = (lhs.pow(rhs)).to_string();
-                        shell.variables.set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
-        },
-        Operator::Equal => {
-            shell.variables.set_var(key.name, &value);
-            true
+impl Display for MathError {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            MathError::RHS => write!(fmt, "right hand side has invalid type"),
+            MathError::LHS => write!(fmt, "left hand side has invalid type"),
+            MathError::Unsupported => write!(fmt, "type does not support operation"),
         }
     }
 }
 
-fn integer_math_export(shell: &Shell, key: Key, operator: Operator, value: &str) -> bool {
-    match operator {
-        Operator::Add => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs + rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs + rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
-        },
-        Operator::Divide => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs / rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs / rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
+fn parse_f64<F: Fn(f64, f64) -> f64>(lhs: &str, rhs: &str, operation: F) -> Result<f64, MathError> {
+    lhs.parse::<f64>().map_err(|_| MathError::LHS).and_then(
+        |lhs| rhs.parse::<f64>().map_err(|_| MathError::RHS).map(|rhs| operation(lhs, rhs)),
+    )
+}
+
+fn parse_i64<F: Fn(i64, i64) -> i64>(lhs: &str, rhs: &str, operation: F) -> Result<i64, MathError> {
+    lhs.parse::<i64>().map_err(|_| MathError::LHS).and_then(
+        |lhs| rhs.parse::<i64>().map_err(|_| MathError::RHS).map(|rhs| operation(lhs, rhs)),
+    )
+}
+
+fn math<'a>(
+    lhs: &str,
+    key: Primitive,
+    operator: Operator,
+    value: &'a str,
+) -> Result<Cow<'a, str>, MathError> {
+    let value: String = match operator {
+        Operator::Add => if Primitive::Any == key || Primitive::Float == key {
+            parse_f64(lhs, value, |lhs, rhs| lhs + rhs)?.to_string()
+        } else if let Primitive::Integer = key {
+            parse_i64(lhs, value, |lhs, rhs| lhs + rhs)?.to_string()
         } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
+            return Err(MathError::Unsupported);
         },
-        Operator::IntegerDivide => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs / rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
+        Operator::Divide => {
+            if Primitive::Any == key || Primitive::Float == key || Primitive::Integer == key {
+                parse_f64(lhs, value, |lhs, rhs| lhs / rhs)?.to_string()
+            } else {
+                return Err(MathError::Unsupported);
             }
-            return false;
+        }
+        Operator::IntegerDivide => if Primitive::Any == key || Primitive::Float == key {
+            parse_i64(lhs, value, |lhs, rhs| lhs / rhs)?.to_string()
         } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
+            return Err(MathError::Unsupported);
         },
-        Operator::Subtract => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs - rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs - rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
+        Operator::Subtract => if Primitive::Any == key || Primitive::Float == key {
+            parse_f64(lhs, value, |lhs, rhs| lhs - rhs)?.to_string()
+        } else if let Primitive::Integer = key {
+            parse_i64(lhs, value, |lhs, rhs| lhs - rhs)?.to_string()
         } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
+            return Err(MathError::Unsupported);
         },
-        Operator::Multiply => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs * rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<i64>() {
-                    Ok(rhs) => {
-                        let value = (lhs * rhs).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
+        Operator::Multiply => if Primitive::Any == key || Primitive::Float == key {
+            parse_f64(lhs, value, |lhs, rhs| lhs * rhs)?.to_string()
+        } else if let Primitive::Integer = key {
+            parse_i64(lhs, value, |lhs, rhs| lhs * rhs)?.to_string()
         } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
+            return Err(MathError::Unsupported);
         },
-        Operator::Exponent => if Primitive::Any == key.kind || Primitive::Float == key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<f64>() {
-                Ok(lhs) => match value.parse::<f64>() {
-                    Ok(rhs) => {
-                        let value = (lhs.powf(rhs)).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
-        } else if let Primitive::Integer = key.kind {
-            match shell.variables.get_var_or_empty(key.name).parse::<i64>() {
-                Ok(lhs) => match value.parse::<u32>() {
-                    Ok(rhs) => {
-                        let value = (lhs.pow(rhs)).to_string();
-                        env::set_var(key.name, &value);
-                        return true;
-                    }
-                    Err(_) => eprintln!("ion: right hand side has invalid value type"),
-                },
-                Err(_) => eprintln!("ion: variable has invalid value type"),
-            }
-            return false;
+        Operator::Exponent => if Primitive::Any == key || Primitive::Float == key {
+            parse_f64(lhs, value, |lhs, rhs| lhs.powf(rhs))?.to_string()
+        } else if let Primitive::Integer = key {
+            parse_i64(lhs, value, |lhs, rhs| lhs.pow(rhs as u32))?.to_string()
         } else {
-            eprintln!("ion: variable does not support this operation");
-            return false;
+            return Err(MathError::Unsupported);
         },
         Operator::Equal => {
-            env::set_var(key.name, &value);
-            true
+            return Ok(Cow::Borrowed(value));
         }
-    }
+    };
+
+    Ok(Cow::Owned(value))
 }
diff --git a/src/shell/mod.rs b/src/shell/mod.rs
index 1471b42d..ee7e8e61 100644
--- a/src/shell/mod.rs
+++ b/src/shell/mod.rs
@@ -45,8 +45,8 @@ use std::process;
 use std::sync::{Arc, Mutex};
 use std::sync::atomic::Ordering;
 use std::time::SystemTime;
-use types::*;
 use sys;
+use types::*;
 
 /// The shell structure is a megastructure that manages all of the state of the shell throughout
 /// the entirety of the
@@ -143,7 +143,7 @@ impl<'a> Shell {
             signals::SIGINT => Some(sys::SIGINT),
             signals::SIGHUP => Some(sys::SIGHUP),
             signals::SIGTERM => Some(sys::SIGTERM),
-            _ => unreachable!()
+            _ => unreachable!(),
         }
     }
 
diff --git a/src/shell/signals.rs b/src/shell/signals.rs
index 75a0e1ed..7fa8a6b4 100644
--- a/src/shell/signals.rs
+++ b/src/shell/signals.rs
@@ -3,7 +3,7 @@
 //! forked
 //! children of the shell.
 
-use std::sync::atomic::{AtomicU8, ATOMIC_U8_INIT};
+use std::sync::atomic::{ATOMIC_U8_INIT, AtomicU8};
 
 use sys;
 
-- 
GitLab