Bye Bye Moore

PoCソルジャーな零細事業主が作業メモを残すブログ

練習用にアドレス帳みたいなのをつくる その4:エラーハンドリングを追加する

ユーザー定義エラーも使えるらしいので、ためしてみました

実際のところ

// カスタムエラー型の追加
#[derive(Debug)]
enum UserError {
    DatabaseError(String),
    DuplicateEmail(String),
    UserNotFound(String),
    IoError(std::io::Error),
    SerdeError(serde_json::Error),
}

impl std::error::Error for UserError {}

impl std::fmt::Display for UserError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            UserError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
            UserError::DuplicateEmail(email) => write!(f, "Email already exists: {}", email),
            UserError::UserNotFound(email) => write!(f, "User not found: {}", email),
            UserError::IoError(e) => write!(f, "IO error: {}", e),
            UserError::SerdeError(e) => write!(f, "Serialization error: {}", e),
        }
    }
}

impl From<std::io::Error> for UserError {
    fn from(error: std::io::Error) -> Self {
        UserError::IoError(error)
    }
}

impl From<serde_json::Error> for UserError {
    fn from(error: serde_json::Error) -> Self {
        UserError::SerdeError(error)
    }
}

impl UserDatabase {
    // ... existing code ...

    fn add_user(&mut self, mut user: User) -> Result<(), UserError> {
        if self.users.iter().any(|u| u.email == user.email) {
            return Err(UserError::DuplicateEmail(user.email));
        }
        
        user.id = Uuid::new_v4();
        self.users.push(user);
        Ok(())
    }

    fn update_user(&mut self, mut user: User) -> Result<(), UserError> {
        let existing_user = self.users
            .iter()
            .find(|u| u.email == user.email)
            .ok_or_else(|| UserError::UserNotFound(user.email.clone()))?;

        user.id = existing_user.id;

        let user_index = self.users
            .iter()
            .position(|u| u.email == user.email)
            .unwrap();

        self.users[user_index] = user;
        Ok(())
    }

    fn save_to_file(&self) -> Result<(), UserError> {
        let json = serde_json::to_string_pretty(&self)?;
        let mut file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open("users.json")?;
        file.write_all(json.as_bytes())?;
        Ok(())
    }

    fn load_from_file() -> Result<Self, UserError> {
        let mut file = match File::open("users.json") {
            Ok(file) => file,
            Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(UserDatabase::new()),
            Err(e) => return Err(UserError::IoError(e)),
        };
        
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        
        let db: UserDatabase = serde_json::from_str(&contents)?;
        Ok(db)
    }
}

fn main() -> Result<(), UserError> {
    let args = Cli::parse();
    let mut db = UserDatabase::load_from_file()?;

    match args.command {    
        Commands::Add { name, email, age } => {
            let user = User {
                id: Uuid::new_v4(),
                name: String::from(name),
                email: String::from(email),
                age,
            };

            db.add_user(user)?;
            db.save_to_file()?;
            println!("User added and saved successfully");
            println!("Current database state: {:?}", db);
        }

        Commands::Update { name, email, age } => {
            let user = User {
                id: Uuid::new_v4(),
                name: String::from(name),
                email: String::from(email),
                age,
            };

            db.update_user(user)?;
            db.save_to_file()?;
            println!("User updated and saved successfully");
        }
    }
    Ok(())
}

参考もと