firefly_types/
boards.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use crate::encode::Encode;
use alloc::borrow::Cow;
use core::fmt::Display;
use serde::{Deserialize, Serialize};

pub enum BoardValidationError {
    EmptyName,
    NameTooLong,
    MinGtMax,
}

impl BoardValidationError {
    #[must_use]
    pub const fn as_str(&self) -> &'static str {
        match self {
            Self::EmptyName => "name must not be empty",
            Self::NameTooLong => "name is too long",
            Self::MinGtMax => "min must be less than or equal to max",
        }
    }
}

impl Display for BoardValidationError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
pub struct Boards<'a> {
    #[serde(borrow)]
    pub boards: Cow<'a, [Board<'a>]>,
}

impl<'a> Boards<'a> {
    #[must_use]
    pub const fn new(boards: Cow<'a, [Board<'a>]>) -> Self {
        Self { boards }
    }
}

impl<'a> Encode<'a> for Boards<'a> {}

#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
pub struct Board<'a> {
    /// The order in which the board should be displayed, ascending.
    pub position: u16,

    /// The minimum value for a score to be added to the board.
    ///
    /// It's possible to have negative scores when the app needs scores on the board
    /// to be ordered in ascending order rather than descending.
    /// So the default minimum is [`i16::MIN`] rather than 0.
    pub min: i16,

    /// The maximum value for a score to be added to the board.
    ///
    /// Useful for filtering out obvious cheating.
    pub max: i16,

    /// If the score should be formatted as time.
    pub time: bool,

    /// Digits after decimal point.
    pub decimals: u8,

    /// Human-readable board name.
    pub name: &'a str,
}

impl<'a> Board<'a> {
    /// Validate board attributes.
    ///
    /// # Errors
    ///
    /// Returns [`BoardValidationError`] if any of the attributes are not valid.
    pub const fn validate(&self) -> Result<(), BoardValidationError> {
        if self.name.is_empty() {
            return Err(BoardValidationError::EmptyName);
        }
        if self.name.len() > 64 {
            return Err(BoardValidationError::NameTooLong);
        }
        if self.min > self.max {
            return Err(BoardValidationError::MinGtMax);
        }
        Ok(())
    }
}