Skip to main content

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(
40        "Invalid login name `{login}`: Invalid character `{character}` encountered at position `{position}`"
41    )]
42    InvalidCharacter {
43        /// The login name that failed validation.
44        login: String,
45        /// Index of the found invalid character in the original string
46        position: usize,
47        /// The invalid character
48        character: char,
49    },
50    /// Login name exceeds maximum length of 25 characters
51    #[error("Invalid login name `{login}`: Login name exceeds maximum length of 25 characters")]
52    TooLong {
53        /// The login name that failed validation.
54        login: String,
55    },
56    /// Login name is too short (must be at least one character long)
57    #[error(
58        "Invalid login name `{login}`: Login name is too short (must be at least one character long)"
59    )]
60    TooShort {
61        /// The login name that failed validation.
62        login: String,
63    },
64}
65
66#[cfg(test)]
67mod tests {
68    use crate::validate::Error;
69    use crate::validate::validate_login;
70
71    #[test]
72    pub fn test_validate_login() {
73        assert_eq!(Ok(()), validate_login("pajlada"));
74        assert_eq!(
75            Err(Error::InvalidCharacter {
76                login: "pajLada".to_owned(),
77                position: 3,
78                character: 'L',
79            }),
80            validate_login("pajLada")
81        );
82        assert_eq!(
83            Err(Error::InvalidCharacter {
84                login: "pajlada,def".to_owned(),
85                position: 7,
86                character: ',',
87            }),
88            validate_login("pajlada,def")
89        );
90        assert_eq!(
91            Err(Error::InvalidCharacter {
92                login: "pajlada-def".to_owned(),
93                position: 7,
94                character: '-',
95            }),
96            validate_login("pajlada-def")
97        );
98        assert_eq!(Ok(()), validate_login("1234567890123456789012345"));
99        assert_eq!(
100            Err(Error::TooLong {
101                login: "12345678901234567890123456".to_owned()
102            }),
103            validate_login("12345678901234567890123456")
104        );
105        assert_eq!(Ok(()), validate_login("a"));
106        assert_eq!(Ok(()), validate_login("abc"));
107        assert_eq!(Ok(()), validate_login("xqco"));
108        assert_eq!(Ok(()), validate_login("cool_user___"));
109        assert_eq!(Ok(()), validate_login("cool_7user___7"));
110        assert_eq!(
111            Err(Error::TooShort {
112                login: String::new()
113            }),
114            validate_login("")
115        );
116    }
117}