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 available_resources: 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 available_resources.checked_sub(resources).is_none() {
107 return Err(Error::InsufficientResources);
108 }
109
110 self.orders.push_back([<$building RecruitOrder>] {
111 id: [<$building RecruitOrderId>]::new(),
112 squad: Squad::new(unit.id(), size),
113 resources,
114 workforce,
115 state: [<$building RecruitOrderState>]::new(workforce),
116 });
117
118 let len = self.orders.len();
119 Ok(unsafe {
120 self
121 .orders
122 .get(len.unchecked_sub(1))
123 .unwrap_unchecked()
124 })
125 }
126
127 #[must_use]
129 pub(crate) fn cancel(&mut self, id: [<$building RecruitOrderId>]) -> Option<[<$building RecruitOrder>]> {
130 let position = self
131 .orders
132 .iter()
133 .position(|order| order.id == id)?;
134
135 self.orders.remove(position)
136 }
137 }
138
139 impl InfrastructureQueue<[<$building RecruitOrder>]> for [<$building RecruitQueue>] {
140 fn queue(&self) -> &VecDeque<[<$building RecruitOrder>]> {
141 &self.orders
142 }
143
144 fn queue_mut(&mut self) -> &mut VecDeque<[<$building RecruitOrder>]> {
145 &mut self.orders
146 }
147 }
148
149 #[must_use]
150 #[derive(Clone, Debug, Deserialize, Serialize)]
151 #[serde(rename_all = "camelCase")]
152 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
153 pub struct [<$building RecruitOrder>] {
154 id: [<$building RecruitOrderId>],
155 squad: Squad,
156 resources: Resources,
157 workforce: Workforce,
158 state: [<$building RecruitOrderState>],
159 }
160
161 impl [<$building RecruitOrder>] {
162 #[inline]
163 pub fn id(&self) -> [<$building RecruitOrderId>] {
164 self.id
165 }
166
167 #[inline]
168 pub fn squad(&self) -> &Squad {
169 &self.squad
170 }
171
172 #[inline]
173 pub fn resources(&self) -> Resources {
174 self.resources
175 }
176 }
177
178 impl From<[<$building RecruitOrder>]> for Squad {
179 fn from(order: [<$building RecruitOrder>]) -> Self {
180 order.squad
181 }
182 }
183
184 impl InfrastructureQueueOrder for [<$building RecruitOrder>] {
185 fn is_done(&self) -> bool {
186 self.state.is_done()
187 }
188
189 fn set_done(&mut self) {
190 self.state = [<$building RecruitOrderState>]::Done;
191 }
192
193 fn pending_workforce(&self) -> Option<Workforce> {
194 self.state.pending_workforce()
195 }
196
197 fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
198 self.state.pending_workforce_mut()
199 }
200 }
201
202 #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
203 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
204 pub struct [<$building RecruitOrderId>](Uuid);
205
206 impl [<$building RecruitOrderId>] {
207 #[must_use]
208 pub fn new() -> Self {
209 Self(Uuid::new_v4())
210 }
211 }
212
213 impl Default for [<$building RecruitOrderId>] {
214 fn default() -> Self {
215 Self::new()
216 }
217 }
218
219 #[derive(Clone, Debug, EnumIs, Deserialize, Serialize)]
220 #[serde(tag = "kind", rename_all = "kebab-case")]
221 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
222 pub enum [<$building RecruitOrderState>] {
223 Pending { workforce: Workforce },
224 Done,
225 }
226
227 impl [<$building RecruitOrderState>] {
228 fn pending_workforce(&self) -> Option<Workforce> {
229 if let Self::Pending { workforce } = self { Some(*workforce) } else { None }
230 }
231
232 fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
233 if let Self::Pending { workforce } = self { Some(workforce) } else { None }
234 }
235 }
236
237 impl [<$building RecruitOrderState>] {
238 fn new(workforce: Workforce) -> Self {
239 Self::Pending { workforce }
240 }
241 }
242
243 #[derive(Builder, Clone, Debug, Deserialize, Serialize)]
244 #[serde(rename_all = "camelCase")]
245 #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
246 pub struct [<$building RecruitOrderRequest>] {
247 #[builder(into)]
248 pub coord: Coord,
249 pub unit: [<$building UnitId>],
250 pub chunks: NonZeroU32,
251 }
252 }
253 };
254}