From d202b8ff488d419630dcaa59fb7b3089846e5c7c Mon Sep 17 00:00:00 2001 From: Abhishek Tripathi Date: Tue, 21 Jan 2025 23:11:03 +0530 Subject: [PATCH] fix: remove conflicts, impl completions command ... so that pb completions can add chat completions via clap_complete - Need to refactor Config struct --- src/cli.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++----- src/option.rs | 80 +++++++++++++++++++++++++------------------- 2 files changed, 130 insertions(+), 41 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index a46ea01aa..431142e74 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -44,7 +44,7 @@ use std::string::String as KafkaSslProtocol; pub const DEFAULT_USERNAME: &str = "admin"; pub const DEFAULT_PASSWORD: &str = "admin"; -#[derive(Parser)] +#[derive(Parser, Debug)] #[command( name = "parseable", bin_name = "parseable", @@ -73,13 +73,88 @@ Join the community at https://logg.ing/community. "#, subcommand_required = true, )] -pub struct Cli { +pub enum Cli { + #[clap(name = "storage")] + /// Storage options + Storage(StorageOptions), + + /// Generate shell completions + #[clap(name = "completions")] + Completion(CommandCompletionOptions), +} + +// taken generously from https://github.com/jj-vcs/jj/blob/be32d4e3efbb9a51deadcc63635a5fb1526d0d6c/cli/src/commands/util/completion.rs#L23C1-L47C36 +// Using an explicit `doc` attribute prevents rustfmt from mangling the list +// formatting without disabling rustfmt for the entire struct. +#[doc = r#"Print a command-line-completion script + +Apply it by running one of these: + +- Bash: `source <(pb util completion bash)` +- Fish: `pb util completion fish | source` +- Nushell: + ```nu + pb util completion nushell | save "completions-pb.nu" + use "completions-pb.nu" * # Or `source "completions-pb.nu"` + ``` +- Zsh: + ```shell + autoload -U compinit + compinit + source <(pb util completion zsh) + ``` +"#] + +#[derive(clap::Args, Debug)] +#[command(verbatim_doc_comment)] +pub struct CommandCompletionOptions { + pub shell: Option, +} + +/// Available shell completions +#[derive(clap::ValueEnum, Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ShellCompletion { + Bash, + Elvish, + Fish, + // Nushell, + PowerShell, + Zsh, +} + +impl ShellCompletion { + pub fn generate(&self, cmd: &mut clap::Command) -> Vec { + use clap_complete::generate; + use clap_complete::Shell; + // use clap_complete_nushell::Nushell; + + let mut buf = Vec::new(); + + let bin_name = "pb"; + + match self { + Self::Bash => generate(Shell::Bash, cmd, bin_name, &mut buf), + Self::Elvish => generate(Shell::Elvish, cmd, bin_name, &mut buf), + Self::Fish => generate(Shell::Fish, cmd, bin_name, &mut buf), + // Self::Nushell => generate(Nushell, cmd, bin_name, &mut buf), + Self::PowerShell => generate(Shell::PowerShell, cmd, bin_name, &mut buf), + Self::Zsh => generate(Shell::Zsh, cmd, bin_name, &mut buf), + } + + buf + } +} + +// todo remove caution +#[derive(clap::Args, Debug)] +pub struct StorageOptions { #[command(subcommand)] - pub storage: StorageOptions, + pub storage: StorageOptionsEnum, } -#[derive(Parser)] -pub enum StorageOptions { +// todo remove caution +#[derive(clap::Subcommand, Debug)] +pub enum StorageOptionsEnum { #[command(name = "local-store")] Local(LocalStoreArgs), @@ -90,7 +165,7 @@ pub enum StorageOptions { Blob(BlobStoreArgs), } -#[derive(Parser)] +#[derive(Parser, Debug)] pub struct LocalStoreArgs { #[command(flatten)] pub options: Options, @@ -98,7 +173,7 @@ pub struct LocalStoreArgs { pub storage: FSConfig, } -#[derive(Parser)] +#[derive(Parser, Debug)] pub struct S3StoreArgs { #[command(flatten)] pub options: Options, @@ -106,7 +181,7 @@ pub struct S3StoreArgs { pub storage: S3Config, } -#[derive(Parser)] +#[derive(Parser, Debug)] pub struct BlobStoreArgs { #[command(flatten)] pub options: Options, diff --git a/src/option.rs b/src/option.rs index e6c2d9200..aa537af79 100644 --- a/src/option.rs +++ b/src/option.rs @@ -16,12 +16,13 @@ * */ -use crate::cli::{Cli, Options, StorageOptions, DEFAULT_PASSWORD, DEFAULT_USERNAME}; +use crate::cli::StorageOptionsEnum; +use crate::cli::{Cli, Options, DEFAULT_PASSWORD, DEFAULT_USERNAME}; use crate::storage::object_storage::parseable_json_path; use crate::storage::{ObjectStorageError, ObjectStorageProvider}; use bytes::Bytes; -use clap::error::ErrorKind; -use clap::Parser; +use clap::CommandFactory; +use clap::{error::ErrorKind, Parser}; use once_cell::sync::Lazy; use parquet::basic::{BrotliLevel, GzipLevel, ZstdLevel}; use serde::{Deserialize, Serialize}; @@ -41,40 +42,53 @@ pub struct Config { impl Config { fn new() -> Self { - match Cli::parse().storage { - StorageOptions::Local(args) => { - if args.options.local_staging_path == args.storage.root { - clap::Error::raw( - ErrorKind::ValueValidation, - "Cannot use same path for storage and staging", - ) - .exit(); + let result = Cli::parse(); + match result { + Cli::Storage(storage_opts) => match storage_opts.storage { + StorageOptionsEnum::Local(args) => { + if args.options.local_staging_path == args.storage.root { + clap::Error::raw( + ErrorKind::ValueValidation, + "Cannot use same path for storage and staging", + ) + .exit(); + } + + if args.options.hot_tier_storage_path.is_some() { + clap::Error::raw( + ErrorKind::ValueValidation, + "Cannot use hot tier with local-store subcommand.", + ) + .exit(); + } + + Config { + options: args.options, + storage: Arc::new(args.storage), + storage_name: "drive", + } } - - if args.options.hot_tier_storage_path.is_some() { - clap::Error::raw( - ErrorKind::ValueValidation, - "Cannot use hot tier with local-store subcommand.", - ) - .exit(); - } - - Config { + StorageOptionsEnum::S3(args) => Config { options: args.options, storage: Arc::new(args.storage), - storage_name: "drive", - } - } - StorageOptions::S3(args) => Config { - options: args.options, - storage: Arc::new(args.storage), - storage_name: "s3", - }, - StorageOptions::Blob(args) => Config { - options: args.options, - storage: Arc::new(args.storage), - storage_name: "blob_store", + storage_name: "s3", + }, + StorageOptionsEnum::Blob(args) => Config { + options: args.options, + storage: Arc::new(args.storage), + storage_name: "blob_store", + }, }, + Cli::Completion(completion_opts) => { + let shell = completion_opts + .shell + .unwrap_or(crate::cli::ShellCompletion::Bash); + let mut cmd = Cli::command(); + let output = shell.generate(&mut cmd); + println!("{}", String::from_utf8_lossy(&output)); + std::process::exit(0); + unreachable!() + } } }