Initial pass at a Shell
authorYehuda Katz <wycats@gmail.com>
Wed, 28 May 2014 01:33:06 +0000 (18:33 -0700)
committerYehuda Katz <wycats@gmail.com>
Wed, 28 May 2014 01:53:37 +0000 (18:53 -0700)
src/cargo/core/shell.rs
tests/support.rs
tests/tests.rs

index a5d98142bcb916d50d310ef10ef69ee94c14d1c2..ea4fb06023434d054eefd2c339b0685a73d8ab22 100644 (file)
@@ -6,33 +6,32 @@ use std::io::IoResult;
 use std::io::stdio::StdWriter;
 
 pub struct ShellConfig {
-    color: bool,
-    verbose: bool
+    pub color: bool,
+    pub verbose: bool,
+    pub tty: bool
 }
 
-enum AdequateTerminal {
-    NoColor(BasicTerminal<StdWriter>),
-    Color(Box<Terminal<StdWriter>>)
+enum AdequateTerminal<T> {
+    NoColor(BasicTerminal<T>),
+    Color(Box<Terminal<T>>)
 }
 
-pub struct Shell {
-    terminal: AdequateTerminal,
+pub struct Shell<T> {
+    terminal: AdequateTerminal<T>,
     config: ShellConfig
 }
 
-impl Shell {
-    fn create(out: StdWriter, config: ShellConfig) -> Option<Shell> {
-        let term = if out.isatty() {
-            let term: Option<term::TerminfoTerminal<StdWriter>> = Terminal::new(out);
-            term.map(|t| Color(box t))
+impl<T: Writer + Send> Shell<T> {
+    pub fn create(out: T, config: ShellConfig) -> Option<Shell<T>> {
+        if config.tty {
+            let term: Option<term::TerminfoTerminal<T>> = Terminal::new(out);
+            term.map(|t| Shell { terminal: Color(box t as Box<Terminal<T>>), config: config })
         } else {
-            Some(NoColor(BasicTerminal { writer: out }))
-        };
-
-        term.map(|term| Shell { terminal: term, config: config })
+            Some(Shell { terminal: NoColor(BasicTerminal { writer: out }), config: config })
+        }
     }
 
-    pub fn verbose(&mut self, callback: |&mut Shell| -> IoResult<()>) -> IoResult<()> {
+    pub fn verbose(&mut self, callback: |&mut Shell<T>| -> IoResult<()>) -> IoResult<()> {
         if self.config.verbose {
             return callback(self)
         }
@@ -50,9 +49,9 @@ impl Shell {
     }
 }
 
-impl Terminal<StdWriter> for Shell {
-    fn new(out: StdWriter) -> Option<Shell> {
-        Shell::create(out, ShellConfig { color: true, verbose: false })
+impl<T: Writer + Send> Terminal<T> for Shell<T> {
+    fn new(out: T) -> Option<Shell<T>> {
+        Shell::create(out, ShellConfig { color: true, verbose: false, tty: false })
     }
 
     fn fg(&mut self, color: color::Color) -> IoResult<bool> {
@@ -90,18 +89,18 @@ impl Terminal<StdWriter> for Shell {
         }
     }
 
-    fn unwrap(self) -> StdWriter {
-        fail!("Can't unwrap a Shell")
+    fn unwrap(self) -> T {
+        fail!("Can't unwrap a Shell");
     }
 
-    fn get_ref<'a>(&'a self) -> &'a StdWriter {
+    fn get_ref<'a>(&'a self) -> &'a T {
         match self.terminal {
             Color(ref c) => c.get_ref(),
             NoColor(ref n) => n.get_ref()
         }
     }
 
-    fn get_mut<'a>(&'a mut self) -> &'a mut StdWriter {
+    fn get_mut<'a>(&'a mut self) -> &'a mut T {
         match self.terminal {
             Color(ref mut c) => c.get_mut(),
             NoColor(ref mut n) => n.get_mut()
@@ -109,7 +108,7 @@ impl Terminal<StdWriter> for Shell {
     }
 }
 
-impl Writer for Shell {
+impl<T: Writer + Send> Writer for Shell<T> {
     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
         match self.terminal {
             Color(ref mut c) => c.write(buf),
@@ -129,7 +128,7 @@ pub struct BasicTerminal<T> {
     writer: T
 }
 
-impl<T: Writer> Terminal<T> for BasicTerminal<T> {
+impl<T: Writer + Send> Terminal<T> for BasicTerminal<T> {
     fn new(out: T) -> Option<BasicTerminal<T>> {
         Some(BasicTerminal { writer: out })
     }
@@ -167,7 +166,7 @@ impl<T: Writer> Terminal<T> for BasicTerminal<T> {
     }
 }
 
-impl<T: Writer> Writer for BasicTerminal<T> {
+impl<T: Writer + Send> Writer for BasicTerminal<T> {
     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
         self.writer.write(buf)
     }
index 9dee66e6e94338099e4accae7ca70bc6448bb395..131bf989179d7a654fd23f55ecf23e2cc5fbc681 100644 (file)
@@ -1,4 +1,5 @@
 // use std::io::fs::{mkdir_recursive,rmdir_recursive};
+use std;
 use std::io;
 use std::io::fs;
 use std::io::process::{ProcessOutput,ProcessExit};
@@ -8,6 +9,7 @@ use std::str;
 use std::vec::Vec;
 use std::fmt::Show;
 use ham = hamcrest;
+use cargo::core::shell;
 use cargo::util::{process,ProcessBuilder,CargoError};
 use cargo::util::result::ProcessError;
 
@@ -246,6 +248,30 @@ pub fn execs() -> Box<Execs> {
     }
 }
 
+#[deriving(Clone,Eq)]
+struct ShellWrites {
+    expected: String
+}
+
+impl ham::SelfDescribing for ShellWrites {
+    fn describe(&self) -> String {
+        format!("`{}` written to the shell", self.expected)
+    }
+}
+
+impl<'a> ham::Matcher<&'a mut shell::Shell<std::io::MemWriter>> for ShellWrites {
+    fn matches(&self, actual: &mut shell::Shell<std::io::MemWriter>) -> ham::MatchResult {
+        use term::Terminal;
+
+        let actual = std::str::from_utf8_lossy(actual.get_ref().get_ref()).to_str();
+        ham::expect(actual == self.expected, actual)
+    }
+}
+
+pub fn shell_writes<T: Show>(string: T) -> Box<ShellWrites> {
+    box ShellWrites { expected: string.to_str() }
+}
+
 pub trait ResultTest<T,E> {
     fn assert(self) -> T;
 }
@@ -258,3 +284,23 @@ impl<T,E: Show> ResultTest<T,E> for Result<T,E> {
         }
     }
 }
+
+impl<T> ResultTest<T,()> for Option<T> {
+    fn assert(self) -> T {
+        match self {
+            Some(val) => val,
+            None => fail!("Option was None")
+        }
+    }
+}
+
+pub trait Tap {
+    fn tap(mut self, callback: |&mut Self|) -> Self;
+}
+
+impl<T> Tap for T {
+    fn tap(mut self, callback: |&mut T|) -> T {
+        callback(&mut self);
+        self
+    }
+}
index 9e3684ba70fe4dc32a73f1ef0420ddd4d30b9970..495adba1e1d6bad907b87b962a90c426c3206330 100644 (file)
@@ -1,6 +1,7 @@
 #![feature(macro_rules)]
 #![allow(deprecated_owned_vector)]
 
+extern crate term;
 extern crate cargo;
 extern crate hamcrest;
 
@@ -16,3 +17,4 @@ macro_rules! test(
 
 mod support;
 mod test_cargo_compile;
+mod test_shell;