mirror of
https://github.com/Llewellynvdm/exa.git
synced 2025-02-04 19:48:25 +00:00
Change views to print to a Writer, not stdout
This will mean that we can test exa's output as a whole, without having to rely on process or IO or anything like that.
This commit is contained in:
parent
476406f43b
commit
a02f37cb45
59
src/main.rs
59
src/main.rs
@ -20,6 +20,7 @@ extern crate zoneinfo_compiled;
|
|||||||
#[macro_use] extern crate lazy_static;
|
#[macro_use] extern crate lazy_static;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::io::{Write, stdout, Result as IOResult};
|
||||||
use std::path::{Component, Path};
|
use std::path::{Component, Path};
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
@ -33,12 +34,20 @@ mod output;
|
|||||||
mod term;
|
mod term;
|
||||||
|
|
||||||
|
|
||||||
struct Exa {
|
/// The main program wrapper.
|
||||||
|
struct Exa<'w, W: Write + 'w> {
|
||||||
|
|
||||||
|
/// List of command-line options, having been successfully parsed.
|
||||||
options: Options,
|
options: Options,
|
||||||
|
|
||||||
|
/// The output handle that we write to. When running the program normally,
|
||||||
|
/// this will be `std::io::Stdout`, but it can accept any struct that’s
|
||||||
|
/// `Write` so we can write into, say, a vector for testing.
|
||||||
|
writer: &'w mut W,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Exa {
|
impl<'w, W: Write + 'w> Exa<'w, W> {
|
||||||
fn run(&mut self, mut args_file_names: Vec<String>) {
|
fn run(&mut self, mut args_file_names: Vec<String>) -> IOResult<()> {
|
||||||
let mut files = Vec::new();
|
let mut files = Vec::new();
|
||||||
let mut dirs = Vec::new();
|
let mut dirs = Vec::new();
|
||||||
|
|
||||||
@ -49,13 +58,13 @@ impl Exa {
|
|||||||
for file_name in args_file_names.iter() {
|
for file_name in args_file_names.iter() {
|
||||||
match File::from_path(Path::new(&file_name), None) {
|
match File::from_path(Path::new(&file_name), None) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}: {}", file_name, e);
|
try!(writeln!(self.writer, "{}: {}", file_name, e));
|
||||||
},
|
},
|
||||||
Ok(f) => {
|
Ok(f) => {
|
||||||
if f.is_directory() && !self.options.dir_action.treat_dirs_as_files() {
|
if f.is_directory() && !self.options.dir_action.treat_dirs_as_files() {
|
||||||
match f.to_dir(self.options.should_scan_for_git()) {
|
match f.to_dir(self.options.should_scan_for_git()) {
|
||||||
Ok(d) => dirs.push(d),
|
Ok(d) => dirs.push(d),
|
||||||
Err(e) => println!("{}: {}", file_name, e),
|
Err(e) => try!(writeln!(self.writer, "{}: {}", file_name, e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -69,13 +78,13 @@ impl Exa {
|
|||||||
let is_only_dir = dirs.len() == 1 && no_files;
|
let is_only_dir = dirs.len() == 1 && no_files;
|
||||||
|
|
||||||
if !no_files {
|
if !no_files {
|
||||||
self.print_files(None, files);
|
try!(self.print_files(None, files));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.print_dirs(dirs, no_files, is_only_dir);
|
self.print_dirs(dirs, no_files, is_only_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_dirs(&self, dir_files: Vec<Dir>, mut first: bool, is_only_dir: bool) {
|
fn print_dirs(&mut self, dir_files: Vec<Dir>, mut first: bool, is_only_dir: bool) -> IOResult<()> {
|
||||||
for dir in dir_files {
|
for dir in dir_files {
|
||||||
|
|
||||||
// Put a gap between directories, or between the list of files and the
|
// Put a gap between directories, or between the list of files and the
|
||||||
@ -84,18 +93,18 @@ impl Exa {
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print!("\n");
|
try!(write!(self.writer, "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_only_dir {
|
if !is_only_dir {
|
||||||
println!("{}:", dir.path.display());
|
try!(writeln!(self.writer, "{}:", dir.path.display()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut children = Vec::new();
|
let mut children = Vec::new();
|
||||||
for file in dir.files() {
|
for file in dir.files() {
|
||||||
match file {
|
match file {
|
||||||
Ok(file) => children.push(file),
|
Ok(file) => children.push(file),
|
||||||
Err((path, e)) => println!("[{}: {}]", path.display(), e),
|
Err((path, e)) => try!(writeln!(self.writer, "[{}: {}]", path.display(), e)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,31 +119,32 @@ impl Exa {
|
|||||||
for child_dir in children.iter().filter(|f| f.is_directory()) {
|
for child_dir in children.iter().filter(|f| f.is_directory()) {
|
||||||
match child_dir.to_dir(false) {
|
match child_dir.to_dir(false) {
|
||||||
Ok(d) => child_dirs.push(d),
|
Ok(d) => child_dirs.push(d),
|
||||||
Err(e) => println!("{}: {}", child_dir.path.display(), e),
|
Err(e) => try!(writeln!(self.writer, "{}: {}", child_dir.path.display(), e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.print_files(Some(&dir), children);
|
try!(self.print_files(Some(&dir), children));
|
||||||
|
|
||||||
if !child_dirs.is_empty() {
|
if !child_dirs.is_empty() {
|
||||||
self.print_dirs(child_dirs, false, false);
|
try!(self.print_dirs(child_dirs, false, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.print_files(Some(&dir), children);
|
try!(self.print_files(Some(&dir), children));
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_files(&self, dir: Option<&Dir>, files: Vec<File>) {
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_files(&mut self, dir: Option<&Dir>, files: Vec<File>) -> IOResult<()> {
|
||||||
match self.options.view {
|
match self.options.view {
|
||||||
View::Grid(g) => g.view(&files),
|
View::Grid(g) => g.view(&files, self.writer),
|
||||||
View::Details(d) => d.view(dir, files),
|
View::Details(d) => d.view(dir, files, self.writer),
|
||||||
View::GridDetails(gd) => gd.view(dir, files),
|
View::GridDetails(gd) => gd.view(dir, files, self.writer),
|
||||||
View::Lines(l) => l.view(files),
|
View::Lines(l) => l.view(files, self.writer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,8 +155,9 @@ fn main() {
|
|||||||
|
|
||||||
match Options::getopts(&args) {
|
match Options::getopts(&args) {
|
||||||
Ok((options, paths)) => {
|
Ok((options, paths)) => {
|
||||||
let mut exa = Exa { options: options };
|
let mut stdout = stdout();
|
||||||
exa.run(paths);
|
let mut exa = Exa { options: options, writer: &mut stdout };
|
||||||
|
exa.run(paths).expect("IO error");
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}", e);
|
println!("{}", e);
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
//! are used in place of the filename.
|
//! are used in place of the filename.
|
||||||
|
|
||||||
|
|
||||||
use std::io;
|
use std::io::{Write, Error as IOError, Result as IOResult};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
@ -186,7 +186,7 @@ impl Details {
|
|||||||
|
|
||||||
/// Print the details of the given vector of files -- all of which will
|
/// Print the details of the given vector of files -- all of which will
|
||||||
/// have been read from the given directory, if present -- to stdout.
|
/// have been read from the given directory, if present -- to stdout.
|
||||||
pub fn view(&self, dir: Option<&Dir>, files: Vec<File>) {
|
pub fn view<W: Write>(&self, dir: Option<&Dir>, files: Vec<File>, w: &mut W) -> IOResult<()> {
|
||||||
|
|
||||||
// First, transform the Columns object into a vector of columns for
|
// First, transform the Columns object into a vector of columns for
|
||||||
// the current directory.
|
// the current directory.
|
||||||
@ -212,8 +212,10 @@ impl Details {
|
|||||||
// Then add files to the table and print it out.
|
// Then add files to the table and print it out.
|
||||||
self.add_files_to_table(&mut table, files, 0);
|
self.add_files_to_table(&mut table, files, 0);
|
||||||
for cell in table.print_table() {
|
for cell in table.print_table() {
|
||||||
println!("{}", cell.strings());
|
try!(writeln!(w, "{}", cell.strings()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds files to the table, possibly recursively. This is easily
|
/// Adds files to the table, possibly recursively. This is easily
|
||||||
@ -230,7 +232,7 @@ impl Details {
|
|||||||
struct Egg<'a> {
|
struct Egg<'a> {
|
||||||
cells: Vec<TextCell>,
|
cells: Vec<TextCell>,
|
||||||
xattrs: Vec<Attribute>,
|
xattrs: Vec<Attribute>,
|
||||||
errors: Vec<(io::Error, Option<PathBuf>)>,
|
errors: Vec<(IOError, Option<PathBuf>)>,
|
||||||
dir: Option<Dir>,
|
dir: Option<Dir>,
|
||||||
file: File<'a>,
|
file: File<'a>,
|
||||||
}
|
}
|
||||||
@ -417,7 +419,7 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> {
|
|||||||
self.rows.push(row);
|
self.rows.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_error(&mut self, error: &io::Error, depth: usize, last: bool, path: Option<PathBuf>) {
|
fn add_error(&mut self, error: &IOError, depth: usize, last: bool, path: Option<PathBuf>) {
|
||||||
let error_message = match path {
|
let error_message = match path {
|
||||||
Some(path) => format!("<{}: {}>", path.display(), error),
|
Some(path) => format!("<{}: {}>", path.display(), error),
|
||||||
None => format!("<{}>", error),
|
None => format!("<{}>", error),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::io::{Write, Result as IOResult};
|
||||||
|
|
||||||
use term_grid as grid;
|
use term_grid as grid;
|
||||||
|
|
||||||
use fs::File;
|
use fs::File;
|
||||||
@ -14,7 +16,7 @@ pub struct Grid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
pub fn view(&self, files: &[File]) {
|
pub fn view<W: Write>(&self, files: &[File], w: &mut W) -> IOResult<()> {
|
||||||
let direction = if self.across { grid::Direction::LeftToRight }
|
let direction = if self.across { grid::Direction::LeftToRight }
|
||||||
else { grid::Direction::TopToBottom };
|
else { grid::Direction::TopToBottom };
|
||||||
|
|
||||||
@ -41,13 +43,14 @@ impl Grid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(display) = grid.fit_into_width(self.console_width) {
|
if let Some(display) = grid.fit_into_width(self.console_width) {
|
||||||
print!("{}", display);
|
write!(w, "{}", display)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// File names too long for a grid - drop down to just listing them!
|
// File names too long for a grid - drop down to just listing them!
|
||||||
for file in files.iter() {
|
for file in files.iter() {
|
||||||
println!("{}", filename(file, &self.colours, false).strings());
|
try!(writeln!(w, "{}", filename(file, &self.colours, false).strings()));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::io::{Write, Result as IOResult};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ansi_term::ANSIStrings;
|
use ansi_term::ANSIStrings;
|
||||||
@ -26,7 +27,8 @@ fn file_has_xattrs(file: &File) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GridDetails {
|
impl GridDetails {
|
||||||
pub fn view(&self, dir: Option<&Dir>, files: Vec<File>) {
|
pub fn view<W>(&self, dir: Option<&Dir>, files: Vec<File>, w: &mut W) -> IOResult<()>
|
||||||
|
where W: Write {
|
||||||
let columns_for_dir = match self.details.columns {
|
let columns_for_dir = match self.details.columns {
|
||||||
Some(cols) => cols.for_dir(dir),
|
Some(cols) => cols.for_dir(dir),
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
@ -63,10 +65,11 @@ impl GridDetails {
|
|||||||
last_working_table = grid;
|
last_working_table = grid;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print!("{}", last_working_table.fit_into_columns(column_count - 1));
|
return write!(w, "{}", last_working_table.fit_into_columns(column_count - 1));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_table<'a>(&'a self, env: Arc<Environment<UsersCache>>, columns_for_dir: &'a [Column]) -> Table<UsersCache> {
|
fn make_table<'a>(&'a self, env: Arc<Environment<UsersCache>>, columns_for_dir: &'a [Column]) -> Table<UsersCache> {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::io::{Write, Result as IOResult};
|
||||||
|
|
||||||
use ansi_term::ANSIStrings;
|
use ansi_term::ANSIStrings;
|
||||||
|
|
||||||
use fs::File;
|
use fs::File;
|
||||||
@ -13,9 +15,10 @@ pub struct Lines {
|
|||||||
|
|
||||||
/// The lines view literally just displays each file, line-by-line.
|
/// The lines view literally just displays each file, line-by-line.
|
||||||
impl Lines {
|
impl Lines {
|
||||||
pub fn view(&self, files: Vec<File>) {
|
pub fn view<W: Write>(&self, files: Vec<File>, w: &mut W) -> IOResult<()> {
|
||||||
for file in files {
|
for file in files {
|
||||||
println!("{}", ANSIStrings(&filename(&file, &self.colours, true)));
|
try!(writeln!(w, "{}", ANSIStrings(&filename(&file, &self.colours, true))));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user