gatelogue_types/node/
located.rs

1use enum_dispatch::enum_dispatch;
2use strum_macros::{EnumIs, EnumString, EnumTryAs};
3
4use crate::{
5    error::Error,
6    from_sql_for_enum,
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            "SELECT node1, node2 FROM Proximity WHERE node1 = ?1 OR node2 = ?1",
46            (self.i(),),
47            |row| {
48                let node1 = row.get(0)?;
49                let node2 = row.get(1)?;
50                Ok((
51                    AnyLocatedNode::from_id(gd, if node1 == self.i() { node2 } else { node1 })?
52                        .unwrap(),
53                    Proximity(node1, node2),
54                ))
55            },
56        )
57    }
58
59    fn shared_facilities(self, gd: &GD) -> Result<Vec<AnyLocatedNode>> {
60        gd.0.query_and_then_get_vec(
61            "SELECT node1, node2 FROM SharedFacility WHERE node1 = ?1 OR node2 = ?1",
62            (self.i(),),
63            |row| {
64                let node1 = row.get(0)?;
65                let node2 = row.get(1)?;
66                AnyLocatedNode::from_id(gd, if node1 == self.i() { node2 } else { node1 })
67                    .map(|a| a.unwrap())
68            },
69        )
70    }
71}
72
73#[enum_dispatch(Node, LocatedNode)]
74#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumIs, EnumTryAs)]
75pub enum AnyLocatedNode {
76    AirAirport,
77    BusStop,
78    RailStation,
79    SeaStop,
80    SpawnWarp,
81    Town,
82}
83macro_rules! impl_any_located_node {
84    ($($Variant:ident),+) => {
85        impl TryFrom<AnyNode> for AnyLocatedNode {
86            type Error = Error;
87
88            fn try_from(value: AnyNode) -> std::result::Result<Self, Self::Error> {
89                match value {
90                    $(AnyNode::$Variant(a) => Ok(Self::$Variant(a)),)+
91                    _ => Err(Error::NodeNotLocated(value.i())),
92                }
93            }
94        }
95
96        impl From<AnyLocatedNode> for AnyNode {
97            fn from(value: AnyLocatedNode) -> Self {
98                match value {
99                    $(AnyLocatedNode::$Variant(a) => Self::$Variant(a),)+
100                }
101            }
102        }
103    };
104}
105impl_any_located_node!(AirAirport, BusStop, RailStation, SeaStop, SpawnWarp, Town);
106
107impl AnyLocatedNode {
108    pub fn from_id(gd: &GD, id: ID) -> Result<Option<Self>> {
109        AnyNode::from_id(gd, id)?.map_or_else(|| Ok(None), |a| a.try_into().map(Some))
110    }
111}
112
113#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumString)]
114pub enum World {
115    Old,
116    New,
117    Space,
118}
119from_sql_for_enum!(World);
120
121pub struct Proximity(pub(crate) ID, pub(crate) ID);
122
123impl Proximity {
124    pub fn distance(self, gd: &GD) -> Result<f64> {
125        gd.0.query_one(
126            "SELECT distance FROM Proximity WHERE node1 = ? AND node2 = ?",
127            (self.0, self.1),
128            |a| a.get(0),
129        )
130        .map_err(Into::into)
131    }
132    pub fn explicit(self, gd: &GD) -> Result<bool> {
133        gd.0.query_one(
134            "SELECT explicit FROM Proximity WHERE node1 = ? AND node2 = ?",
135            (self.0, self.1),
136            |a| a.get(0),
137        )
138        .map_err(Into::into)
139    }
140}