use crate::core::Skill;
use enumflags2::BitFlags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum SkillLevel {
Proficient,
Expertise,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SkillProficiencies {
proficient: BitFlags<Skill>,
expertise: BitFlags<Skill>,
}
impl SkillProficiencies {
#[must_use]
pub const fn new() -> Self {
Self {
proficient: BitFlags::EMPTY,
expertise: BitFlags::EMPTY,
}
}
#[must_use]
pub fn with_proficiencies(proficient: impl Iterator<Item = (Skill, SkillLevel)>) -> Self {
let mut profs = Self::new();
profs.set_proficiencies(proficient);
profs
}
#[must_use]
pub fn is_proficient(&self, skill: Skill) -> bool {
self.proficient.contains(skill)
}
#[must_use]
pub fn has_expertise(&self, skill: Skill) -> bool {
self.expertise.contains(skill)
}
#[must_use]
pub fn get_proficiency(&self, skill: Skill) -> Option<SkillLevel> {
if self.has_expertise(skill) {
Some(SkillLevel::Expertise)
} else if self.is_proficient(skill) {
Some(SkillLevel::Proficient)
} else {
None
}
}
pub fn set_proficiency(&mut self, skill: Skill, proficiency: SkillLevel) -> &mut Self {
match proficiency {
SkillLevel::Proficient => {
self.proficient.insert(skill);
self.expertise.remove(skill);
}
SkillLevel::Expertise => {
self.expertise.insert(skill);
self.proficient.remove(skill);
}
}
self
}
pub fn set_proficiencies(
&mut self,
skills: impl Iterator<Item = (Skill, SkillLevel)>,
) -> &mut Self {
for (skill, proficiency) in skills {
self.set_proficiency(skill, proficiency);
}
self
}
pub fn set_proficient(&mut self, skill: Skill) -> &mut Self {
self.set_proficiency(skill, SkillLevel::Proficient)
}
pub fn set_expertise(&mut self, skill: Skill) -> &mut Self {
self.set_proficiency(skill, SkillLevel::Expertise)
}
pub fn clear_proficiency(&mut self, skill: Skill) -> &mut Self {
self.proficient.remove(skill);
self.expertise.remove(skill);
self
}
pub fn clear_all(&mut self) -> &mut Self {
self.proficient = BitFlags::EMPTY;
self.expertise = BitFlags::EMPTY;
self
}
pub fn iter(&self) -> impl Iterator<Item = (Skill, SkillLevel)> + '_ {
Skill::all().iter().filter_map(move |&skill| {
if self.has_expertise(skill) {
Some((skill, SkillLevel::Expertise))
} else if self.is_proficient(skill) {
Some((skill, SkillLevel::Proficient))
} else {
None
}
})
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use alloc::vec::Vec;
#[test]
fn new() {
let profs = SkillProficiencies::new();
assert!(profs.iter().next().is_none());
}
#[test]
fn set_proficient() {
let mut profs = SkillProficiencies::new();
profs.set_proficient(Skill::Acrobatics);
assert!(profs.is_proficient(Skill::Acrobatics));
assert!(!profs.has_expertise(Skill::Acrobatics));
assert_eq!(
profs.get_proficiency(Skill::Acrobatics),
Some(SkillLevel::Proficient)
);
let skills: Vec<_> = profs.iter().collect();
assert_eq!(&skills, &[(Skill::Acrobatics, SkillLevel::Proficient)]);
}
#[test]
fn set_expertise() {
let mut profs = SkillProficiencies::new();
profs.set_expertise(Skill::Acrobatics);
assert!(!profs.is_proficient(Skill::Acrobatics));
assert!(profs.has_expertise(Skill::Acrobatics));
assert_eq!(
profs.get_proficiency(Skill::Acrobatics),
Some(SkillLevel::Expertise)
);
let skills: Vec<_> = profs.iter().collect();
assert_eq!(&skills, &[(Skill::Acrobatics, SkillLevel::Expertise)]);
}
#[test]
fn clear_proficiency() {
let mut profs = SkillProficiencies::new();
profs.set_proficient(Skill::Acrobatics);
profs.clear_proficiency(Skill::Acrobatics);
assert!(!profs.is_proficient(Skill::Acrobatics));
assert!(!profs.has_expertise(Skill::Acrobatics));
assert_eq!(profs.get_proficiency(Skill::Acrobatics), None);
let skills: Vec<_> = profs.iter().collect();
assert!(skills.is_empty());
}
#[test]
fn clear_all() {
let mut profs = SkillProficiencies::new();
profs.set_proficient(Skill::Acrobatics);
profs.set_expertise(Skill::Stealth);
profs.clear_all();
assert!(!profs.is_proficient(Skill::Acrobatics));
assert!(!profs.has_expertise(Skill::Stealth));
assert_eq!(profs.get_proficiency(Skill::Acrobatics), None);
assert_eq!(profs.get_proficiency(Skill::Stealth), None);
let skills: Vec<_> = profs.iter().collect();
assert!(skills.is_empty());
}
#[test]
fn iter() {
let mut profs = SkillProficiencies::new();
profs.set_proficient(Skill::Acrobatics);
profs.set_expertise(Skill::Stealth);
let skills: Vec<_> = profs.iter().collect();
assert_eq!(
&skills,
&[
(Skill::Acrobatics, SkillLevel::Proficient),
(Skill::Stealth, SkillLevel::Expertise)
]
);
}
#[test]
fn with_proficiencies() {
let profs = SkillProficiencies::with_proficiencies(
[
(Skill::Acrobatics, SkillLevel::Proficient),
(Skill::Stealth, SkillLevel::Expertise),
]
.into_iter(),
);
assert!(profs.is_proficient(Skill::Acrobatics));
assert!(profs.has_expertise(Skill::Stealth));
}
}