1#![warn(clippy::pedantic)]
13#![allow(clippy::module_name_repetitions)]
14#![allow(clippy::doc_markdown)]
15#![allow(clippy::struct_excessive_bools)]
16
17use anyhow::Result;
18use clap::{ArgAction, Parser, ValueEnum};
19
20mod balance;
21mod check;
22mod device;
23mod filesystem;
24mod inspect;
25mod property;
26mod qgroup;
27mod quota;
28mod receive;
29mod replace;
30mod rescue;
31mod restore;
32mod scrub;
33mod send;
34mod subvolume;
35mod util;
36
37pub use crate::{
38 balance::*, check::*, device::*, filesystem::*, inspect::*, property::*,
39 qgroup::*, quota::*, receive::*, replace::*, rescue::*, restore::*,
40 scrub::*, send::*, subvolume::*,
41};
42
43#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
45pub enum Format {
46 #[default]
47 Text,
48 Json,
49}
50
51#[derive(
53 Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum,
54)]
55pub enum Level {
56 Debug,
57 #[default]
58 Info,
59 Warn,
60 Error,
61}
62
63#[derive(Parser, Debug)]
73#[allow(clippy::doc_markdown)]
74#[clap(version, infer_subcommands = true)]
75pub struct Arguments {
76 #[clap(flatten)]
77 pub global: GlobalOptions,
78
79 #[clap(subcommand)]
80 pub command: Command,
81}
82
83const GLOBAL_OPTIONS: &str = "Global options";
84
85#[derive(Parser, Debug)]
87pub struct GlobalOptions {
88 #[clap(global = true, short, long, action = ArgAction::Count, help_heading = GLOBAL_OPTIONS)]
90 pub verbose: u8,
91
92 #[clap(global = true, short, long, help_heading = GLOBAL_OPTIONS)]
94 pub quiet: bool,
95
96 #[clap(global = true, long, help_heading = GLOBAL_OPTIONS)]
98 pub dry_run: bool,
99
100 #[clap(global = true, long, help_heading = GLOBAL_OPTIONS)]
102 pub log: Option<Level>,
103
104 #[clap(global = true, long, help_heading = GLOBAL_OPTIONS)]
106 pub format: Option<Format>,
107}
108
109pub trait Runnable {
111 fn run(&self, format: Format, dry_run: bool) -> Result<()>;
117}
118
119#[derive(Parser, Debug)]
120pub enum Command {
121 Balance(BalanceCommand),
122 Check(CheckCommand),
123 Device(DeviceCommand),
124 Filesystem(FilesystemCommand),
125 #[command(alias = "inspect-internal")]
126 Inspect(InspectCommand),
127 #[cfg(feature = "mkfs")]
128 Mkfs(btrfs_mkfs::args::Arguments),
129 Property(PropertyCommand),
130 Qgroup(QgroupCommand),
131 Quota(QuotaCommand),
132 Receive(ReceiveCommand),
133 Replace(ReplaceCommand),
134 Rescue(RescueCommand),
135 Restore(RestoreCommand),
136 Scrub(ScrubCommand),
137 Send(SendCommand),
138 Subvolume(SubvolumeCommand),
139 #[cfg(feature = "tune")]
140 Tune(btrfs_tune::args::Arguments),
141}
142
143impl Runnable for Command {
144 fn run(&self, format: Format, dry_run: bool) -> Result<()> {
145 match self {
146 Command::Balance(cmd) => cmd.run(format, dry_run),
147 Command::Check(cmd) => cmd.run(format, dry_run),
148 Command::Device(cmd) => cmd.run(format, dry_run),
149 Command::Filesystem(cmd) => cmd.run(format, dry_run),
150 Command::Inspect(cmd) => cmd.run(format, dry_run),
151 #[cfg(feature = "mkfs")]
152 Command::Mkfs(args) => btrfs_mkfs::run::run(args),
153 Command::Property(cmd) => cmd.run(format, dry_run),
154 Command::Qgroup(cmd) => cmd.run(format, dry_run),
155 Command::Quota(cmd) => cmd.run(format, dry_run),
156 Command::Receive(cmd) => cmd.run(format, dry_run),
157 Command::Replace(cmd) => cmd.run(format, dry_run),
158 Command::Rescue(cmd) => cmd.run(format, dry_run),
159 Command::Restore(cmd) => cmd.run(format, dry_run),
160 Command::Scrub(cmd) => cmd.run(format, dry_run),
161 Command::Send(cmd) => cmd.run(format, dry_run),
162 Command::Subvolume(cmd) => cmd.run(format, dry_run),
163 #[cfg(feature = "tune")]
164 Command::Tune(args) => btrfs_tune::run::run(args),
165 }
166 }
167}
168
169impl Arguments {
170 pub fn run(&self) -> Result<()> {
176 let level = if let Some(explicit) = self.global.log {
177 match explicit {
178 Level::Debug => log::LevelFilter::Debug,
179 Level::Info => log::LevelFilter::Info,
180 Level::Warn => log::LevelFilter::Warn,
181 Level::Error => log::LevelFilter::Error,
182 }
183 } else if self.global.quiet {
184 log::LevelFilter::Error
185 } else {
186 match self.global.verbose {
187 0 => log::LevelFilter::Warn,
188 1 => log::LevelFilter::Info,
189 2 => log::LevelFilter::Debug,
190 _ => log::LevelFilter::Trace,
191 }
192 };
193 env_logger::Builder::new().filter_level(level).init();
194 self.command
195 .run(self.global.format.unwrap_or_default(), self.global.dry_run)
196 }
197}