fish-lib 0.2.3

A work-in-progress fishing game library containing the game/storage logic for a discord fishing game I'm working on.
Documentation
use crate::data::species_data::SpeciesData;
use crate::game::errors::resource::GameResourceError;
use crate::game::errors::GameResult;
use crate::game::repositories::specimen_repository::SpecimenRepositoryInterface;
use crate::models::specimen::{NewSpecimen, Specimen};
use crate::models::user::User;
use std::sync::Arc;

pub trait SpecimenServiceInterface: Send + Sync {
    fn generate_and_save_specimen(
        &self,
        owner_user: &User,
        species_data: Arc<SpeciesData>,
    ) -> GameResult<Specimen>;

    fn process_catch(&self, user: &User, species_data: Arc<SpeciesData>) -> GameResult<Specimen>;
}

pub struct SpecimenService {
    specimen_repository: Arc<dyn SpecimenRepositoryInterface>,
}

impl SpecimenService {
    pub fn new(specimen_repository: Arc<dyn SpecimenRepositoryInterface>) -> Self {
        Self {
            specimen_repository,
        }
    }
}

impl SpecimenServiceInterface for SpecimenService {
    fn generate_and_save_specimen(
        &self,
        owner_user: &User,
        species_data: Arc<SpeciesData>,
    ) -> GameResult<Specimen> {
        let new_fish = NewSpecimen::generate(owner_user.id, species_data.id);
        self.specimen_repository
            .create(new_fish)
            .map_err(|e| match e.get_database_error() {
                Some(db_error) if db_error.is_foreign_key_violation() => {
                    GameResourceError::user_not_found(owner_user.external_id).into()
                }
                _ => e.into(),
            })
    }

    fn process_catch(&self, user: &User, species_data: Arc<SpeciesData>) -> GameResult<Specimen> {
        let fish = self.generate_and_save_specimen(user, species_data)?;
        Ok(fish)
    }
}