nil_core/infrastructure/
queue.rs1use crate::resources::workforce::Workforce;
5use std::collections::VecDeque;
6
7pub trait InfrastructureQueue<T>
8where
9 T: InfrastructureQueueOrder,
10{
11 fn queue(&self) -> &VecDeque<T>;
12 fn queue_mut(&mut self) -> &mut VecDeque<T>;
13
14 fn process(&mut self, mut workforce: Workforce) -> Vec<T> {
16 let mut orders = Vec::new();
17 loop {
18 if workforce == 0 {
19 break;
20 }
21
22 match self
23 .queue_mut()
24 .pop_front_if(|order| order.consume(&mut workforce))
25 {
26 Some(order) => orders.push(order),
27 None => break,
28 }
29 }
30
31 if !orders.is_empty() {
32 self.queue_mut().shrink_to_fit();
33 }
34
35 orders
36 }
37
38 fn iter<'a>(&'a self) -> impl Iterator<Item = &'a T>
39 where
40 T: 'a,
41 {
42 self.queue().iter()
43 }
44
45 fn len(&self) -> usize {
46 self.queue().len()
47 }
48
49 fn is_empty(&self) -> bool {
50 self.queue().is_empty()
51 }
52
53 fn sum_pending_workforce(&self) -> Workforce {
54 self
55 .iter()
56 .filter_map(InfrastructureQueueOrder::pending_workforce)
57 .map(u32::from)
58 .sum::<u32>()
59 .into()
60 }
61}
62
63pub trait InfrastructureQueueOrder {
64 fn is_done(&self) -> bool;
65 fn set_done(&mut self);
66
67 fn pending_workforce(&self) -> Option<Workforce>;
68 fn pending_workforce_mut(&mut self) -> Option<&mut Workforce>;
69
70 fn consume(&mut self, workforce: &mut Workforce) -> bool {
71 if let Some(pending) = self.pending_workforce_mut() {
72 if *pending > 0 {
73 let previous = *pending;
74 *pending -= *workforce;
75
76 *workforce -= previous - *pending;
78 }
79
80 if *pending == 0 {
81 self.set_done();
82 }
83 }
84
85 self.is_done()
86 }
87}
88
89#[doc(hidden)]
90#[macro_export]
91macro_rules! decl_recruit_queue {
92 ($building:ident) => {
93 paste::paste! {
94 impl [<$building RecruitQueue>] {
95 pub(crate) fn recruit(
96 &mut self,
97 request: &[<$building RecruitOrderRequest>],
98 current_resources: Option<&Resources>,
99 ) -> Result<&[<$building RecruitOrder>]> {
100 let unit = UnitBox::from(request.unit);
101 let chunk = unit.as_dyn().chunk();
102 let size = SquadSize::new(chunk.size() * request.chunks);
103 let resources = &chunk.resources() * request.chunks;
104 let workforce = chunk.workforce() * request.chunks;
105
106 if let Some(current_resources) = current_resources
107 && current_resources
108 .checked_sub(&resources)
109 .is_none()
110 {
111 return Err(Error::InsufficientResources);
112 }
113
114 self.orders.push_back([<$building RecruitOrder>] {
115 id: [<$building RecruitOrderId>]::new(),
116 squad: Squad::new(unit.id(), size),
117 resources,
118 workforce,
119 state: [<$building RecruitOrderState>]::new(workforce),
120 });
121
122 let len = self.orders.len();
123 Ok(unsafe {
124 self
125 .orders
126 .get(len.unchecked_sub(1))
127 .unwrap_unchecked()
128 })
129 }
130
131 #[must_use]
133 pub(crate) fn cancel(&mut self, id: [<$building RecruitOrderId>]) -> Option<[<$building RecruitOrder>]> {
134 let position = self
135 .orders
136 .iter()
137 .position(|order| order.id == id)?;
138
139 self.orders.remove(position)
140 }
141 }
142
143 impl InfrastructureQueue<[<$building RecruitOrder>]> for [<$building RecruitQueue>] {
144 fn queue(&self) -> &VecDeque<[<$building RecruitOrder>]> {
145 &self.orders
146 }
147
148 fn queue_mut(&mut self) -> &mut VecDeque<[<$building RecruitOrder>]> {
149 &mut self.orders
150 }
151 }
152
153 #[must_use]
154 #[derive(Clone, Debug, Deserialize, Serialize)]
155 #[serde(rename_all = "camelCase")]
156 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
157 pub struct [<$building RecruitOrder>] {
158 id: [<$building RecruitOrderId>],
159 squad: Squad,
160 resources: Resources,
161 workforce: Workforce,
162 state: [<$building RecruitOrderState>],
163 }
164
165 impl [<$building RecruitOrder>] {
166 #[inline]
167 pub fn id(&self) -> [<$building RecruitOrderId>] {
168 self.id
169 }
170
171 #[inline]
172 pub fn squad(&self) -> &Squad {
173 &self.squad
174 }
175
176 #[inline]
177 pub fn resources(&self) -> &Resources {
178 &self.resources
179 }
180 }
181
182 impl From<[<$building RecruitOrder>]> for Squad {
183 fn from(order: [<$building RecruitOrder>]) -> Self {
184 order.squad
185 }
186 }
187
188 impl InfrastructureQueueOrder for [<$building RecruitOrder>] {
189 fn is_done(&self) -> bool {
190 self.state.is_done()
191 }
192
193 fn set_done(&mut self) {
194 self.state = [<$building RecruitOrderState>]::Done;
195 }
196
197 fn pending_workforce(&self) -> Option<Workforce> {
198 self.state.pending_workforce()
199 }
200
201 fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
202 self.state.pending_workforce_mut()
203 }
204 }
205
206 #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
207 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
208 pub struct [<$building RecruitOrderId>](Uuid);
209
210 impl [<$building RecruitOrderId>] {
211 #[must_use]
212 pub fn new() -> Self {
213 Self(Uuid::new_v4())
214 }
215 }
216
217 impl Default for [<$building RecruitOrderId>] {
218 fn default() -> Self {
219 Self::new()
220 }
221 }
222
223 #[derive(Clone, Debug, EnumIs, Deserialize, Serialize)]
224 #[serde(tag = "kind", rename_all = "kebab-case")]
225 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
226 pub enum [<$building RecruitOrderState>] {
227 Pending { workforce: Workforce },
228 Done,
229 }
230
231 impl [<$building RecruitOrderState>] {
232 fn pending_workforce(&self) -> Option<Workforce> {
233 if let Self::Pending { workforce } = self { Some(*workforce) } else { None }
234 }
235
236 fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
237 if let Self::Pending { workforce } = self { Some(workforce) } else { None }
238 }
239 }
240
241 impl [<$building RecruitOrderState>] {
242 fn new(workforce: Workforce) -> Self {
243 Self::Pending { workforce }
244 }
245 }
246
247 #[derive(Builder, Clone, Debug, Deserialize, Serialize)]
248 #[serde(rename_all = "camelCase")]
249 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
250 pub struct [<$building RecruitOrderRequest>] {
251 #[builder(into)]
252 pub coord: Coord,
253 pub unit: [<$building UnitId>],
254 pub chunks: NonZeroU32,
255 }
256 }
257 };
258}