use serde::{Deserialize, Serialize}; use std::collections::HashSet; use crate::models::locations::farm::FarmData; use crate::models::locations::market::{Market, MarketDb}; use crate::models::locations::shop::Shop; use crate::models::locations::town::{Town, TownDb}; use crate::models::player::Player; use crate::models::{Position, Tunnel}; use crate::GeoffreyDatabaseModel; use std::fmt::{Display, Formatter}; use std::str::FromStr; pub mod farm; pub mod market; pub mod shop; pub mod town; #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, PartialEq)] pub enum LocationType { Base, Shop, Attraction, Town, Farm, Market, } impl From for LocationType { fn from(l: LocationDataDb) -> Self { match l { LocationDataDb::Base => LocationType::Base, LocationDataDb::Shop(_) => LocationType::Shop, LocationDataDb::Attraction => LocationType::Attraction, LocationDataDb::Town(_) => LocationType::Town, LocationDataDb::Farm(_) => LocationType::Farm, LocationDataDb::Market(_) => LocationType::Market, } } } impl Display for LocationType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let name = match self { LocationType::Base => "Base", LocationType::Shop => "Shop", LocationType::Attraction => "Attraction", LocationType::Town => "Town", LocationType::Farm => "Farm", LocationType::Market => "Market", }; write!(f, "{}", name) } } impl FromStr for LocationType { type Err = String; fn from_str(s: &str) -> Result { let t = match s.to_lowercase().as_str() { "base" => LocationType::Base, "shop" => LocationType::Shop, "attraction" => LocationType::Attraction, "town" => LocationType::Town, "farm" => LocationType::Farm, "market" => LocationType::Market, &_ => return Err(format!("Location type invalid: '{}'", s)), }; Ok(t) } } #[derive(Serialize, Deserialize, Debug, Clone)] pub enum LocationDataDb { Base, Shop(Shop), Attraction, Town(TownDb), Farm(FarmData), Market(MarketDb), } impl From for LocationDataDb { fn from(t: LocationType) -> Self { match t { LocationType::Base => LocationDataDb::Base, LocationType::Shop => LocationDataDb::Shop(Default::default()), LocationType::Attraction => LocationDataDb::Attraction, LocationType::Town => LocationDataDb::Town(Default::default()), LocationType::Farm => LocationDataDb::Farm(Default::default()), LocationType::Market => LocationDataDb::Market(Default::default()), } } } #[derive(Serialize, Deserialize, Debug, Clone)] pub enum LocationData { Base, Shop(Shop), Attraction, Town(Town), Farm(FarmData), Market(Market), } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct LocationDb { id: Option, pub name: String, pub position: Position, owners: HashSet, pub tunnel: Option, pub loc_data: LocationDataDb, } impl LocationDb { pub fn new( name: &str, position: Position, owner: u64, tunnel: Option, loc_type: LocationDataDb, ) -> Self { let mut owners = HashSet::new(); owners.insert(owner); Self { id: None, name: name.to_string(), position, owners, tunnel, loc_data: loc_type, } } pub fn owners(&self) -> Vec { self.owners.iter().cloned().collect() } pub fn add_owner(&mut self, owner: u64) { self.owners.insert(owner); } pub fn remove_owner(&mut self, owner: u64) { self.owners.remove(&owner); } } impl GeoffreyDatabaseModel for LocationDb { fn id(&self) -> Option { self.id } fn set_id(&mut self, id: u64) { self.id = Some(id) } fn tree() -> String { "location".to_string() } fn check_unique(&self, o: &Self) -> bool { o.name.to_lowercase() != self.name.to_lowercase() } } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Location { pub id: u64, pub name: String, pub position: Position, pub owners: Vec, pub tunnel: Option, pub loc_data: LocationData, } impl Location { pub fn from_db_location( location: LocationDb, owners: Vec, loc_data: LocationData, ) -> Self { Self { id: location.id.unwrap(), name: location.name, position: location.position, owners, tunnel: location.tunnel, loc_data, } } } #[cfg(test)] mod tests { use crate::models::locations::{LocationDataDb, LocationDb}; use crate::models::{Dimension, Position}; use crate::GeoffreyDatabaseModel; #[test] fn test_location_check_unique() { let l1 = LocationDb::new( "Test", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationDataDb::Base, ); let l2 = LocationDb::new( "NotTest", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationDataDb::Base, ); assert!(l1.check_unique(&l2)); let l1 = LocationDb::new( "Test", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationDataDb::Base, ); let l2 = LocationDb::new( "teSt", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationDataDb::Base, ); assert!(!l1.check_unique(&l2)); } }