Skip to main content

gatelogue_types/node/
located.rs

1use enum_dispatch::enum_dispatch;
2use strum_macros::{EnumIs, EnumString, EnumTryAs};
3
4use crate::{
5    _from_sql_for_enum,
6    error::Error,
7    node::{
8        air::AirAirport, bus::BusStop, rail::RailStation, sea::SeaStop, spawn_warp::SpawnWarp,
9        town::Town, AnyNode, Node,
10    },
11    util::{ConnectionExt, ID},
12    Result, GD,
13};
14
15#[enum_dispatch]
16pub trait LocatedNode: Node + Copy {
17    fn world(self, gd: &GD) -> Result<Option<World>> {
18        gd.0.query_one(
19            "SELECT world FROM NodeLocation WHERE i = ?",
20            (self.i(),),
21            |a| a.get(0),
22        )
23        .map_err(Into::into)
24    }
25
26    fn coordinates(self, gd: &GD) -> Result<Option<(f64, f64)>> {
27        gd.0.query_one(
28            "SELECT x, y FROM NodeLocation WHERE i = ?",
29            (self.i(),),
30            |a| {
31                let Some(x) = a.get::<_, Option<f64>>(0)? else {
32                    return Ok(None);
33                };
34                let Some(y) = a.get::<_, Option<f64>>(1)? else {
35                    return Ok(None);
36                };
37                Ok(Some((x, y)))
38            },
39        )
40        .map_err(Into::into)
41    }
42
43    fn nodes_in_proximity(self, gd: &GD) -> Result<Vec<(AnyLocatedNode, Proximity)>> {
44        gd.0.query_and_then_get_vec(
45            include_str!("../sql/located/nodes_in_proximity.sql"),
46            (self.i(),),
47            |row| {
48                let other_i = row.get(0)?;
49                Ok((
50                    AnyLocatedNode::from_id(gd, other_i)?.unwrap(),
51                    Proximity(self.i().min(other_i), self.i().max(other_i)),
52                ))
53            },
54        )
55    }
56
57    fn shared_facilities(self, gd: &GD) -> Result<Vec<AnyLocatedNode>> {
58        gd.0.query_and_then_get_vec(
59            include_str!("../sql/located/shared_facilities.sql"),
60            (self.i(),),
61            |row| {
62                let other_i = row.get(0)?;
63                AnyLocatedNode::from_id(gd, other_i).map(|a| a.unwrap())
64            },
65        )
66    }
67}
68
69#[enum_dispatch(Node, LocatedNode)]
70#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumIs, EnumTryAs)]
71pub enum AnyLocatedNode {
72    AirAirport,
73    BusStop,
74    RailStation,
75    SeaStop,
76    SpawnWarp,
77    Town,
78}
79macro_rules! impl_any_located_node {
80    ($($Variant:ident),+) => {
81        impl TryFrom<AnyNode> for AnyLocatedNode {
82            type Error = Error;
83
84            fn try_from(value: AnyNode) -> std::result::Result<Self, Self::Error> {
85                match value {
86                    $(AnyNode::$Variant(a) => Ok(Self::$Variant(a)),)+
87                    _ => Err(Error::NodeNotLocated(value.i())),
88                }
89            }
90        }
91
92        impl From<AnyLocatedNode> for AnyNode {
93            fn from(value: AnyLocatedNode) -> Self {
94                match value {
95                    $(AnyLocatedNode::$Variant(a) => Self::$Variant(a),)+
96                }
97            }
98        }
99    };
100}
101impl_any_located_node!(AirAirport, BusStop, RailStation, SeaStop, SpawnWarp, Town);
102
103impl AnyLocatedNode {
104    pub fn from_id(gd: &GD, id: ID) -> Result<Option<Self>> {
105        AnyNode::from_id(gd, id)?.map_or_else(|| Ok(None), |a| a.try_into().map(Some))
106    }
107}
108
109#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumString)]
110pub enum World {
111    Old,
112    New,
113    Space,
114}
115_from_sql_for_enum!(World);
116
117pub struct Proximity(pub(crate) ID, pub(crate) ID);
118
119impl Proximity {
120    pub fn distance(self, gd: &GD) -> Result<f64> {
121        gd.0.query_one(
122            "SELECT distance FROM Proximity WHERE node1 = ? AND node2 = ?",
123            (self.0, self.1),
124            |a| a.get(0),
125        )
126        .map_err(Into::into)
127    }
128    pub fn explicit(self, gd: &GD) -> Result<bool> {
129        gd.0.query_one(
130            "SELECT explicit FROM Proximity WHERE node1 = ? AND node2 = ?",
131            (self.0, self.1),
132            |a| a.get(0),
133        )
134        .map_err(Into::into)
135    }
136}