use regex::Regex;
use std::sync::LazyLock;
pub static USERNAME_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9_-]+$").unwrap());
#[must_use]
pub fn is_valid_username(username: &str) -> bool {
if username.len() < 3 || username.len() > 32 {
return false;
}
USERNAME_REGEX.is_match(username)
}
#[must_use]
pub fn is_valid_email(email: &str) -> bool {
if email.len() <= 3 {
return false;
}
let at_pos = email.find('@');
at_pos.is_some_and(|pos| pos > 0 && pos < email.len() - 1)
}
#[must_use]
pub const fn is_valid_password(password: &str) -> bool {
password.len() >= 8
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
#[test]
fn test_is_valid_username() {
let long_username = "a".repeat(33);
let test_cases = vec![
("valid_user", true),
("user123", true),
("user-name", true),
("user_name", true),
("User", true),
("ab", false), (long_username.as_str(), false), ("user@name", false), ("user name", false), ("user.name", false), ("user#name", false), ("", false), ];
for (username, expected) in test_cases {
assert_eq!(
is_valid_username(username),
expected,
"Failed for username: {username}"
);
}
}
#[rstest]
#[case("test@example.com", true)]
#[case("user@domain.org", true)]
#[case("a@b.c", true)]
#[case("test", false)] #[case("@domain.com", false)] #[case("test@", false)] #[case("", false)] #[case("ab", false)] fn test_is_valid_email(#[case] email: &str, #[case] expected: bool) {
assert_eq!(is_valid_email(email), expected);
}
#[test]
fn test_is_valid_password() {
let min_password = "a".repeat(8);
let test_cases = vec![
("password123", true),
("12345678", true),
(min_password.as_str(), true),
("1234567", false), ("", false), ];
for (password, expected) in test_cases {
assert_eq!(
is_valid_password(password),
expected,
"Failed for password: {password}"
);
}
}
#[test]
fn test_username_regex_compilation() {
assert!(USERNAME_REGEX.is_match("valid_user"));
assert!(!USERNAME_REGEX.is_match("invalid@user"));
}
}