- Route bank sync payloads through the client bridge - Refresh account state without rebuilding the full session - Split CAD dispatcher UI into modular source files
261 lines
8.2 KiB
Rust
261 lines
8.2 KiB
Rust
//! 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::{GarageHotRepository, GarageRepository};
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
/// Service layer implementation for garage business logic and operations.
|
|
pub struct GarageService<R: GarageRepository> {
|
|
repository: R,
|
|
}
|
|
|
|
pub struct GarageHotStateService<R: GarageRepository, H: GarageHotRepository> {
|
|
service: GarageService<R>,
|
|
repository: H,
|
|
}
|
|
|
|
impl<R: GarageRepository> GarageService<R> {
|
|
/// 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<Garage, String> {
|
|
// 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<String, Vehicle>,
|
|
) -> Result<Garage, String> {
|
|
// 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<f64>,
|
|
fuel: Option<f64>,
|
|
hit_points_json: Option<String>,
|
|
) -> Result<Garage, String> {
|
|
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<Garage, String> {
|
|
// 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<Garage, String> {
|
|
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<Garage, String> {
|
|
// 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<bool, String> {
|
|
self.repository.exists(&key)
|
|
}
|
|
}
|
|
|
|
impl<R: GarageRepository, H: GarageHotRepository> GarageHotStateService<R, H> {
|
|
pub fn new(repository: R, hot_repository: H) -> Self {
|
|
Self {
|
|
service: GarageService::new(repository),
|
|
repository: hot_repository,
|
|
}
|
|
}
|
|
|
|
pub fn init_garage(&self, uid: String) -> Result<Garage, String> {
|
|
if let Some(garage) = self.repository.get(&uid)? {
|
|
return Ok(garage);
|
|
}
|
|
|
|
let garage = match self.service.get_garage(uid.clone()) {
|
|
Ok(garage) => garage,
|
|
Err(_) => self.service.create_garage(uid.clone())?,
|
|
};
|
|
self.repository.save(&garage, &uid)?;
|
|
Ok(garage)
|
|
}
|
|
|
|
pub fn get_garage(&self, uid: String) -> Result<Garage, String> {
|
|
self.init_garage(uid)
|
|
}
|
|
|
|
pub fn override_garage(
|
|
&self,
|
|
uid: String,
|
|
vehicles: HashMap<String, Vehicle>,
|
|
) -> Result<Garage, String> {
|
|
for vehicle in vehicles.values() {
|
|
vehicle
|
|
.validate()
|
|
.map_err(|e| format!("Validation failed for vehicle {}: {}", vehicle.plate, e))?;
|
|
}
|
|
|
|
let garage = Garage { vehicles };
|
|
if garage.vehicles.len() > 5 {
|
|
return Err("Garage exceeds maximum capacity of 5 vehicles.".to_string());
|
|
}
|
|
|
|
self.repository.save(&garage, &uid)?;
|
|
Ok(garage)
|
|
}
|
|
|
|
pub fn save_garage(&self, uid: String) -> Result<Garage, String> {
|
|
let garage = self
|
|
.repository
|
|
.get(&uid)?
|
|
.ok_or_else(|| format!("No garage found for player '{}'", uid))?;
|
|
let saved = self
|
|
.service
|
|
.update_garage(uid.clone(), garage.vehicles.clone())?;
|
|
self.repository.save(&saved, &uid)?;
|
|
Ok(saved)
|
|
}
|
|
|
|
pub fn add_vehicle(
|
|
&self,
|
|
uid: String,
|
|
classname: String,
|
|
fuel: f64,
|
|
damage: f64,
|
|
hit_points_json: String,
|
|
) -> Result<Garage, String> {
|
|
let garage =
|
|
self.service
|
|
.add_vehicle(uid.clone(), classname, fuel, damage, hit_points_json)?;
|
|
self.repository.save(&garage, &uid)?;
|
|
Ok(garage)
|
|
}
|
|
|
|
pub fn remove_vehicle(&self, uid: String, plate: String) -> Result<Garage, String> {
|
|
let garage = self.service.remove_vehicle(uid.clone(), plate)?;
|
|
self.repository.save(&garage, &uid)?;
|
|
Ok(garage)
|
|
}
|
|
|
|
pub fn remove_garage(&self, uid: String) -> Result<(), String> {
|
|
self.repository.delete(&uid)
|
|
}
|
|
}
|