nuts_tool/cli/
global.rs

1// MIT License
2//
3// Copyright (c) 2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23use clap::{ArgAction, ArgGroup, Args};
24use log::debug;
25use std::cell::RefCell;
26use std::os::fd::RawFd;
27use std::path::PathBuf;
28
29use crate::say::Say;
30
31thread_local! {
32    pub static GLOBALS: RefCell<GlobalValues> = RefCell::new(Default::default());
33}
34
35#[derive(Default)]
36pub struct GlobalValues {
37    pub verbose: u8,
38    pub say: Say,
39    pub password_source: PasswordSource,
40}
41
42impl GlobalValues {
43    fn init_password_source(&mut self, args: &GlobalArgs) {
44        if let Some(fd) = args.password_from_fd {
45            debug!("read password from fd {fd}");
46            self.password_source = PasswordSource::Fd(fd);
47        } else if let Some(path) = args.password_from_file.as_ref() {
48            debug!("read password from path {}", path.display());
49            self.password_source = PasswordSource::Path(path.clone());
50        } else {
51            debug!("read password from console");
52            self.password_source = PasswordSource::Console;
53        }
54    }
55}
56
57pub enum PasswordSource {
58    Fd(RawFd),
59    Path(PathBuf),
60    Console,
61}
62
63impl PasswordSource {
64    pub fn new(fd: Option<RawFd>, path: Option<PathBuf>) -> PasswordSource {
65        if let Some(fd) = fd {
66            Self::Fd(fd)
67        } else if let Some(path) = path {
68            Self::Path(path)
69        } else {
70            Self::Console
71        }
72    }
73}
74
75impl Default for PasswordSource {
76    fn default() -> Self {
77        Self::Console
78    }
79}
80
81#[derive(Args, Clone, Debug)]
82#[clap(group(ArgGroup::new("password").required(false).multiple(false)))]
83pub struct GlobalArgs {
84    /// Enable verbose output. Can be called multiple times
85    #[clap(short, long, action = ArgAction::Count, global = true)]
86    pub verbose: u8,
87
88    /// Be quiet. Don't produce any output
89    #[clap(short, long, action = ArgAction::SetTrue, global = true)]
90    pub quiet: bool,
91
92    /// Reads the password from the specified file descriptor <FD>. The
93    /// password is the first line until a `\n` is read.
94    #[clap(long, group = "password", global = true, value_name = "FD")]
95    pub password_from_fd: Option<RawFd>,
96
97    /// Reads the password from the specified file <PATH>. The password is the
98    /// first line until a `\n` is read.
99    #[clap(long, group = "password", global = true, value_name = "PATH")]
100    pub password_from_file: Option<PathBuf>,
101}
102
103impl GlobalArgs {
104    pub fn init(&self) {
105        GLOBALS.with_borrow_mut(|g| {
106            g.verbose = self.verbose;
107            g.say.set_quiet(self.quiet);
108            g.init_password_source(self);
109        });
110    }
111}