Skip to main content

nil_core/continent/
index.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use 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}