summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Sago <ogham@bsago.me>2020-10-10 01:43:42 +0100
committerBenjamin Sago <ogham@bsago.me>2020-10-10 01:43:42 +0100
commit5ca3548bb1ebc61cc4733239a0f5dadfa4da8786 (patch)
treeb93231fd3f27e75640bcc49decc860aad53ca8e9
parentdf81a24dae58b85afc96587a8764aaa52aeb9464 (diff)
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.
-rw-r--r--Cargo.toml6
-rw-r--r--Justfile2
-rw-r--r--Vagrantfile2
-rw-r--r--src/bin/main.rs87
-rw-r--r--src/main.rs (renamed from src/exa.rs)88
5 files changed, 87 insertions, 98 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 2432144..d3862c3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/Justfile b/Justfile
index 953e9df..d892165 100644
--- a/Justfile
+++ b/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
diff --git a/Vagrantfile b/Vagrantfile
index 1da3bc8..e9f2334 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -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
diff --git a/src/bin/main.rs b/src/bin/main.rs
deleted file mode 100644
index 9b36526..0000000
--- a/src/bin/main.rs
+++ /dev/null
@@ -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;
-}
diff --git a/src/exa.rs b/src/main.rs
index c87c200..6806b81 100644
--- a/src/exa.rs
+++ b/src/main.rs
@@ -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;
+}