nil_core/continent/
index.rs1use crate::continent::{ContinentSize, Coord};
5use crate::error::{Error, Result};
6use derive_more::Display;
7use nil_util::ConstDeref;
8use serde::{Deserialize, Serialize, Serializer, ser};
9use std::ops::{Div, Rem};
10
11#[derive(Copy, Debug, Display, Hash, Deserialize, ConstDeref)]
12#[derive_const(Clone, PartialEq, Eq, PartialOrd, Ord)]
13#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
14#[cfg_attr(feature = "typescript", ts(as = "u32"))]
15pub struct ContinentIndex(usize);
16
17impl ContinentIndex {
18 #[inline]
19 pub const fn new(index: usize) -> Self {
20 Self(index)
21 }
22
23 pub fn from_coord(coord: Coord, size: ContinentSize) -> Self {
24 let size = usize::from(size);
25 let x = usize::from(coord.x());
26 let y = usize::from(coord.y());
27 let index = (y * size) + x;
28
29 debug_assert!(x < size);
30 debug_assert!(y < size);
31
32 ContinentIndex(index)
33 }
34
35 pub fn to_coord(self, size: ContinentSize) -> Result<Coord> {
36 let x = self % size;
37 let y = self / size;
38
39 debug_assert!(x < size);
40 debug_assert!(y < size);
41
42 Ok(Coord::new(
43 u8::try_from(x).map_err(|_| Error::IndexOutOfBounds(self))?,
44 u8::try_from(y).map_err(|_| Error::IndexOutOfBounds(self))?,
45 ))
46 }
47}
48
49impl Serialize for ContinentIndex {
50 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51 where
52 S: Serializer,
53 {
54 u32::try_from(self.0)
55 .map_err(|_| ser::Error::custom("index out of bounds"))?
56 .serialize(serializer)
57 }
58}
59
60impl const From<usize> for ContinentIndex {
61 fn from(value: usize) -> Self {
62 Self::new(value)
63 }
64}
65
66impl const From<ContinentIndex> for usize {
67 fn from(value: ContinentIndex) -> Self {
68 value.0
69 }
70}
71
72impl const Div<usize> for ContinentIndex {
73 type Output = usize;
74
75 fn div(self, rhs: usize) -> Self::Output {
76 self.0 / rhs
77 }
78}
79
80impl const Div<ContinentSize> for ContinentIndex {
81 type Output = usize;
82
83 fn div(self, rhs: ContinentSize) -> Self::Output {
84 self.0 / usize::from(rhs)
85 }
86}
87
88impl const Rem<usize> for ContinentIndex {
89 type Output = usize;
90
91 fn rem(self, rhs: usize) -> Self::Output {
92 self.0 % rhs
93 }
94}
95
96impl const Rem<ContinentSize> for ContinentIndex {
97 type Output = usize;
98
99 fn rem(self, rhs: ContinentSize) -> Self::Output {
100 self.0 % usize::from(rhs)
101 }
102}
103
104pub trait ContinentKey {
105 fn into_index(self, size: ContinentSize) -> ContinentIndex;
106
107 fn into_coord(self, size: ContinentSize) -> Result<Coord>
108 where
109 Self: Sized,
110 {
111 self.into_index(size).to_coord(size)
112 }
113}
114
115impl ContinentKey for ContinentIndex {
116 fn into_index(self, _: ContinentSize) -> ContinentIndex {
117 self
118 }
119}
120
121impl ContinentKey for Coord {
122 fn into_index(self, size: ContinentSize) -> ContinentIndex {
123 ContinentIndex::from_coord(self, size)
124 }
125
126 fn into_coord(self, _: ContinentSize) -> Result<Coord>
127 where
128 Self: Sized,
129 {
130 Ok(self)
131 }
132}
133
134impl ContinentKey for usize {
135 fn into_index(self, _: ContinentSize) -> ContinentIndex {
136 ContinentIndex::new(self)
137 }
138}