use super::types::*;
use crate::resources::Resource;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WorldMapRegistry {
locations: HashMap<LocationId, Location>,
routes: HashMap<RouteId, Route>,
#[serde(skip)]
adjacency: HashMap<LocationId, Vec<RouteId>>,
}
impl Resource for WorldMapRegistry {}
impl WorldMapRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_location(&mut self, location: Location) -> LocationId {
let id = location.id.clone();
self.locations.insert(id.clone(), location);
id
}
pub fn register_route(&mut self, route: Route) -> RouteId {
let id = route.id.clone();
self.adjacency
.entry(route.from.clone())
.or_default()
.push(id.clone());
if route.bidirectional {
self.adjacency
.entry(route.to.clone())
.or_default()
.push(id.clone());
}
self.routes.insert(id.clone(), route);
id
}
pub fn get_location(&self, id: &LocationId) -> Option<&Location> {
self.locations.get(id)
}
pub fn get_location_mut(&mut self, id: &LocationId) -> Option<&mut Location> {
self.locations.get_mut(id)
}
pub fn get_route(&self, id: &RouteId) -> Option<&Route> {
self.routes.get(id)
}
pub fn get_route_mut(&mut self, id: &RouteId) -> Option<&mut Route> {
self.routes.get_mut(id)
}
pub fn get_routes_from(&self, location_id: &LocationId) -> Vec<&Route> {
if let Some(route_ids) = self.adjacency.get(location_id) {
route_ids
.iter()
.filter_map(|id| self.routes.get(id))
.collect()
} else {
Vec::new()
}
}
pub fn locations(&self) -> &HashMap<LocationId, Location> {
&self.locations
}
pub fn routes(&self) -> &HashMap<RouteId, Route> {
&self.routes
}
pub fn calculate_distance(&self, from: &LocationId, to: &LocationId) -> Option<f32> {
let from_loc = self.get_location(from)?;
let to_loc = self.get_location(to)?;
Some(from_loc.position.distance_to(&to_loc.position))
}
pub fn has_route(&self, from: &LocationId, to: &LocationId) -> bool {
self.get_routes_from(from)
.iter()
.any(|r| &r.to == to || (r.bidirectional && &r.from == to))
}
pub fn remove_location(&mut self, id: &LocationId) -> Option<Location> {
self.adjacency.remove(id);
self.locations.remove(id)
}
pub fn remove_route(&mut self, id: &RouteId) -> Option<Route> {
if let Some(route) = self.routes.remove(id) {
if let Some(routes) = self.adjacency.get_mut(&route.from) {
routes.retain(|rid| rid != id);
}
if route.bidirectional {
if let Some(routes) = self.adjacency.get_mut(&route.to) {
routes.retain(|rid| rid != id);
}
}
Some(route)
} else {
None
}
}
pub fn rebuild_adjacency(&mut self) {
self.adjacency.clear();
for (route_id, route) in &self.routes {
self.adjacency
.entry(route.from.clone())
.or_default()
.push(route_id.clone());
if route.bidirectional {
self.adjacency
.entry(route.to.clone())
.or_default()
.push(route_id.clone());
}
}
}
pub fn location_count(&self) -> usize {
self.locations.len()
}
pub fn route_count(&self) -> usize {
self.routes.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_registry_new() {
let registry = WorldMapRegistry::new();
assert_eq!(registry.location_count(), 0);
assert_eq!(registry.route_count(), 0);
}
#[test]
fn test_register_location() {
let mut registry = WorldMapRegistry::new();
let location =
Location::new("city_1", "Test City").with_position(Position::new(10.0, 20.0));
let id = registry.register_location(location);
assert_eq!(id, "city_1");
assert_eq!(registry.location_count(), 1);
assert!(registry.get_location(&id).is_some());
}
#[test]
fn test_register_route() {
let mut registry = WorldMapRegistry::new();
registry.register_location(Location::new("city_a", "City A"));
registry.register_location(Location::new("city_b", "City B"));
let route = Route::new("route_1", "city_a", "city_b");
let id = registry.register_route(route);
assert_eq!(id, "route_1");
assert_eq!(registry.route_count(), 1);
assert!(registry.get_route(&id).is_some());
}
#[test]
fn test_get_routes_from() {
let mut registry = WorldMapRegistry::new();
registry.register_location(Location::new("city_a", "City A"));
registry.register_location(Location::new("city_b", "City B"));
registry.register_location(Location::new("city_c", "City C"));
registry.register_route(Route::new("route_1", "city_a", "city_b"));
registry.register_route(Route::new("route_2", "city_a", "city_c"));
let routes = registry.get_routes_from(&"city_a".to_string());
assert_eq!(routes.len(), 2);
}
#[test]
fn test_calculate_distance() {
let mut registry = WorldMapRegistry::new();
registry.register_location(
Location::new("city_a", "City A").with_position(Position::new(0.0, 0.0)),
);
registry.register_location(
Location::new("city_b", "City B").with_position(Position::new(3.0, 4.0)),
);
let distance = registry
.calculate_distance(&"city_a".to_string(), &"city_b".to_string())
.unwrap();
assert_eq!(distance, 5.0);
}
#[test]
fn test_has_route() {
let mut registry = WorldMapRegistry::new();
registry.register_location(Location::new("city_a", "City A"));
registry.register_location(Location::new("city_b", "City B"));
registry.register_location(Location::new("city_c", "City C"));
registry.register_route(Route::new("route_1", "city_a", "city_b"));
assert!(registry.has_route(&"city_a".to_string(), &"city_b".to_string()));
assert!(registry.has_route(&"city_b".to_string(), &"city_a".to_string())); assert!(!registry.has_route(&"city_a".to_string(), &"city_c".to_string()));
}
#[test]
fn test_remove_location() {
let mut registry = WorldMapRegistry::new();
registry.register_location(Location::new("city_a", "City A"));
assert_eq!(registry.location_count(), 1);
let removed = registry.remove_location(&"city_a".to_string());
assert!(removed.is_some());
assert_eq!(registry.location_count(), 0);
}
#[test]
fn test_remove_route() {
let mut registry = WorldMapRegistry::new();
registry.register_location(Location::new("city_a", "City A"));
registry.register_location(Location::new("city_b", "City B"));
registry.register_route(Route::new("route_1", "city_a", "city_b"));
assert_eq!(registry.route_count(), 1);
let removed = registry.remove_route(&"route_1".to_string());
assert!(removed.is_some());
assert_eq!(registry.route_count(), 0);
}
#[test]
fn test_rebuild_adjacency() {
let mut registry = WorldMapRegistry::new();
registry.register_location(Location::new("city_a", "City A"));
registry.register_location(Location::new("city_b", "City B"));
registry.register_route(Route::new("route_1", "city_a", "city_b"));
registry.adjacency.clear();
assert_eq!(registry.get_routes_from(&"city_a".to_string()).len(), 0);
registry.rebuild_adjacency();
assert_eq!(registry.get_routes_from(&"city_a".to_string()).len(), 1);
}
}