//! Garage service layer providing business logic for vehicle garage management. //! //! Handles validation, storage, and retrieval of player vehicle garages. use forge_models::garage::{Garage, HitPoints, Vehicle}; use forge_repositories::GarageRepository; use std::collections::HashMap; use uuid::Uuid; /// Service layer implementation for garage business logic and operations. pub struct GarageService { repository: R, } impl GarageService { /// Creates a new garage service with the provided repository. pub fn new(repository: R) -> Self { Self { repository } } /// Creates a new empty garage for a player pub fn create_garage(&self, key: String) -> Result { // Business rule: Check if garage already exists if self.repository.exists(&key)? { return Err(format!("Garage for '{}' already exists", key)); } // Create empty garage (no vehicles) let garage = Garage::new().map_err(|e| format!("Validation failed: {}", e))?; self.repository.create(&key, &garage)?; Ok(garage) } /// Replaces the entire garage content with the provided vehicles map pub fn update_garage( &self, key: String, vehicles: HashMap, ) -> Result { // Validate all vehicles for vehicle in vehicles.values() { vehicle .validate() .map_err(|e| format!("Validation failed for vehicle {}: {}", vehicle.plate, e))?; } // Create garage object let garage = Garage { vehicles }; // Validate garage (capacity, etc.) // Business rule: Check if garage has reached maximum capacity if garage.vehicles.len() > 5 { return Err("Garage exceeds maximum capacity of 5 vehicles.".to_string()); } // Update repository self.repository.update(&key, &garage)?; Ok(garage) } pub fn patch_vehicle( &self, key: String, plate: String, damage: Option, fuel: Option, hit_points_json: Option, ) -> Result { let mut garage = self.repository.get(&key)?.unwrap_or(Garage { vehicles: HashMap::new(), }); if let Some(vehicle) = garage.vehicles.get_mut(&plate) { if let Some(d) = damage { vehicle.damage = d; } if let Some(f) = fuel { vehicle.fuel = f; } if let Some(hp_json) = hit_points_json { if let Ok(hp) = HitPoints::from_json_str(&hp_json) { vehicle.hit_points = hp; } } } else { return Err(format!("Vehicle with plate {} not found", plate)); } self.repository.update(&key, &garage)?; Ok(garage) } /// Adds a new vehicle to a player's garage pub fn add_vehicle( &self, key: String, classname: String, fuel: f64, damage: f64, hit_points_json: String, ) -> Result { // Get existing garage or create new one let mut garage = match self.repository.get(&key)? { Some(g) => g, None => Garage::new().map_err(|e| format!("Failed to create garage: {}", e))?, }; // Business rule: Check if garage has reached maximum capacity (5 vehicles) if garage.vehicles.len() >= 5 { return Err("Garage is full. Maximum of 5 vehicles allowed.".to_string()); } // Generate a unique plate (vehicle ID) using UUID let plate = Uuid::new_v4().to_string(); // Parse hit points from Arma 3 JSON let hit_points = HitPoints::from_json_str(&hit_points_json)?; // Create new vehicle entry with validation let new_vehicle = Vehicle::new(plate, classname, fuel, damage, hit_points) .map_err(|e| format!("Validation failed: {}", e))?; // Add new vehicle to garage garage .add_vehicle(new_vehicle) .map_err(|e| format!("Failed to add vehicle: {}", e))?; // Update garage with new vehicle self.repository.update(&key, &garage)?; Ok(garage) } /// Retrieves a player's garage pub fn get_garage(&self, key: String) -> Result { match self.repository.get(&key)? { Some(garage) => Ok(garage), None => Err(format!("No garage found for player '{}'", key)), } } /// Removes a vehicle from the garage by plate number pub fn remove_vehicle(&self, key: String, plate: String) -> Result { // Get existing garage let mut garage = match self.repository.get(&key)? { Some(g) => g, None => return Err(format!("No garage found for player '{}'", key)), }; // Remove the vehicle by plate garage .remove_vehicle(&plate) .ok_or_else(|| format!("Vehicle with plate '{}' not found in garage", plate))?; // Update garage after removing vehicle self.repository.update(&key, &garage)?; Ok(garage) } /// Deletes all vehicles from a player's garage pub fn delete_garage(&self, key: String) -> Result<(), String> { self.repository.delete(&key) } /// Checks if a player has a garage (even if empty) pub fn garage_exists(&self, key: String) -> Result { self.repository.exists(&key) } }