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::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}