]> git.sesse.net Git - bcachefs-tools-debian/commitdiff
add command to generate Rust-part CLI completions
authorZhai Can <bczhc0@126.com>
Fri, 10 Nov 2023 12:13:03 +0000 (20:13 +0800)
committerZhai Can <bczhc0@126.com>
Fri, 10 Nov 2023 13:20:01 +0000 (21:20 +0800)
bcachefs.c
cmds.h
rust-src/Cargo.lock
rust-src/Cargo.toml
rust-src/src/cmd_completions.rs [new file with mode: 0644]
rust-src/src/cmd_list.rs
rust-src/src/cmd_mount.rs
rust-src/src/lib.rs

index 827996f8b3ecb7ecb2e638ac1e59ee11d8544b3d..4efe29edaad25c0b3fb96865e0a5f4907b3f1d55 100644 (file)
@@ -97,6 +97,9 @@ static void usage(void)
             "  fusemount                Mount a filesystem via FUSE\n"
             "\n"
             "Miscellaneous:\n"
+#ifndef BCACHEFS_NO_RUST
+         "  completions              Generate shell completions\n"
+#endif
             "  version                  Display the version of the invoked bcachefs tool\n");
 }
 
@@ -273,6 +276,8 @@ int main(int argc, char *argv[])
 #ifndef BCACHEFS_NO_RUST
        if (!strcmp(cmd, "mount"))
                return cmd_mount(argc, argv);
+    if (strstr(cmd, "completions"))
+        return cmd_completions(argc, argv);
 #endif
 
 #ifdef BCACHEFS_FUSE
diff --git a/cmds.h b/cmds.h
index 8b56795398bafc7a73b7e0e32747788c9a6c4004..ad810ab3b4791d648ab55e8cef568bef32bac08b 100644 (file)
--- a/cmds.h
+++ b/cmds.h
@@ -60,6 +60,7 @@ int cmd_subvolume_delete(int argc, char *argv[]);
 int cmd_subvolume_snapshot(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 */
index f43cc63c95982cd517f2076488fccd27026bed7a..d270cb41625d6d066184089efdb762f5b6d7f5bf 100644 (file)
@@ -93,6 +93,7 @@ dependencies = [
  "byteorder",
  "chrono",
  "clap",
+ "clap_complete",
  "colored",
  "either",
  "errno 0.2.8",
@@ -237,6 +238,15 @@ dependencies = [
  "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]]
 name = "clap_derive"
 version = "4.3.12"
index b490400c7d03e07dac1f1b6b44ca1f13e26bd71b..e88c05fe2591b69e1d8f986e81c67d4d07d6b985 100644 (file)
@@ -14,6 +14,7 @@ log = { version = "0.4", features = ["std"] }
 chrono = { version = "0.4", default-features = false }
 colored = "2"
 clap = { version = "4.0.32", features = ["derive", "wrap_help"] }
+clap_complete = "4.4.4"
 anyhow = "1.0"
 libc = "0.2.69"
 udev = "0.7.0"
diff --git a/rust-src/src/cmd_completions.rs b/rust-src/src/cmd_completions.rs
new file mode 100644 (file)
index 0000000..5185969
--- /dev/null
@@ -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
+}
index 3f86b8cd427db4f18b287d8c973d3a07cc912cd6..574f7cee01e1ebef9eb6d419c02de7fb89c44172 100644 (file)
@@ -8,9 +8,9 @@ use bch_bindgen::btree::BtreeTrans;
 use bch_bindgen::btree::BtreeIter;
 use bch_bindgen::btree::BtreeNodeIter;
 use bch_bindgen::btree::BtreeIterFlags;
-use clap::Parser;
-use std::ffi::{CStr, OsStr, c_int, c_char};
-use std::os::unix::ffi::OsStrExt;
+use clap::{Args, Parser};
+use std::ffi::{c_int, c_char};
+use crate::transform_c_args;
 
 fn list_keys(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
     let trans = BtreeTrans::new(fs);
@@ -84,7 +84,7 @@ fn list_nodes_ondisk(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
     Ok(())
 }
 
-#[derive(Clone, clap::ValueEnum)]
+#[derive(Clone, clap::ValueEnum, Debug)]
 enum Mode {
     Keys,
     Formats,
@@ -92,8 +92,8 @@ enum Mode {
     NodesOndisk,
 }
 
-#[derive(Parser)]
-struct Cli {
+#[derive(Parser, Debug)]
+pub struct Cli {
     /// Btree to list from
     #[arg(short, long, default_value_t=bcachefs::btree_id::BTREE_ID_extents)]
     btree:      bcachefs::btree_id,
@@ -120,7 +120,7 @@ struct Cli {
     /// Force color on/off. Default: autodetect tty
     #[arg(short, long, action = clap::ArgAction::Set, default_value_t=atty::is(Stream::Stdout))]
     colorize:   bool,
-   
+
     /// Verbose mode
     #[arg(short, long)]
     verbose:    bool,
@@ -157,12 +157,9 @@ fn cmd_list_inner(opt: Cli) -> anyhow::Result<()> {
 }
 
 #[no_mangle]
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
 pub extern "C" fn cmd_list(argc: c_int, argv: *const *const c_char) {
-    let argv: Vec<_> = (0..argc)
-        .map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
-        .map(|i| OsStr::from_bytes(i.to_bytes()))
-        .collect();
-
+    transform_c_args!(argv, argc, argv);
     let opt = Cli::parse_from(argv);
     colored::control::set_override(opt.colorize);
     if let Err(e) = cmd_list_inner(opt) {
index f7c6d920cfde33e42c4bcea1797f38d16ae3d055..9d58cb3ea4eef100b3c4882ffa49bddc815d791c 100644 (file)
@@ -1,10 +1,10 @@
 use atty::Stream;
 use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle};
 use log::{info, debug, error, LevelFilter};
-use clap::Parser;
+use clap::{Parser, Subcommand};
 use uuid::Uuid;
 use std::path::PathBuf;
-use crate::key;
+use crate::{key, transform_c_args};
 use crate::key::KeyLoc;
 use crate::logger::SimpleLogger;
 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.
 #[derive(Parser, Debug)]
 #[command(author, version, about, long_about = None)]
-struct Cli {
+pub struct Cli {
     /// Where the password would be loaded from.
     ///
     /// Possible values are:
@@ -228,12 +228,9 @@ fn cmd_mount_inner(opt: Cli) -> anyhow::Result<()> {
 }
 
 #[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 {
-    let argv: Vec<_> = (0..argc)
-        .map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
-        .map(|i| OsStr::from_bytes(i.to_bytes()))
-        .collect();
-
+    transform_c_args!(argv, argc, argv);
     let opt = Cli::parse_from(argv);
 
     log::set_boxed_logger(Box::new(SimpleLogger)).unwrap();
index 159d049de51feaffbd7c5619224929783451bbeb..64297b41d7dce8d56c6a1bdbb0c6ef7ea74add00 100644 (file)
@@ -1,7 +1,24 @@
+use clap::Subcommand;
+
 pub mod key;
 pub mod logger;
 pub mod cmd_mount;
 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_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)]
 struct ErrnoError(errno::Errno);
 impl std::fmt::Display for ErrnoError {