mod actions;
mod render;
use std::io::{IsTerminal, Read};
use clap::{Args as ClapArgs, Subcommand};
use crate::confirm::{decide_without_prompt, prompt_yes_no, ConfirmCfg, Severity};
use crate::context::Ctx;
use crate::errors::CliError;
#[derive(Debug, ClapArgs)]
pub struct Args {
#[command(subcommand)]
pub cmd: KvCmd,
}
#[derive(Debug, Subcommand)]
pub enum KvCmd {
#[command(subcommand)]
Set(SetCmd),
#[command(subcommand)]
List(ListCmd),
}
#[derive(Debug, Subcommand)]
pub enum SetCmd {
Put { key: String, value: String },
Get { key: String },
Ls(SetsLsArgs),
Delete { key: String },
Bulk(BulkArgs),
}
#[derive(Debug, ClapArgs)]
pub struct SetsLsArgs {
#[arg(long)]
pub limit: Option<i64>,
#[arg(long)]
pub cursor: Option<String>,
}
#[derive(Debug, ClapArgs)]
pub struct BulkArgs {
#[arg(long = "add")]
pub add: Vec<String>,
#[arg(long = "delete")]
pub delete: Vec<String>,
}
#[derive(Debug, Subcommand)]
pub enum ListCmd {
Ls(ListsLsArgs),
Get(ListGetArgs),
Create { key: String, items: Vec<String> },
Append { key: String, item: String },
Contains { key: String, item: String },
RemoveItem { key: String, item: String },
Update(ListUpdateArgs),
Delete { key: String },
}
#[derive(Debug, ClapArgs)]
pub struct ListsLsArgs {
#[arg(long)]
pub limit: Option<i64>,
#[arg(long)]
pub cursor: Option<String>,
}
#[derive(Debug, ClapArgs)]
pub struct ListGetArgs {
pub key: String,
#[arg(long)]
pub limit: Option<i64>,
#[arg(long)]
pub cursor: Option<String>,
}
#[derive(Debug, ClapArgs)]
pub struct ListUpdateArgs {
pub key: String,
#[arg(long = "add")]
pub add_items: Vec<String>,
#[arg(long = "remove")]
pub remove_items: Vec<String>,
}
pub async fn run(args: Args, ctx: Ctx) -> Result<(), CliError> {
match args.cmd {
KvCmd::Set(c) => actions::set(c, ctx).await,
KvCmd::List(c) => actions::list(c, ctx).await,
}
}
fn confirm_mild(ctx: &Ctx, prompt: &str) -> Result<(), CliError> {
let cfg = ConfirmCfg::new(
ctx.global.yes_count,
ctx.global.no_input,
ctx.out.stdout_is_tty,
);
let proceed = match decide_without_prompt(Severity::Mild, cfg)? {
true => true,
false => prompt_yes_no(prompt)?,
};
if !proceed {
return Err(CliError::Cancelled);
}
Ok(())
}
fn read_stdin() -> Result<String, CliError> {
if std::io::stdin().is_terminal() {
return Err(CliError::Arg(
"value `-` requires stdin to be piped".to_string(),
));
}
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
Ok(buf.trim_end_matches('\n').to_string())
}