add command to generate Rust-part CLI completions

This commit is contained in:
Zhai Can 2023-11-10 20:13:03 +08:00
parent 61134a06fa
commit 1d1fe7b0b6
No known key found for this signature in database
GPG Key ID: 41355DA0B11F584F
8 changed files with 85 additions and 21 deletions

View File

@ -97,6 +97,9 @@ static void usage(void)
" fusemount Mount a filesystem via FUSE\n" " fusemount Mount a filesystem via FUSE\n"
"\n" "\n"
"Miscellaneous:\n" "Miscellaneous:\n"
#ifndef BCACHEFS_NO_RUST
" completions Generate shell completions\n"
#endif
" version Display the version of the invoked bcachefs tool\n"); " version Display the version of the invoked bcachefs tool\n");
} }
@ -273,6 +276,8 @@ int main(int argc, char *argv[])
#ifndef BCACHEFS_NO_RUST #ifndef BCACHEFS_NO_RUST
if (!strcmp(cmd, "mount")) if (!strcmp(cmd, "mount"))
return cmd_mount(argc, argv); return cmd_mount(argc, argv);
if (strstr(cmd, "completions"))
return cmd_completions(argc, argv);
#endif #endif
#ifdef BCACHEFS_FUSE #ifdef BCACHEFS_FUSE

3
cmds.h
View File

@ -60,6 +60,7 @@ int cmd_subvolume_delete(int argc, char *argv[]);
int cmd_subvolume_snapshot(int argc, char *argv[]); int cmd_subvolume_snapshot(int argc, char *argv[]);
int cmd_fusemount(int argc, char *argv[]); int cmd_fusemount(int argc, char *argv[]);
int cmd_mount(int agc, char *argv[]); int cmd_mount(int argc, char *argv[]);
int cmd_completions(int argc, char *argv[]);
#endif /* _CMDS_H */ #endif /* _CMDS_H */

10
rust-src/Cargo.lock generated
View File

@ -93,6 +93,7 @@ dependencies = [
"byteorder", "byteorder",
"chrono", "chrono",
"clap", "clap",
"clap_complete",
"colored", "colored",
"either", "either",
"errno 0.2.8", "errno 0.2.8",
@ -237,6 +238,15 @@ dependencies = [
"terminal_size", "terminal_size",
] ]
[[package]]
name = "clap_complete"
version = "4.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bffe91f06a11b4b9420f62103854e90867812cd5d01557f853c5ee8e791b12ae"
dependencies = [
"clap",
]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.3.12" version = "4.3.12"

View File

@ -14,6 +14,7 @@ log = { version = "0.4", features = ["std"] }
chrono = { version = "0.4", default-features = false } chrono = { version = "0.4", default-features = false }
colored = "2" colored = "2"
clap = { version = "4.0.32", features = ["derive", "wrap_help"] } clap = { version = "4.0.32", features = ["derive", "wrap_help"] }
clap_complete = "4.4.4"
anyhow = "1.0" anyhow = "1.0"
libc = "0.2.69" libc = "0.2.69"
udev = "0.7.0" udev = "0.7.0"

View File

@ -0,0 +1,24 @@
use crate::transform_c_args;
use clap::{Command, CommandFactory, Parser};
use clap_complete::{generate, Generator, Shell};
use std::ffi::{c_char, c_int};
use std::io;
/// Generate shell completions
#[derive(clap::Parser, Debug)]
pub struct Cli {
shell: Shell,
}
fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn cmd_completions(argc: c_int, argv: *const *const c_char) -> c_int {
transform_c_args!(argv, argc, argv);
let cli = Cli::parse_from(argv);
print_completions(cli.shell, &mut super::Cli::command());
0
}

View File

@ -8,9 +8,9 @@ use bch_bindgen::btree::BtreeTrans;
use bch_bindgen::btree::BtreeIter; use bch_bindgen::btree::BtreeIter;
use bch_bindgen::btree::BtreeNodeIter; use bch_bindgen::btree::BtreeNodeIter;
use bch_bindgen::btree::BtreeIterFlags; use bch_bindgen::btree::BtreeIterFlags;
use clap::Parser; use clap::{Args, Parser};
use std::ffi::{CStr, OsStr, c_int, c_char}; use std::ffi::{c_int, c_char};
use std::os::unix::ffi::OsStrExt; use crate::transform_c_args;
fn list_keys(fs: &Fs, opt: Cli) -> anyhow::Result<()> { fn list_keys(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
let trans = BtreeTrans::new(fs); let trans = BtreeTrans::new(fs);
@ -84,7 +84,7 @@ fn list_nodes_ondisk(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[derive(Clone, clap::ValueEnum)] #[derive(Clone, clap::ValueEnum, Debug)]
enum Mode { enum Mode {
Keys, Keys,
Formats, Formats,
@ -92,8 +92,8 @@ enum Mode {
NodesOndisk, NodesOndisk,
} }
#[derive(Parser)] #[derive(Parser, Debug)]
struct Cli { pub struct Cli {
/// Btree to list from /// Btree to list from
#[arg(short, long, default_value_t=bcachefs::btree_id::BTREE_ID_extents)] #[arg(short, long, default_value_t=bcachefs::btree_id::BTREE_ID_extents)]
btree: bcachefs::btree_id, btree: bcachefs::btree_id,
@ -120,7 +120,7 @@ struct Cli {
/// Force color on/off. Default: autodetect tty /// Force color on/off. Default: autodetect tty
#[arg(short, long, action = clap::ArgAction::Set, default_value_t=atty::is(Stream::Stdout))] #[arg(short, long, action = clap::ArgAction::Set, default_value_t=atty::is(Stream::Stdout))]
colorize: bool, colorize: bool,
/// Verbose mode /// Verbose mode
#[arg(short, long)] #[arg(short, long)]
verbose: bool, verbose: bool,
@ -157,12 +157,9 @@ fn cmd_list_inner(opt: Cli) -> anyhow::Result<()> {
} }
#[no_mangle] #[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn cmd_list(argc: c_int, argv: *const *const c_char) { pub extern "C" fn cmd_list(argc: c_int, argv: *const *const c_char) {
let argv: Vec<_> = (0..argc) transform_c_args!(argv, argc, argv);
.map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
.map(|i| OsStr::from_bytes(i.to_bytes()))
.collect();
let opt = Cli::parse_from(argv); let opt = Cli::parse_from(argv);
colored::control::set_override(opt.colorize); colored::control::set_override(opt.colorize);
if let Err(e) = cmd_list_inner(opt) { if let Err(e) = cmd_list_inner(opt) {

View File

@ -1,10 +1,10 @@
use atty::Stream; use atty::Stream;
use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle}; use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle};
use log::{info, debug, error, LevelFilter}; use log::{info, debug, error, LevelFilter};
use clap::Parser; use clap::{Parser, Subcommand};
use uuid::Uuid; use uuid::Uuid;
use std::path::PathBuf; use std::path::PathBuf;
use crate::key; use crate::{key, transform_c_args};
use crate::key::KeyLoc; use crate::key::KeyLoc;
use crate::logger::SimpleLogger; use crate::logger::SimpleLogger;
use std::ffi::{CStr, CString, OsStr, c_int, c_char, c_void}; use std::ffi::{CStr, CString, OsStr, c_int, c_char, c_void};
@ -129,7 +129,7 @@ fn get_devices_by_uuid(uuid: Uuid) -> anyhow::Result<Vec<(PathBuf, bch_sb_handle
/// Mount a bcachefs filesystem by its UUID. /// Mount a bcachefs filesystem by its UUID.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Cli { pub struct Cli {
/// Where the password would be loaded from. /// Where the password would be loaded from.
/// ///
/// Possible values are: /// Possible values are:
@ -228,12 +228,9 @@ fn cmd_mount_inner(opt: Cli) -> anyhow::Result<()> {
} }
#[no_mangle] #[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn cmd_mount(argc: c_int, argv: *const *const c_char) -> c_int { pub extern "C" fn cmd_mount(argc: c_int, argv: *const *const c_char) -> c_int {
let argv: Vec<_> = (0..argc) transform_c_args!(argv, argc, argv);
.map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
.map(|i| OsStr::from_bytes(i.to_bytes()))
.collect();
let opt = Cli::parse_from(argv); let opt = Cli::parse_from(argv);
log::set_boxed_logger(Box::new(SimpleLogger)).unwrap(); log::set_boxed_logger(Box::new(SimpleLogger)).unwrap();

View File

@ -1,7 +1,24 @@
use clap::Subcommand;
pub mod key; pub mod key;
pub mod logger; pub mod logger;
pub mod cmd_mount; pub mod cmd_mount;
pub mod cmd_list; pub mod cmd_list;
pub mod cmd_completions;
#[derive(clap::Parser, Debug)]
#[command(name = "bcachefs")]
pub struct Cli {
#[command(subcommand)]
subcommands: Subcommands,
}
#[derive(Subcommand, Debug)]
enum Subcommands {
List(cmd_list::Cli),
Mount(cmd_mount::Cli),
Completions(cmd_completions::Cli),
}
#[macro_export] #[macro_export]
macro_rules! c_str { macro_rules! c_str {
@ -14,6 +31,18 @@ macro_rules! c_str {
}; };
} }
#[macro_export]
macro_rules! transform_c_args {
($var:ident, $argc:expr, $argv:expr) => {
// TODO: `OsStr::from_bytes` only exists on *nix
use ::std::os::unix::ffi::OsStrExt;
let $var: Vec<_> = (0..$argc)
.map(|i| unsafe { ::std::ffi::CStr::from_ptr(*$argv.add(i as usize)) })
.map(|i| ::std::ffi::OsStr::from_bytes(i.to_bytes()))
.collect();
};
}
#[derive(Debug)] #[derive(Debug)]
struct ErrnoError(errno::Errno); struct ErrnoError(errno::Errno);
impl std::fmt::Display for ErrnoError { impl std::fmt::Display for ErrnoError {