use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use crate::world::WorldTag;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum VehicleType {
Wheeled,
Tracked,
Motorcycle,
}
pub(crate) mod sealed {
use super::VehicleType;
pub trait Kind {
const TYPE: VehicleType;
}
}
pub trait VehicleKind: sealed::Kind {}
macro_rules! vehicle_kinds {
($($(#[$doc:meta])* $name:ident => $kind:ident;)*) => {
$(
$(#[$doc])*
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum $name {}
impl sealed::Kind for $name {
const TYPE: VehicleType = VehicleType::$kind;
}
impl VehicleKind for $name {}
)*
};
}
vehicle_kinds! {
WheeledVehicle => Wheeled;
TrackedVehicle => Tracked;
Motorcycle => Motorcycle;
}
pub struct VehicleId<K = WheeledVehicle> {
pub(super) raw: u32,
pub(crate) world: WorldTag,
kind: PhantomData<fn() -> K>,
}
impl<K: VehicleKind> VehicleId<K> {
pub(super) fn new(raw: u32, world: WorldTag) -> Self {
Self {
raw,
world,
kind: PhantomData,
}
}
}
impl<K> VehicleId<K> {
pub fn to_raw(self) -> u32 {
self.raw
}
}
impl<K> Clone for VehicleId<K> {
fn clone(&self) -> Self {
*self
}
}
impl<K> Copy for VehicleId<K> {}
impl<K> PartialEq for VehicleId<K> {
fn eq(&self, other: &Self) -> bool {
(self.raw, self.world) == (other.raw, other.world)
}
}
impl<K> Eq for VehicleId<K> {}
impl<K> PartialOrd for VehicleId<K> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<K> Ord for VehicleId<K> {
fn cmp(&self, other: &Self) -> Ordering {
(self.raw, self.world).cmp(&(other.raw, other.world))
}
}
impl<K> Hash for VehicleId<K> {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.raw, self.world).hash(state);
}
}
impl<K: VehicleKind> fmt::Debug for VehicleId<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("VehicleId")
.field(&K::TYPE)
.field(&self.raw)
.finish()
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AnyVehicleId {
pub(super) raw: u32,
pub(super) world: WorldTag,
pub(super) kind: VehicleType,
}
impl AnyVehicleId {
pub fn to_raw(self) -> u32 {
self.raw
}
pub fn kind(self) -> VehicleType {
self.kind
}
pub fn downcast<K: VehicleKind>(self) -> Option<VehicleId<K>> {
(self.kind == K::TYPE).then(|| VehicleId::new(self.raw, self.world))
}
}
impl<K: VehicleKind> From<VehicleId<K>> for AnyVehicleId {
fn from(id: VehicleId<K>) -> Self {
Self {
raw: id.raw,
world: id.world,
kind: K::TYPE,
}
}
}
impl fmt::Debug for AnyVehicleId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("VehicleId")
.field(&self.kind)
.field(&self.raw)
.finish()
}
}