use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use crate::database::Result;
use dorsal::DefaultReturn;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum SkillType {
ModifierD,
ModifierP,
AddD,
AddP,
Ability,
Title,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SkillName {
Master,
Patron,
Trustworthy,
Protected,
Absolute,
Vanish,
God,
Administrator,
Manager,
Normal,
}
impl Into<Skill> for SkillName {
fn into(self) -> Skill {
use SkillName::*;
match self {
Master => ((SkillType::ModifierP, self), 2.0),
Patron => ((SkillType::ModifierD, self), 2.0),
Trustworthy => ((SkillType::ModifierP, self), 1.05),
Protected => ((SkillType::ModifierD, self), 1.05),
Absolute => ((SkillType::Ability, self), 1.0),
Vanish => ((SkillType::Ability, self), 1.0),
God => ((SkillType::Title, self), 100_000.0),
Administrator => ((SkillType::Title, self), 10_000.0),
Manager => ((SkillType::Title, self), 1_000.0),
Normal => ((SkillType::Title, self), 1.0),
}
}
}
impl SkillName {
pub fn is_valid(&self, stats: ProfileStats) -> bool {
if (self == &SkillName::Absolute) && (stats.power < 100_000.0) {
return false;
} else if self == &SkillName::God {
return false;
}
true
}
}
pub type Skill = (SkillIdentifier, f32);
pub type SkillSet = Vec<Skill>;
pub type SkillIdentifier = (SkillType, SkillName);
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Profile {
pub id: String,
pub username: String,
pub metadata: ProfileMetadata,
pub skills: SkillSet,
pub joined: u128,
}
impl Default for Profile {
fn default() -> Self {
Self {
id: String::new(),
username: String::new(),
metadata: ProfileMetadata::default(),
skills: [SkillName::Normal.into()].to_vec(),
joined: dorsal::utility::unix_epoch_timestamp(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ProfileMetadata {
#[serde(default)]
pub secondary_token: String,
}
impl Default for ProfileMetadata {
fn default() -> Self {
Self {
secondary_token: String::new(),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ProfileCreate {
pub username: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ProfileLogin {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GrantSkill {
pub skill: Skill,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RevokeSkill {
pub skill: SkillName,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GrantTitle {
pub title: SkillName,
}
pub enum StrawError {
MustBeUnique,
NotAllowed,
ValueError,
NotFound,
Other,
}
impl StrawError {
pub fn to_string(&self) -> String {
use StrawError::*;
match self {
MustBeUnique => String::from("One of the given values must be unique."),
NotAllowed => String::from("You are not allowed to access this resource."),
ValueError => String::from("One of the field values given is invalid."),
NotFound => String::from("No asset with this ID could be found."),
_ => String::from("An unspecified error has occured"),
}
}
}
impl IntoResponse for StrawError {
fn into_response(self) -> Response {
use crate::model::StrawError::*;
match self {
NotAllowed => (
StatusCode::UNAUTHORIZED,
Json(DefaultReturn::<u16> {
success: false,
message: self.to_string(),
payload: 401,
}),
)
.into_response(),
NotFound => (
StatusCode::NOT_FOUND,
Json(DefaultReturn::<u16> {
success: false,
message: self.to_string(),
payload: 404,
}),
)
.into_response(),
_ => (
StatusCode::INTERNAL_SERVER_ERROR,
Json(DefaultReturn::<u16> {
success: false,
message: self.to_string(),
payload: 500,
}),
)
.into_response(),
}
}
}
#[derive(Clone)]
pub struct SkillManager(pub SkillSet);
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ProfileStats {
pub power: f32,
pub defense: f32,
pub title: SkillName,
pub abilities: HashMap<SkillName, f32>,
pub skills: SkillSet,
}
impl Default for ProfileStats {
fn default() -> Self {
Self {
power: 1.0,
defense: 1.0,
title: SkillName::Normal,
abilities: HashMap::new(),
skills: [SkillName::Normal.into()].to_vec(),
}
}
}
impl SkillManager {
pub fn get_stats(&self) -> ProfileStats {
let mut iter = self.0.iter();
let title = iter
.find(|s| s.0 .0 == SkillType::Title)
.unwrap_or(&((SkillType::Title, SkillName::Normal), 0.0))
.clone();
let mut power: f32 = 1.0;
let mut defense: f32 = 1.0;
let mut abilities = HashMap::new();
for skill in iter {
match skill.0 .0 {
SkillType::ModifierD => defense *= skill.1,
SkillType::ModifierP => power *= skill.1,
SkillType::AddD => defense += skill.1,
SkillType::AddP => power += skill.1,
SkillType::Ability => {
abilities.insert(skill.0 .1.clone(), skill.1);
()
}
_ => continue,
}
}
power *= title.1;
defense *= title.1;
ProfileStats {
power,
defense,
title: title.0 .1,
abilities,
skills: self.0.clone(),
}
}
pub fn title(&mut self, skill: Skill) -> Result<()> {
for (i, skill) in self.0.clone().iter().enumerate() {
if skill.0 .0 != SkillType::Title {
continue;
}
let _ = std::mem::replace(&mut self.0[i], skill.to_owned());
return Ok(());
}
self.0.insert(0, skill);
Ok(())
}
pub fn remove(&mut self, name: SkillName) -> Result<()> {
for (i, skill) in self.0.clone().iter().enumerate() {
if skill.0 .1 != name {
continue;
}
self.0.remove(i);
}
Ok(())
}
pub fn push(&mut self, skill: Skill) -> Result<()> {
if !skill.0 .1.is_valid(self.get_stats()) {
return Err(StrawError::ValueError);
}
self.0.push(skill);
Ok(())
}
pub fn act(&self, other: SkillManager) -> bool {
let me = self.get_stats();
let them = other.get_stats();
((me.power > them.defense) && (them.power <= me.power)) | (me.title == SkillName::God)
}
pub fn has_skill(&self, skill: SkillName) -> bool {
self.0.iter().find(|s| s.0 .1 == skill).is_some()
}
}