1use cli_table::Table;
10use derive_more::Display;
11use serde::{Deserialize, Serialize};
12use std::fs::create_dir_all;
13use uuid::Uuid;
14
15use crate::{
16 entity::{Entity, EntityIdentifier},
17 epoch::{
18 EpochId, SUB_NAME, SUBFOLDER_NAMING, TagList,
19 epoch_traits::{ConfigWritable, Prettiable},
20 },
21 error::EpochError,
22 stats::{EntityType, Position, PositionIdentifier, Symbols, TypedLink},
23};
24
25#[derive(Debug, Clone, Serialize, Deserialize, Display, Table)]
33#[display(
34 "Epoch ({epoch_id:>5}) | seed ({rng_seed:<10}) | tags ({tags:<10}) | summary ({summary:<20})"
35)]
36pub struct EpochInfo {
37 #[table(title = "ID")]
39 pub epoch_id: EpochId,
40
41 #[table(title = "Seed")]
45 pub rng_seed: u64,
46
47 #[table(title = "Tags")]
53 pub tags: TagList,
54
55 #[table(title = "Summary")]
57 pub summary: String,
58 #[table(skip)]
60 pub events: Vec<Entity>,
62 #[table(skip)]
63 pub characters: Vec<Entity>,
65 #[table(skip)]
66 pub towns: Vec<Entity>,
68 #[table(skip)]
69 pub calamities: Vec<Entity>,
71 #[table(skip)]
72 pub blockers: Vec<Entity>,
74}
75
76impl Prettiable for EpochInfo {}
77
78impl Default for EpochInfo {
79 fn default() -> Self {
80 Self {
81 epoch_id: EpochId::new(0),
82 rng_seed: 0,
83 tags: TagList::new(),
84 summary: String::new(),
85 events: Vec::new(),
86 characters: Vec::new(),
87 towns: Vec::new(),
88 calamities: Vec::new(),
89 blockers: Vec::new(),
90 }
91 }
92}
93
94impl EpochInfo {
95 pub fn create_subfolders(&self) -> Result<(), EpochError> {
99 io_err_with_help!(create_dir_all(self.save_folder()), "Creating subfolders")
100 }
101
102 pub(crate) fn with_id(epoch_id: &EpochId) -> Self {
104 Self {
105 epoch_id: *epoch_id,
106 ..Default::default()
107 }
108 }
109}
110
111impl ConfigWritable for EpochInfo {
112 fn save_path(&self) -> String {
113 format!("{}/{}/{}", SUBFOLDER_NAMING, self.epoch_id, SUB_NAME)
114 }
115
116 fn save_folder(&self) -> String {
117 format!("{}/{}/", SUBFOLDER_NAMING, self.epoch_id)
118 }
119}
120
121impl EpochInfo {
122 pub fn find_entity_of_type(
137 &self,
138 entity_to_find: &Option<EntityIdentifier>,
139 e_type: &EntityType,
140 ) -> Result<&Entity, EpochError> {
141 self.vec_entities(e_type)
142 .iter()
143 .find(|&entity| entity.match_identifier(entity_to_find))
144 .ok_or(EpochError::EntityNotFound(*e_type, entity_to_find.clone()))
145 }
146
147 pub fn add_to_links(
152 &mut self,
153 target_id: Uuid,
154 target_t: &EntityType,
155 link_back: TypedLink,
156 ) -> Result<(), EpochError> {
157 log::info!("Adding {} to {}[{:?}]", link_back, target_id, target_t);
158 let e = self
159 .vec_entities_mut(target_t)
160 .iter_mut()
161 .find(|entity| entity.id == target_id)
162 .ok_or(EpochError::EntityNotFoundById(
163 EntityIdentifier::Id(target_id.to_string()),
164 Some(*target_t),
165 ))?;
166 e.add_to_links(link_back);
167 Ok(())
168 }
169
170 pub fn del_from_links(
177 &mut self,
178 target_id: Uuid,
179 target_t: &EntityType,
180 link_back: TypedLink,
181 ) -> Result<(), EpochError> {
182 let e = self
183 .vec_entities_mut(target_t)
184 .iter_mut()
185 .find(|entity| entity.id == target_id)
186 .ok_or(EpochError::EntityNotFoundById(
187 EntityIdentifier::Id(target_id.to_string()),
188 Some(*target_t),
189 ))?;
190
191 e.del_from_links(link_back);
192 Ok(())
193 }
194
195 pub fn find_any_entity(&self, id: EntityIdentifier) -> Result<&Entity, EpochError> {
210 let predicate = |entity: &&Entity| match &id {
211 EntityIdentifier::Name(name) => {
212 entity.name.to_lowercase().starts_with(&name.to_lowercase())
213 }
214 EntityIdentifier::Id(id) => entity.id.to_string().starts_with(id),
215 };
216 self.events
217 .iter()
218 .find(predicate)
219 .or(self.characters.iter().find(predicate))
220 .or(self.towns.iter().find(predicate))
221 .or(self.blockers.iter().find(predicate))
222 .or(self.calamities.iter().find(predicate))
223 .ok_or(EpochError::EntityNotFoundById(id, None))
224 }
225 pub fn find_entity_strict(
227 &self,
228 id: Uuid,
229 strict: Option<EntityType>,
230 ) -> Result<&Entity, EpochError> {
231 let predicate = |entity: &&Entity| entity.id == id;
232 if let Some(e_type) = strict {
233 self.vec_entities(&e_type)
234 .iter()
235 .find(|entity| entity.id == id)
236 .ok_or(EpochError::EntityNotFound(
237 e_type,
238 Some(EntityIdentifier::Id(id.to_string())),
239 ))
240 } else {
241 self.events
242 .iter()
243 .find(predicate)
244 .or(self.characters.iter().find(predicate))
245 .or(self.towns.iter().find(predicate))
246 .or(self.blockers.iter().find(predicate))
247 .or(self.calamities.iter().find(predicate))
248 .ok_or(EpochError::EntityNotFound(
249 EntityType::Player,
250 Some(EntityIdentifier::Id(id.to_string())),
251 ))
252 }
253 }
254 pub fn find_entity_strict_as_mut(
257 &mut self,
258 id: Uuid,
259 strict: Option<EntityType>,
260 ) -> Result<&mut Entity, EpochError> {
261 let predicate = |entity: &&mut Entity| entity.id == id;
262 if let Some(e_type) = strict {
263 self.vec_entities_mut(&e_type)
264 .iter_mut()
265 .find(|entity| entity.id == id)
266 .ok_or(EpochError::EntityNotFound(
267 e_type,
268 Some(EntityIdentifier::Id(id.to_string())),
269 ))
270 } else {
271 self.events
272 .iter_mut()
273 .find(predicate)
274 .or(self.characters.iter_mut().find(predicate))
275 .or(self.towns.iter_mut().find(predicate))
276 .or(self.blockers.iter_mut().find(predicate))
277 .or(self.calamities.iter_mut().find(predicate))
278 .ok_or(EpochError::EntityNotFound(
279 EntityType::Player,
280 Some(EntityIdentifier::Id(id.to_string())),
281 ))
282 }
283 }
284 pub fn vec_entities(&self, e_type: &EntityType) -> &Vec<Entity> {
290 match e_type {
291 EntityType::Character => &self.characters,
292 EntityType::Player => unimplemented!("No players for now"),
293 EntityType::Town => &self.towns,
294 EntityType::Monster => &self.calamities,
295 EntityType::Blocker => &self.blockers,
296 EntityType::Event => &self.events,
297 }
298 }
299
300 pub fn remove_entity(
307 &mut self,
308 entity_identifier: &Option<EntityIdentifier>,
309 e_type: &EntityType,
310 ) -> Result<Entity, EpochError> {
311 let vec_entities: &mut Vec<Entity> = self.vec_entities_mut(e_type);
312 let index = vec_entities
313 .iter()
314 .position(|entity| entity.match_identifier(entity_identifier))
315 .ok_or(EpochError::EntityNotFound(
316 *e_type,
317 entity_identifier.clone(),
318 ))?;
319
320 let entity = vec_entities.swap_remove(index);
321 Ok(entity)
322 }
323 pub fn vec_entities_mut(&mut self, e_type: &EntityType) -> &mut Vec<Entity> {
329 match e_type {
330 EntityType::Character => &mut self.characters,
331 EntityType::Player => unimplemented!("No players for now"),
332 EntityType::Town => &mut self.towns,
333 EntityType::Monster => &mut self.calamities,
334 EntityType::Blocker => &mut self.blockers,
335 EntityType::Event => &mut self.events,
336 }
337 }
338
339 pub fn iter_all_entities_mut(&mut self) -> impl Iterator<Item = &mut Entity> {
342 self.events
343 .iter_mut()
344 .chain(self.characters.iter_mut())
345 .chain(self.towns.iter_mut())
346 .chain(self.calamities.iter_mut())
347 .chain(self.blockers.iter_mut())
348 }
349
350 pub fn resolve_position(&self, position: &PositionIdentifier) -> Result<Position, EpochError> {
352 Ok(match position {
353 PositionIdentifier::Exact(pos) => pos.clone(),
354 PositionIdentifier::Grid(pos) => pos.to_position(),
355 PositionIdentifier::Id(ident) => {
356 self.find_any_entity(ident.clone())?.position().clone()
357 }
358 })
359 }
360
361 pub fn swap_entity(&mut self, entity: Entity) -> Result<Entity, EpochError> {
363 let entities = self.vec_entities_mut(&entity.entity_type);
365
366 let idx_opt = entities.iter().position(|e| e.id == entity.id);
368
369 match idx_opt {
370 Some(idx) => {
371 Ok(std::mem::replace(&mut entities[idx], entity))
373 }
374 None => Err(EpochError::EntityNotFound(
375 entity.entity_type,
376 Some(EntityIdentifier::Id(entity.id.to_string())),
377 )),
378 }
379 }
380}
381
382impl Symbols for EpochId {
383 fn to_symbol(&self) -> String {
384 format!("[ {:<2}]", self)
385 }
386}