twitch_irc/
validate.rs

1//! Contains an utility to validate channel names
2
3use thiserror::Error;
4
5/// Validate a given login name. Returns an error detailing the issue
6/// if the string is found to be invalid.
7pub fn validate_login(channel_login: &str) -> Result<(), Error> {
8    let mut length: usize = 0;
9    for char in channel_login.chars() {
10        if !(matches!(char, 'a'..='z' | '0'..='9' | '_')) {
11            return Err(Error::InvalidCharacter {
12                login: channel_login.to_owned(),
13                position: length,
14                character: char,
15            });
16        }
17
18        length += 1;
19        if length > 25 {
20            return Err(Error::TooLong {
21                login: channel_login.to_owned(),
22            });
23        }
24    }
25    if length < 1 {
26        return Err(Error::TooShort {
27            login: channel_login.to_owned(),
28        });
29    }
30
31    Ok(())
32}
33
34/// Types of errors that can be found as a result of validating a channel login name. See the enum
35/// variants for details
36#[derive(Error, Debug, PartialEq, Eq)]
37pub enum Error {
38    /// A character not allowed in login names was found at a certain position in the given string
39    #[error("Invalid login name `{login}`: Invalid character `{character}` encountered at position `{position}`")]
40    InvalidCharacter {
41        /// The login name that failed validation.
42        login: String,
43        /// Index of the found invalid character in the original string
44        position: usize,
45        /// The invalid character
46        character: char,
47    },
48    /// Login name exceeds maximum length of 25 characters
49    #[error("Invalid login name `{login}`: Login name exceeds maximum length of 25 characters")]
50    TooLong {
51        /// The login name that failed validation.
52        login: String,
53    },
54    /// Login name is too short (must be at least one character long)
55    #[error("Invalid login name `{login}`: Login name is too short (must be at least one character long)")]
56    TooShort {
57        /// The login name that failed validation.
58        login: String,
59    },
60}
61
62#[cfg(test)]
63mod tests {
64    use crate::validate::validate_login;
65    use crate::validate::Error;
66
67    #[test]
68    pub fn test_validate_login() {
69        assert_eq!(Ok(()), validate_login("pajlada"));
70        assert_eq!(
71            Err(Error::InvalidCharacter {
72                login: "pajLada".to_owned(),
73                position: 3,
74                character: 'L',
75            }),
76            validate_login("pajLada")
77        );
78        assert_eq!(
79            Err(Error::InvalidCharacter {
80                login: "pajlada,def".to_owned(),
81                position: 7,
82                character: ',',
83            }),
84            validate_login("pajlada,def")
85        );
86        assert_eq!(
87            Err(Error::InvalidCharacter {
88                login: "pajlada-def".to_owned(),
89                position: 7,
90                character: '-',
91            }),
92            validate_login("pajlada-def")
93        );
94        assert_eq!(Ok(()), validate_login("1234567890123456789012345"));
95        assert_eq!(
96            Err(Error::TooLong {
97                login: "12345678901234567890123456".to_owned()
98            }),
99            validate_login("12345678901234567890123456")
100        );
101        assert_eq!(Ok(()), validate_login("a"));
102        assert_eq!(Ok(()), validate_login("abc"));
103        assert_eq!(Ok(()), validate_login("xqco"));
104        assert_eq!(Ok(()), validate_login("cool_user___"));
105        assert_eq!(Ok(()), validate_login("cool_7user___7"));
106        assert_eq!(
107            Err(Error::TooShort {
108                login: "".to_owned()
109            }),
110            validate_login("")
111        );
112    }
113}