use crate::continent::{ContinentSize, Coord};
use crate::error::{Error, Result};
use derive_more::{Deref, Display};
use serde::{Deserialize, Serialize, Serializer, ser};
use std::ops::{Div, Rem};
#[derive(Clone, Copy, Debug, Deref, Display, Hash, Deserialize)]
#[derive_const(PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[cfg_attr(feature = "typescript", ts(as = "u32"))]
pub struct ContinentIndex(usize);
impl ContinentIndex {
#[inline]
pub const fn new(index: usize) -> Self {
Self(index)
}
pub fn from_coord(coord: Coord, size: ContinentSize) -> Self {
let size = usize::from(size);
let x = usize::from(coord.x());
let y = usize::from(coord.y());
let index = (y * size) + x;
debug_assert!(x < size);
debug_assert!(y < size);
ContinentIndex(index)
}
pub fn to_coord(self, size: ContinentSize) -> Result<Coord> {
let x = self % size;
let y = self / size;
debug_assert!(x < size);
debug_assert!(y < size);
Ok(Coord::new(
u8::try_from(x).map_err(|_| Error::IndexOutOfBounds(self))?,
u8::try_from(y).map_err(|_| Error::IndexOutOfBounds(self))?,
))
}
}
impl Serialize for ContinentIndex {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
u32::try_from(self.0)
.map_err(|_| ser::Error::custom("index out of bounds"))?
.serialize(serializer)
}
}
impl const From<usize> for ContinentIndex {
fn from(value: usize) -> Self {
Self::new(value)
}
}
impl const From<ContinentIndex> for usize {
fn from(value: ContinentIndex) -> Self {
value.0
}
}
impl const Div<usize> for ContinentIndex {
type Output = usize;
fn div(self, rhs: usize) -> Self::Output {
self.0 / rhs
}
}
impl const Div<ContinentSize> for ContinentIndex {
type Output = usize;
fn div(self, rhs: ContinentSize) -> Self::Output {
self.0 / usize::from(rhs)
}
}
impl const Rem<usize> for ContinentIndex {
type Output = usize;
fn rem(self, rhs: usize) -> Self::Output {
self.0 % rhs
}
}
impl const Rem<ContinentSize> for ContinentIndex {
type Output = usize;
fn rem(self, rhs: ContinentSize) -> Self::Output {
self.0 % usize::from(rhs)
}
}
pub trait ContinentKey {
fn into_index(self, size: ContinentSize) -> ContinentIndex;
fn into_coord(self, size: ContinentSize) -> Result<Coord>
where
Self: Sized,
{
self.into_index(size).to_coord(size)
}
}
impl ContinentKey for ContinentIndex {
fn into_index(self, _: ContinentSize) -> ContinentIndex {
self
}
}
impl ContinentKey for Coord {
fn into_index(self, size: ContinentSize) -> ContinentIndex {
ContinentIndex::from_coord(self, size)
}
fn into_coord(self, _: ContinentSize) -> Result<Coord>
where
Self: Sized,
{
Ok(self)
}
}
impl ContinentKey for usize {
fn into_index(self, _: ContinentSize) -> ContinentIndex {
ContinentIndex::new(self)
}
}