feat: Added interaction with accounts in CLI
- Account creation - Account deletion - Account balance management - Account quest completion - Added account CRUD test in tests/io.rs
This commit is contained in:
parent
0e8cdde697
commit
dc94f2060c
3 changed files with 215 additions and 18 deletions
199
src/bin/cli.rs
199
src/bin/cli.rs
|
|
@ -2,13 +2,13 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Parser,Subcommand,Args,ValueEnum};
|
use clap::{Parser,Subcommand,Args,ValueEnum};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use squad_quest::{SquadObject, config::Config, quest::{Quest,QuestDifficulty as LibQuestDifficulty}};
|
use squad_quest::{SquadObject, account::Account, config::Config, quest::{Quest,QuestDifficulty as LibQuestDifficulty}};
|
||||||
use toml::value::Date;
|
use toml::value::Date;
|
||||||
use chrono::{Datelike, NaiveDate, Utc};
|
use chrono::{Datelike, NaiveDate, Utc};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct DateWrapper {
|
struct DateWrapper {
|
||||||
date: Date
|
date: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_date(arg: &str) -> Result<Date,toml::de::Error> {
|
fn parse_date(arg: &str) -> Result<Date,toml::de::Error> {
|
||||||
|
|
@ -33,7 +33,10 @@ struct Cli {
|
||||||
enum Objects {
|
enum Objects {
|
||||||
/// Operations on the quests
|
/// Operations on the quests
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Quest(QuestCommands)
|
Quest(QuestCommands),
|
||||||
|
/// Operations on the accounts
|
||||||
|
#[command(subcommand)]
|
||||||
|
Account(AccountCommands),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||||
|
|
@ -45,7 +48,7 @@ enum QuestDifficulty {
|
||||||
/// Hard quest
|
/// Hard quest
|
||||||
Hard,
|
Hard,
|
||||||
/// Special case of hard quests.
|
/// Special case of hard quests.
|
||||||
Secret
|
Secret,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<QuestDifficulty> for LibQuestDifficulty {
|
impl From<QuestDifficulty> for LibQuestDifficulty {
|
||||||
|
|
@ -75,11 +78,12 @@ enum QuestCommands {
|
||||||
Publish(QuestPublishArgs),
|
Publish(QuestPublishArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
struct QuestListArgs {
|
struct QuestListArgs {
|
||||||
/// Only list id and name of the quest
|
/// Only list id and name of the quest
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
short: bool
|
short: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
|
|
@ -137,7 +141,7 @@ struct QuestUpdateArgs {
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
struct QuestDeleteArgs {
|
struct QuestDeleteArgs {
|
||||||
/// Id of the quest to delete
|
/// Id of the quest to delete
|
||||||
id: u16
|
id: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
|
|
@ -149,6 +153,61 @@ struct QuestPublishArgs {
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum AccountCommands {
|
||||||
|
/// List accounts
|
||||||
|
List,
|
||||||
|
/// Create empty account
|
||||||
|
Create(AccountCreateArgs),
|
||||||
|
/// Update balance value
|
||||||
|
Balance(AccountBalanceArgs),
|
||||||
|
/// Approve account answer for quest
|
||||||
|
Complete(AccountCompleteArgs),
|
||||||
|
/// Delete account
|
||||||
|
Delete(AccountDeleteArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct AccountCreateArgs {
|
||||||
|
/// Account will be created with this id
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, ValueEnum)]
|
||||||
|
enum AccountBalanceActions {
|
||||||
|
Set,
|
||||||
|
Add,
|
||||||
|
Remove,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct AccountBalanceArgs {
|
||||||
|
/// Account id
|
||||||
|
id: String,
|
||||||
|
/// What to do with the balance
|
||||||
|
#[arg(value_enum)]
|
||||||
|
action: AccountBalanceActions,
|
||||||
|
/// Amount of doing
|
||||||
|
value: u32,
|
||||||
|
/// If action is remove, set balance to 0 if the result is negative instead of returning error
|
||||||
|
#[arg(short,long)]
|
||||||
|
negative_ok: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct AccountCompleteArgs {
|
||||||
|
/// Id of the account
|
||||||
|
account: String,
|
||||||
|
/// Id of the quest
|
||||||
|
quest: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct AccountDeleteArgs {
|
||||||
|
/// Id of the account to delete
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
fn print_quest_short(quest: &Quest) {
|
fn print_quest_short(quest: &Quest) {
|
||||||
println!("Quest #{}: {}", quest.id, quest.name);
|
println!("Quest #{}: {}", quest.id, quest.name);
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +355,133 @@ fn main() {
|
||||||
eprintln!("Error: couldn't find quest with id {}.", args.id);
|
eprintln!("Error: couldn't find quest with id {}.", args.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Objects::Account(args) => {
|
||||||
|
match args {
|
||||||
|
AccountCommands::List => {
|
||||||
|
let accounts = config.load_accounts();
|
||||||
|
|
||||||
|
for account in accounts {
|
||||||
|
println!("\"{}\": Balance {}", account.id, account.balance);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AccountCommands::Create(args) => {
|
||||||
|
let account = Account {
|
||||||
|
id: args.id.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let accounts = config.load_accounts();
|
||||||
|
|
||||||
|
if let Some(_) = accounts.iter().find(|a| a.id == account.id) {
|
||||||
|
eprintln!("Error: account {} exists.", account.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let accounts_path = config.full_accounts_path();
|
||||||
|
|
||||||
|
match account.save(accounts_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Successfully created account \"{}\"", account.id);
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("Error while saving account: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AccountCommands::Balance(args) => {
|
||||||
|
let mut accounts = config.load_accounts();
|
||||||
|
|
||||||
|
let account = match accounts.iter_mut().find(|a| a.id == args.id) {
|
||||||
|
Some(acc) => acc,
|
||||||
|
None => {
|
||||||
|
eprintln!("Could not find account \"{}\"", args.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match args.action {
|
||||||
|
AccountBalanceActions::Set => {
|
||||||
|
account.balance = args.value;
|
||||||
|
},
|
||||||
|
AccountBalanceActions::Add => {
|
||||||
|
account.balance += args.value;
|
||||||
|
},
|
||||||
|
AccountBalanceActions::Remove => {
|
||||||
|
if args.value > account.balance {
|
||||||
|
if args.negative_ok {
|
||||||
|
account.balance = 0u32;
|
||||||
|
} else {
|
||||||
|
eprintln!("Error: balance ({}) is less than {}.", account.balance, args.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
account.balance -= args.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let accounts_path = config.full_accounts_path();
|
||||||
|
|
||||||
|
match account.save(accounts_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Successfully updated account \"{}\" balance.", account.id);
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("Error while saving account: {error}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
AccountCommands::Complete(args) => {
|
||||||
|
let mut accounts = config.load_accounts();
|
||||||
|
|
||||||
|
let account = match accounts.iter_mut().find(|a| a.id == args.account) {
|
||||||
|
Some(acc) => acc,
|
||||||
|
None => {
|
||||||
|
eprintln!("Could not find account \"{}\"", args.account);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let quests = config.load_quests();
|
||||||
|
|
||||||
|
if let None = quests.iter().find(|q| q.id == args.quest) {
|
||||||
|
eprintln!("Could not find quest #{}", args.quest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match account.quests_completed.iter().find(|qid| **qid == args.quest) {
|
||||||
|
Some(_) => {
|
||||||
|
println!("Quest #{} is already completed on account \"{}\"", args.quest, args.account);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
account.quests_completed.push(args.quest);
|
||||||
|
let accounts_path = config.full_accounts_path();
|
||||||
|
match account.save(accounts_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Account \"{}\" completed quest #{}.", args.account, args.quest);
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("Error while saving account: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AccountCommands::Delete(args) => {
|
||||||
|
let mut accounts_path = config.full_accounts_path();
|
||||||
|
accounts_path.push(format!("{}.toml", args.id));
|
||||||
|
match Account::delete(accounts_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Successfully deleted account \"{}\".", args.id);
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("Error deleting account: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
tests/io.rs
33
tests/io.rs
|
|
@ -1,5 +1,4 @@
|
||||||
use squad_quest::{SquadObject, config::Config, error::Error, quest::Quest};
|
use squad_quest::{SquadObject, account::Account, config::Config, quest::Quest};
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
const CONFIG_PATH: &str = "tests/io/config.toml";
|
const CONFIG_PATH: &str = "tests/io/config.toml";
|
||||||
|
|
||||||
|
|
@ -7,23 +6,35 @@ const CONFIG_PATH: &str = "tests/io/config.toml";
|
||||||
// and Quest::save can override files,
|
// and Quest::save can override files,
|
||||||
// so this test covers full quest CRUD
|
// so this test covers full quest CRUD
|
||||||
#[test]
|
#[test]
|
||||||
fn quest_crud() -> Result<(), Error> {
|
fn quest_crud() {
|
||||||
let config = Config::load(CONFIG_PATH.into());
|
let config = Config::load(CONFIG_PATH.into());
|
||||||
|
|
||||||
let mut quests_path = PathBuf::from(CONFIG_PATH).parent().unwrap().to_owned();
|
let mut quests_path = config.full_quests_path();
|
||||||
quests_path.push(config.quests_path);
|
|
||||||
|
|
||||||
let quest = Quest::default();
|
let quest = Quest::default();
|
||||||
|
|
||||||
println!("{:?}", quests_path.clone());
|
quest.save(quests_path.clone()).unwrap();
|
||||||
|
|
||||||
quest.save(quests_path.clone())?;
|
|
||||||
|
|
||||||
let filename = format!("{}.toml", quest.id);
|
let filename = format!("{}.toml", quest.id);
|
||||||
|
|
||||||
quests_path.push(filename);
|
quests_path.push(filename);
|
||||||
|
|
||||||
Quest::delete(quests_path)?;
|
Quest::delete(quests_path).unwrap();
|
||||||
|
}
|
||||||
Ok(())
|
|
||||||
|
#[test]
|
||||||
|
fn account_crud() {
|
||||||
|
let config = Config::load(CONFIG_PATH.into());
|
||||||
|
|
||||||
|
let mut accounts_path = config.full_accounts_path();
|
||||||
|
|
||||||
|
let account = Account::default();
|
||||||
|
|
||||||
|
account.save(accounts_path.clone()).unwrap();
|
||||||
|
|
||||||
|
let filename = format!("{}.toml", account.id);
|
||||||
|
|
||||||
|
accounts_path.push(filename);
|
||||||
|
|
||||||
|
Account::delete(accounts_path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
tests/io/accounts/.placeholder
Normal file
1
tests/io/accounts/.placeholder
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Placeholder file for git
|
||||||
Loading…
Add table
Add a link
Reference in a new issue