twilight_validate/
sticker.rs

1//! Constants, error types, and functions for validating [`Sticker`] fields.
2//!
3//! [`Sticker`]: twilight_model::channel::message::Sticker
4
5use std::{
6    error::Error,
7    fmt::{Display, Formatter, Result as FmtResult},
8};
9
10/// Maximum length of a sticker description.
11pub const STICKER_DESCRIPTION_LENGTH_MAX: usize = 200;
12
13/// Minimum length of a sticker description.
14pub const STICKER_DESCRIPTION_LENGTH_MIN: usize = 2;
15
16/// Maximum length of a sticker name.
17pub const STICKER_NAME_LENGTH_MAX: usize = 30;
18
19/// Minimum length of a sticker name.
20pub const STICKER_NAME_LENGTH_MIN: usize = 2;
21
22/// Maximum length of the sticker's tags.
23pub const STICKER_TAGS_LENGTH_MAX: usize = 200;
24
25/// Minimum length of the sticker's tags.
26pub const STICKER_TAGS_LENGTH_MIN: usize = 2;
27
28/// Error created if validation of a sticker field fails.
29#[derive(Debug)]
30pub struct StickerValidationError {
31    /// Type of error that occurred.
32    kind: StickerValidationErrorType,
33}
34
35impl StickerValidationError {
36    /// Immutable reference to the type of error that occurred.
37    #[must_use = "retrieving the type has no effect if left unused"]
38    pub const fn kind(&self) -> &StickerValidationErrorType {
39        &self.kind
40    }
41
42    /// Consume the error, returning the source error if there is any.
43    #[allow(clippy::unused_self)]
44    #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
45    pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
46        None
47    }
48
49    /// Consume the error, returning the owned error type and the source error.
50    #[must_use = "consuming the error into its parts has no effect if left unused"]
51    pub fn into_parts(
52        self,
53    ) -> (
54        StickerValidationErrorType,
55        Option<Box<dyn Error + Send + Sync>>,
56    ) {
57        (self.kind, None)
58    }
59}
60
61impl Display for StickerValidationError {
62    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
63        match self.kind {
64            StickerValidationErrorType::DescriptionInvalid => {
65                f.write_str("sticker's description is invalid")
66            }
67            StickerValidationErrorType::NameInvalid => f.write_str("sticker's name is invalid"),
68            StickerValidationErrorType::TagsInvalid => f.write_str("sticker's tags are invalid"),
69        }
70    }
71}
72
73impl Error for StickerValidationError {}
74
75/// Type of [`StickerValidationError`] that occurred.
76#[derive(Debug)]
77pub enum StickerValidationErrorType {
78    /// Sticker's description is invalid.
79    DescriptionInvalid,
80    /// Sticker's name is invalid.
81    NameInvalid,
82    /// Sticker's tags are invalid.
83    TagsInvalid,
84}
85
86/// Ensure that a sticker's description is correct.
87///
88/// The length must be at least [`STICKER_DESCRIPTION_LENGTH_MIN`] and at most
89/// [`STICKER_DESCRIPTION_LENGTH_MAX`]. This is based on
90/// [this documentation entry].
91///
92/// # Errors
93///
94/// Returns an error of type [`DescriptionInvalid`] if the length is invalid.
95///
96/// [`DescriptionInvalid`]: StickerValidationErrorType::DescriptionInvalid
97/// [this documentation entry]: https://discord.com/developers/docs/resources/sticker#create-guild-sticker
98pub fn description(value: impl AsRef<str>) -> Result<(), StickerValidationError> {
99    let len = value.as_ref().chars().count();
100
101    if (STICKER_DESCRIPTION_LENGTH_MIN..=STICKER_DESCRIPTION_LENGTH_MAX).contains(&len) {
102        Ok(())
103    } else {
104        Err(StickerValidationError {
105            kind: StickerValidationErrorType::DescriptionInvalid,
106        })
107    }
108}
109
110/// Ensure that a sticker's name is correct.
111///
112/// The length must be at least [`STICKER_NAME_LENGTH_MIN`] and at most
113/// [`STICKER_NAME_LENGTH_MAX`]. This is based on [this documentation entry].
114///
115/// # Errors
116///
117/// Returns an error of type [`NameInvalid`] if the length is invalid.
118///
119/// [`NameInvalid`]: StickerValidationErrorType::NameInvalid
120/// [this documentation entry]: https://discord.com/developers/docs/resources/sticker#create-guild-sticker
121pub fn name(value: impl AsRef<str>) -> Result<(), StickerValidationError> {
122    let len = value.as_ref().chars().count();
123
124    if (STICKER_NAME_LENGTH_MIN..=STICKER_NAME_LENGTH_MAX).contains(&len) {
125        Ok(())
126    } else {
127        Err(StickerValidationError {
128            kind: StickerValidationErrorType::NameInvalid,
129        })
130    }
131}
132
133/// Ensure that a sticker's tags is correct.
134///
135/// The length must be at least [`STICKER_TAGS_LENGTH_MIN`] and at most
136/// [`STICKER_TAGS_LENGTH_MAX`]. This is based on [this documentation entry].
137///
138/// # Errors
139///
140/// Returns an error of type [`TagsInvalid`] if the length is invalid.
141///
142/// [`TagsInvalid`]: StickerValidationErrorType::TagsInvalid
143/// [this documentation entry]: https://discord.com/developers/docs/resources/sticker#create-guild-sticker
144pub fn tags(value: impl AsRef<str>) -> Result<(), StickerValidationError> {
145    let len = value.as_ref().chars().count();
146
147    if (STICKER_TAGS_LENGTH_MIN..=STICKER_TAGS_LENGTH_MAX).contains(&len) {
148        Ok(())
149    } else {
150        Err(StickerValidationError {
151            kind: StickerValidationErrorType::TagsInvalid,
152        })
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn description_length() {
162        assert!(description("aa").is_ok());
163        assert!(description("a".repeat(200)).is_ok());
164
165        assert!(description("a").is_err());
166        assert!(description("a".repeat(201)).is_err());
167    }
168
169    #[test]
170    fn name_length() {
171        assert!(name("aa").is_ok());
172        assert!(name("a".repeat(30)).is_ok());
173
174        assert!(name("a").is_err());
175        assert!(name("a".repeat(31)).is_err());
176    }
177
178    #[test]
179    fn tags_length() {
180        assert!(tags("aa").is_ok());
181        assert!(tags("a".repeat(200)).is_ok());
182
183        assert!(tags("a").is_err());
184        assert!(tags("a".repeat(201)).is_err());
185    }
186}