feat: CLI quest CRUD

- Quest creation
- Quest list retrieving
- Quest update
- Quest deletion
This commit is contained in:
Alexey 2025-11-29 14:40:23 +03:00
commit d61011f5ea
2 changed files with 124 additions and 9 deletions

View file

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use clap::{Parser,Subcommand,Args,ValueEnum}; use clap::{Parser,Subcommand,Args,ValueEnum};
use squad_quest::{config::Config,quest::{Quest,QuestDifficulty as LibQuestDifficulty}};
#[derive(Parser)] #[derive(Parser)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
@ -33,6 +34,17 @@ enum QuestDifficulty {
Secret Secret
} }
impl From<QuestDifficulty> for LibQuestDifficulty {
fn from(value: QuestDifficulty) -> Self {
match value {
QuestDifficulty::Easy => LibQuestDifficulty::Easy,
QuestDifficulty::Normal => LibQuestDifficulty::Normal,
QuestDifficulty::Hard => LibQuestDifficulty::Hard,
QuestDifficulty::Secret => LibQuestDifficulty::Secret,
}
}
}
#[derive(Subcommand)] #[derive(Subcommand)]
enum QuestCommands { enum QuestCommands {
/// List available quests /// List available quests
@ -72,16 +84,20 @@ struct QuestUpdateArgs {
/// Id of the quest to update /// Id of the quest to update
id: u16, id: u16,
/// Difficulty of the quest /// Difficulty of the quest
#[arg(value_enum)] #[arg(value_enum,long)]
difficulty: QuestDifficulty, difficulty: Option<QuestDifficulty>,
/// Reward for the quest /// Reward for the quest
reward: u32, #[arg(long)]
reward: Option<u32>,
/// Name of the quest /// Name of the quest
name: String, #[arg(long)]
name: Option<String>,
/// Visible description of the quest /// Visible description of the quest
description: String, #[arg(long)]
description: Option<String>,
/// Answer for the quest for admins /// Answer for the quest for admins
answer: String #[arg(long)]
answer: Option<String>
} }
#[derive(Args)] #[derive(Args)]
@ -90,6 +106,105 @@ struct QuestDeleteArgs {
id: u16 id: u16
} }
fn main() { fn print_quest_short(quest: &Quest) {
let _cli = Cli::parse(); println!("Quest #{}: {}", quest.id, quest.name);
}
fn print_quest_long(quest: &Quest) {
print_quest_short(quest);
println!("Difficulty: {:?}", quest.difficulty);
println!("Description:\n{}", quest.description);
println!("Answer:\n{}", quest.answer);
}
fn main() {
let cli = Cli::parse();
let config = Config::load(cli.config.clone());
match &cli.command {
Objects::Quest(commands) => {
match commands {
QuestCommands::List(args) => {
let quests = config.load_quests();
for quest in quests {
if args.short {
print_quest_short(&quest);
} else {
print_quest_long(&quest);
}
}
},
QuestCommands::Create(args) => {
let mut quests = config.load_quests();
quests.sort_by(|a,b| a.id.cmp(&b.id));
let next_id = match quests.last() {
Some(quest) if quest.id == u16::MAX => {
panic!("Error: quest list contains quest with u16::MAX id.");
}
Some(quest) => quest.id + 1u16,
None => 0u16
};
let path = config.full_quests_path();
let mut quest_path = path.clone();
quest_path.push(format!("{next_id}.toml"));
match std::fs::exists(&quest_path) {
Ok(exists) => {
if exists {
panic!("Error: {:?} is not empty.", quest_path);
}
},
Err(error) => {
panic!("Error while retrieving {:?}: {}.", quest_path, error);
}
}
let quest = Quest {
id: next_id,
difficulty: args.difficulty.into(),
reward: args.reward,
name: args.name.clone(),
description: args.description.clone(),
answer: args.answer.clone(),
};
if let Err(error) = quest.save(path) {
eprintln!("Error while saving quest: {error}.");
} else {
println!("Successfully saved quest #{}.", quest.id);
}
},
QuestCommands::Update(args) => {
let quests = config.load_quests();
let Some(quest) = quests.iter().find(|q| q.id == args.id) else {
panic!("Error: Quest #{} not found.", args.id);
};
let quest = Quest {
id: args.id,
difficulty: match args.difficulty {
Some(diff) => diff.into(),
None => quest.difficulty
},
reward: args.reward.unwrap_or(quest.reward),
name: args.name.clone().unwrap_or(quest.name.clone()),
description: args.description.clone().unwrap_or(quest.description.clone()),
answer: args.answer.clone().unwrap_or(quest.answer.clone())
};
let path = config.full_quests_path();
match quest.save(path) {
Ok(_) => println!("Updated quest #{}", quest.id),
Err(error) => eprintln!("Error while updating quest: {error}")
}
},
QuestCommands::Delete(args) => {
let mut path = config.full_quests_path();
path.push(format!("{}.toml", args.id));
match Quest::delete(path) {
Ok(_) => println!("Successfully deleted quest #{}", args.id),
Err(error) => eprintln!("Error deleting quest #{}: {}", args.id, error),
}
},
}
}
}
} }

View file

@ -8,7 +8,7 @@ use serde::{ Serialize, Deserialize };
use error::QuestError; use error::QuestError;
/// Difficulty of the quest /// Difficulty of the quest
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
pub enum QuestDifficulty { pub enum QuestDifficulty {
/// Easy quest /// Easy quest
Easy, Easy,