summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Gallant <jamslam@gmail.com>2016-09-13 21:11:46 -0400
committerAndrew Gallant <jamslam@gmail.com>2016-09-13 21:11:46 -0400
commitfdca74148dbb168035f538506621e59b92766090 (patch)
tree40abfca4c8d3686a986d6551fd35a95463aa910e
parentf11d9fb9221a8c20452dee82af6429e88a8f2785 (diff)
Stream results when feasible.0.0.19
For example, when only a single file (or stdin) is being searched, then we should be able to print directly to the terminal instead of intermediate buffers. (The buffers are only necessary for parallelism.) Closes #4.
-rw-r--r--src/args.rs35
-rw-r--r--src/gitignore.rs22
-rw-r--r--src/glob.rs5
-rw-r--r--src/main.rs118
-rw-r--r--src/out.rs513
-rw-r--r--src/printer.rs2
-rw-r--r--src/search_buffer.rs31
-rw-r--r--src/search_stream.rs22
-rw-r--r--src/terminal.rs202
-rw-r--r--src/terminal_win.rs176
-rw-r--r--src/types.rs2
-rw-r--r--src/walk.rs5
12 files changed, 488 insertions, 645 deletions
diff --git a/src/args.rs b/src/args.rs
index 66af8f1a..1ddc0497 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -9,16 +9,20 @@ use grep::{Grep, GrepBuilder};
use log;
use num_cpus;
use regex;
-use term::Terminal;
+use term::{self, Terminal};
+#[cfg(windows)]
+use term::WinConsole;
use walkdir::WalkDir;
use atty;
use gitignore::{Gitignore, GitignoreBuilder};
use ignore::Ignore;
-use out::{Out, OutBuffer};
+use out::{Out, ColoredTerminal};
use printer::Printer;
use search_buffer::BufferSearcher;
use search_stream::{InputBuffer, Searcher};
+#[cfg(windows)]
+use terminal_win::WindowsBuffer;
use types::{FileTypeDef, Types, TypesBuilder};
use walk;
@@ -442,7 +446,7 @@ impl Args {
/// Create a new printer of individual search results that writes to the
/// writer given.
- pub fn printer<W: Send + Terminal>(&self, wtr: W) -> Printer<W> {
+ pub fn printer<W: Terminal + Send>(&self, wtr: W) -> Printer<W> {
let mut p = Printer::new(wtr)
.column(self.column)
.context_separator(self.context_separator.clone())
@@ -469,8 +473,29 @@ impl Args {
}
/// Create a new buffer for use with searching.
- pub fn outbuf(&self) -> OutBuffer {
- OutBuffer::new(self.color)
+ #[cfg(not(windows))]
+ pub fn outbuf(&self) -> ColoredTerminal<term::TerminfoTerminal<Vec<u8>>> {
+ ColoredTerminal::new(vec![], self.color)
+ }
+
+ /// Create a new buffer for use with searching.
+ #[cfg(windows)]
+ pub fn outbuf(&self) -> ColoredTerminal<WindowsBuffer> {
+ ColoredTerminal::new_buffer(self.color)
+ }
+
+ /// Create a new buffer for use with searching.
+ #[cfg(not(windows))]
+ pub fn stdout(
+ &self,
+ ) -> ColoredTerminal<term::TerminfoTerminal<io::BufWriter<io::Stdout>>> {
+ ColoredTerminal::new(io::BufWriter::new(io::stdout()), self.color)
+ }
+
+ /// Create a new buffer for use with searching.
+ #[cfg(windows)]
+ pub fn stdout(&self) -> ColoredTerminal<WinConsole<io::Stdout>> {
+ ColoredTerminal::new_stdout(self.color)
}
/// Return the paths that should be searched.
diff --git a/src/gitignore.rs b/src/gitignore.rs
index fd8fd6f2..31a16ea6 100644
--- a/src/gitignore.rs
+++ b/src/gitignore.rs
@@ -21,7 +21,6 @@ additional rules such as whitelists (prefix of `!`) or directory-only globs
// TODO(burntsushi): Implement something similar, but for Mercurial. We can't
// use this exact implementation because hgignore files are different.
-use std::env;
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
@@ -89,21 +88,10 @@ pub struct Gitignore {
}
impl Gitignore {
- /// Create a new gitignore glob matcher from the gitignore file at the
- /// given path. The root of the gitignore file is the basename of path.
- pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Gitignore, Error> {
- let root = match path.as_ref().parent() {
- Some(parent) => parent.to_path_buf(),
- None => env::current_dir().unwrap_or(Path::new("/").to_path_buf()),
- };
- let mut builder = GitignoreBuilder::new(root);
- try!(builder.add_path(path));
- builder.build()
- }
-
/// Create a new gitignore glob matcher from the given root directory and
/// string containing the contents of a gitignore file.
- pub fn from_str<P: AsRef<Path>>(
+ #[allow(dead_code)]
+ fn from_str<P: AsRef<Path>>(
root: P,
gitignore: &str,
) -> Result<Gitignore, Error> {
@@ -159,11 +147,6 @@ impl Gitignore {
pub fn num_ignores(&self) -> u64 {
self.num_ignores
}
-
- /// Returns the total number of whitelisted patterns.
- pub fn num_whitelist(&self) -> u64 {
- self.num_whitelist
- }
}
/// The result of a glob match.
@@ -182,6 +165,7 @@ pub enum Match<'a> {
impl<'a> Match<'a> {
/// Returns true if the match result implies the path should be ignored.
+ #[allow(dead_code)]
pub fn is_ignored(&self) -> bool {
match *self {
Match::Ignored(_) => true,
diff --git a/src/glob.rs b/src/glob.rs
index aaee018d..f16a75e4 100644
--- a/src/glob.rs
+++ b/src/glob.rs
@@ -95,6 +95,7 @@ impl Set {
}
/// Returns the number of glob patterns in this set.
+ #[allow(dead_code)]
pub fn len(&self) -> usize {
self.set.len()
}
@@ -137,6 +138,7 @@ impl SetBuilder {
///
/// If the pattern could not be parsed as a glob, then an error is
/// returned.
+ #[allow(dead_code)]
pub fn add(&mut self, pat: &str) -> Result<(), Error> {
self.add_with(pat, &MatchOptions::default())
}
@@ -205,6 +207,7 @@ impl Pattern {
/// Convert this pattern to a string that is guaranteed to be a valid
/// regular expression and will represent the matching semantics of this
/// glob pattern. This uses a default set of options.
+ #[allow(dead_code)]
pub fn to_regex(&self) -> String {
self.to_regex_with(&MatchOptions::default())
}
@@ -315,7 +318,7 @@ impl<'a> Parser<'a> {
}
return Ok(());
}
- let last = self.p.tokens.pop().unwrap();
+ self.p.tokens.pop().unwrap();
if prev != Some('/') {
return Err(Error::InvalidRecursive);
}
diff --git a/src/main.rs b/src/main.rs
index 97cfc888..a8c82d04 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,3 @@
-#![allow(dead_code, unused_variables)]
-
extern crate crossbeam;
extern crate docopt;
extern crate env_logger;
@@ -25,7 +23,7 @@ extern crate winapi;
use std::error::Error;
use std::fs::File;
use std::io;
-use std::path::Path;
+use std::path::{Path, PathBuf};
use std::process;
use std::result;
use std::sync::{Arc, Mutex};
@@ -38,9 +36,11 @@ use term::Terminal;
use walkdir::DirEntry;
use args::Args;
-use out::{NoColorTerminal, Out, OutBuffer};
+use out::{ColoredTerminal, Out};
use printer::Printer;
use search_stream::InputBuffer;
+#[cfg(windows)]
+use terminal_win::WindowsBuffer;
macro_rules! errored {
($($tt:tt)*) => {
@@ -64,7 +64,8 @@ mod out;
mod printer;
mod search_buffer;
mod search_stream;
-mod terminal;
+#[cfg(windows)]
+mod terminal_win;
mod types;
mod walk;
@@ -73,7 +74,7 @@ pub type Result<T> = result::Result<T, Box<Error + Send + Sync>>;
fn main() {
match Args::parse().and_then(run) {
Ok(count) if count == 0 => process::exit(1),
- Ok(count) => process::exit(0),
+ Ok(_) => process::exit(0),
Err(err) => {
eprintln!("{}", err);
process::exit(1);
@@ -82,34 +83,40 @@ fn main() {
}
fn run(args: Args) -> Result<u64> {
+ let args = Arc::new(args);
+ let paths = args.paths();
if args.files() {
- return run_files(args);
+ return run_files(args.clone());
}
if args.type_list() {
- return run_types(args);
+ return run_types(args.clone());
}
- let args = Arc::new(args);
+ if paths.len() == 1 && (paths[0] == Path::new("-") || paths[0].is_file()) {
+ return run_one(args.clone(), &paths[0]);
+ }
+
let out = Arc::new(Mutex::new(args.out()));
- let outbuf = args.outbuf();
let mut workers = vec![];
let mut workq = {
let (workq, stealer) = chase_lev::deque();
for _ in 0..args.threads() {
- let worker = Worker {
- args: args.clone(),
- out: out.clone(),
+ let worker = MultiWorker {
chan_work: stealer.clone(),
- inpbuf: args.input_buffer(),
- outbuf: Some(outbuf.clone()),
- grep: args.grep(),
- match_count: 0,
+ out: out.clone(),
+ outbuf: Some(args.outbuf()),
+ worker: Worker {
+ args: args.clone(),
+ inpbuf: args.input_buffer(),
+ grep: args.grep(),
+ match_count: 0,
+ },
};
workers.push(thread::spawn(move || worker.run()));
}
workq
};
- for p in args.paths() {
+ for p in paths {
if p == Path::new("-") {
workq.push(Work::Stdin)
} else {
@@ -128,8 +135,27 @@ fn run(args: Args) -> Result<u64> {
Ok(match_count)
}
-fn run_files(args: Args) -> Result<u64> {
- let term = NoColorTerminal::new(io::BufWriter::new(io::stdout()));
+fn run_one(args: Arc<Args>, path: &Path) -> Result<u64> {
+ let mut worker = Worker {
+ args: args.clone(),
+ inpbuf: args.input_buffer(),
+ grep: args.grep(),
+ match_count: 0,
+ };
+ let term = args.stdout();
+ let mut printer = args.printer(term);
+ let work =
+ if path == Path::new("-") {
+ WorkReady::Stdin
+ } else {
+ WorkReady::PathFile(path.to_path_buf(), try!(File::open(path)))
+ };
+ worker.do_work(&mut printer, work);
+ Ok(worker.match_count)
+}
+
+fn run_files(args: Arc<Args>) -> Result<u64> {
+ let term = args.stdout();
let mut printer = args.printer(term);
let mut file_count = 0;
for p in args.paths() {
@@ -146,8 +172,8 @@ fn run_files(args: Args) -> Result<u64> {
Ok(file_count)
}
-fn run_types(args: Args) -> Result<u64> {
- let term = NoColorTerminal::new(io::BufWriter::new(io::stdout()));
+fn run_types(args: Arc<Args>) -> Result<u64> {
+ let term = args.stdout();
let mut printer = args.printer(term);
let mut ty_count = 0;
for def in args.type_defs() {
@@ -165,22 +191,29 @@ enum Work {
enum WorkReady {
Stdin,
- File(DirEntry, File),
+ DirFile(DirEntry, File),
+ PathFile(PathBuf, File),
+}
+
+struct MultiWorker {
+ chan_work: Stealer<Work>,
+ out: Arc<Mutex<Out>>,
+ #[cfg(not(windows))]
+ outbuf: Option<ColoredTerminal<term::TerminfoTerminal<Vec<u8>>>>,
+ #[cfg(windows)]
+ outbuf: Option<ColoredTerminal<WindowsBuffer>>,
+ worker: Worker,
}
struct Worker {
args: Arc<Args>,
- out: Arc<Mutex<Out>>,
- chan_work: Stealer<Work>,
inpbuf: InputBuffer,
- outbuf: Option<OutBuffer>,
grep: Grep,
match_count: u64,
}
-impl Worker {
+impl MultiWorker {
fn run(mut self) -> u64 {
- self.match_count = 0;
loop {
let work = match self.chan_work.steal() {
Steal::Empty | Steal::Abort => continue,
@@ -188,7 +221,7 @@ impl Worker {
Steal::Data(Work::Stdin) => WorkReady::Stdin,
Steal::Data(Work::File(ent)) => {
match File::open(ent.path()) {
- Ok(file) => WorkReady::File(ent, file),
+ Ok(file) => WorkReady::DirFile(ent, file),
Err(err) => {
eprintln!("{}: {}", ent.path().display(), err);
continue;
@@ -198,8 +231,8 @@ impl Worker {
};
let mut outbuf = self.outbuf.take().unwrap();
outbuf.clear();
- let mut printer = self.args.printer(outbuf);
- self.do_work(&mut printer, work);
+ let mut printer = self.worker.args.printer(outbuf);
+ self.worker.do_work(&mut printer, work);
let outbuf = printer.into_inner();
if !outbuf.get_ref().is_empty() {
let mut out = self.out.lock().unwrap();
@@ -207,10 +240,12 @@ impl Worker {
}
self.outbuf = Some(outbuf);
}
- self.match_count
+ self.worker.match_count
}
+}
- fn do_work<W: Send + Terminal>(
+impl Worker {
+ fn do_work<W: Terminal + Send>(
&mut self,
printer: &mut Printer<W>,
work: WorkReady,
@@ -221,7 +256,7 @@ impl Worker {
let stdin = stdin.lock();
self.search(printer, &Path::new("<stdin>"), stdin)
}
- WorkReady::File(ent, file) => {
+ WorkReady::DirFile(ent, file) => {
let mut path = ent.path();
if let Ok(p) = path.strip_prefix("./") {
path = p;
@@ -232,6 +267,17 @@ impl Worker {
self.search(printer, path, file)
}
}
+ WorkReady::PathFile(path, file) => {
+ let mut path = &*path;
+ if let Ok(p) = path.strip_prefix("./") {
+ path = p;
+ }
+ if self.args.mmap() {
+ self.search_mmap(printer, path, &file)
+ } else {
+ self.search(printer, path, file)
+ }
+ }
};
match result {
Ok(count) => {
@@ -243,7 +289,7 @@ impl Worker {
}
}
- fn search<R: io::Read, W: Send + Terminal>(
+ fn search<R: io::Read, W: Terminal + Send>(
&mut self,
printer: &mut Printer<W>,
path: &Path,
@@ -258,7 +304,7 @@ impl Worker {
).run().map_err(From::from)
}
- fn search_mmap<W: Send + Terminal>(
+ fn search_mmap<W: Terminal + Send>(
&mut self,
printer: &mut Printer<W>,
path: &Path,
diff --git a/src/out.rs b/src/out.rs
index 8a9d6abb..384e227c 100644
--- a/src/out.rs
+++ b/src/out.rs
@@ -1,40 +1,12 @@
use std::io::{self, Write};
-use std::sync::Arc;
use term::{self, Terminal};
-use term::color::Color;
use term::terminfo::TermInfo;
#[cfg(windows)]
use term::WinConsole;
-use terminal::TerminfoTerminal;
-
-pub type StdoutTerminal = Box<Terminal<Output=io::Stdout> + Send>;
-
-/// Gets a terminal that supports color if available.
#[cfg(windows)]
-fn term_stdout(color: bool) -> StdoutTerminal {
- let stdout = io::stdout();
- WinConsole::new(stdout)
- .ok()
- .map(|t| Box::new(t) as StdoutTerminal)
- .unwrap_or_else(|| {
- let stdout = io::stdout();
- Box::new(NoColorTerminal::new(stdout)) as StdoutTerminal
- })
-}
-
-/// Gets a terminal that supports color if available.
-#[cfg(not(windows))]
-fn term_stdout(color: bool) -> StdoutTerminal {
- let stdout = io::stdout();
- if !color || TERMINFO.is_none() {
- Box::new(NoColorTerminal::new(stdout))
- } else {
- let info = TERMINFO.clone().unwrap();
- Box::new(TerminfoTerminal::new_with_terminfo(stdout, info))
- }
-}
+use terminal_win::WindowsBuffer;
/// Out controls the actual output of all search results for a particular file
/// to the end user.
@@ -43,16 +15,31 @@ fn term_stdout(color: bool) -> StdoutTerminal {
/// individual search results where as Out works with search results for each
/// file as a whole. For example, it knows when to print a file separator.)
pub struct Out {
- term: StdoutTerminal,
+ #[cfg(not(windows))]
+ term: ColoredTerminal<term::TerminfoTerminal<io::BufWriter<io::Stdout>>>,
+ #[cfg(windows)]
+ term: ColoredTerminal<WinConsole<io::Stdout>>,
printed: bool,
file_separator: Option<Vec<u8>>,
}
impl Out {
/// Create a new Out that writes to the wtr given.
+ #[cfg(not(windows))]
+ pub fn new(color: bool) -> Out {
+ let wtr = io::BufWriter::new(io::stdout());
+ Out {
+ term: ColoredTerminal::new(wtr, color),
+ printed: false,
+ file_separator: None,
+ }
+ }
+
+ /// Create a new Out that writes to the wtr given.
+ #[cfg(windows)]
pub fn new(color: bool) -> Out {
Out {
- term: term_stdout(color),
+ term: ColoredTerminal::new_stdout(color),
printed: false,
file_separator: None,
}
@@ -69,154 +56,194 @@ impl Out {
/// Write the search results of a single file to the underlying wtr and
/// flush wtr.
- pub fn write(&mut self, buf: &OutBuffer) {
- if let Some(ref sep) = self.file_separator {
- if self.printed {
- let _ = self.term.write_all(sep);
- let _ = self.term.write_all(b"\n");
- }
- }
+ #[cfg(not(windows))]
+ pub fn write(
+ &mut self,
+ buf: &ColoredTerminal<term::TerminfoTerminal<Vec<u8>>>,
+ ) {
+ self.write_sep();
match *buf {
- OutBuffer::Colored(ref tt) => {
+ ColoredTerminal::Colored(ref tt) => {
let _ = self.term.write_all(tt.get_ref());
}
- OutBuffer::Windows(ref w) => {
- w.print_stdout(&mut self.term);
+ ColoredTerminal::NoColor(ref buf) => {
+ let _ = self.term.write_all(buf);
}
- OutBuffer::NoColor(ref buf) => {
+ }
+ self.write_done();
+ }
+ /// Write the search results of a single file to the underlying wtr and
+ /// flush wtr.
+ #[cfg(windows)]
+ pub fn write(
+ &mut self,
+ buf: &ColoredTerminal<WindowsBuffer>,
+ ) {
+ self.write_sep();
+ match *buf {
+ ColoredTerminal::Colored(ref tt) => {
+ tt.print_stdout(&mut self.term);
+ }
+ ColoredTerminal::NoColor(ref buf) => {
let _ = self.term.write_all(buf);
}
}
- let _ = self.term.flush();
- self.printed = true;
+ self.write_done();
}
-}
-/// OutBuffer corresponds to the final output buffer for search results. All
-/// search results are written to a buffer and then a buffer is flushed to
-/// stdout only after the full search has completed.
-#[derive(Clone, Debug)]
-pub enum OutBuffer {
- Colored(TerminfoTerminal<Vec<u8>>),
- Windows(WindowsBuffer),
- NoColor(Vec<u8>),
-}
-
-#[derive(Clone, Debug)]
-pub struct WindowsBuffer {
- buf: Vec<u8>,
- pos: usize,
- colors: Vec<WindowsColor>,
-}
+ fn write_sep(&mut self) {
+ if let Some(ref sep) = self.file_separator {
+ if self.printed {
+ let _ = self.term.write_all(sep);
+ let _ = self.term.write_all(b"\n");
+ }
+ }
+ }
-#[derive(Clone, Debug)]
-pub struct WindowsColor {
- pos: usize,
- opt: WindowsOption,
+ fn write_done(&mut self) {
+ let _ = self.term.flush();
+ self.printed = true;
+ }
}
+/// ColoredTerminal provides optional colored output through the term::Terminal
+/// trait. In particular, it will dynamically configure itself to use coloring
+/// if it's available in the environment.
#[derive(Clone, Debug)]
-pub enum WindowsOption {
- Foreground(Color),
- Background(Color),
- Reset,
-}
-
-lazy_static! {
- static ref TERMINFO: Option<Arc<TermInfo>> = {
- match TermInfo::from_env() {
- Ok(info) => Some(Arc::new(info)),
- Err(err) => {
- debug!("error loading terminfo for coloring: {}", err);
- None
- }
- }
- };
+pub enum ColoredTerminal<T: Terminal + Send> {
+ Colored(T),
+ NoColor(T::Output),
}
-impl OutBuffer {
+#[cfg(not(windows))]
+impl<W: io::Write + Send> ColoredTerminal<term::TerminfoTerminal<W>> {
/// Create a new output buffer.
///
/// When color is true, the buffer will attempt to support coloring.
- pub fn new(color: bool) -> OutBuffer {
- // If we want color, build a TerminfoTerminal and see if the current
- // environment supports coloring. If not, bail with NoColor. To avoid
- // losing our writer (ownership), do this the long way.
- if !color {
- return OutBuffer::NoColor(vec![]);
- }
- if cfg!(windows) {
- return OutBuffer::Windows(WindowsBuffer {
- buf: vec![],
- pos: 0,
- colors: vec![]
- });
+ pub fn new(wtr: W, color: bool) -> Self {
+ lazy_static! {
+ // Only pay for parsing the terminfo once.
+ static ref TERMINFO: Option<TermInfo> = {
+ match TermInfo::from_env() {
+ Ok(info) => Some(info),
+ Err(err) => {
+ debug!("error loading terminfo for coloring: {}", err);
+ None
+ }
+ }
+ };
}
- if TERMINFO.is_none() {
- return OutBuffer::NoColor(vec![]);
+ // If we want color, build a term::TerminfoTerminal and see if the
+ // current environment supports coloring. If not, bail with NoColor. To
+ // avoid losing our writer (ownership), do this the long way.
+ if !color {
+ return ColoredTerminal::NoColor(wtr);
}
- let info = TERMINFO.clone().unwrap();
- let tt = TerminfoTerminal::new_with_terminfo(vec![], info);
+ let terminfo = match *TERMINFO {
+ None => return ColoredTerminal::NoColor(wtr),
+ Some(ref ti) => {
+ // Ug, this should go away with the next release of `term`.
+ TermInfo {
+ names: ti.names.clone(),
+ bools: ti.bools.clone(),
+ numbers: ti.numbers.clone(),
+ strings: ti.strings.clone(),
+ }
+ }
+ };
+ let tt = term::TerminfoTerminal::new_with_terminfo(wtr, terminfo);
if !tt.supports_color() {
debug!("environment doesn't support coloring");
- return OutBuffer::NoColor(tt.into_inner());
+ return ColoredTerminal::NoColor(tt.into_inner());
}
- OutBuffer::Colored(tt)
+ ColoredTerminal::Colored(tt)
}
+}
+#[cfg(not(windows))]
+impl ColoredTerminal<term::TerminfoTerminal<Vec<u8>>> {
/// Clear the give buffer of all search results such that it is reusable
/// in another search.
pub fn clear(&mut self) {
match *self {
- OutBuffer::Colored(ref mut tt) => {
+ ColoredTerminal::Colored(ref mut tt) => {
tt.get_mut().clear();
}
- OutBuffer::Windows(ref mut win) => {
- win.buf.clear();
- win.colors.clear();
- win.pos = 0;
- }
- OutBuffer::NoColor(ref mut buf) => {
+ ColoredTerminal::NoColor(ref mut buf) => {
buf.clear();
}
}
}
+}
+
+#[cfg(windows)]
+impl ColoredTerminal<WindowsBuffer> {
+ /// Create a new output buffer.
+ ///
+ /// When color is true, the buffer will attempt to support coloring.
+ pub fn new_buffer(color: bool) -> Self {
+ if !color {
+ ColoredTerminal::NoColor(vec![])
+ } else {
+ ColoredTerminal::Colored(WindowsBuffer::new())
+ }
+ }
- fn map_result<F, G>(
+ /// Clear the give buffer of all search results such that it is reusable
+ /// in another search.
+ pub fn clear(&mut self) {
+ match *self {
+ ColoredTerminal::Colored(ref mut win) => win.clear(),
+ ColoredTerminal::NoColor(ref mut buf) => buf.clear(),
+ }
+ }
+}
+
+#[cfg(windows)]
+impl ColoredTerminal<WinConsole<io::Stdout>> {
+ /// Create a new output buffer.
+ ///
+ /// When color is true, the buffer will attempt to support coloring.
+ pub fn new_stdout(color: bool) -> Self {
+ if !color {
+ return ColoredTerminal::NoColor(io::stdout());
+ }
+ match WinConsole::new(io::stdout()) {
+ Ok(win) => ColoredTerminal::Colored(win),
+ Err(_) => ColoredTerminal::NoColor(io::stdout()),
+ }
+ }
+}
+
+impl<T: Terminal + Send> ColoredTerminal<T> {
+ fn map_result<F>(
&mut self,
mut f: F,
- mut g: G,
) -> term::Result<()>
- where F: FnMut(&mut TerminfoTerminal<Vec<u8>>) -> term::Result<()>,
- G: FnMut(&mut WindowsBuffer) -> term::Result<()> {
+ where F: FnMut(&mut T) -> term::Result<()> {
match *self {
- OutBuffer::Colored(ref mut w) => f(w),
- OutBuffer::Windows(ref mut w) => g(w),
- OutBuffer::NoColor(_) => Err(term::Error::NotSupported),
+ ColoredTerminal::Colored(ref mut w) => f(w),
+ ColoredTerminal::NoColor(_) => Err(term::Error::NotSupported),
}
}
- fn map_bool<F, G>(
+ fn map_bool<F>(
&self,
mut f: F,
- mut g: G,
) -> bool
- where F: FnMut(&TerminfoTerminal<Vec<u8>>) -> bool,
- G: FnMut(&WindowsBuffer) -> bool {
+ where F: FnMut(&T) -> bool {
match *self {
- OutBuffer::Colored(ref w) => f(w),
- OutBuffer::Windows(ref w) => g(w),
- OutBuffer::NoColor(_) => false,
+ ColoredTerminal::Colored(ref w) => f(w),
+ ColoredTerminal::NoColor(_) => false,
}
}
}
-impl io::Write for OutBuffer {
+impl<T: Terminal + Send> io::Write for ColoredTerminal<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match *self {
- OutBuffer::Colored(ref mut w) => w.write(buf),
- OutBuffer::Windows(ref mut w) => w.write(buf),
- OutBuffer::NoColor(ref mut w) => w.write(buf),
+ ColoredTerminal::Colored(ref mut w) => w.write(buf),
+ ColoredTerminal::NoColor(ref mut w) => w.write(buf),
}
}
@@ -225,259 +252,67 @@ impl io::Write for OutBuffer {
}
}
-impl term::Terminal for OutBuffer {
- type Output = Vec<u8>;
+impl<T: Terminal + Send> term::Terminal for ColoredTerminal<T> {
+ type Output = T::Output;
fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
- self.map_result(|w| w.fg(fg), |w| w.fg(fg))
+ self.map_result(|w| w.fg(fg))
}
fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
- self.map_result(|w| w.bg(bg), |w| w.bg(bg))
+ self.map_result(|w| w.bg(bg))
}
fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
- self.map_result(|w| w.attr(attr), |w| w.attr(attr))
+ self.map_result(|w| w.attr(attr))
}
fn supports_attr(&self, attr: term::Attr) -> bool {
- self.map_bool(|w| w.supports_attr(attr), |w| w.supports_attr(attr))
+ self.map_bool(|w| w.supports_attr(attr))
}
fn reset(&mut self) -> term::Result<()> {
- self.map_result(|w| w.reset(), |w| w.reset())
+ self.map_result(|w| w.reset())
}
fn supports_reset(&self) -> bool {
- self.map_bool(|w| w.supports_reset(), |w| w.supports_reset())
+ self.map_bool(|w| w.supports_reset())
}
fn supports_color(&self) -> bool {
- self.map_bool(|w| w.supports_color(), |w| w.supports_color())
+ self.map_bool(|w| w.supports_color())
}
fn cursor_up(&mut self) -> term::Result<()> {
- self.map_result(|w| w.cursor_up(), |w| w.cursor_up())
+ self.map_result(|w| w.cursor_up())
}
fn delete_line(&mut self) -> term::Result<()> {
- self.map_result(|w| w.delete_line(), |w| w.delete_line())
+ self.map_result(|w| w.delete_line())
}
fn carriage_return(&mut self) -> term::Result<()> {
- self.map_result(|w| w.carriage_return(), |w| w.carriage_return())
+ self.map_result(|w| w.carriage_return())
}
- fn get_ref(&self) -> &Vec<u8> {
+ fn get_ref(&self) -> &Self::Output {
match *self {
- OutBuffer::Colored(ref w) => w.get_ref(),
- OutBuffer::Windows(ref w) => w.get_ref(),
- OutBuffer::NoColor(ref w) => w,
+ ColoredTerminal::Colored(ref w) => w.get_ref(),
+ ColoredTerminal::NoColor(ref w) => w,
}
}
- fn get_mut(&mut self) -> &mut Vec<u8> {
+ fn get_mut(&mut self) -> &mut Self::Output {
match *self {
- OutBuffer::Colored(ref mut w) => w.get_mut(),
- OutBuffer::Windows(ref mut w) => w.get_mut(),
- OutBuffer::NoColor(ref mut w) => w,
+ ColoredTerminal::Colored(ref mut w) => w.get_mut(),
+ ColoredTerminal::NoColor(ref mut w) => w,
}
}
- fn into_inner(self) -> Vec<u8> {
+ fn into_inner(self) -> Self::Output {
match self {
- OutBuffer::Colored(w) => w.into_inner(),
- OutBuffer::Windows(w) => w.into_inner(),
- OutBuffer::NoColor(w) => w,
- }
- }
-}
-
-impl WindowsBuffer {
- fn push(&mut self, opt: WindowsOption) {
- let pos = self.pos;
- self.colors.push(WindowsColor { pos: pos, opt: opt });
- }
-}
-
-impl WindowsBuffer {
- /// Print the contents to the given terminal.
- pub fn print_stdout(&self, tt: &mut StdoutTerminal) {
- if !tt.supports_color() {
- let _ = tt.write_all(&self.buf);
- let _ = tt.flush();
- return;
- }
- let mut last = 0;
- for col in &self.colors {
- let _ = tt.write_all(&self.buf[last..col.pos]);
- match col.opt {
- WindowsOption::Foreground(c) => {
- let _ = tt.fg(c);
- }
- WindowsOption::Background(c) => {
- let _ = tt.bg(c);
- }
- WindowsOption::Reset => {
- let _ = tt.reset();
- }
- }
- last = col.pos;
- }
- let _ = tt.write_all(&self.buf[last..]);
- let _ = tt.flush();
- }
-}
-
-impl io::Write for WindowsBuffer {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- let n = try!(self.buf.write(buf));
- self.pos += n;
- Ok(n)
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl term::Terminal for WindowsBuffer {
- type Output = Vec<u8>;
-
- fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
- self.push(WindowsOption::Foreground(fg));
- Ok(())
- }
-
- fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
- self.push(WindowsOption::Background(bg));
- Ok(())
- }
-
- fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
- Err(term::Error::NotSupported)
- }
-
- fn supports_attr(&self, attr: term::Attr) -> bool {
- false
- }
-
- fn reset(&mut self) -> term::Result<()> {
- self.push(WindowsOption::Reset);
- Ok(())
- }
-
- fn supports_reset(&self) -> bool {
- true
- }