firefly_types/
badges.rs

1use crate::encode::Encode;
2use alloc::borrow::Cow;
3use core::fmt::Display;
4use serde::{Deserialize, Serialize};
5
6pub enum BadgeValidationError {
7    EmptyName,
8    NameTooLong,
9    DescrTooLong,
10    TooMuchXp,
11}
12
13impl BadgeValidationError {
14    #[must_use]
15    pub const fn as_str(&self) -> &'static str {
16        match self {
17            Self::EmptyName => "name must not be empty",
18            Self::NameTooLong => "name is too long",
19            Self::DescrTooLong => "descr is too long",
20            Self::TooMuchXp => "one badge cannot reward more than 200 XP",
21        }
22    }
23}
24
25impl Display for BadgeValidationError {
26    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
27        write!(f, "{}", self.as_str())
28    }
29}
30
31#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
32pub struct Badges<'a> {
33    /// Detailed information about achievements.
34    #[serde(borrow)]
35    pub badges: Cow<'a, [Badge<'a>]>,
36}
37
38impl<'a> Badges<'a> {
39    #[must_use]
40    pub const fn new(badges: Cow<'a, [Badge<'a>]>) -> Self {
41        Self { badges }
42    }
43}
44
45impl<'a> Encode<'a> for Badges<'a> {}
46
47#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
48pub struct Badge<'a> {
49    /// The order in which badges should be displayed, ascending.
50    ///
51    /// Earned achievments bubble up.
52    pub position: u16,
53
54    /// How much XP earning the badge brings to the player.
55    pub xp: u8,
56
57    /// The number of steps required for the badge to be shown.
58    ///
59    /// If zero, the badge is always shown. If equal to the number of steps
60    /// required to earn the badge, the badge will be shown only when earned.
61    pub hidden: u16,
62
63    /// Human-readable badge name.
64    pub name: &'a str,
65
66    /// Human-readable badge description. Typically, a hint on how to earn it.
67    pub descr: &'a str,
68}
69
70impl Badge<'_> {
71    /// Validate badge attributes.
72    ///
73    /// # Errors
74    ///
75    /// Returns [`BadgeValidationError`] if any of the attributes are not valid.
76    pub const fn validate(&self) -> Result<(), BadgeValidationError> {
77        if self.name.is_empty() {
78            return Err(BadgeValidationError::EmptyName);
79        }
80        if self.name.len() > 64 {
81            return Err(BadgeValidationError::NameTooLong);
82        }
83        if self.descr.len() > 256 {
84            return Err(BadgeValidationError::DescrTooLong);
85        }
86        if self.xp > 200 {
87            return Err(BadgeValidationError::TooMuchXp);
88        }
89        Ok(())
90    }
91}