use super::axles::Axles;
use crate::types::basic::{Double, OSString};
use crate::types::enums::VehicleCategory;
use crate::types::geometry::BoundingBox;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Performance {
#[serde(rename = "@maxSpeed")]
pub max_speed: Double,
#[serde(rename = "@maxAcceleration")]
pub max_acceleration: Double,
#[serde(rename = "@maxDeceleration")]
pub max_deceleration: Double,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Properties {
#[serde(rename = "Property", default)]
pub properties: Vec<Property>,
#[serde(rename = "File", default)]
pub files: Vec<File>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Property {
#[serde(rename = "@name")]
pub name: String,
#[serde(rename = "@value")]
pub value: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct File {
#[serde(rename = "@filepath")]
pub filepath: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Vehicle {
#[serde(rename = "@name")]
pub name: OSString,
#[serde(rename = "@vehicleCategory")]
pub vehicle_category: VehicleCategory,
#[serde(rename = "BoundingBox")]
pub bounding_box: BoundingBox,
#[serde(rename = "Performance")]
pub performance: Performance,
#[serde(rename = "Axles")]
pub axles: Axles,
#[serde(rename = "Properties", skip_serializing_if = "Option::is_none")]
pub properties: Option<Properties>,
}
impl Default for Performance {
fn default() -> Self {
Self {
max_speed: Double::literal(200.0),
max_acceleration: Double::literal(10.0),
max_deceleration: Double::literal(10.0),
}
}
}
impl Vehicle {
pub fn new_car(name: String) -> Self {
Self {
name: crate::types::basic::Value::literal(name),
vehicle_category: VehicleCategory::Car,
bounding_box: BoundingBox::default(),
performance: Performance::default(),
axles: Axles::car(),
properties: None,
}
}
pub fn new_truck(name: String) -> Self {
Self {
name: crate::types::basic::Value::literal(name),
vehicle_category: VehicleCategory::Truck,
bounding_box: BoundingBox {
center: crate::types::geometry::Center::default(),
dimensions: crate::types::geometry::Dimensions::truck_default(),
},
performance: Performance {
max_speed: Double::literal(120.0),
max_acceleration: Double::literal(3.0),
max_deceleration: Double::literal(8.0),
},
axles: Axles::truck(),
properties: None,
}
}
pub fn new_motorcycle(name: String) -> Self {
Self {
name: crate::types::basic::Value::literal(name),
vehicle_category: VehicleCategory::Motorbike,
bounding_box: BoundingBox {
center: crate::types::geometry::Center::default(),
dimensions: crate::types::geometry::Dimensions::motorcycle(),
},
performance: Performance {
max_speed: Double::literal(180.0),
max_acceleration: Double::literal(8.0),
max_deceleration: Double::literal(12.0),
},
axles: Axles::motorcycle(),
properties: None,
}
}
pub fn wheelbase(
&self,
params: &std::collections::HashMap<String, String>,
) -> crate::error::Result<f64> {
self.axles.wheelbase(params)
}
pub fn is_steerable(
&self,
params: &std::collections::HashMap<String, String>,
) -> crate::error::Result<bool> {
self.axles.is_steerable(params)
}
pub fn axle_count(&self) -> usize {
self.axles.axle_count()
}
pub fn footprint_area(
&self,
params: &std::collections::HashMap<String, String>,
) -> crate::error::Result<f64> {
self.bounding_box.dimensions.footprint_area(params)
}
}
impl Default for Vehicle {
fn default() -> Self {
Self {
name: crate::types::basic::Value::literal("DefaultVehicle".to_string()),
vehicle_category: VehicleCategory::Car,
bounding_box: BoundingBox::default(),
performance: Performance::default(),
axles: Axles::default(),
properties: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vehicle_default() {
let vehicle = Vehicle::default();
assert_eq!(vehicle.name.as_literal().unwrap(), "DefaultVehicle");
assert_eq!(vehicle.vehicle_category, VehicleCategory::Car);
assert_eq!(
vehicle.bounding_box.dimensions.width.as_literal().unwrap(),
&2.0
);
}
#[test]
fn test_vehicle_creation() {
let vehicle = Vehicle {
name: crate::types::basic::Value::literal("TestCar".to_string()),
vehicle_category: VehicleCategory::Car,
bounding_box: BoundingBox::default(),
performance: Performance::default(),
axles: Axles::default(),
properties: None,
};
assert_eq!(vehicle.name.as_literal().unwrap(), "TestCar");
assert_eq!(vehicle.vehicle_category, VehicleCategory::Car);
}
#[test]
fn test_vehicle_serialization() {
let vehicle = Vehicle::default();
let xml = quick_xml::se::to_string(&vehicle).unwrap();
assert!(xml.contains("name=\"DefaultVehicle\""));
assert!(xml.contains("vehicleCategory=\"car\""));
assert!(xml.contains("BoundingBox"));
}
#[test]
fn test_vehicle_new_car() {
let car = Vehicle::new_car("TestCar".to_string());
assert_eq!(car.name.as_literal().unwrap(), "TestCar");
assert_eq!(car.vehicle_category, VehicleCategory::Car);
assert_eq!(car.axle_count(), 2);
}
#[test]
fn test_vehicle_new_truck() {
let truck = Vehicle::new_truck("TestTruck".to_string());
assert_eq!(truck.name.as_literal().unwrap(), "TestTruck");
assert_eq!(truck.vehicle_category, VehicleCategory::Truck);
assert_eq!(truck.axle_count(), 3); }
#[test]
fn test_vehicle_new_motorcycle() {
let motorcycle = Vehicle::new_motorcycle("TestBike".to_string());
assert_eq!(motorcycle.name.as_literal().unwrap(), "TestBike");
assert_eq!(motorcycle.vehicle_category, VehicleCategory::Motorbike);
assert_eq!(motorcycle.axle_count(), 2);
}
#[test]
fn test_vehicle_wheelbase() {
use std::collections::HashMap;
let car = Vehicle::new_car("TestCar".to_string());
let params = HashMap::new();
let wheelbase = car.wheelbase(¶ms).unwrap();
assert!(wheelbase > 0.0);
}
#[test]
fn test_vehicle_is_steerable() {
use std::collections::HashMap;
let car = Vehicle::new_car("TestCar".to_string());
let params = HashMap::new();
assert!(car.is_steerable(¶ms).unwrap());
}
#[test]
fn test_vehicle_footprint_area() {
use std::collections::HashMap;
let car = Vehicle::new_car("TestCar".to_string());
let params = HashMap::new();
let area = car.footprint_area(¶ms).unwrap();
assert!(area > 0.0);
}
}