gatelogue-types 3.1.3+13

Types for loading and reading Gatelogue data
Documentation
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)
    }
}