use crate::core::Ability;
use core::{fmt::Display, str::FromStr};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
pub struct Skill {
name: &'static str,
#[cfg_attr(feature = "serde", serde(skip))]
ability: Ability,
}
#[cfg(feature = "serde")]
impl<'a> serde::Deserialize<'a> for Skill {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
let name: &'a str = serde::Deserialize::deserialize(deserializer)?;
Skill::from_str(name).map_err(serde::de::Error::custom)
}
}
impl Skill {
#[must_use]
pub const fn all() -> &'static [Skill] {
&[
Self::ACROBATICS,
Self::ANIMAL_HANDLING,
Self::ARCANA,
Self::ATHLETICS,
Self::DECEPTION,
Self::HISTORY,
Self::INSIGHT,
Self::INTIMIDATION,
Self::INVESTIGATION,
Self::MEDICINE,
Self::NATURE,
Self::PERCEPTION,
Self::PERFORMANCE,
Self::PERSUASION,
Self::RELIGION,
Self::SLEIGHT_OF_HAND,
Self::STEALTH,
Self::SURVIVAL,
]
}
pub const ACROBATICS: Skill = Skill::new("Acrobatics", Ability::Dexterity);
pub const ANIMAL_HANDLING: Skill = Skill::new("Animal Handling", Ability::Wisdom);
pub const ARCANA: Skill = Skill::new("Arcana", Ability::Intelligence);
pub const ATHLETICS: Skill = Skill::new("Athletics", Ability::Strength);
pub const DECEPTION: Skill = Skill::new("Deception", Ability::Charisma);
pub const HISTORY: Skill = Skill::new("History", Ability::Intelligence);
pub const INSIGHT: Skill = Skill::new("Insight", Ability::Wisdom);
pub const INTIMIDATION: Skill = Skill::new("Intimidation", Ability::Charisma);
pub const INVESTIGATION: Skill = Skill::new("Investigation", Ability::Intelligence);
pub const MEDICINE: Skill = Skill::new("Medicine", Ability::Wisdom);
pub const NATURE: Skill = Skill::new("Nature", Ability::Intelligence);
pub const PERCEPTION: Skill = Skill::new("Perception", Ability::Wisdom);
pub const PERFORMANCE: Skill = Skill::new("Performance", Ability::Charisma);
pub const PERSUASION: Skill = Skill::new("Persuasion", Ability::Charisma);
pub const RELIGION: Skill = Skill::new("Religion", Ability::Intelligence);
pub const SLEIGHT_OF_HAND: Skill = Skill::new("Sleight of Hand", Ability::Dexterity);
pub const STEALTH: Skill = Skill::new("Stealth", Ability::Dexterity);
pub const SURVIVAL: Skill = Skill::new("Survival", Ability::Wisdom);
#[must_use]
const fn new(name: &'static str, ability: Ability) -> Self {
Self { name, ability }
}
#[must_use]
pub const fn name(&self) -> &'static str {
self.name
}
#[must_use]
pub const fn ability(&self) -> Ability {
self.ability
}
}
impl Display for Skill {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.name())
}
}
impl AsRef<str> for Skill {
fn as_ref(&self) -> &str {
self.name()
}
}
impl FromStr for Skill {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::all()
.iter()
.find(|&skill| skill.name() == s)
.copied()
.ok_or("Unknown skill")
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use core::fmt::Write;
#[test]
fn names() {
for skill in Skill::all() {
assert!(!skill.name().is_empty());
}
}
#[test]
fn abilities() {
for skill in Skill::all() {
assert!(skill.ability().skills().contains(skill));
}
}
#[test]
fn parse() {
for skill in Skill::all() {
assert_eq!(Skill::from_str(skill.name()).unwrap(), *skill);
}
}
#[test]
fn display() {
for skill in Skill::all() {
let mut output = alloc::string::String::new();
write!(&mut output, "{skill}").unwrap();
assert_eq!(output, skill.name());
}
}
#[test]
#[cfg(feature = "serde")]
fn serde() {
use serde_json;
let skill = Skill::ACROBATICS;
let serialized = serde_json::to_string(&skill).unwrap();
assert_eq!(serialized, "\"Acrobatics\"");
let deserialized: Skill = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, skill);
}
}