use arma_rs::{FromArma, IntoArma}; use forge_shared::GarageValidationError; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Garage { pub vehicles: HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Vehicle { pub plate: String, pub classname: String, pub fuel: f64, pub damage: f64, pub hit_points: HitPoints, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HitPoints { pub names: Vec, pub selections: Vec, pub values: Vec, } impl HitPoints { pub fn new() -> Self { Self { names: Vec::new(), selections: Vec::new(), values: Vec::new(), } } pub fn from_json_str(json_str: &str) -> Result { let hit_points: HitPoints = serde_json::from_str(json_str) .map_err(|e| format!("Failed to parse hit_points JSON: {}", e))?; let names_len = hit_points.names.len(); let selections_len = hit_points.selections.len(); let values_len = hit_points.values.len(); if names_len != selections_len || names_len != values_len { return Err(format!( "Hitpoint array length mismatch: names={}, selections={}, values={}", names_len, selections_len, values_len )); } Ok(hit_points) } } impl Vehicle { pub fn new>( plate: S, classname: S, fuel: f64, damage: f64, hit_points: HitPoints, ) -> Result { let vehicle = Self { plate: plate.into(), classname: classname.into(), fuel, damage, hit_points, }; vehicle.validate()?; Ok(vehicle) } pub fn validate(&self) -> Result<(), GarageValidationError> { if self.classname.trim().is_empty() { return Err(GarageValidationError::ClassnameEmpty); } if self.fuel < 0.0 || self.fuel > 1.0 { return Err(GarageValidationError::FuelInvalid); } if self.damage < 0.0 || self.damage > 1.0 { return Err(GarageValidationError::DamageInvalid); } let names_len = self.hit_points.names.len(); let selections_len = self.hit_points.selections.len(); let values_len = self.hit_points.values.len(); if names_len != selections_len || names_len != values_len { return Err(GarageValidationError::HitpointArrayLengthMismatch); } for (i, value) in self.hit_points.values.iter().enumerate() { if *value < 0.0 || *value > 1.0 { return Err(GarageValidationError::HitpointValueInvalid(i)); } } Ok(()) } } impl Garage { pub fn new() -> Result { let garage = Self { vehicles: HashMap::new(), }; garage.validate()?; Ok(garage) } pub fn validate(&self) -> Result<(), GarageValidationError> { for vehicle in self.vehicles.values() { vehicle.validate()?; } Ok(()) } pub fn add_vehicle(&mut self, vehicle: Vehicle) -> Result<(), GarageValidationError> { vehicle.validate()?; self.vehicles.insert(vehicle.plate.clone(), vehicle); Ok(()) } pub fn remove_vehicle(&mut self, plate: &str) -> Option { self.vehicles.remove(plate) } pub fn get_vehicle(&self, plate: &str) -> Option<&Vehicle> { self.vehicles.get(plate) } pub fn get_vehicle_mut(&mut self, plate: &str) -> Option<&mut Vehicle> { self.vehicles.get_mut(plate) } } impl FromArma for Vehicle { fn from_arma(s: String) -> Result { serde_json::from_str(&s) .map_err(|e| arma_rs::FromArmaError::InvalidPrimitive(format!("Invalid JSON: {}", e))) } } impl IntoArma for Vehicle { fn to_arma(&self) -> arma_rs::Value { let json_str = serde_json::to_string(self).unwrap_or_default(); arma_rs::Value::String(json_str) } }