use enum_dispatch::enum_dispatch;
use strum_macros::{EnumIs, EnumString, EnumTryAs};
use crate::{
_from_sql_for_enum,
error::Error,
node::{
air::AirAirport, bus::BusStop, rail::RailStation, sea::SeaStop, spawn_warp::SpawnWarp,
town::Town, AnyNode, Node,
},
util::{ConnectionExt, ID},
Result, GD,
};
#[enum_dispatch]
pub trait LocatedNode: Node + Copy {
fn world(self, gd: &GD) -> Result<Option<World>> {
gd.0.query_one(
"SELECT world FROM NodeLocation WHERE i = ?",
(self.i(),),
|a| a.get(0),
)
.map_err(Into::into)
}
fn coordinates(self, gd: &GD) -> Result<Option<(f64, f64)>> {
gd.0.query_one(
"SELECT x, y FROM NodeLocation WHERE i = ?",
(self.i(),),
|a| {
let Some(x) = a.get::<_, Option<f64>>(0)? else {
return Ok(None);
};
let Some(y) = a.get::<_, Option<f64>>(1)? else {
return Ok(None);
};
Ok(Some((x, y)))
},
)
.map_err(Into::into)
}
fn nodes_in_proximity(self, gd: &GD) -> Result<Vec<(AnyLocatedNode, Proximity)>> {
gd.0.query_and_then_get_vec(
include_str!("../sql/located/nodes_in_proximity.sql"),
(self.i(),),
|row| {
let other_i = row.get(0)?;
Ok((
AnyLocatedNode::from_id(gd, other_i)?.unwrap(),
Proximity(self.i().min(other_i), self.i().max(other_i)),
))
},
)
}
fn shared_facilities(self, gd: &GD) -> Result<Vec<AnyLocatedNode>> {
gd.0.query_and_then_get_vec(
include_str!("../sql/located/shared_facilities.sql"),
(self.i(),),
|row| {
let other_i = row.get(0)?;
AnyLocatedNode::from_id(gd, other_i).map(|a| a.unwrap())
},
)
}
}
#[enum_dispatch(Node, LocatedNode)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumIs, EnumTryAs)]
pub enum AnyLocatedNode {
AirAirport,
BusStop,
RailStation,
SeaStop,
SpawnWarp,
Town,
}
macro_rules! impl_any_located_node {
($($Variant:ident),+) => {
impl TryFrom<AnyNode> for AnyLocatedNode {
type Error = Error;
fn try_from(value: AnyNode) -> std::result::Result<Self, Self::Error> {
match value {
$(AnyNode::$Variant(a) => Ok(Self::$Variant(a)),)+
_ => Err(Error::NodeNotLocated(value.i())),
}
}
}
impl From<AnyLocatedNode> for AnyNode {
fn from(value: AnyLocatedNode) -> Self {
match value {
$(AnyLocatedNode::$Variant(a) => Self::$Variant(a),)+
}
}
}
};
}
impl_any_located_node!(AirAirport, BusStop, RailStation, SeaStop, SpawnWarp, Town);
impl AnyLocatedNode {
pub fn from_id(gd: &GD, id: ID) -> Result<Option<Self>> {
AnyNode::from_id(gd, id)?.map_or_else(|| Ok(None), |a| a.try_into().map(Some))
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumString)]
pub enum World {
Old,
New,
Space,
}
_from_sql_for_enum!(World);
pub struct Proximity(pub(crate) ID, pub(crate) ID);
impl Proximity {
pub fn distance(self, gd: &GD) -> Result<f64> {
gd.0.query_one(
"SELECT distance FROM Proximity WHERE node1 = ? AND node2 = ?",
(self.0, self.1),
|a| a.get(0),
)
.map_err(Into::into)
}
pub fn explicit(self, gd: &GD) -> Result<bool> {
gd.0.query_one(
"SELECT explicit FROM Proximity WHERE node1 = ? AND node2 = ?",
(self.0, self.1),
|a| a.get(0),
)
.map_err(Into::into)
}
}