🌡️

Rustでオブジェクト設計&バリデーション(ガード節)"^[A-Za-z0-9_-]+$"

2023/10/06に公開

main.rs

[dependencies]
regex = "1.4"
struct Username {
    username_min_length: usize,
    username_max_length: usize,
    username_valid_characters: regex::Regex, //ここまでクラス変数
    username: String,
}

impl Username {
    const USERNAME_MINIMUM_LENGTH: usize = 4;
    const USERNAME_MAXIMUM_LENGTH: usize = 40;
    const USERNAME_VALID_CHARACTERS: &str = "^[A-Za-z0-9_-]+$";

    fn new(username: &str) -> Result<Self, String> {
        let re = regex::Regex::new(Self::USERNAME_VALID_CHARACTERS).unwrap();
        let stripped_username = username.trim().to_string();

        // ガード節
        let result = Self::validate_username(&re, &stripped_username);
        match result {
            Ok(_) => Ok(Username {
                username_min_length: Self::USERNAME_MINIMUM_LENGTH,
                username_max_length: Self::USERNAME_MAXIMUM_LENGTH,
                username_valid_characters: re,
                username: stripped_username,
            }),
            Err(err) => Err(err),
        }
    }

    fn validate_username(re: &regex::Regex, username: &str) -> Result<(), String> {
        if username.is_empty() {
            return Err("Username cannot be blank".to_string());
        }

        let username_length = username.len();
        if username_length < Self::USERNAME_MINIMUM_LENGTH || username_length > Self::USERNAME_MAXIMUM_LENGTH {
            return Err(format!(
                "Username length should be between {} and {}",
                Self::USERNAME_MINIMUM_LENGTH, Self::USERNAME_MAXIMUM_LENGTH
            ));
        }

        if !re.is_match(username) {
            return Err(format!(
                "Username contains invalid characters. Allowed characters are: {}",
                Self::USERNAME_VALID_CHARACTERS
            ));
        }

        Ok(())
    }

    fn to_string(&self) -> String {
        format!("Username(username={})", self.username)
    }
}

struct User {
    id: i32,
    username: Username, //依存の注入
}

impl User {
    fn new(id: i32, username: Username) -> Result<Self, String> {
        //ガード節
        if id == 0 {
            return Err("ID cannot be zero".to_string());
        }
        Ok(User { id, username })
    }

    fn get_user_info(&self) -> String {
        format!("User(id={}, username={})", self.id, self.username.to_string())
    }
}


fn main() {
    let username1_result = Username::new("my_username123");
    match username1_result {
        Ok(username1) => {
            let user1_result = User::new(1, username1);
            match user1_result {
                Ok(user1) => println!("{}", user1.get_user_info()),
                Err(err) => eprintln!("Validation error: {}", err),
            }
        }
        Err(err) => eprintln!("Validation error: {}", err),
    }

    let username2_result = Username::new("<script>alert(0101);</script>");
    match username2_result {
        Ok(username2) => {
            let user2_result = User::new(2, username2);
            match user2_result {
                Ok(user2) => println!("{}", user2.get_user_info()),
                Err(err) => eprintln!("Validation error: {}", err),
            }
        }
        Err(err) => eprintln!("Validation error: {}", err),
    }
}

出力

User(id=1, username=Username(username=my_username123))
Validation error: Username contains invalid characters. Allowed characters are: ^[A-Za-z0-9_-]+$

Discussion