Merge pull request #635 from matthiasbeyer/bin/clap
Use clap in bin/imag binary.
This commit is contained in:
commit
198170cf57
2 changed files with 123 additions and 80 deletions
|
@ -7,4 +7,12 @@ authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
version = "2.0"
|
version = "2.0"
|
||||||
walkdir = "0.1.5"
|
walkdir = "0.1.5"
|
||||||
crossbeam = "0.2.9"
|
crossbeam = "0.2.9"
|
||||||
|
clap = "2.*"
|
||||||
|
log = "0.3"
|
||||||
|
|
||||||
|
[dependencies.libimagrt]
|
||||||
|
path = "../libimagrt"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
|
195
bin/src/main.rs
195
bin/src/main.rs
|
@ -1,7 +1,12 @@
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
|
extern crate clap;
|
||||||
#[macro_use] extern crate version;
|
#[macro_use] extern crate version;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
extern crate walkdir;
|
extern crate walkdir;
|
||||||
|
|
||||||
|
extern crate libimagrt;
|
||||||
|
extern crate libimagerror;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
@ -10,11 +15,15 @@ use std::io::ErrorKind;
|
||||||
|
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
use crossbeam::*;
|
use crossbeam::*;
|
||||||
|
use clap::{Arg, AppSettings, SubCommand};
|
||||||
|
|
||||||
const DBG_FLAG: &'static str = "--debug";
|
use libimagrt::runtime::Runtime;
|
||||||
|
use libimagerror::trace::trace_error;
|
||||||
|
|
||||||
fn help(cmds: Vec<String>) {
|
/// Returns the helptext, putting the Strings in cmds as possible
|
||||||
println!(r#"
|
/// subcommands into it
|
||||||
|
fn help_text(cmds: Vec<String>) -> String {
|
||||||
|
let text = format!(r#"
|
||||||
|
|
||||||
_
|
_
|
||||||
(_)_ __ ___ __ _ __ _
|
(_)_ __ ___ __ _ __ _
|
||||||
|
@ -33,13 +42,8 @@ fn help(cmds: Vec<String>) {
|
||||||
modules can be used independently.
|
modules can be used independently.
|
||||||
|
|
||||||
Available commands:
|
Available commands:
|
||||||
"#);
|
|
||||||
|
|
||||||
for cmd in cmds.iter() {
|
{imagbins}
|
||||||
println!("\t{}", cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(r#"
|
|
||||||
|
|
||||||
Call a command with 'imag <command> <args>'
|
Call a command with 'imag <command> <args>'
|
||||||
Each command can be called with "--help" to get the respective helptext.
|
Each command can be called with "--help" to get the respective helptext.
|
||||||
|
@ -49,9 +53,16 @@ fn help(cmds: Vec<String>) {
|
||||||
|
|
||||||
imag is free software. It is released under the terms of LGPLv2.1
|
imag is free software. It is released under the terms of LGPLv2.1
|
||||||
|
|
||||||
(c) 2016 Matthias Beyer and contributors"#);
|
(c) 2016 Matthias Beyer and contributors"#, imagbins = cmds.into_iter()
|
||||||
|
.map(|cmd| format!("\t{}\n", cmd))
|
||||||
|
.fold(String::new(), |s, c| {
|
||||||
|
let s = s + c.as_str();
|
||||||
|
s
|
||||||
|
}));
|
||||||
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the list of imag-* executables found in $PATH
|
||||||
fn get_commands() -> Vec<String> {
|
fn get_commands() -> Vec<String> {
|
||||||
let path = env::var("PATH");
|
let path = env::var("PATH");
|
||||||
if path.is_err() {
|
if path.is_err() {
|
||||||
|
@ -80,7 +91,7 @@ fn get_commands() -> Vec<String> {
|
||||||
.filter_map(|path| {
|
.filter_map(|path| {
|
||||||
path.file_name()
|
path.file_name()
|
||||||
.to_str()
|
.to_str()
|
||||||
.and_then(|s| s.splitn(2, "-").nth(1).map(|s| format!("imag {}", s)))
|
.and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
|
@ -97,78 +108,94 @@ fn get_commands() -> Vec<String> {
|
||||||
execs
|
execs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_command() -> Option<String> {
|
|
||||||
env::args().skip(1).filter(|x| !x.starts_with("-")).next()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_flag() -> Option<String> {
|
|
||||||
env::args().skip(1).filter(|x| x.starts_with("-")).next()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_debug_flag<T: AsRef<str>>(ref s: &T) -> bool {
|
|
||||||
s.as_ref() == DBG_FLAG
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_args(command: &str) -> Vec<String> {
|
|
||||||
env::args()
|
|
||||||
.skip(1)
|
|
||||||
.position(|e| e == command)
|
|
||||||
.map(|pos| env::args().skip(pos + 2).collect::<Vec<String>>())
|
|
||||||
.unwrap_or(vec![])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let commands = get_commands();
|
// Initialize the Runtime and build the CLI
|
||||||
let mut args = env::args();
|
let appname = "imag";
|
||||||
let _ = args.next();
|
let version = &version!();
|
||||||
let first_arg = match find_command() {
|
let about = "imag - the PIM suite for the commandline";
|
||||||
Some(s) => s,
|
let commands = get_commands();
|
||||||
None => match find_flag() {
|
let helptext = help_text(commands.clone());
|
||||||
Some(s) => s,
|
let app = Runtime::get_default_cli_builder(appname, version, about)
|
||||||
None => {
|
.settings(&[AppSettings::AllowExternalSubcommands, AppSettings::ArgRequiredElseHelp])
|
||||||
help(commands);
|
.arg(Arg::with_name("version")
|
||||||
exit(0);
|
.long("version")
|
||||||
},
|
.takes_value(false)
|
||||||
},
|
.required(false)
|
||||||
};
|
.multiple(false)
|
||||||
let is_debug = env::args().skip(1).find(is_debug_flag).is_some();
|
.help("Get the version of imag"))
|
||||||
|
.arg(Arg::with_name("versions")
|
||||||
|
.long("versions")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.multiple(false)
|
||||||
|
.help("Get the versions of the imag commands"))
|
||||||
|
.subcommand(SubCommand::with_name("help").help("Show help"))
|
||||||
|
.help(helptext.as_str());
|
||||||
|
let rt = Runtime::new(app)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
println!("Runtime couldn't be setup. Exiting");
|
||||||
|
trace_error(&e);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
let matches = rt.cli();
|
||||||
|
|
||||||
match &first_arg[..] {
|
debug!("matches: {:?}", matches);
|
||||||
"--help" | "-h" => {
|
|
||||||
help(commands);
|
|
||||||
exit(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
"--version" => println!("imag {}", &version!()[..]),
|
// Begin checking for arguments
|
||||||
|
|
||||||
"--versions" => {
|
if matches.is_present("version") {
|
||||||
let mut result = vec![];
|
debug!("Showing version");
|
||||||
for command in commands.iter() {
|
println!("imag {}", &version!()[..]);
|
||||||
result.push(crossbeam::scope(|scope| {
|
exit(0);
|
||||||
scope.spawn(|| {
|
}
|
||||||
let v = Command::new(command).arg("--version").output();
|
|
||||||
match v {
|
if matches.is_present("versions") {
|
||||||
Ok(v) => match String::from_utf8(v.stdout) {
|
debug!("Showing versions");
|
||||||
Ok(s) => format!("{} -> {}", command, s),
|
let mut result = vec![];
|
||||||
Err(e) => format!("Failed calling {} -> {:?}", command, e),
|
for command in commands.iter() {
|
||||||
},
|
result.push(crossbeam::scope(|scope| {
|
||||||
|
scope.spawn(|| {
|
||||||
|
let v = Command::new(format!("imag-{}",command)).arg("--version").output();
|
||||||
|
match v {
|
||||||
|
Ok(v) => match String::from_utf8(v.stdout) {
|
||||||
|
Ok(s) => format!("{:10} -> {}", command, s),
|
||||||
Err(e) => format!("Failed calling {} -> {:?}", command, e),
|
Err(e) => format!("Failed calling {} -> {:?}", command, e),
|
||||||
}
|
},
|
||||||
})
|
Err(e) => format!("Failed calling {} -> {:?}", command, e),
|
||||||
}))
|
}
|
||||||
}
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
for versionstring in result.into_iter().map(|handle| handle.join()) {
|
for versionstring in result.into_iter().map(|handle| handle.join()) {
|
||||||
println!("{}", versionstring);
|
// The amount of newlines may differ depending on the subprocess
|
||||||
}
|
println!("{}", versionstring.trim());
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s => {
|
// Matches any subcommand given
|
||||||
let mut subcommand_args = find_args(s);
|
match matches.subcommand() {
|
||||||
if is_debug && subcommand_args.iter().find(is_debug_flag).is_none() {
|
(subcommand, Some(scmd)) => {
|
||||||
subcommand_args.insert(0, String::from(DBG_FLAG));
|
// Get all given arguments and further subcommands to pass to
|
||||||
|
// the imag-<> binary
|
||||||
|
// Providing no arguments is OK, and is therefore ignored here
|
||||||
|
let subcommand_args : Vec<&str> = match scmd.values_of("") {
|
||||||
|
Some(values) => values.collect(),
|
||||||
|
None => Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typos happen, so check if the given subcommand is one found in $PATH
|
||||||
|
if !commands.contains(&String::from(subcommand)) {
|
||||||
|
println!("No such command: 'imag-{}'", subcommand);
|
||||||
|
println!("See 'imag --help' for available subcommands");
|
||||||
|
exit(2);
|
||||||
}
|
}
|
||||||
match Command::new(format!("imag-{}", s))
|
|
||||||
|
debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);
|
||||||
|
|
||||||
|
// Create a Command, and pass it the gathered arguments
|
||||||
|
match Command::new(format!("imag-{}", subcommand))
|
||||||
.stdin(Stdio::inherit())
|
.stdin(Stdio::inherit())
|
||||||
.stdout(Stdio::inherit())
|
.stdout(Stdio::inherit())
|
||||||
.stderr(Stdio::inherit())
|
.stderr(Stdio::inherit())
|
||||||
|
@ -178,29 +205,37 @@ fn main() {
|
||||||
{
|
{
|
||||||
Ok(exit_status) => {
|
Ok(exit_status) => {
|
||||||
if !exit_status.success() {
|
if !exit_status.success() {
|
||||||
println!("{} exited with non-zero exit code", s);
|
debug!("{} exited with non-zero exit code: {:?}", subcommand, exit_status);
|
||||||
exit(exit_status.code().unwrap_or(42));
|
println!("{} exited with non-zero exit code", subcommand);
|
||||||
|
exit(exit_status.code().unwrap_or(1));
|
||||||
}
|
}
|
||||||
|
debug!("Successful exit!");
|
||||||
},
|
},
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
debug!("Error calling the subcommand");
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
ErrorKind::NotFound => {
|
ErrorKind::NotFound => {
|
||||||
println!("No such command: 'imag-{}'", s);
|
// With the check above, this absolutely should not happen.
|
||||||
|
// Keeping it to be safe
|
||||||
|
println!("No such command: 'imag-{}'", subcommand);
|
||||||
|
println!("See 'imag --help' for available subcommands");
|
||||||
exit(2);
|
exit(2);
|
||||||
},
|
},
|
||||||
ErrorKind::PermissionDenied => {
|
ErrorKind::PermissionDenied => {
|
||||||
println!("No permission to execute: 'imag-{}'", s);
|
println!("No permission to execute: 'imag-{}'", subcommand);
|
||||||
exit(1);
|
exit(1);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
println!("Error spawning: {:?}", e);
|
println!("Error spawning: {:?}", e);
|
||||||
exit(1337);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
// Calling for example 'imag --versions' will lead here, as this option does not exit.
|
||||||
|
// There's nothing to do in such a case
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue