feat: Added interactions with accounts/quests/map

- Bump version to 0.5.0
- Added Quest::complete_for_account public function
- Added Map::unlock_room_for_account public function
- cli: Added "account unlock" command
- cli: "account complete" now uses Quest::complete_for_account
- cli: refactored logging
This commit is contained in:
Alexey 2025-12-05 15:38:14 +03:00
commit f88e010b4f
9 changed files with 231 additions and 138 deletions

View file

@ -26,3 +26,41 @@ impl fmt::Display for Error {
}
}
}
/// Error related to quest logic
#[derive(Debug)]
#[non_exhaustive]
pub enum QuestError {
/// Quest (self.0) is already completed for given account (self.1)
AlreadyCompleted(u16, String),
}
impl fmt::Display for QuestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AlreadyCompleted(quest_id, account_id) => write!(f, "quest #{quest_id} is already completed for account \"{account_id}\""),
}
}
}
/// Error related to map logic
#[derive(Debug)]
#[non_exhaustive]
pub enum MapError {
/// Room not found in map file
RoomNotFound(u16),
/// Room (self.0) is already unlocked on account (self.1)
RoomAlreadyUnlocked(u16, String),
/// Account (self.1) does not have much money (self.0)
InsufficientFunds(u16, String),
}
impl fmt::Display for MapError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RoomNotFound(id) => write!(f, "could not find room #{id}"),
Self::RoomAlreadyUnlocked(room_id, account_id) => write!(f, "room #{room_id} is already unlocked on account \"{account_id}\""),
Self::InsufficientFunds(room_id, account_id) => write!(f, "account \"{account_id}\" does not have enough money to unlock room #{room_id}"),
}
}
}

View file

@ -4,7 +4,7 @@ use std::{fs, io::Write, path::PathBuf};
use serde::{Deserialize, Serialize};
use crate::{SquadObject, error::Error};
use crate::{SquadObject, account::Account, error::{Error, MapError}};
/// THE Graph. Actually, this is a Vec.
#[derive(Serialize, Deserialize)]
@ -71,7 +71,43 @@ impl SquadObject for Map {
Ok(())
}
}
impl Map {
/// Try to unlock room for account, or return [MapError]
///
/// # Examples
/// ```rust
/// use squad_quest::{account::Account,map::{Map,Room},error::MapError};
///
/// let map = Map {
/// room: vec![Room { id: 0, value: 100, ..Default::default() }],
/// };
///
/// let mut account = Account { balance: 100, ..Default::default() };
///
/// if let Err(error) = map.unlock_room_for_account(0, &mut account) {
/// // handle error
/// }
/// ```
pub fn unlock_room_for_account(&self, room_id: u16, account: &mut Account) -> Result<(), MapError> {
let Some(room) = self.room.iter().find(|r| r.id == room_id) else {
return Err(MapError::RoomNotFound(room_id));
};
if let Some(_) = account.rooms_unlocked.iter().find(|rid| **rid == room_id) {
return Err(MapError::RoomAlreadyUnlocked(room_id, account.id.clone()));
}
if account.balance < room.value {
return Err(MapError::InsufficientFunds(room_id, account.id.clone()));
}
account.balance -= room.value;
account.rooms_unlocked.push(room_id);
Ok(())
}
}
/// Component of the map

View file

@ -3,7 +3,7 @@
use std::{fs, io::Write, path::PathBuf};
use serde::{ Serialize, Deserialize };
use crate::{SquadObject, error::Error};
use crate::{SquadObject, account::Account, error::{Error, QuestError}};
use toml::value::Date;
/// Difficulty of the quest
@ -137,3 +137,32 @@ impl SquadObject for Quest {
Ok(())
}
}
impl Quest {
/// Complete quest for account and add reward to it's balance.
/// Does nothing and returns [QuestError::AlreadyCompleted]
/// if it is already completed.
///
/// # Examples
///
/// ```rust
/// use squad_quest::{account::Account,quest::Quest};
///
/// let quest = Quest::default();
/// let mut account = Account::default();
///
/// if let Err(error) = quest.complete_for_account(&mut account) {
/// // handle error
/// }
/// ```
pub fn complete_for_account(&self, account: &mut Account) -> Result<(),QuestError> {
match account.quests_completed.iter().find(|qid| **qid == self.id) {
Some(_) => Err(QuestError::AlreadyCompleted(self.id, account.id.clone())),
None => {
account.quests_completed.push(self.id);
account.balance += self.reward;
Ok(())
},
}
}
}