mirror of
https://github.com/Llewellynvdm/exa.git
synced 2025-01-13 17:02:58 +00:00
Inline the library into the binary
This commit removes the library portion of exa. Cargo now only builds a binary. The original intent was for exa to have its own internal library, and have the binary just call the library. This is usually done for code cleanliness reasons: it separates the code that implements the purpose of the program (the "plumbing") from the code that the user interacts with (the "porcelain"), ensuring a well-defined interface between the two. However, in exa, this split was in completely the wrong place. Logging was handled in the binary, but option parsing was handled in the library. The library could theoretically print to any Writer ("for testing", it said), but it's far easier to run integration tests by executing the binary than to change the code to handle unit tests, so this abstraction isn't gaining us anything. I've also had several people ask me if exa should be packaged for Linux distributions as a library, or just a binary. Clearly, this is confusing! In several of my other Rust projects, I've done this better, with the command-line option parsing and log printing done on the binary side. It also turns out that you don't need to have a [lib] section in the Cargo.toml, so that's gone too.
This commit is contained in:
parent
df81a24dae
commit
5ca3548bb1
@ -19,14 +19,8 @@ exclude = ["/devtools/*", "/Justfile", "/Vagrantfile", "/screenshots.png"]
|
||||
|
||||
[[bin]]
|
||||
name = "exa"
|
||||
path = "src/bin/main.rs"
|
||||
doc = false
|
||||
|
||||
|
||||
[lib]
|
||||
name = "exa"
|
||||
path = "src/exa.rs"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.12.0"
|
||||
datetime = "0.5"
|
||||
|
2
Justfile
2
Justfile
@ -25,7 +25,7 @@ build-features:
|
||||
|
||||
# runs unit tests with every combination of feature flags
|
||||
test-features:
|
||||
cargo hack test --feature-powerset --lib -- --quiet
|
||||
cargo hack test --feature-powerset -- --quiet
|
||||
|
||||
|
||||
# prints versions of the necessary build tools
|
||||
|
2
Vagrantfile
vendored
2
Vagrantfile
vendored
@ -92,7 +92,7 @@ Vagrant.configure(2) do |config|
|
||||
echo -e "#!/bin/sh\ncargo build --manifest-path /vagrant/Cargo.toml \\$@" > /usr/bin/build-exa
|
||||
ln -sf /usr/bin/build-exa /usr/bin/b
|
||||
|
||||
echo -e "#!/bin/sh\ncargo test --manifest-path /vagrant/Cargo.toml --lib \\$@ -- --quiet" > /usr/bin/test-exa
|
||||
echo -e "#!/bin/sh\ncargo test --manifest-path /vagrant/Cargo.toml \\$@ -- --quiet" > /usr/bin/test-exa
|
||||
ln -sf /usr/bin/test-exa /usr/bin/t
|
||||
|
||||
echo -e "#!/bin/sh\n/vagrant/xtests/run.sh" > /usr/bin/run-xtests
|
||||
|
@ -1,87 +0,0 @@
|
||||
extern crate exa;
|
||||
use exa::Exa;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::env::{args_os, var_os};
|
||||
use std::io::{stdout, stderr, Write, ErrorKind};
|
||||
use std::process::exit;
|
||||
|
||||
|
||||
fn main() {
|
||||
configure_logger();
|
||||
|
||||
let args: Vec<OsString> = args_os().skip(1).collect();
|
||||
match Exa::from_args(args.iter(), &mut stdout()) {
|
||||
Ok(mut exa) => {
|
||||
match exa.run() {
|
||||
Ok(exit_status) => exit(exit_status),
|
||||
Err(e) => {
|
||||
match e.kind() {
|
||||
ErrorKind::BrokenPipe => exit(exits::SUCCESS),
|
||||
_ => {
|
||||
eprintln!("{}", e);
|
||||
exit(exits::RUNTIME_ERROR);
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
Err(ref e) if e.is_error() => {
|
||||
let mut stderr = stderr();
|
||||
writeln!(stderr, "{}", e).unwrap();
|
||||
|
||||
if let Some(s) = e.suggestion() {
|
||||
let _ = writeln!(stderr, "{}", s);
|
||||
}
|
||||
|
||||
exit(exits::OPTIONS_ERROR);
|
||||
},
|
||||
|
||||
Err(ref e) => {
|
||||
println!("{}", e);
|
||||
exit(exits::SUCCESS);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Sets up a global logger if one is asked for.
|
||||
/// The ‘EXA_DEBUG’ environment variable controls whether log messages are
|
||||
/// displayed or not. Currently there are just two settings (on and off).
|
||||
///
|
||||
/// This can’t be done in exa’s own option parsing because that part of it
|
||||
/// logs as well, so by the time execution gets there, the logger needs to
|
||||
/// have already been set up.
|
||||
pub fn configure_logger() {
|
||||
extern crate env_logger;
|
||||
extern crate log;
|
||||
|
||||
let present = match var_os(exa::vars::EXA_DEBUG) {
|
||||
Some(debug) => debug.len() > 0,
|
||||
None => false,
|
||||
};
|
||||
|
||||
let mut logs = env_logger::Builder::new();
|
||||
if present {
|
||||
logs.filter(None, log::LevelFilter::Debug);
|
||||
}
|
||||
else {
|
||||
logs.filter(None, log::LevelFilter::Off);
|
||||
}
|
||||
|
||||
logs.init()
|
||||
}
|
||||
|
||||
|
||||
mod exits {
|
||||
|
||||
/// Exit code for when exa runs OK.
|
||||
pub const SUCCESS: i32 = 0;
|
||||
|
||||
/// Exit code for when there was at least one I/O error during execution.
|
||||
pub const RUNTIME_ERROR: i32 = 1;
|
||||
|
||||
/// Exit code for when the command-line options are invalid.
|
||||
pub const OPTIONS_ERROR: i32 = 3;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
#![warn(trivial_casts, trivial_numeric_casts)]
|
||||
#![warn(unused_results)]
|
||||
|
||||
use std::env::var_os;
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::{stderr, Write, Result as IOResult};
|
||||
use std::io::{stdout, stderr, Write, Result as IOResult, ErrorKind};
|
||||
use std::path::{Component, PathBuf};
|
||||
|
||||
use ansi_term::{ANSIStrings, Style};
|
||||
@ -25,6 +25,75 @@ mod output;
|
||||
mod style;
|
||||
|
||||
|
||||
fn main() {
|
||||
use std::process::exit;
|
||||
|
||||
configure_logger();
|
||||
|
||||
let args: Vec<OsString> = env::args_os().skip(1).collect();
|
||||
match Exa::from_args(args.iter(), &mut stdout()) {
|
||||
Ok(mut exa) => {
|
||||
match exa.run() {
|
||||
Ok(exit_status) => exit(exit_status),
|
||||
Err(e) => {
|
||||
match e.kind() {
|
||||
ErrorKind::BrokenPipe => exit(exits::SUCCESS),
|
||||
_ => {
|
||||
eprintln!("{}", e);
|
||||
exit(exits::RUNTIME_ERROR);
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
Err(ref e) if e.is_error() => {
|
||||
let mut stderr = stderr();
|
||||
writeln!(stderr, "{}", e).unwrap();
|
||||
|
||||
if let Some(s) = e.suggestion() {
|
||||
let _ = writeln!(stderr, "{}", s);
|
||||
}
|
||||
|
||||
exit(exits::OPTIONS_ERROR);
|
||||
},
|
||||
|
||||
Err(ref e) => {
|
||||
println!("{}", e);
|
||||
exit(exits::SUCCESS);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Sets up a global logger if one is asked for.
|
||||
/// The ‘EXA_DEBUG’ environment variable controls whether log messages are
|
||||
/// displayed or not. Currently there are just two settings (on and off).
|
||||
///
|
||||
/// This can’t be done in exa’s own option parsing because that part of it
|
||||
/// logs as well, so by the time execution gets there, the logger needs to
|
||||
/// have already been set up.
|
||||
pub fn configure_logger() {
|
||||
extern crate env_logger;
|
||||
extern crate log;
|
||||
|
||||
let present = match env::var_os(vars::EXA_DEBUG) {
|
||||
Some(debug) => debug.len() > 0,
|
||||
None => false,
|
||||
};
|
||||
|
||||
let mut logs = env_logger::Builder::new();
|
||||
if present {
|
||||
logs.filter(None, log::LevelFilter::Debug);
|
||||
}
|
||||
else {
|
||||
logs.filter(None, log::LevelFilter::Off);
|
||||
}
|
||||
|
||||
logs.init()
|
||||
}
|
||||
|
||||
|
||||
/// The main program wrapper.
|
||||
pub struct Exa<'args, 'w, W: Write + 'w> {
|
||||
|
||||
@ -52,7 +121,7 @@ pub struct Exa<'args, 'w, W: Write + 'w> {
|
||||
struct LiveVars;
|
||||
impl Vars for LiveVars {
|
||||
fn get(&self, name: &'static str) -> Option<OsString> {
|
||||
var_os(name)
|
||||
env::var_os(name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,3 +294,16 @@ impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mod exits {
|
||||
|
||||
/// Exit code for when exa runs OK.
|
||||
pub const SUCCESS: i32 = 0;
|
||||
|
||||
/// Exit code for when there was at least one I/O error during execution.
|
||||
pub const RUNTIME_ERROR: i32 = 1;
|
||||
|
||||
/// Exit code for when the command-line options are invalid.
|
||||
pub const OPTIONS_ERROR: i32 = 3;
|
||||
}
|
Loading…
Reference in New Issue
Block a user