lib/config/cli/mod.rs
1mod internal;
2
3use crate::common::{util, Platform};
4use std::{ffi::OsString, path::PathBuf};
5use structopt::StructOpt;
6
7/// The portion of the configuration read from CLI arguments
8#[derive(Debug, Clone)]
9pub struct Config {
10 /// Enables verbose output.
11 pub verbose: bool,
12
13 /// Paths (relative to the dotfiles folder) of items to be excluded.
14 /// This is in addition to any excludes defined in your dotrc.
15 /// Globs are accepted - just make sure to enclose them in single quotes to
16 /// avoid your shell trying to expand them.
17 pub excludes: Vec<PathBuf>,
18
19 /// Tags to enable. This is in addition to any tags enabled in your dotrc.
20 pub tags: Vec<String>,
21
22 /// The folder in which to search for dotfiles. The default is ~/.dotfiles.
23 pub dotfiles_path: Option<PathBuf>,
24
25 /// The hostname to use. The default is the system hostname.
26 pub hostname: Option<String>,
27
28 /// The platform to use. The default is the actual platform.
29 /// Valid values are macos, windows, linux, and wsl.
30 pub platform: Option<Platform>,
31
32 pub command: Command,
33}
34
35impl Config {
36 pub fn get() -> Self {
37 let app = internal::RawConfig::clap();
38 let raw_config = internal::RawConfig::from_clap(&app.get_matches());
39
40 let (command, command_options) = match raw_config.command {
41 internal::Command::Ls { options } => (Ls, options),
42 internal::Command::Link { dry_run, options } => (Link { dry_run }, options),
43 };
44
45 let verbose = raw_config.options.verbose || command_options.verbose;
46 let excludes = util::append_vecs(raw_config.options.excludes, command_options.excludes);
47 let tags = util::append_vecs(raw_config.options.tags, command_options.tags);
48
49 /// Given the name of an argument which should be unique, tries to get
50 /// it from either the main command or a subcommand. If it is
51 /// provided multiple times in a way that `clap` won't catch
52 /// (e.g. given once to the main command and again to a subcommand),
53 /// produces an appropriate `clap` error and exits.
54 macro_rules! get_unique_arg {
55 ($name: ident) => {
56 match (raw_config.options.$name, command_options.$name) {
57 (None, None) => None,
58 (Some($name), None) | (None, Some($name)) => Some($name),
59 (Some(_), Some(_)) => {
60 // We simply append an extra usage of --$name onto the existing args, then
61 // try to parse them again. This should trigger an error about
62 // $name appearing twice, which we display then exit.
63 //
64 // This should make this particular hack transparent to the user, since the
65 // error is just like if `clap` had caught the error.
66
67 let mut args: Vec<OsString> = std::env::args_os().collect();
68 args.push(OsString::from(concat!("--", stringify!($name))));
69
70 internal::RawConfig::from_iter_safe(args.iter())
71 .expect_err("This argument should not allow duplicates")
72 .exit()
73 },
74 }
75 };
76 }
77
78 let dotfiles_path = get_unique_arg!(dotfiles_path);
79 let hostname = get_unique_arg!(hostname);
80 let platform = get_unique_arg!(platform);
81
82 let res = Config {
83 verbose,
84 excludes,
85 tags,
86 dotfiles_path,
87 hostname,
88 platform,
89 command,
90 };
91
92 util::set_verbosity(res.verbose);
93
94 res
95 }
96}
97
98#[derive(Debug, Clone, Copy)]
99pub enum Command {
100 /// Lists the active dotfiles
101 Ls,
102
103 /// Links all active dotfiles
104 Link {
105 /// Skips the actual linking step. Everything else (e.g. errors and
106 /// prompts) remains unchanged.
107 dry_run: bool,
108 },
109}
110use Command::*;