kinda wanna structure my plan out

This commit is contained in:
lda committed 2026-05-21 04:46:59 +07:00 Verified
1 parent 19414dc9cd
commit 6a547bceb9
6 files changed
+241 -18

No files matched your search

+10 -4
View File
@@ -6,11 +6,17 @@ csa show <acc> -> verbose account <acc>
csa swap -- codex resume -> csa swap auth to the best account, then execvpe
csa swap <acc> -- codex logout -> csa swap to that acount, then execvpe
optional features
// important feature
csa refresh (--all --expired OR <acc name>)
csa login
csa logout
csa refresh
// account:
csa account show (--all, --expired, <name>) default current
csa account rename
csa accound add
csa account refresh
csa login <account>
csa logout <account> --DO NOT Support --all, DO support --expired ONLY with confirmation
logout and refresh have official bindings in codex-login (logout with revoke, refresh_token)
+1
View File
@@ -0,0 +1 @@
//! this deals with authentication. validate, refresh, and quota check.
+222
View File
@@ -0,0 +1,222 @@
#[derive(clap::Parser, Debug)]
#[clap(
name = "csa",
version,
about = "A CLI tool to manage multiple Codex accounts"
)]
pub struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(clap::Subcommand, Debug)]
pub enum Commands {
Show(ShowCommand),
Add(AddCommand),
Swap(SwapCommand),
Account(AccountCommand),
}
#[derive(Clone, Debug, clap::Parser)]
pub struct ShowCommand {
/// specify to show the account.
pub acc: Option<String>,
}
/// `csa add --use --name myacc` or `csa add myacc --use` would add an account with nickname "myacc" and switch to it right away.
#[derive(Clone, Debug, clap::Parser)]
pub struct AddCommand {
/// use the account right away after adding, instead of just adding to the store.
#[clap(long = "use", short = 'u')]
use_rn: bool,
/// use to specify a nickname for the account. If not specified, will be the email or something else identifiable.
/// highly recommend.
pub name: Option<String>,
}
/// `csa swap acc1` would swap to acc1 if it exists. Else error out. `csa swap` would swap to the most capable account, which is determined by the internal logic of csa, and is usually the best valid account with the best availability.
///
/// to use something right after add/swap, use your favourite shell feature, the semicolon `;`.
// that looks like an emoji yo (`;`) wth
#[derive(Clone, Debug, clap::Parser)]
pub struct SwapCommand {
/// specify to swap to the account, instead of best effort swap to the most capable account.
pub acc: Option<String>,
}
#[derive(Clone, Debug, clap::Parser)]
pub struct AccountCommand {
#[clap(subcommand)]
subcommand: AccountSubCommand,
}
#[derive(Clone, Debug, clap::Subcommand)]
pub enum AccountSubCommand {
Refresh {
/// specify to refresh the account. Else refresh all accounts.
// this and AccountCommand.account at the same time is an error.
// chat how do i enforce above rule?
acc: Option<String>,
/// specify to only refresh the account if it's expired. Else refresh anyway.
expired: bool,
},
// also logout
Remove {
/// specify to remove the account
// do NOT support remove all accounts.
acc: String,
},
Rename {
/// specify to rename the account
acc: String,
/// the new name for the account
new_name: String,
},
// same thing as Show above
Show {
/// specify to show the account. Else show all accounts.
acc: Option<String>,
},
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[test]
fn show() {
let cli = Cli::try_parse_from(["test", "show"]).expect("csa show");
println!("{cli:#?}");
}
#[test]
fn show_me() {
let cli = Cli::try_parse_from(["test", "show", "me"]).expect("csa show me");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Show(ShowCommand { acc: Some(acc) }) if acc == "me")
);
}
#[test]
fn add() {
let cli = Cli::try_parse_from(["test", "add"]).expect("csa add");
println!("{cli:#?}");
}
#[test]
fn add_use() {
let cli = Cli::try_parse_from(["test", "add", "--use"]).expect("csa add --use");
println!("{cli:#?}");
assert!(matches!(
cli.command,
Commands::Add(AddCommand {
use_rn: true,
name: None,
})
));
}
#[test]
fn add_use_name() {
let cli = Cli::try_parse_from(["test", "add", "--use", "--name", "myacc"])
.expect("csa add --use --name myacc");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Add(AddCommand { use_rn: true, name: Some(name) }) if name == "myacc")
);
}
#[test]
fn add_name() {
let cli =
Cli::try_parse_from(["test", "add", "--name", "myacc"]).expect("csa add --name myacc");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Add(AddCommand { use_rn: false, name: Some(name) }) if name == "myacc")
);
}
#[test]
fn swap() {
let cli = Cli::try_parse_from(["test", "swap"]).expect("csa swap");
println!("{cli:#?}");
}
#[test]
fn swap_acc() {
let cli = Cli::try_parse_from(["test", "swap", "acc1"]).expect("csa swap acc1");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Swap(SwapCommand { acc: Some(acc) }) if acc == "acc1")
);
}
#[test]
fn refresh() {
let cli = Cli::try_parse_from(["test", "refresh"]).expect("csa refresh");
println!("{cli:#?}");
}
#[test]
fn refresh_acc() {
let cli = Cli::try_parse_from(["test", "refresh", "--acc", "acc1"])
.expect("csa refresh --acc acc1");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Account(AccountCommand { subcommand: AccountSubCommand::Refresh { acc: Some(acc), expired }, .. }) if acc == "acc1" && !expired)
);
}
#[test]
fn refresh_acc_expired() {
let cli = Cli::try_parse_from(["test", "refresh", "--acc", "acc1", "--expired"])
.expect("csa refresh --acc acc1 --expired");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Account(AccountCommand { subcommand: AccountSubCommand::Refresh { acc: Some(acc), expired }, .. }) if acc == "acc1" && expired)
);
}
#[test]
fn refresh_expired() {
let cli =
Cli::try_parse_from(["test", "refresh", "--expired"]).expect("csa refresh --expired");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Account(AccountCommand { subcommand: AccountSubCommand::Refresh { acc: None, expired }, .. }) if expired)
);
}
#[test]
fn remove() {
let cli = Cli::try_parse_from(["test", "remove", "--acc", "acc1"])
.expect("csa remove --acc acc1");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Account(AccountCommand { subcommand: AccountSubCommand::Remove { acc }, .. }) if acc == "acc1")
);
}
#[test]
#[should_panic]
fn remove_no_acc() {
let cli = Cli::try_parse_from(["test", "remove"]).expect("csa remove");
println!("{cli:#?}");
}
#[test]
fn rename() {
let cli = Cli::try_parse_from(["test", "rename", "acc1", "acc_new"])
.expect("csa rename acc1 acc_new");
println!("{cli:#?}");
assert!(
matches!(cli.command, Commands::Account(AccountCommand { subcommand: AccountSubCommand::Rename { acc, new_name }, .. }) if acc == "acc1" && new_name == "acc_new")
);
}
#[test]
#[should_panic]
fn rename_no_args() {
let cli = Cli::try_parse_from(["test", "rename"]).expect("csa rename");
println!("{cli:#?}");
}
#[test]
#[should_panic]
fn rename_no_new_name() {
let cli = Cli::try_parse_from(["test", "rename", "acc1"]).expect("csa rename acc1");
println!("{cli:#?}");
}
}
+4 -14
View File
@@ -1,14 +1,4 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
pub mod cli;
pub mod store;
pub mod util;
pub mod auth;
+3
View File
@@ -0,0 +1,3 @@
//! deals with storage of accounts.
//! updates, pull out, standard crud... all here.
//! 0 logic to do with the actual accounts, just storage and retrieval.
+1
View File
@@ -0,0 +1 @@
//! whatever looks like an general utility goes here.