Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
194 lines
6.7 KiB
Rust
194 lines
6.7 KiB
Rust
use std::path::PathBuf;
|
|
use std::str::FromStr;
|
|
|
|
use clap::*;
|
|
|
|
/**
|
|
* Main struct for command-line arguments.
|
|
*/
|
|
#[derive(Parser, Debug, Clone)]
|
|
#[command(author, version, about, long_about = None)]
|
|
pub struct Args {
|
|
#[command(flatten)]
|
|
pub mode: ModeArgs,
|
|
#[command(flatten)]
|
|
pub item: ItemArgs,
|
|
#[command(flatten)]
|
|
pub options: OptionsArgs,
|
|
|
|
#[arg(help("A list of either item IDs or tags"))]
|
|
#[arg(value_parser = clap::value_parser!(NumberOrString))]
|
|
#[arg(required = false)]
|
|
pub ids_or_tags: Vec<NumberOrString>,
|
|
}
|
|
|
|
/**
|
|
* Struct for mode-specific arguments.
|
|
*/
|
|
#[derive(Parser, Debug, Clone)]
|
|
pub struct ModeArgs {
|
|
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["get", "diff", "list", "delete", "info", "status"]))]
|
|
#[arg(help("Save an item using any tags or metadata provided"))]
|
|
pub save: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "diff", "list", "delete", "info", "status"]))]
|
|
#[arg(help(
|
|
"Get an item either by it's ID or by a combination of matching tags and metatdata"
|
|
))]
|
|
pub get: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), long, conflicts_with_all(["save", "get", "list", "delete", "info", "status"]))]
|
|
#[arg(help("Show a diff between two items by ID"))]
|
|
pub diff: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "delete", "info", "status"]))]
|
|
#[arg(help("List items, filtering on tags or metadata if given"))]
|
|
pub list: bool,
|
|
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "info", "status"]))]
|
|
#[arg(help("Delete items either by ID or by matching tags"))]
|
|
#[arg(requires = "ids_or_tags")]
|
|
pub delete: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "delete", "status"]))]
|
|
#[arg(help(
|
|
"Get an item either by it's ID or by a combination of matching tags and metatdata"
|
|
))]
|
|
pub info: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), short('S'), long, conflicts_with_all(["save", "get", "diff", "list", "delete", "info", "server", "status_plugins"]))]
|
|
#[arg(help("Show status of directories and supported compression algorithms"))]
|
|
pub status: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), long, conflicts_with_all(["save", "get", "diff", "list", "delete", "info", "status", "server"]))]
|
|
#[arg(help("Show available plugins and their configurations"))]
|
|
pub status_plugins: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), long, conflicts_with_all(["save", "get", "diff", "list", "delete", "info", "status"]))]
|
|
#[arg(help("Start REST HTTP server"))]
|
|
pub server: bool,
|
|
|
|
#[arg(group("mode"), help_heading("Mode Options"), long, conflicts_with_all(["save", "get", "diff", "list", "delete", "info", "status", "server"]))]
|
|
#[arg(help("Generate default configuration and output to stdout"))]
|
|
pub generate_config: bool,
|
|
|
|
#[arg(help_heading("Server Options"), long, env("KEEP_SERVER_ADDRESS"))]
|
|
#[arg(help("Server address to bind to"))]
|
|
pub server_address: Option<String>,
|
|
|
|
#[arg(help_heading("Server Options"), long, env("KEEP_SERVER_PORT"))]
|
|
#[arg(help("Server port to bind to"))]
|
|
pub server_port: Option<u16>,
|
|
}
|
|
|
|
/**
|
|
* Struct for item-specific arguments.
|
|
*/
|
|
#[derive(Parser, Debug, Clone)]
|
|
pub struct ItemArgs {
|
|
#[arg(help_heading("Item Options"), short, long, env("KEEP_COMPRESSION"))]
|
|
#[arg(help("Compression algorithm to use when saving items"))]
|
|
pub compression: Option<String>,
|
|
|
|
#[arg(help_heading("Item Options"), short('M'), long, env("KEEP_META_PLUGINS"))]
|
|
#[arg(help("Meta plugins to use when saving items"))]
|
|
pub meta_plugins: Vec<String>,
|
|
|
|
#[arg(help_heading("Item Options"), long, env("KEEP_FILTERS"))]
|
|
#[arg(help("Filter string to apply to content when getting items"))]
|
|
pub filters: Option<String>,
|
|
}
|
|
|
|
|
|
/**
|
|
* Struct for general options.
|
|
*/
|
|
#[derive(Parser, Debug, Default, Clone)]
|
|
pub struct OptionsArgs {
|
|
#[arg(long, env("KEEP_CONFIG"))]
|
|
#[arg(help("Specify the configuration file to use"))]
|
|
pub config: Option<PathBuf>,
|
|
|
|
#[arg(long, env("KEEP_DIR"))]
|
|
#[arg(help("Specify the directory to use for storage"))]
|
|
pub dir: Option<PathBuf>,
|
|
|
|
#[arg(
|
|
long,
|
|
env("KEEP_LIST_FORMAT"),
|
|
default_value("id,time,size,tags,meta:hostname")
|
|
)]
|
|
#[arg(help("A comma separated list of columns to display with --list"))]
|
|
pub list_format: String,
|
|
|
|
#[arg(short('H'), long)]
|
|
#[arg(help("Display file sizes with units"))]
|
|
pub human_readable: bool,
|
|
|
|
#[arg(short, long, action = clap::ArgAction::Count, conflicts_with("quiet"))]
|
|
#[arg(help("Increase message verbosity, can be given more than once"))]
|
|
pub verbose: u8,
|
|
|
|
#[arg(short, long)]
|
|
#[arg(help("Do not show any messages"))]
|
|
pub quiet: bool,
|
|
|
|
#[arg(long, value_enum, default_value("table"))]
|
|
#[arg(help("Output format (only works with --info, --status, --list)"))]
|
|
pub output_format: Option<String>,
|
|
|
|
#[arg(long, env("KEEP_SERVER_PASSWORD"))]
|
|
#[arg(help("Password for server authentication (requires --server)"))]
|
|
pub server_password: Option<String>,
|
|
|
|
#[arg(long, env("KEEP_SERVER_PASSWORD_HASH"))]
|
|
#[arg(help("Password hash for server authentication (requires --server)"))]
|
|
pub server_password_hash: Option<String>,
|
|
|
|
#[arg(long, help("Force output even when binary data would be sent to a TTY"))]
|
|
pub force: bool,
|
|
}
|
|
|
|
use derive_more::From;
|
|
|
|
/**
|
|
* Enum for representing either a number or a string.
|
|
*/
|
|
#[derive(Debug, Clone, From)]
|
|
pub enum NumberOrString {
|
|
Number(i64),
|
|
Str(String),
|
|
}
|
|
|
|
impl FromStr for NumberOrString {
|
|
type Err = Error;
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(s.parse::<i64>()
|
|
.map(NumberOrString::Number)
|
|
.unwrap_or_else(|_| NumberOrString::Str(s.to_string())))
|
|
}
|
|
}
|
|
|
|
impl Args {
|
|
/// Validate the arguments based on the selected mode
|
|
pub fn validate(&self) -> Result<(), String> {
|
|
// Check if --delete is used and ids_or_tags is empty
|
|
if self.mode.delete && self.ids_or_tags.is_empty() {
|
|
return Err("At least one ID is required when using --delete".to_string());
|
|
}
|
|
|
|
// Check if --delete is used and any of the ids_or_tags are tags (strings)
|
|
if self.mode.delete {
|
|
for item in &self.ids_or_tags {
|
|
if let NumberOrString::Str(_) = item {
|
|
return Err("Tags are not supported for --delete, only IDs".to_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|