1pub mod archive;
24pub mod container;
25pub mod error;
26pub mod global;
27pub mod password;
28pub mod plugin;
29
30use anyhow::{anyhow, Result};
31use clap::{crate_version, Parser, Subcommand};
32use env_logger::Builder;
33use log::LevelFilter;
34use nuts_container::{Container, OpenOptionsBuilder};
35use nuts_tool_api::tool::Plugin;
36use rprompt::prompt_reply;
37
38use crate::backend::{PluginBackend, PluginBackendOpenBuilder};
39use crate::cli::archive::ArchiveArgs;
40use crate::cli::container::ContainerArgs;
41use crate::cli::global::{GlobalArgs, GLOBALS};
42use crate::cli::password::password_from_source;
43use crate::cli::plugin::PluginArgs;
44use crate::config::{ContainerConfig, PluginConfig};
45
46#[derive(Debug, Parser)]
47#[clap(name = "nuts", bin_name = "nuts")]
48#[clap(version = crate_version!())]
49pub struct NutsCli {
50 #[clap(subcommand)]
51 command: Commands,
52
53 #[command(flatten)]
54 global_args: GlobalArgs,
55}
56
57impl NutsCli {
58 pub fn configure_logging(&self) {
59 let filter = match self.global_args.verbose {
60 0 => LevelFilter::Off,
61 1 => LevelFilter::Info,
62 2 => LevelFilter::Debug,
63 _ => LevelFilter::Trace,
64 };
65
66 Builder::new().filter_level(filter).init();
67 }
68
69 pub fn run(&self) -> Result<()> {
70 self.global_args.init();
71
72 self.command.run()
73 }
74}
75
76#[derive(Debug, Subcommand)]
77pub enum Commands {
78 Plugin(PluginArgs),
80
81 Container(ContainerArgs),
83
84 Archive(ArchiveArgs),
86}
87
88impl Commands {
89 pub fn run(&self) -> Result<()> {
90 match self {
91 Self::Plugin(args) => args.run(),
92 Self::Container(args) => args.run(),
93 Self::Archive(args) => args.run(),
94 }
95 }
96}
97
98fn open_container(name: &str) -> Result<Container<PluginBackend>> {
99 let container_config = ContainerConfig::load()?;
100 let plugin_config = PluginConfig::load()?;
101 let verbose = GLOBALS.with_borrow(|g| g.verbose);
102
103 let plugin = container_config
104 .get_plugin(name)
105 .ok_or_else(|| anyhow!("no such container: {}", name))?;
106 let exe = plugin_config.path(plugin)?;
107
108 let plugin = Plugin::new(&exe);
109 let plugin_builder = PluginBackendOpenBuilder::new(plugin, name, verbose)?;
110
111 let builder = OpenOptionsBuilder::new().with_password_callback(password_from_source);
112 let options = builder.build::<PluginBackend>()?;
113
114 Container::open(plugin_builder, options).map_err(|err| err.into())
115}
116
117pub fn prompt_yes_no(prompt: &str, force: bool) -> Result<bool> {
118 let ok = force || {
119 let msg = format!("{} [yes/NO] ", prompt);
120 let reply = prompt_reply(msg)?;
121
122 reply == "yes"
123 };
124
125 Ok(ok)
126}