fix: resolve import issues for Args and ProgramWriter in tests

Co-authored-by: aider (openai/andrew/openrouter/qwen/qwen3-coder) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-08-14 13:04:22 -03:00
parent a0e79bc90a
commit 6af1ac30df
3 changed files with 159 additions and 188 deletions

151
src/args.rs Normal file
View File

@@ -0,0 +1,151 @@
use std::path::PathBuf;
use clap::*;
use serde::{Deserialize, Serialize};
/**
* 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"))]
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", "update", "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", "update", "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", "update", "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", "update", "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", "delete", "info", "status"]), requires("ids_or_tags"))]
#[arg(help("Update a specified item ID's tags and/or metadata"))]
pub update: bool,
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "update", "info", "status"]), requires("ids_or_tags"))]
#[arg(help("Delete items either by ID or by matching tags"))]
pub delete: bool,
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "update", "delete", "status"]), requires("ids_or_tags"))]
#[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", "update", "delete", "info", "server"]))]
#[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", "update", "delete", "info", "status"]))]
#[arg(help("Start REST HTTP server on specified address:port or socket path"))]
pub server: Option<String>,
}
/**
* Struct for item-specific arguments.
*/
#[derive(Parser, Debug, Clone)]
pub struct ItemArgs {
#[arg(help_heading("Item Options"), short, long, conflicts_with_all(["get", "delete", "status"]))]
#[arg(help(
"Set metadata for the item using the format KEY=[VALUE], the metadata will be removed if VALUE is not provided"
))]
pub meta: Vec<KeyValue>,
#[arg(help_heading("Item Options"), long, env("KEEP_DIGEST"))]
#[arg(help("Digest algorithm to use when saving items"))]
pub digest: Option<String>,
#[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>,
}
/**
* Struct for general options.
*/
#[derive(Parser, Debug, Default, Clone)]
pub struct OptionsArgs {
#[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, help("Force output even when binary data would be sent to a TTY"))]
pub force: bool,
}
/**
* Enum for representing either a number or a string.
*/
#[derive(Debug, Clone)]
pub enum NumberOrString {
Number(i64),
Str(String),
}
/**
* Struct for key-value pairs.
*/
#[derive(Debug, Clone)]
pub struct KeyValue {
pub key: String,
pub value: String,
}

View File

@@ -4,6 +4,11 @@ pub mod compression_engine;
pub mod db; pub mod db;
pub mod meta_plugin; pub mod meta_plugin;
pub mod modes; pub mod modes;
pub mod plugins;
pub mod args;
// Re-export Args struct for library usage
pub use args::Args;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@@ -1,10 +1,10 @@
use std::path::PathBuf; mod args;
mod modes;
use anyhow::{Context, Error, Result, anyhow}; use anyhow::{Context, Error, Result, anyhow};
use clap::*; use clap::*;
use clap::error::ErrorKind; use clap::error::ErrorKind;
use log::*; use log::*;
mod modes;
extern crate directories; extern crate directories;
use directories::ProjectDirs; use directories::ProjectDirs;
@@ -27,192 +27,7 @@ extern crate serde_json;
extern crate serde_yaml; extern crate serde_yaml;
extern crate serde; extern crate serde;
use args::{Args, KeyValue, NumberOrString, KeepModes};
/**
* Main struct for command-line arguments.
*/
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[command(flatten)]
mode: ModeArgs,
#[command(flatten)]
item: ItemArgs,
#[command(flatten)]
options: OptionsArgs,
#[arg(help("A list of either item IDs or tags"))]
ids_or_tags: Vec<NumberOrString>,
}
/**
* Struct for mode-specific arguments.
*/
#[derive(Parser, Debug, Clone)]
struct ModeArgs {
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["get", "diff", "list", "update", "delete", "info", "status"]))]
#[arg(help("Save an item using any tags or metadata provided"))]
save: bool,
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "diff", "list", "update", "delete", "info", "status"]))]
#[arg(help(
"Get an item either by it's ID or by a combination of matching tags and metatdata"
))]
get: bool,
#[arg(group("mode"), help_heading("Mode Options"), long, conflicts_with_all(["save", "get", "list", "update", "delete", "info", "status"]))]
#[arg(help("Show a diff between two items by ID"))]
diff: bool,
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "update", "delete", "info", "status"]))]
#[arg(help("List items, filtering on tags or metadata if given"))]
list: bool,
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "delete", "info", "status"]), requires("ids_or_tags"))]
#[arg(help("Update a specified item ID's tags and/or metadata"))]
update: bool,
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "update", "info", "status"]), requires("ids_or_tags"))]
#[arg(help("Delete items either by ID or by matching tags"))]
delete: bool,
#[arg(group("mode"), help_heading("Mode Options"), short, long, conflicts_with_all(["save", "get", "diff", "list", "update", "delete", "status"]), requires("ids_or_tags"))]
#[arg(help(
"Get an item either by it's ID or by a combination of matching tags and metatdata"
))]
info: bool,
#[arg(group("mode"), help_heading("Mode Options"), short('S'), long, conflicts_with_all(["save", "get", "diff", "list", "update", "delete", "info", "server"]))]
#[arg(help("Show status of directories and supported compression algorithms"))]
status: bool,
#[arg(group("mode"), help_heading("Mode Options"), long, conflicts_with_all(["save", "get", "diff", "list", "update", "delete", "info", "status"]))]
#[arg(help("Start REST HTTP server on specified address:port or socket path"))]
server: Option<String>,
}
/**
* Struct for item-specific arguments.
*/
#[derive(Parser, Debug, Clone)]
struct ItemArgs {
#[arg(help_heading("Item Options"), short, long, conflicts_with_all(["get", "delete", "status"]))]
#[arg(help(
"Set metadata for the item using the format KEY=[VALUE], the metadata will be removed if VALUE is not provided"
))]
meta: Vec<KeyValue>,
#[arg(help_heading("Item Options"), long, env("KEEP_DIGEST"))]
#[arg(help("Digest algorithm to use when saving items"))]
digest: Option<String>,
#[arg(help_heading("Item Options"), short, long, env("KEEP_COMPRESSION"))]
#[arg(help("Compression algorithm to use when saving items"))]
compression: Option<String>,
#[arg(help_heading("Item Options"), short('M'), long, env("KEEP_META_PLUGINS"))]
#[arg(help("Meta plugins to use when saving items"))]
meta_plugins: Vec<String>,
}
/**
* Struct for general options.
*/
#[derive(Parser, Debug, Default, Clone)]
struct OptionsArgs {
#[arg(long, env("KEEP_DIR"))]
#[arg(help("Specify the directory to use for storage"))]
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"))]
list_format: String,
#[arg(short('H'), long)]
#[arg(help("Display file sizes with units"))]
human_readable: bool,
#[arg(short, long, action = clap::ArgAction::Count, conflicts_with("quiet"))]
#[arg(help("Increase message verbosity, can be given more than once"))]
verbose: u8,
#[arg(short, long)]
#[arg(help("Do not show any messages"))]
quiet: bool,
#[arg(long, value_enum, default_value("table"))]
#[arg(help("Output format (only works with --info, --status, --list)"))]
output_format: Option<String>,
#[arg(long, env("KEEP_SERVER_PASSWORD"))]
#[arg(help("Password for server authentication (requires --server)"))]
server_password: Option<String>,
#[arg(long, help("Force output even when binary data would be sent to a TTY"))]
force: bool,
}
/**
* Enum representing the different modes of operation.
*/
#[derive(Debug, PartialEq)]
enum KeepModes {
Unknown,
Save,
Get,
Diff,
List,
Update,
Delete,
Info,
Status,
Server,
}
/**
* Struct for key-value pairs.
*/
#[derive(Debug, Clone)]
struct KeyValue {
key: String,
value: String,
}
impl FromStr for KeyValue {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
match s.split_once('=') {
Some(kv) => Ok(KeyValue {
key: kv.0.to_string(),
value: kv.1.to_string(),
}),
None => Err(anyhow!("Unable to parse key=value pair")),
}
}
}
/**
* Enum for representing either a number or a string.
*/
#[derive(Debug, Clone)]
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())))
}
}
/** /**
* Main function to handle command-line arguments and execute the appropriate mode. * Main function to handle command-line arguments and execute the appropriate mode.