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