Browse Source

Reorganize argument parsing.

tags/v0.1.0
Thomas Kerber 1 year ago
parent
commit
e2c4d0f8f2
Signed by: Thomas Kerber <tk@drwx.org> GPG Key ID: 8489B911F9ED617B
20 changed files with 309 additions and 252 deletions
  1. 0
    1
      rustfmt.toml
  2. 211
    0
      src/args.rs
  3. 31
    0
      src/cfg.rs
  4. 2
    1
      src/cmd/add_password.rs
  5. 3
    2
      src/cmd/copy.rs
  6. 5
    7
      src/cmd/cp.rs
  7. 2
    1
      src/cmd/create.rs
  8. 2
    1
      src/cmd/daemon.rs
  9. 3
    5
      src/cmd/echo.rs
  10. 3
    2
      src/cmd/import.rs
  11. 5
    7
      src/cmd/insert.rs
  12. 5
    7
      src/cmd/ln.rs
  13. 8
    8
      src/cmd/ls.rs
  14. 5
    7
      src/cmd/mv.rs
  15. 4
    6
      src/cmd/rm.rs
  16. 2
    1
      src/cmd/rm_password.rs
  17. 7
    6
      src/cmd/unlock.rs
  18. 2
    1
      src/cmd/which.rs
  19. 8
    188
      src/main.rs
  20. 1
    1
      src/util.rs

+ 0
- 1
rustfmt.toml View File

@@ -1,4 +1,3 @@
reorder_imported_names = true
reorder_imports = true
format_strings = true
max_width = 79

+ 211
- 0
src/args.rs View File

@@ -0,0 +1,211 @@
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
use std::str::FromStr;

pub fn goblin() -> App<'static, 'static> {
App::new(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!())
.max_term_width(80)
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(daemon())
.subcommand(create())
.subcommand(unlock())
.subcommand(echo())
.subcommand(copy())
.subcommand(insert())
.subcommand(import())
.subcommand(which())
.subcommand(rm())
.subcommand(mv())
.subcommand(cp())
.subcommand(ln())
.subcommand(ls())
.subcommand(add_password())
.subcommand(rm_password())
}

pub fn daemon() -> App<'static, 'static> {
let abt = "Starts the goblin daemon. This is typically done automatically\
when it is required";
subcommand("daemon").about(abt).display_order(1)
}

pub fn create() -> App<'static, 'static> {
let abt = "Creates a new vault. Will refuse to overwrite an existing one";
subcommand("create").about(abt).display_order(1)
}

pub fn unlock() -> App<'static, 'static> {
let exphelp = "How long to unlock for. Accepts positive integers with the \
suffixes 's', 'm', and 'h' for seconds, minutes, and hours \
respectively, or 'perm'/'permanent' for a permanent unlock. If no \
value is specified, a default of two minutes is used";
subcommand("unlock")
.about("Unlocks (parts of) a vault")
.display_order(1)
.group(
ArgGroup::with_name("scope")
.args(&["path", "prefix", "global"])
.required(true),
)
.arg(Arg::from_usage("-p --path [PATH] 'The path to unlock'"))
.arg(Arg::from_usage(
"-P --prefix [PREFIX] 'The prefix to unlock'",
))
.arg(Arg::from_usage("-G --global 'Unlock globally'"))
.arg(Arg::from_usage("-w --write 'Unlock for writing'"))
.arg(Arg::with_name("EXPIRES").help(exphelp))
// TODO: add validator for EXPIRES
}

pub fn echo() -> App<'static, 'static> {
subcommand("echo")
.about("Echos PATHISH to STDOUT")
.display_order(0)
.arg(noninteractive())
.arg(exact())
.arg(Arg::from_usage("<PATHISH> 'The path to retrieve'"))
}

pub fn copy() -> App<'static, 'static> {
subcommand("copy")
.about("Copies PATHISH to the system clipboard")
.display_order(0)
.arg(exact())
.arg(Arg::from_usage("<PATHISH> 'The path to copy'"))
}

pub fn insert() -> App<'static, 'static> {
subcommand("insert")
.about("Inserts a value to PATH")
.display_order(0)
.arg(noninteractive())
.arg(nocommit())
.arg(Arg::from_usage("<PATH> 'The path to insert into'"))
.arg(Arg::from_usage(
"[VALUE] 'The value to insert. If no value is supplied, one is \
read from STDIN'",
))
}

pub fn import() -> App<'static, 'static> {
subcommand("import").about(
"Imports a JSON map representing the keyring, piped to STDIN.",
)
}

pub fn which() -> App<'static, 'static> {
subcommand("which").about("Prints the full location of the vault")
}

pub fn rm() -> App<'static, 'static> {
subcommand("rm")
.about("Removes PATH from the keyring")
.display_order(0)
.arg(noninteractive())
.arg(nocommit())
.arg(Arg::from_usage("<PATH> 'The path to remove'"))
}

pub fn mv() -> App<'static, 'static> {
subcommand("mv")
.about("Moves a value from SOURCE to DEST")
.display_order(0)
.arg(noninteractive())
.arg(nocommit())
.arg(Arg::from_usage("<SOURCE> 'The path to move from'"))
.arg(Arg::from_usage("<DEST> 'The path to move to'"))
}

pub fn cp() -> App<'static, 'static> {
subcommand("cp")
.about("Copies a value from SOURCE to DEST")
.display_order(0)
.arg(noninteractive())
.arg(nocommit())
.arg(Arg::from_usage("<SOURCE> 'The path to copy from'"))
.arg(Arg::from_usage("<DEST> 'The path to copy to'"))
}

pub fn ln() -> App<'static, 'static> {
subcommand("ln")
.about("Links LINK_PATH to TARGET")
.display_order(0)
.arg(noninteractive())
.arg(nocommit())
.arg(Arg::from_usage("<TARGET> 'The link target'"))
.arg(Arg::from_usage("<LINK_PATH> 'The link's path'"))
}

pub fn ls() -> App<'static, 'static> {
subcommand("ls")
.about("Lists (part of) the keyring")
.display_order(0)
.arg(noninteractive())
.arg(Arg::from_usage(
"-a --all 'Show all paths, including hidden ones'",
))
.group(ArgGroup::with_name("output").args(&["depth", "raw"]))
.arg(
Arg::from_usage("--depth [DEPTH] 'The maximum depth to display'")
.validator(validate_usize),
)
.arg(Arg::from_usage(
"--raw 'Outputs raw paths instead of a tree'",
))
.arg(Arg::from_usage("[PREFIX] 'The path to list'"))
}

pub fn add_password() -> App<'static, 'static> {
subcommand("add-password")
.about("Adds a password used to unlock the vault")
.display_order(1)
}

pub fn rm_password() -> App<'static, 'static> {
subcommand("add-password")
.about("Removes a password used to unlock the vault")
.display_order(1)
}

pub fn validate_usize(s: String) -> Result<(), String> {
match usize::from_str(&s) {
Ok(_) => Ok(()),
Err(_) => Err("expected unsigned integer".into()),
}
}

pub fn nocommit() -> Arg<'static, 'static> {
Arg::from_usage(
"[nocommit] -c --no-commit 'Runs without input from the user'",
)
}

pub fn noninteractive() -> Arg<'static, 'static> {
Arg::from_usage(
"[noninteractive] -n --non-interactive \
'Runs without input from the user'",
)
}

pub fn exact() -> Arg<'static, 'static> {
Arg::from_usage("-e --exact 'Match the path exactly instead of fuzzily'")
}

pub fn subcommand(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
.group(ArgGroup::with_name("verbosity").args(&["verbose", "quiet"]))
.arg(Arg::from_usage(
"[verbose] -v... 'Increase output. Can be used up to three times \
to further increase output'",
))
.arg(Arg::from_usage(
"[quiet] -q... 'Reduce output. Can be used twice to further \
reduce output'",
))
.arg(Arg::from_usage(
"-V --vault [VAULT] 'The vault to open. Defaults to \"Default\".'",
))
.setting(AppSettings::DontCollapseArgsInUsage)
}

+ 31
- 0
src/cfg.rs View File

@@ -1,7 +1,38 @@
use atty;
use clap::ArgMatches;
use err::Result;
use goblin_core::VaultLocation;
use log;

pub struct Config {
pub vault: VaultLocation,
pub verbosity: i64,
pub interactive: bool,
}

impl Config {
pub fn init(matches: &ArgMatches) -> Result<Self> {
let cfg = Config {
vault: matches
.value_of("vault")
.map(|v| VaultLocation::new(v))
.unwrap_or_else(|| VaultLocation::default())?,
verbosity: matches.occurrences_of("verbose") as i64 -
matches.occurrences_of("quiet") as i64,
interactive: atty::is(atty::Stream::Stdin) &&
!matches.is_present("noninteractive"),
};
log::Builder({
use LogLevelFilter::*;
match cfg.verbosity {
i if i <= -2 => Off,
-1 => Error,
0 => Warn,
1 => Info,
2 => Debug,
_ => Trace,
}
}).init();
Ok(cfg)
}
}

+ 2
- 1
src/cmd/add_password.rs View File

@@ -4,7 +4,8 @@ use err::Result;
use goblin_core::{Access, Scope};
use util::{ClientExt, read_new_password, require_client};

pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
let pass = read_new_password("Add password to vault", &cfg)?;
cli.require(&Access::new(Scope::Global, true), &cfg)?;

+ 3
- 2
src/cmd/copy.rs View File

@@ -6,10 +6,11 @@ use goblin_core::{Access, ZeroBox};
use term::Stylable;
use util::{ClientExt, copy, init_timeout, require_client};

pub fn main(m: &ArgMatches, cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
// TODO: fuzzy matching stuff.
let path = m.value_of("PATHISH").unwrap();
let path = matches.value_of("PATHISH").unwrap();
cli.require(
&Access::path(&ZeroBox::new(path.to_owned()), false),
&cfg,

+ 5
- 7
src/cmd/cp.rs View File

@@ -5,17 +5,15 @@ use goblin_core::Access;
use term::Stylable;
use util::{ClientExt, require_client};

pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
if m.is_present("noninteractive") {
cfg.interactive = false;
}
let source = m.value_of("SOURCE").unwrap();
let dest = m.value_of("DEST").unwrap();
let source = matches.value_of("SOURCE").unwrap();
let dest = matches.value_of("DEST").unwrap();
cli.require(&Access::path(&source.into(), false), &cfg)?;
cli.require(&Access::path(&dest.into(), true), &cfg)?;
let _: () = cli.call("goblin.keyring.cp", (source, dest))?;
if !m.is_present("nocommit") {
if !matches.is_present("nocommit") {
let _: () = cli.call("goblin.keyring.commit", ())?;
let _: () = cli.call("goblin.vault.flush", ())?;
}

+ 2
- 1
src/cmd/create.rs View File

@@ -4,7 +4,8 @@ use err::Result;
use term::Stylable;
use util::{read_new_password, require_client};

pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
let pass = read_new_password("Creating vault", &cfg)?;
let _: () = cli.call("goblin.vault.create", (pass,))?;

+ 2
- 1
src/cmd/daemon.rs View File

@@ -4,7 +4,8 @@ use err::Result;
use goblin_core::VaultState;
use goblin_core::ipc::start_server;

pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
start_server(VaultState::new(cfg.vault))?;
Ok(())
}

+ 3
- 5
src/cmd/echo.rs View File

@@ -12,13 +12,11 @@ fn prefix(path: &str) {
print!("Entry at {} (press any key to wipe): ", path.bold());
}

pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
// TODO: fuzzy matching stuff.
let path = m.value_of("PATHISH").unwrap();
if m.is_present("noninteractive") {
cfg.interactive = false;
}
let path = matches.value_of("PATHISH").unwrap();
cli.require(
&Access::path(&ZeroBox::new(path.to_owned()), false),
&cfg,

+ 3
- 2
src/cmd/import.rs View File

@@ -9,9 +9,10 @@ use std::io::stdin;
use term::Stylable;
use util::{ClientExt, require_client};

pub fn main(_: &ArgMatches, mut cfg: Config) -> Result<()> {
let mut cli = require_client(&cfg)?;
pub fn main(matches: &ArgMatches) -> Result<()> {
let mut cfg = Config::init(matches)?;
cfg.interactive = false;
let mut cli = require_client(&cfg)?;
if atty::is(atty::Stream::Stdin) {
return Err(mkerr!("import STDIN is tty; use piping instead"));
}

+ 5
- 7
src/cmd/insert.rs View File

@@ -7,13 +7,11 @@ use std::io::{Read, stdin};
use term::Stylable;
use util::{ClientExt, require_client};

pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
if m.is_present("noninteractive") {
cfg.interactive = false;
}
let path = m.value_of("PATH").unwrap();
let value = Value::new(match m.value_of("VALUE") {
let path = matches.value_of("PATH").unwrap();
let value = Value::new(match matches.value_of("VALUE") {
Some(v) => v.to_owned().into_bytes(),
None => {
if cfg.interactive {
@@ -31,7 +29,7 @@ pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
&cfg,
)?;
let _: () = cli.call("goblin.keyring.insert", (path, value))?;
if !m.is_present("nocommit") {
if !matches.is_present("nocommit") {
let _: () = cli.call("goblin.keyring.commit", ())?;
let _: () = cli.call("goblin.vault.flush", ())?;
}

+ 5
- 7
src/cmd/ln.rs View File

@@ -5,17 +5,15 @@ use goblin_core::Access;
use term::Stylable;
use util::{ClientExt, require_client};

pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
if m.is_present("noninteractive") {
cfg.interactive = false;
}
let target = m.value_of("TARGET").unwrap();
let link_path = m.value_of("LINK_PATH").unwrap();
let target = matches.value_of("TARGET").unwrap();
let link_path = matches.value_of("LINK_PATH").unwrap();
cli.require(&Access::path(&target.into(), false), &cfg)?;
cli.require(&Access::path(&link_path.into(), true), &cfg)?;
let _: () = cli.call("goblin.keyring.ln", (target, link_path))?;
if !m.is_present("nocommit") {
if !matches.is_present("nocommit") {
let _: () = cli.call("goblin.keyring.commit", ())?;
let _: () = cli.call("goblin.vault.flush", ())?;
}

+ 8
- 8
src/cmd/ls.rs View File

@@ -58,13 +58,11 @@ impl Tree {
}
}

pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
let all = m.is_present("all");
if m.is_present("noninteractive") {
cfg.interactive = false;
}
let prefix: Path = m.value_of("PREFIX").unwrap_or("").into();
let all = matches.is_present("all");
let prefix: Path = matches.value_of("PREFIX").unwrap_or("").into();
let access = if &prefix[..] != "" {
Access::new(Scope::Prefix(Cow::Borrowed(&prefix)), false)
} else {
@@ -81,12 +79,14 @@ pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
.map(|p| p[pref.len()..].into())
.collect();
}
if m.is_present("raw") {
if matches.is_present("raw") {
for path in paths {
println!("{}", path.bold());
}
} else {
let depth = m.value_of("depth").map(|d| usize::from_str(d).unwrap());
let depth = matches.value_of("depth").map(
|d| usize::from_str(d).unwrap(),
);
let mut tree = Tree::new();
for path in paths {
tree.insert(&path);

+ 5
- 7
src/cmd/mv.rs View File

@@ -5,17 +5,15 @@ use goblin_core::Access;
use term::Stylable;
use util::{ClientExt, require_client};

pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
if m.is_present("noninteractive") {
cfg.interactive = false;
}
let source = m.value_of("SOURCE").unwrap();
let dest = m.value_of("DEST").unwrap();
let source = matches.value_of("SOURCE").unwrap();
let dest = matches.value_of("DEST").unwrap();
cli.require(&Access::path(&source.into(), true), &cfg)?;
cli.require(&Access::path(&dest.into(), true), &cfg)?;
let _: () = cli.call("goblin.keyring.mv", (source, dest))?;
if !m.is_present("nocommit") {
if !matches.is_present("nocommit") {
let _: () = cli.call("goblin.keyring.commit", ())?;
let _: () = cli.call("goblin.vault.flush", ())?;
}

+ 4
- 6
src/cmd/rm.rs View File

@@ -5,18 +5,16 @@ use goblin_core::{Access, ZeroBox};
use term::Stylable;
use util::{ClientExt, require_client};

pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
if m.is_present("noninteractive") {
cfg.interactive = false;
}
let path = m.value_of("PATH").unwrap();
let path = matches.value_of("PATH").unwrap();
cli.require(
&Access::path(&ZeroBox::new(path.to_owned()), true),
&cfg,
)?;
let _: () = cli.call("goblin.keyring.rm", (path,))?;
if !m.is_present("nocommit") {
if !matches.is_present("nocommit") {
let _: () = cli.call("goblin.keyring.commit", ())?;
let _: () = cli.call("goblin.vault.flush", ())?;
}

+ 2
- 1
src/cmd/rm_password.rs View File

@@ -5,7 +5,8 @@ use goblin_core::{Scope, UnlockSpec};
use std::time::Duration;
use util::{read_password, require_client};

pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let mut cli = require_client(&cfg)?;
warn!("the password entered here will be removed!");
let pass = read_password("to remove password", &cfg)?;

+ 7
- 6
src/cmd/unlock.rs View File

@@ -27,17 +27,18 @@ fn parse_time(t: &str) -> Result<Option<Duration>> {
}
}

pub fn main(m: &ArgMatches, cfg: Config) -> Result<()> {
let scope = if let Some(p) = m.value_of("path") {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
let scope = if let Some(p) = matches.value_of("path") {
Scope::Path(Cow::Owned(p.into()))
} else if let Some(p) = m.value_of("prefix") {
} else if let Some(p) = matches.value_of("prefix") {
Scope::Prefix(Cow::Owned(p.into()))
} else if m.is_present("global") {
} else if matches.is_present("global") {
Scope::Global
} else {
unreachable!()
};
let expires = if let Some(t) = m.value_of("EXPIRES") {
let expires = if let Some(t) = matches.value_of("EXPIRES") {
parse_time(t)?
} else {
Some(Duration::from_secs(120))
@@ -45,7 +46,7 @@ pub fn main(m: &ArgMatches, cfg: Config) -> Result<()> {
let spec = UnlockSpec {
scope: scope,
expires: expires,
write: m.is_present("write"),
write: matches.is_present("write"),
};
let mut cli = require_client(&cfg)?;
let pass = read_password(&spec, &cfg)?;

+ 2
- 1
src/cmd/which.rs View File

@@ -3,7 +3,8 @@ use clap::ArgMatches;
use err::Result;
use goblin_core::VaultLocation;

pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
pub fn main(matches: &ArgMatches) -> Result<()> {
let cfg = Config::init(matches)?;
if let &VaultLocation::File(ref path, _) = &cfg.vault {
println!("{}", path.display());
} else {

+ 8
- 188
src/main.rs View File

@@ -18,20 +18,17 @@ mod err;
mod term;
mod cmd;
mod cfg;
mod args;

use cfg::Config;
use clap::{ArgMatches, SubCommand};
use clap::ArgMatches;
use err::Result;
use goblin_core::VaultLocation;
use log_crate::LogLevelFilter;
use std::collections::HashMap;
use std::error::Error;
use std::process::exit;
use std::str::FromStr;

macro_rules! cmds {
($($name:ident),*) => {{
type SubMain = fn(&ArgMatches, Config) -> Result<()>;
type SubMain = fn(&ArgMatches) -> Result<()>;
let mut hmap = HashMap::<_, SubMain>::new();
$(
hmap.insert(stringify!($name).replace('_', "-"), cmd::$name::main);
@@ -41,192 +38,15 @@ macro_rules! cmds {
}

fn main() {
let app = clap_app!((crate_name!()) =>
(version: crate_version!())
(author: crate_authors!())
(about: crate_description!())
(max_term_width: 80)
(@setting SubcommandRequiredElseHelp)
(@group verbosity =>
(@arg quiet: -q...
"Reduce output. Can be used twice to further reduce output")
(@arg verbose: -v...
"Increase output. Can be used up to three times to further \
increase output")
)
(@arg VAULT: -V --vault +takes_value
"The vault to open. Defaults to 'default'")
(@subcommand daemon =>
// TODO
(about: "something something demonic")
(display_order: 1)
)
(@subcommand create =>
// TODO
(about: "something something create")
(display_order: 1)
)
(@subcommand unlock =>
// TODO
(about: "something something echo")
(display_order: 1)
(@setting DontCollapseArgsInUsage)
(@group scope =>
(@attributes +required)
(@arg path: -p --path +takes_value "The path to unlock")
(@arg prefix: -P --prefix +takes_value "The prefix to unlock")
(@arg global: -G --global "Unlock globally")
)
(@arg write: -w --write "Unlock for writing")
(@arg EXPIRES:
"How long to unlock for. Accepts positive integers with the \
suffixes 's', 'm', and 'h' for seconds, minutes, and hours \
respectively, or 'perm'/'permanent' for a permanent unlock. \
If no value is specified, a default of two minutes is used.")
)
(@subcommand echo =>
// TODO
(about: "something something echo")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg noninteractive: -n --("non-interactive")
"Runs without input from the user")
(@arg exact: -e --exact
"Match the path exactly instead of fuzzily")
(@arg PATHISH: +required "The path to retrieve")
)
(@subcommand copy =>
// TODO
(about: "something something copy")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg exact: -e --exact
"Match the path exactly instead of fuzzily")
(@arg PATHISH: +required "The path to copy")
)
(@subcommand insert =>
// TODO
(about: "something something insert")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg noninteractive: -n --("non-interactive")
"Runs without input from the user")
(@arg nocommit: -c --no-commit
"Does not automatically commit the insert")
(@arg PATH: +required "The path to insert to")
(@arg VALUE: "The value to insert")
)
(@subcommand import =>
// TODO
(about: "something something import")
)
(@subcommand which =>
(about: "something something which")
)
(@subcommand rm =>
(about: "something something rm")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg noninteractive: -n --("non-interactive")
"Runs without input from the user")
(@arg nocommit: -c --no-commit
"Does not automatically commit the insert")
(@arg PATH: +required "The path to insert remove")
)
(@subcommand mv =>
(about: "something something mv")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg noninteractive: -n --("non-interactive")
"Runs without input from the user")
(@arg nocommit: -c --no-commit
"Does not automatically commit the insert")
(@arg SOURCE: +required "The path to move from")
(@arg DEST: +required "The path to move to")
)
(@subcommand cp =>
(about: "something something cp")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg noninteractive: -n --("non-interactive")
"Runs without input from the user")
(@arg nocommit: -c --no-commit
"Does not automatically commit the insert")
(@arg SOURCE: +required "The path to copy from")
(@arg DEST: +required "The path to copy to")
)
(@subcommand ln =>
(about: "something something ln")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg noninteractive: -n --("non-interactive")
"Runs without input from the user")
(@arg nocommit: -c --no-commit
"Does not automatically commit the insert")
(@arg TARGET: +required "The link target path")
(@arg LINK_PATH: +required "The link's path")
)
(@subcommand ls =>
(about: "something something ls")
(display_order: 0)
(@setting DontCollapseArgsInUsage)
(@arg noninteractive: -n --("non-interactive")
"Runs without input from the user")
(@arg exact: -e --exact
"Match the path exactly instead of fuzzily")
(@arg all: -a --all "Show all paths, including hidden ones")
(@group output =>
(@arg depth: -d --depth +takes_value
{|d| match usize::from_str(&d) {
Ok(_) => Ok(()),
Err(e) => Err(format!("{}", e.description())),
}}
"The maximum depth to display")
(@arg raw: --raw "Output raw paths instead of a tree")
)
(@arg PREFIX: "The path to list")
)
).subcommand(
SubCommand::with_name("add-password")
.about("something something add password")
.display_order(1),
)
.subcommand(
SubCommand::with_name("rm-password")
.about("something something rm password")
.display_order(1),
);
let matches = app.get_matches();
let verbosity = matches.occurrences_of("verbose") as i64 -
matches.occurrences_of("quiet") as i64;
log::Builder({
use LogLevelFilter::*;
match verbosity {
i if i <= -2 => Off,
-1 => Error,
0 => Warn,
1 => Info,
2 => Debug,
_ => Trace,
}
}).init();
let matches = args::goblin().get_matches();

if let Err(e) = main_err(&matches, verbosity) {
if let Err(e) = main_err(&matches) {
error!("{}", e);
exit(1);
}
}

fn main_err(matches: &ArgMatches, verbosity: i64) -> Result<()> {
let vault = matches
.value_of("VAULT")
.map(|v| VaultLocation::new(v))
.unwrap_or_else(|| VaultLocation::default())?;
let cfg = Config {
vault: vault,
verbosity: verbosity,
interactive: atty::is(atty::Stream::Stdin),
};
fn main_err(matches: &ArgMatches) -> Result<()> {
let subcmds = cmds!(
daemon,
create,
@@ -247,8 +67,8 @@ fn main_err(matches: &ArgMatches, verbosity: i64) -> Result<()> {
let (cmd, subm) = matches.subcommand();
// We are requiring a subcommand.
let subm = subm.unwrap();
if let Some(s) = subcmds.get(cmd) {
s(subm, cfg)
if let Some(subcmd) = subcmds.get(cmd) {
subcmd(subm)
} else {
unreachable!()
}

+ 1
- 1
src/util.rs View File

@@ -20,10 +20,10 @@ pub fn require_client(cfg: &Config) -> Result<Client> {
return Ok(cli);
}
Command::new(env::args().next().ok_or(Error::ExecutableUnknown)?)
.arg("daemon")
.arg("-qq")
.arg("--vault")
.arg(format!("{}", &cfg.vault))
.arg("daemon")
.spawn()?;
let timeout = Instant::now() + Duration::new(1, 0);
while Instant::now() < timeout {

Loading…
Cancel
Save