nuts_tool_api/plugin/
cli.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
23//! Command line interface for the plugin.
24
25use clap::{crate_version, ArgAction, Args, Parser, Subcommand, ValueEnum};
26use std::convert::{TryFrom, TryInto};
27use std::ops::Deref;
28use std::str::FromStr;
29
30#[derive(Clone, Debug)]
31pub struct SizeArg<T>(T);
32
33impl<T: TryFrom<u64>> FromStr for SizeArg<T> {
34    type Err = String;
35
36    fn from_str(s: &str) -> Result<Self, String> {
37        let (num_str, factor) = if let Some(s) = s.strip_suffix('k') {
38            (s, 1024)
39        } else if let Some(s) = s.strip_suffix('m') {
40            (s, 1024 * 1024)
41        } else if let Some(s) = s.strip_suffix('g') {
42            (s, 1024 * 1024 * 1024)
43        } else {
44            (s, 1)
45        };
46
47        if let Ok(n) = num_str.parse::<u64>() {
48            if let Ok(n) = TryInto::<T>::try_into(n * factor) {
49                return Ok(Self(n));
50            }
51        }
52
53        Err("must be a number or a number suffixed with 'k', 'm', 'g'".to_string())
54    }
55}
56
57impl<T> Deref for SizeArg<T> {
58    type Target = T;
59
60    fn deref(&self) -> &T {
61        &self.0
62    }
63}
64
65#[derive(Clone, Copy, Debug, ValueEnum)]
66pub enum Format {
67    Text,
68    Bson,
69}
70
71#[derive(Args, Debug)]
72pub struct InfoArgs {
73    #[clap(long, default_value = "text")]
74    pub format: Format,
75}
76
77#[derive(Args, Debug)]
78pub struct OpenArgs {
79    /// Name of the container
80    pub name: String,
81}
82
83#[derive(Args, Debug)]
84pub struct CreateArgs<CX: Args> {
85    /// Name of the container
86    pub name: String,
87
88    #[clap(flatten)]
89    pub extra: CX,
90}
91
92#[derive(Debug, Subcommand)]
93pub enum PluginCommand<CX: Args> {
94    /// Prints information about the plugin
95    Info(InfoArgs),
96
97    /// Opens a backend instance
98    Open(OpenArgs),
99
100    /// Creates a new backend instance
101    Create(CreateArgs<CX>),
102}
103
104impl<CX: Args> PluginCommand<CX> {
105    pub fn as_info(&self) -> Option<&InfoArgs> {
106        match self {
107            Self::Info(args) => Some(args),
108            _ => None,
109        }
110    }
111
112    pub fn as_open(&self) -> Option<&OpenArgs> {
113        match self {
114            Self::Open(args) => Some(args),
115            _ => None,
116        }
117    }
118
119    pub fn as_create(&self) -> Option<&CreateArgs<CX>> {
120        match self {
121            Self::Create(args) => Some(args),
122            _ => None,
123        }
124    }
125}
126
127#[derive(Debug, Parser)]
128#[clap(version = crate_version!())]
129pub struct PluginCli<CX: Args> {
130    #[clap(subcommand)]
131    pub command: PluginCommand<CX>,
132
133    /// Enable verbose output. Can be called multiple times
134    #[clap(short, long, action = ArgAction::Count, global = true)]
135    pub verbose: u8,
136}