Browse Source

Reorganize argument parsing.

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 @@
1 1
 reorder_imported_names = true
2 2
 reorder_imports = true
3
-format_strings = true
4 3
 max_width = 79

+ 211
- 0
src/args.rs View File

@@ -0,0 +1,211 @@
1
+use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
2
+use std::str::FromStr;
3
+
4
+pub fn goblin() -> App<'static, 'static> {
5
+    App::new(crate_name!())
6
+        .version(crate_version!())
7
+        .author(crate_authors!())
8
+        .about(crate_description!())
9
+        .max_term_width(80)
10
+        .setting(AppSettings::SubcommandRequiredElseHelp)
11
+        .subcommand(daemon())
12
+        .subcommand(create())
13
+        .subcommand(unlock())
14
+        .subcommand(echo())
15
+        .subcommand(copy())
16
+        .subcommand(insert())
17
+        .subcommand(import())
18
+        .subcommand(which())
19
+        .subcommand(rm())
20
+        .subcommand(mv())
21
+        .subcommand(cp())
22
+        .subcommand(ln())
23
+        .subcommand(ls())
24
+        .subcommand(add_password())
25
+        .subcommand(rm_password())
26
+}
27
+
28
+pub fn daemon() -> App<'static, 'static> {
29
+    let abt = "Starts the goblin daemon. This is typically done automatically\
30
+        when it is required";
31
+    subcommand("daemon").about(abt).display_order(1)
32
+}
33
+
34
+pub fn create() -> App<'static, 'static> {
35
+    let abt = "Creates a new vault. Will refuse to overwrite an existing one";
36
+    subcommand("create").about(abt).display_order(1)
37
+}
38
+
39
+pub fn unlock() -> App<'static, 'static> {
40
+    let exphelp = "How long to unlock for. Accepts positive integers with the \
41
+        suffixes 's', 'm', and 'h' for seconds, minutes, and hours \
42
+        respectively, or 'perm'/'permanent' for a permanent unlock. If no \
43
+        value is specified, a default of two minutes is used";
44
+    subcommand("unlock")
45
+        .about("Unlocks (parts of) a vault")
46
+        .display_order(1)
47
+        .group(
48
+            ArgGroup::with_name("scope")
49
+                .args(&["path", "prefix", "global"])
50
+                .required(true),
51
+        )
52
+        .arg(Arg::from_usage("-p --path [PATH] 'The path to unlock'"))
53
+        .arg(Arg::from_usage(
54
+            "-P --prefix [PREFIX] 'The prefix to unlock'",
55
+        ))
56
+        .arg(Arg::from_usage("-G --global 'Unlock globally'"))
57
+        .arg(Arg::from_usage("-w --write 'Unlock for writing'"))
58
+        .arg(Arg::with_name("EXPIRES").help(exphelp))
59
+    // TODO: add validator for EXPIRES
60
+}
61
+
62
+pub fn echo() -> App<'static, 'static> {
63
+    subcommand("echo")
64
+        .about("Echos PATHISH to STDOUT")
65
+        .display_order(0)
66
+        .arg(noninteractive())
67
+        .arg(exact())
68
+        .arg(Arg::from_usage("<PATHISH> 'The path to retrieve'"))
69
+}
70
+
71
+pub fn copy() -> App<'static, 'static> {
72
+    subcommand("copy")
73
+        .about("Copies PATHISH to the system clipboard")
74
+        .display_order(0)
75
+        .arg(exact())
76
+        .arg(Arg::from_usage("<PATHISH> 'The path to copy'"))
77
+}
78
+
79
+pub fn insert() -> App<'static, 'static> {
80
+    subcommand("insert")
81
+        .about("Inserts a value to PATH")
82
+        .display_order(0)
83
+        .arg(noninteractive())
84
+        .arg(nocommit())
85
+        .arg(Arg::from_usage("<PATH> 'The path to insert into'"))
86
+        .arg(Arg::from_usage(
87
+            "[VALUE] 'The value to insert. If no value is supplied, one is \
88
+            read from STDIN'",
89
+        ))
90
+}
91
+
92
+pub fn import() -> App<'static, 'static> {
93
+    subcommand("import").about(
94
+        "Imports a JSON map representing the keyring, piped to STDIN.",
95
+    )
96
+}
97
+
98
+pub fn which() -> App<'static, 'static> {
99
+    subcommand("which").about("Prints the full location of the vault")
100
+}
101
+
102
+pub fn rm() -> App<'static, 'static> {
103
+    subcommand("rm")
104
+        .about("Removes PATH from the keyring")
105
+        .display_order(0)
106
+        .arg(noninteractive())
107
+        .arg(nocommit())
108
+        .arg(Arg::from_usage("<PATH> 'The path to remove'"))
109
+}
110
+
111
+pub fn mv() -> App<'static, 'static> {
112
+    subcommand("mv")
113
+        .about("Moves a value from SOURCE to DEST")
114
+        .display_order(0)
115
+        .arg(noninteractive())
116
+        .arg(nocommit())
117
+        .arg(Arg::from_usage("<SOURCE> 'The path to move from'"))
118
+        .arg(Arg::from_usage("<DEST> 'The path to move to'"))
119
+}
120
+
121
+pub fn cp() -> App<'static, 'static> {
122
+    subcommand("cp")
123
+        .about("Copies a value from SOURCE to DEST")
124
+        .display_order(0)
125
+        .arg(noninteractive())
126
+        .arg(nocommit())
127
+        .arg(Arg::from_usage("<SOURCE> 'The path to copy from'"))
128
+        .arg(Arg::from_usage("<DEST> 'The path to copy to'"))
129
+}
130
+
131
+pub fn ln() -> App<'static, 'static> {
132
+    subcommand("ln")
133
+        .about("Links LINK_PATH to TARGET")
134
+        .display_order(0)
135
+        .arg(noninteractive())
136
+        .arg(nocommit())
137
+        .arg(Arg::from_usage("<TARGET> 'The link target'"))
138
+        .arg(Arg::from_usage("<LINK_PATH> 'The link's path'"))
139
+}
140
+
141
+pub fn ls() -> App<'static, 'static> {
142
+    subcommand("ls")
143
+        .about("Lists (part of) the keyring")
144
+        .display_order(0)
145
+        .arg(noninteractive())
146
+        .arg(Arg::from_usage(
147
+            "-a --all 'Show all paths, including hidden ones'",
148
+        ))
149
+        .group(ArgGroup::with_name("output").args(&["depth", "raw"]))
150
+        .arg(
151
+            Arg::from_usage("--depth [DEPTH] 'The maximum depth to display'")
152
+                .validator(validate_usize),
153
+        )
154
+        .arg(Arg::from_usage(
155
+            "--raw 'Outputs raw paths instead of a tree'",
156
+        ))
157
+        .arg(Arg::from_usage("[PREFIX] 'The path to list'"))
158
+}
159
+
160
+pub fn add_password() -> App<'static, 'static> {
161
+    subcommand("add-password")
162
+        .about("Adds a password used to unlock the vault")
163
+        .display_order(1)
164
+}
165
+
166
+pub fn rm_password() -> App<'static, 'static> {
167
+    subcommand("add-password")
168
+        .about("Removes a password used to unlock the vault")
169
+        .display_order(1)
170
+}
171
+
172
+pub fn validate_usize(s: String) -> Result<(), String> {
173
+    match usize::from_str(&s) {
174
+        Ok(_) => Ok(()),
175
+        Err(_) => Err("expected unsigned integer".into()),
176
+    }
177
+}
178
+
179
+pub fn nocommit() -> Arg<'static, 'static> {
180
+    Arg::from_usage(
181
+        "[nocommit] -c --no-commit 'Runs without input from the user'",
182
+    )
183
+}
184
+
185
+pub fn noninteractive() -> Arg<'static, 'static> {
186
+    Arg::from_usage(
187
+        "[noninteractive] -n --non-interactive \
188
+        'Runs without input from the user'",
189
+    )
190
+}
191
+
192
+pub fn exact() -> Arg<'static, 'static> {
193
+    Arg::from_usage("-e --exact 'Match the path exactly instead of fuzzily'")
194
+}
195
+
196
+pub fn subcommand(name: &'static str) -> App<'static, 'static> {
197
+    SubCommand::with_name(name)
198
+        .group(ArgGroup::with_name("verbosity").args(&["verbose", "quiet"]))
199
+        .arg(Arg::from_usage(
200
+            "[verbose] -v... 'Increase output. Can be used up to three times \
201
+            to further increase output'",
202
+        ))
203
+        .arg(Arg::from_usage(
204
+            "[quiet] -q... 'Reduce output. Can be used twice to further \
205
+            reduce output'",
206
+        ))
207
+        .arg(Arg::from_usage(
208
+            "-V --vault [VAULT] 'The vault to open. Defaults to \"Default\".'",
209
+        ))
210
+        .setting(AppSettings::DontCollapseArgsInUsage)
211
+}

+ 31
- 0
src/cfg.rs View File

@@ -1,7 +1,38 @@
1
+use atty;
2
+use clap::ArgMatches;
3
+use err::Result;
1 4
 use goblin_core::VaultLocation;
5
+use log;
2 6
 
3 7
 pub struct Config {
4 8
     pub vault: VaultLocation,
5 9
     pub verbosity: i64,
6 10
     pub interactive: bool,
7 11
 }
12
+
13
+impl Config {
14
+    pub fn init(matches: &ArgMatches) -> Result<Self> {
15
+        let cfg = Config {
16
+            vault: matches
17
+                .value_of("vault")
18
+                .map(|v| VaultLocation::new(v))
19
+                .unwrap_or_else(|| VaultLocation::default())?,
20
+            verbosity: matches.occurrences_of("verbose") as i64 -
21
+                matches.occurrences_of("quiet") as i64,
22
+            interactive: atty::is(atty::Stream::Stdin) &&
23
+                !matches.is_present("noninteractive"),
24
+        };
25
+        log::Builder({
26
+            use LogLevelFilter::*;
27
+            match cfg.verbosity {
28
+                i if i <= -2 => Off,
29
+                -1 => Error,
30
+                0 => Warn,
31
+                1 => Info,
32
+                2 => Debug,
33
+                _ => Trace,
34
+            }
35
+        }).init();
36
+        Ok(cfg)
37
+    }
38
+}

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

@@ -4,7 +4,8 @@ use err::Result;
4 4
 use goblin_core::{Access, Scope};
5 5
 use util::{ClientExt, read_new_password, require_client};
6 6
 
7
-pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
7
+pub fn main(matches: &ArgMatches) -> Result<()> {
8
+    let cfg = Config::init(matches)?;
8 9
     let mut cli = require_client(&cfg)?;
9 10
     let pass = read_new_password("Add password to vault", &cfg)?;
10 11
     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};
6 6
 use term::Stylable;
7 7
 use util::{ClientExt, copy, init_timeout, require_client};
8 8
 
9
-pub fn main(m: &ArgMatches, cfg: Config) -> Result<()> {
9
+pub fn main(matches: &ArgMatches) -> Result<()> {
10
+    let cfg = Config::init(matches)?;
10 11
     let mut cli = require_client(&cfg)?;
11 12
     // TODO: fuzzy matching stuff.
12
-    let path = m.value_of("PATHISH").unwrap();
13
+    let path = matches.value_of("PATHISH").unwrap();
13 14
     cli.require(
14 15
         &Access::path(&ZeroBox::new(path.to_owned()), false),
15 16
         &cfg,

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

@@ -5,17 +5,15 @@ use goblin_core::Access;
5 5
 use term::Stylable;
6 6
 use util::{ClientExt, require_client};
7 7
 
8
-pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
8
+pub fn main(matches: &ArgMatches) -> Result<()> {
9
+    let cfg = Config::init(matches)?;
9 10
     let mut cli = require_client(&cfg)?;
10
-    if m.is_present("noninteractive") {
11
-        cfg.interactive = false;
12
-    }
13
-    let source = m.value_of("SOURCE").unwrap();
14
-    let dest = m.value_of("DEST").unwrap();
11
+    let source = matches.value_of("SOURCE").unwrap();
12
+    let dest = matches.value_of("DEST").unwrap();
15 13
     cli.require(&Access::path(&source.into(), false), &cfg)?;
16 14
     cli.require(&Access::path(&dest.into(), true), &cfg)?;
17 15
     let _: () = cli.call("goblin.keyring.cp", (source, dest))?;
18
-    if !m.is_present("nocommit") {
16
+    if !matches.is_present("nocommit") {
19 17
         let _: () = cli.call("goblin.keyring.commit", ())?;
20 18
         let _: () = cli.call("goblin.vault.flush", ())?;
21 19
     }

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

@@ -4,7 +4,8 @@ use err::Result;
4 4
 use term::Stylable;
5 5
 use util::{read_new_password, require_client};
6 6
 
7
-pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
7
+pub fn main(matches: &ArgMatches) -> Result<()> {
8
+    let cfg = Config::init(matches)?;
8 9
     let mut cli = require_client(&cfg)?;
9 10
     let pass = read_new_password("Creating vault", &cfg)?;
10 11
     let _: () = cli.call("goblin.vault.create", (pass,))?;

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

@@ -4,7 +4,8 @@ use err::Result;
4 4
 use goblin_core::VaultState;
5 5
 use goblin_core::ipc::start_server;
6 6
 
7
-pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
7
+pub fn main(matches: &ArgMatches) -> Result<()> {
8
+    let cfg = Config::init(matches)?;
8 9
     start_server(VaultState::new(cfg.vault))?;
9 10
     Ok(())
10 11
 }

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

@@ -12,13 +12,11 @@ fn prefix(path: &str) {
12 12
     print!("Entry at {} (press any key to wipe): ", path.bold());
13 13
 }
14 14
 
15
-pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
15
+pub fn main(matches: &ArgMatches) -> Result<()> {
16
+    let cfg = Config::init(matches)?;
16 17
     let mut cli = require_client(&cfg)?;
17 18
     // TODO: fuzzy matching stuff.
18
-    let path = m.value_of("PATHISH").unwrap();
19
-    if m.is_present("noninteractive") {
20
-        cfg.interactive = false;
21
-    }
19
+    let path = matches.value_of("PATHISH").unwrap();
22 20
     cli.require(
23 21
         &Access::path(&ZeroBox::new(path.to_owned()), false),
24 22
         &cfg,

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

@@ -9,9 +9,10 @@ use std::io::stdin;
9 9
 use term::Stylable;
10 10
 use util::{ClientExt, require_client};
11 11
 
12
-pub fn main(_: &ArgMatches, mut cfg: Config) -> Result<()> {
13
-    let mut cli = require_client(&cfg)?;
12
+pub fn main(matches: &ArgMatches) -> Result<()> {
13
+    let mut cfg = Config::init(matches)?;
14 14
     cfg.interactive = false;
15
+    let mut cli = require_client(&cfg)?;
15 16
     if atty::is(atty::Stream::Stdin) {
16 17
         return Err(mkerr!("import STDIN is tty; use piping instead"));
17 18
     }

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

@@ -7,13 +7,11 @@ use std::io::{Read, stdin};
7 7
 use term::Stylable;
8 8
 use util::{ClientExt, require_client};
9 9
 
10
-pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
10
+pub fn main(matches: &ArgMatches) -> Result<()> {
11
+    let cfg = Config::init(matches)?;
11 12
     let mut cli = require_client(&cfg)?;
12
-    if m.is_present("noninteractive") {
13
-        cfg.interactive = false;
14
-    }
15
-    let path = m.value_of("PATH").unwrap();
16
-    let value = Value::new(match m.value_of("VALUE") {
13
+    let path = matches.value_of("PATH").unwrap();
14
+    let value = Value::new(match matches.value_of("VALUE") {
17 15
         Some(v) => v.to_owned().into_bytes(),
18 16
         None => {
19 17
             if cfg.interactive {
@@ -31,7 +29,7 @@ pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
31 29
         &cfg,
32 30
     )?;
33 31
     let _: () = cli.call("goblin.keyring.insert", (path, value))?;
34
-    if !m.is_present("nocommit") {
32
+    if !matches.is_present("nocommit") {
35 33
         let _: () = cli.call("goblin.keyring.commit", ())?;
36 34
         let _: () = cli.call("goblin.vault.flush", ())?;
37 35
     }

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

@@ -5,17 +5,15 @@ use goblin_core::Access;
5 5
 use term::Stylable;
6 6
 use util::{ClientExt, require_client};
7 7
 
8
-pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
8
+pub fn main(matches: &ArgMatches) -> Result<()> {
9
+    let cfg = Config::init(matches)?;
9 10
     let mut cli = require_client(&cfg)?;
10
-    if m.is_present("noninteractive") {
11
-        cfg.interactive = false;
12
-    }
13
-    let target = m.value_of("TARGET").unwrap();
14
-    let link_path = m.value_of("LINK_PATH").unwrap();
11
+    let target = matches.value_of("TARGET").unwrap();
12
+    let link_path = matches.value_of("LINK_PATH").unwrap();
15 13
     cli.require(&Access::path(&target.into(), false), &cfg)?;
16 14
     cli.require(&Access::path(&link_path.into(), true), &cfg)?;
17 15
     let _: () = cli.call("goblin.keyring.ln", (target, link_path))?;
18
-    if !m.is_present("nocommit") {
16
+    if !matches.is_present("nocommit") {
19 17
         let _: () = cli.call("goblin.keyring.commit", ())?;
20 18
         let _: () = cli.call("goblin.vault.flush", ())?;
21 19
     }

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

@@ -58,13 +58,11 @@ impl Tree {
58 58
     }
59 59
 }
60 60
 
61
-pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
61
+pub fn main(matches: &ArgMatches) -> Result<()> {
62
+    let cfg = Config::init(matches)?;
62 63
     let mut cli = require_client(&cfg)?;
63
-    let all = m.is_present("all");
64
-    if m.is_present("noninteractive") {
65
-        cfg.interactive = false;
66
-    }
67
-    let prefix: Path = m.value_of("PREFIX").unwrap_or("").into();
64
+    let all = matches.is_present("all");
65
+    let prefix: Path = matches.value_of("PREFIX").unwrap_or("").into();
68 66
     let access = if &prefix[..] != "" {
69 67
         Access::new(Scope::Prefix(Cow::Borrowed(&prefix)), false)
70 68
     } else {
@@ -81,12 +79,14 @@ pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
81 79
             .map(|p| p[pref.len()..].into())
82 80
             .collect();
83 81
     }
84
-    if m.is_present("raw") {
82
+    if matches.is_present("raw") {
85 83
         for path in paths {
86 84
             println!("{}", path.bold());
87 85
         }
88 86
     } else {
89
-        let depth = m.value_of("depth").map(|d| usize::from_str(d).unwrap());
87
+        let depth = matches.value_of("depth").map(
88
+            |d| usize::from_str(d).unwrap(),
89
+        );
90 90
         let mut tree = Tree::new();
91 91
         for path in paths {
92 92
             tree.insert(&path);

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

@@ -5,17 +5,15 @@ use goblin_core::Access;
5 5
 use term::Stylable;
6 6
 use util::{ClientExt, require_client};
7 7
 
8
-pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
8
+pub fn main(matches: &ArgMatches) -> Result<()> {
9
+    let cfg = Config::init(matches)?;
9 10
     let mut cli = require_client(&cfg)?;
10
-    if m.is_present("noninteractive") {
11
-        cfg.interactive = false;
12
-    }
13
-    let source = m.value_of("SOURCE").unwrap();
14
-    let dest = m.value_of("DEST").unwrap();
11
+    let source = matches.value_of("SOURCE").unwrap();
12
+    let dest = matches.value_of("DEST").unwrap();
15 13
     cli.require(&Access::path(&source.into(), true), &cfg)?;
16 14
     cli.require(&Access::path(&dest.into(), true), &cfg)?;
17 15
     let _: () = cli.call("goblin.keyring.mv", (source, dest))?;
18
-    if !m.is_present("nocommit") {
16
+    if !matches.is_present("nocommit") {
19 17
         let _: () = cli.call("goblin.keyring.commit", ())?;
20 18
         let _: () = cli.call("goblin.vault.flush", ())?;
21 19
     }

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

@@ -5,18 +5,16 @@ use goblin_core::{Access, ZeroBox};
5 5
 use term::Stylable;
6 6
 use util::{ClientExt, require_client};
7 7
 
8
-pub fn main(m: &ArgMatches, mut cfg: Config) -> Result<()> {
8
+pub fn main(matches: &ArgMatches) -> Result<()> {
9
+    let cfg = Config::init(matches)?;
9 10
     let mut cli = require_client(&cfg)?;
10
-    if m.is_present("noninteractive") {
11
-        cfg.interactive = false;
12
-    }
13
-    let path = m.value_of("PATH").unwrap();
11
+    let path = matches.value_of("PATH").unwrap();
14 12
     cli.require(
15 13
         &Access::path(&ZeroBox::new(path.to_owned()), true),
16 14
         &cfg,
17 15
     )?;
18 16
     let _: () = cli.call("goblin.keyring.rm", (path,))?;
19
-    if !m.is_present("nocommit") {
17
+    if !matches.is_present("nocommit") {
20 18
         let _: () = cli.call("goblin.keyring.commit", ())?;
21 19
         let _: () = cli.call("goblin.vault.flush", ())?;
22 20
     }

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

@@ -5,7 +5,8 @@ use goblin_core::{Scope, UnlockSpec};
5 5
 use std::time::Duration;
6 6
 use util::{read_password, require_client};
7 7
 
8
-pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
8
+pub fn main(matches: &ArgMatches) -> Result<()> {
9
+    let cfg = Config::init(matches)?;
9 10
     let mut cli = require_client(&cfg)?;
10 11
     warn!("the password entered here will be removed!");
11 12
     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>> {
27 27
     }
28 28
 }
29 29
 
30
-pub fn main(m: &ArgMatches, cfg: Config) -> Result<()> {
31
-    let scope = if let Some(p) = m.value_of("path") {
30
+pub fn main(matches: &ArgMatches) -> Result<()> {
31
+    let cfg = Config::init(matches)?;
32
+    let scope = if let Some(p) = matches.value_of("path") {
32 33
         Scope::Path(Cow::Owned(p.into()))
33
-    } else if let Some(p) = m.value_of("prefix") {
34
+    } else if let Some(p) = matches.value_of("prefix") {
34 35
         Scope::Prefix(Cow::Owned(p.into()))
35
-    } else if m.is_present("global") {
36
+    } else if matches.is_present("global") {
36 37
         Scope::Global
37 38
     } else {
38 39
         unreachable!()
39 40
     };
40
-    let expires = if let Some(t) = m.value_of("EXPIRES") {
41
+    let expires = if let Some(t) = matches.value_of("EXPIRES") {
41 42
         parse_time(t)?
42 43
     } else {
43 44
         Some(Duration::from_secs(120))
@@ -45,7 +46,7 @@ pub fn main(m: &ArgMatches, cfg: Config) -> Result<()> {
45 46
     let spec = UnlockSpec {
46 47
         scope: scope,
47 48
         expires: expires,
48
-        write: m.is_present("write"),
49
+        write: matches.is_present("write"),
49 50
     };
50 51
     let mut cli = require_client(&cfg)?;
51 52
     let pass = read_password(&spec, &cfg)?;

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

@@ -3,7 +3,8 @@ use clap::ArgMatches;
3 3
 use err::Result;
4 4
 use goblin_core::VaultLocation;
5 5
 
6
-pub fn main(_: &ArgMatches, cfg: Config) -> Result<()> {
6
+pub fn main(matches: &ArgMatches) -> Result<()> {
7
+    let cfg = Config::init(matches)?;
7 8
     if let &VaultLocation::File(ref path, _) = &cfg.vault {
8 9
         println!("{}", path.display());
9 10
     } else {

+ 8
- 188
src/main.rs View File

@@ -18,20 +18,17 @@ mod err;
18 18
 mod term;
19 19
 mod cmd;
20 20
 mod cfg;
21
+mod args;
21 22
 
22
-use cfg::Config;
23
-use clap::{ArgMatches, SubCommand};
23
+use clap::ArgMatches;
24 24
 use err::Result;
25
-use goblin_core::VaultLocation;
26 25
 use log_crate::LogLevelFilter;
27 26
 use std::collections::HashMap;
28
-use std::error::Error;
29 27
 use std::process::exit;
30
-use std::str::FromStr;
31 28
 
32 29
 macro_rules! cmds {
33 30
     ($($name:ident),*) => {{
34
-        type SubMain = fn(&ArgMatches, Config) -> Result<()>;
31
+        type SubMain = fn(&ArgMatches) -> Result<()>;
35 32
         let mut hmap = HashMap::<_, SubMain>::new();
36 33
         $(
37 34
             hmap.insert(stringify!($name).replace('_', "-"), cmd::$name::main);
@@ -41,192 +38,15 @@ macro_rules! cmds {
41 38
 }
42 39
 
43 40
 fn main() {
44
-    let app = clap_app!((crate_name!()) =>
45
-        (version: crate_version!())
46
-        (author: crate_authors!())
47
-        (about: crate_description!())
48
-        (max_term_width: 80)
49
-        (@setting SubcommandRequiredElseHelp)
50
-        (@group verbosity =>
51
-            (@arg quiet: -q...
52
-                "Reduce output. Can be used twice to further reduce output")
53
-            (@arg verbose: -v...
54
-                "Increase output. Can be used up to three times to further \
55
-                increase output")
56
-        )
57
-        (@arg VAULT: -V --vault +takes_value
58
-            "The vault to open. Defaults to 'default'")
59
-        (@subcommand daemon =>
60
-            // TODO
61
-            (about: "something something demonic")
62
-            (display_order: 1)
63
-        )
64
-        (@subcommand create =>
65
-            // TODO
66
-            (about: "something something create")
67
-            (display_order: 1)
68
-        )
69
-        (@subcommand unlock =>
70
-            // TODO
71
-            (about: "something something echo")
72
-            (display_order: 1)
73
-            (@setting DontCollapseArgsInUsage)
74
-            (@group scope =>
75
-                (@attributes +required)
76
-                (@arg path: -p --path +takes_value "The path to unlock")
77
-                (@arg prefix: -P --prefix +takes_value "The prefix to unlock")
78
-                (@arg global: -G --global "Unlock globally")
79
-            )
80
-            (@arg write: -w --write "Unlock for writing")
81
-            (@arg EXPIRES:
82
-                "How long to unlock for. Accepts positive integers with the \
83
-                suffixes 's', 'm', and 'h' for seconds, minutes, and hours \
84
-                respectively, or 'perm'/'permanent' for a permanent unlock. \
85
-                If no value is specified, a default of two minutes is used.")
86
-        )
87
-        (@subcommand echo =>
88
-            // TODO
89
-            (about: "something something echo")
90
-            (display_order: 0)
91
-            (@setting DontCollapseArgsInUsage)
92
-            (@arg noninteractive: -n --("non-interactive")
93
-                "Runs without input from the user")
94
-            (@arg exact: -e --exact
95
-                "Match the path exactly instead of fuzzily")
96
-            (@arg PATHISH: +required "The path to retrieve")
97
-        )
98
-        (@subcommand copy =>
99
-            // TODO
100
-            (about: "something something copy")
101
-            (display_order: 0)
102
-            (@setting DontCollapseArgsInUsage)
103
-            (@arg exact: -e --exact
104
-                "Match the path exactly instead of fuzzily")
105
-            (@arg PATHISH: +required "The path to copy")
106
-        )
107
-        (@subcommand insert =>
108
-            // TODO
109
-            (about: "something something insert")
110
-            (display_order: 0)
111
-            (@setting DontCollapseArgsInUsage)
112
-            (@arg noninteractive: -n --("non-interactive")
113
-                "Runs without input from the user")
114
-            (@arg nocommit: -c --no-commit
115
-                "Does not automatically commit the insert")
116
-            (@arg PATH: +required "The path to insert to")
117
-            (@arg VALUE: "The value to insert")
118
-        )
119
-        (@subcommand import =>
120
-            // TODO
121
-            (about: "something something import")
122
-        )
123
-        (@subcommand which =>
124
-            (about: "something something which")
125
-        )
126
-        (@subcommand rm =>
127
-            (about: "something something rm")
128
-            (display_order: 0)
129
-            (@setting DontCollapseArgsInUsage)
130
-            (@arg noninteractive: -n --("non-interactive")
131
-                "Runs without input from the user")
132
-            (@arg nocommit: -c --no-commit
133
-                "Does not automatically commit the insert")
134
-            (@arg PATH: +required "The path to insert remove")
135
-        )
136
-        (@subcommand mv =>
137
-            (about: "something something mv")
138
-            (display_order: 0)
139
-            (@setting DontCollapseArgsInUsage)
140
-            (@arg noninteractive: -n --("non-interactive")
141
-                "Runs without input from the user")
142
-            (@arg nocommit: -c --no-commit
143
-                "Does not automatically commit the insert")
144
-            (@arg SOURCE: +required "The path to move from")
145
-            (@arg DEST: +required "The path to move to")
146
-        )
147
-        (@subcommand cp =>
148
-            (about: "something something cp")
149
-            (display_order: 0)
150
-            (@setting DontCollapseArgsInUsage)
151
-            (@arg noninteractive: -n --("non-interactive")
152
-                "Runs without input from the user")
153
-            (@arg nocommit: -c --no-commit
154
-                "Does not automatically commit the insert")
155
-            (@arg SOURCE: +required "The path to copy from")
156
-            (@arg DEST: +required "The path to copy to")
157
-        )
158
-        (@subcommand ln =>
159
-            (about: "something something ln")
160
-            (display_order: 0)
161
-            (@setting DontCollapseArgsInUsage)
162
-            (@arg noninteractive: -n --("non-interactive")
163
-                "Runs without input from the user")
164
-            (@arg nocommit: -c --no-commit
165
-                "Does not automatically commit the insert")
166
-            (@arg TARGET: +required "The link target path")
167
-            (@arg LINK_PATH: +required "The link's path")
168
-        )
169
-        (@subcommand ls =>
170
-            (about: "something something ls")
171
-            (display_order: 0)
172
-            (@setting DontCollapseArgsInUsage)
173
-            (@arg noninteractive: -n --("non-interactive")
174
-                "Runs without input from the user")
175
-            (@arg exact: -e --exact
176
-                "Match the path exactly instead of fuzzily")
177
-            (@arg all: -a --all "Show all paths, including hidden ones")
178
-            (@group output =>
179
-                (@arg depth: -d --depth +takes_value
180
-                    {|d| match usize::from_str(&d) {
181
-                        Ok(_) => Ok(()),
182
-                        Err(e) => Err(format!("{}", e.description())),
183
-                    }}
184
-                    "The maximum depth to display")
185
-                (@arg raw: --raw "Output raw paths instead of a tree")
186
-            )
187
-            (@arg PREFIX: "The path to list")
188
-        )
189
-    ).subcommand(
190
-        SubCommand::with_name("add-password")
191
-            .about("something something add password")
192
-            .display_order(1),
193
-    )
194
-        .subcommand(
195
-            SubCommand::with_name("rm-password")
196
-                .about("something something rm password")
197
-                .display_order(1),
198
-        );
199
-    let matches = app.get_matches();
200
-    let verbosity = matches.occurrences_of("verbose") as i64 -
201
-        matches.occurrences_of("quiet") as i64;
202
-    log::Builder({
203
-        use LogLevelFilter::*;
204
-        match verbosity {
205
-            i if i <= -2 => Off,
206
-            -1 => Error,
207
-            0 => Warn,
208
-            1 => Info,
209
-            2 => Debug,
210
-            _ => Trace,
211
-        }
212
-    }).init();
41
+    let matches = args::goblin().get_matches();
213 42
 
214
-    if let Err(e) = main_err(&matches, verbosity) {
43
+    if let Err(e) = main_err(&matches) {
215 44
         error!("{}", e);
216 45
         exit(1);
217 46
     }
218 47
 }
219 48
 
220
-fn main_err(matches: &ArgMatches, verbosity: i64) -> Result<()> {
221
-    let vault = matches
222
-        .value_of("VAULT")
223
-        .map(|v| VaultLocation::new(v))
224
-        .unwrap_or_else(|| VaultLocation::default())?;
225
-    let cfg = Config {
226
-        vault: vault,
227
-        verbosity: verbosity,
228
-        interactive: atty::is(atty::Stream::Stdin),
229
-    };
49
+fn main_err(matches: &ArgMatches) -> Result<()> {
230 50
     let subcmds = cmds!(
231 51
         daemon,
232 52
         create,
@@ -247,8 +67,8 @@ fn main_err(matches: &ArgMatches, verbosity: i64) -> Result<()> {
247 67
     let (cmd, subm) = matches.subcommand();
248 68
     // We are requiring a subcommand.
249 69
     let subm = subm.unwrap();
250
-    if let Some(s) = subcmds.get(cmd) {
251
-        s(subm, cfg)
70
+    if let Some(subcmd) = subcmds.get(cmd) {
71
+        subcmd(subm)
252 72
     } else {
253 73
         unreachable!()
254 74
     }

+ 1
- 1
src/util.rs View File

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

Loading…
Cancel
Save