ck3_history_extractor/parser/
mod.rs1mod types;
7
8use derive_more::From;
9use std::{
10 error,
11 fmt::{self, Debug, Display},
12 num::ParseIntError,
13};
14
15mod game_object;
24pub use game_object::{
25 ConversionError, GameObjectCollection, GameObjectMap, GameObjectMapping, KeyError,
26 SaveFileObject, SaveFileValue, SaveObjectError,
27};
28
29mod save_file;
33use jomini::common::DateError;
34pub use save_file::{SaveFile, SaveFileError};
35
36mod section;
39pub use section::{Section, SectionError};
40
41mod section_reader;
44pub use section_reader::yield_section;
45
46mod game_state;
51pub use game_state::{GameRef, GameState};
52use section_reader::SectionReaderError;
53
54mod tokens;
56
57#[derive(Debug, From)]
59pub enum ParsingError {
60 SectionError(SectionError),
62 StructureError(SaveObjectError),
64 ReaderError(String),
66 JominiError(jomini::Error),
68 DateError(DateError),
69}
70
71impl From<ConversionError> for ParsingError {
72 fn from(value: ConversionError) -> Self {
73 ParsingError::StructureError(SaveObjectError::ConversionError(value))
74 }
75}
76
77impl From<KeyError> for ParsingError {
78 fn from(value: KeyError) -> Self {
79 ParsingError::StructureError(SaveObjectError::KeyError(value))
80 }
81}
82
83impl From<ParseIntError> for ParsingError {
84 fn from(err: ParseIntError) -> Self {
85 ParsingError::StructureError(SaveObjectError::ConversionError(err.into()))
86 }
87}
88
89impl<'a, 'b: 'a> From<SectionReaderError<'b>> for ParsingError {
90 fn from(value: SectionReaderError<'b>) -> Self {
91 ParsingError::ReaderError(format!("{:?}", value))
92 }
93}
94
95impl<'a> Display for ParsingError {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 match self {
98 Self::ReaderError(err) => write!(f, "error during section reading: {}", err),
99 val => write!(f, "{}", val),
100 }
101 }
102}
103
104impl<'a> error::Error for ParsingError {
105 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
106 match self {
107 Self::SectionError(err) => Some(err),
108 Self::StructureError(err) => Some(err),
109 Self::JominiError(err) => Some(err),
110 Self::DateError(err) => Some(err),
111 _ => None,
112 }
113 }
114}
115
116use super::{
117 structures::{FromGameObject, Player},
118 types::{GameId, HashMap},
119};
120
121pub fn process_section(
128 i: &mut Section,
129 game_state: &mut GameState,
130 players: &mut Vec<Player>,
131) -> Result<(), ParsingError> {
132 match i.get_name() {
133 "meta_data" => {
134 let parsed = i.parse()?;
135 let map = parsed.as_map()?;
136 game_state
137 .set_current_date(map.get_date("meta_date")?, map.get_date("meta_real_date")?);
138 }
139 "traits_lookup" => {
141 let lookup: Result<Vec<_>, _> = i
142 .parse()?
143 .as_array()?
144 .into_iter()
145 .map(|x| x.as_string())
146 .collect();
147 game_state.add_lookup(lookup?);
148 }
149 "landed_titles" => {
150 let parsed = i.parse()?;
151 let map = parsed.as_map()?;
152 for (key, v) in map.get_object("landed_titles")?.as_map()? {
153 if let SaveFileValue::Object(o) = v {
154 game_state.add_title(&key.parse::<GameId>()?, o.as_map()?)?;
155 }
156 }
157 }
158 "county_manager" => {
159 let parsed = i.parse()?;
160 let map = parsed.as_map()?;
161 let mut key_assoc = HashMap::default();
164 for (key, p) in map.get_object("counties")?.as_map()? {
165 let p = p.as_object()?.as_map()?;
166 let faith = game_state.get_faith(&p.get_game_id("faith")?);
167 let culture = game_state.get_culture(&p.get_game_id("culture")?);
168 key_assoc.insert(key.to_owned(), (faith, culture));
169 }
170 game_state.add_county_data(key_assoc);
171 }
172 "dynasties" => {
173 let m = i.parse()?;
174 let m = m.as_map()?;
175 for (key, house) in m.get("dynasty_house").unwrap().as_object()?.as_map()? {
176 if let SaveFileValue::Object(o) = house {
177 game_state.add_house(&key.parse::<GameId>()?, o.as_map()?)?;
178 }
179 }
180 for (key, dynasty) in m.get("dynasties").unwrap().as_object()?.as_map()? {
181 if let SaveFileValue::Object(o) = dynasty {
182 game_state.add_dynasty(&key.parse::<GameId>()?, o.as_map()?)?;
183 }
184 }
185 }
186 "character_lookup" => {
187 let parsed = i.parse()?;
188 let mut transform = HashMap::new();
189 for (key, val) in parsed.as_map()? {
190 if let Ok(key) = key.parse::<GameId>() {
191 transform.insert(key, val.as_id()?);
192 }
193 }
194 game_state.add_character_transform(transform);
195 }
196 "living" => {
197 for (key, l) in i.parse()?.as_map()? {
198 match l {
199 SaveFileValue::Object(o) => {
200 game_state.add_character(&key.parse::<GameId>()?, o.as_map()?)?;
201 }
202 _ => {
203 continue;
204 }
205 }
206 }
207 }
208 "dead_unprunable" => {
209 for (key, d) in i.parse()?.as_map()? {
210 if let SaveFileValue::Object(o) = d {
211 game_state.add_character(&key.parse::<GameId>()?, o.as_map()?)?;
212 }
213 }
214 }
215 "characters" => {
216 if let Some(dead_prunable) = i.parse()?.as_map()?.get("dead_prunable") {
217 for (key, d) in dead_prunable.as_object()?.as_map()? {
218 match d {
219 SaveFileValue::Object(o) => {
220 game_state.add_character(&key.parse::<GameId>()?, o.as_map()?)?;
221 }
222 _ => {
223 continue;
224 }
225 }
226 }
227 }
228 }
229 "vassal_contracts" => {
230 let r = i.parse()?;
231 let map = r.as_map()?;
232 for (key, contract) in map
234 .get("database")
235 .or(map.get("active"))
236 .ok_or_else(|| KeyError::MissingKey("database".to_string(), map.clone()))?
237 .as_object()?
238 .as_map()?
239 {
240 if let SaveFileValue::Object(val) = contract {
241 let val = val.as_map()?;
242 game_state
243 .add_contract(&key.parse::<GameId>().unwrap(), &val.get_game_id("vassal")?)
244 }
245 }
246 }
247 "religion" => {
248 let parsed = i.parse()?;
249 let map = parsed.as_map()?;
250 for (key, f) in map.get_object("faiths")?.as_map()? {
251 game_state.add_faith(&key.parse::<GameId>()?, f.as_object()?.as_map()?)?;
252 }
253 }
254 "culture_manager" => {
255 let parsed = i.parse()?;
256 let map = parsed.as_map()?;
257 let cultures = map.get_object("cultures")?.as_map()?;
258 for (key, c) in cultures {
259 game_state.add_culture(&key.parse::<GameId>()?, c.as_object()?.as_map()?)?;
260 }
261 }
262 "character_memory_manager" => {
263 let parsed = i.parse()?;
264 let map = parsed.as_map()?;
265 for (key, d) in map.get_object("database")?.as_map()? {
266 if let SaveFileValue::Object(o) = d {
267 game_state.add_memory(&key.parse::<GameId>()?, o.as_map()?)?;
268 }
269 }
270 }
271 "played_character" => {
272 let p = Player::from_game_object(i.parse()?.as_map()?, game_state)?;
273 players.push(p);
274 }
275 "artifacts" => {
276 let parsed = i.parse()?;
277 let map = parsed.as_map()?;
278 for (key, a) in map.get_object("artifacts")?.as_map()?.into_iter() {
279 if let SaveFileValue::Object(o) = a {
280 game_state.add_artifact(&key.parse::<GameId>()?, o.as_map()?)?;
281 }
282 }
283 }
284 _ => {
285 i.skip()?;
286 }
287 }
288 return Ok(());
289}
290
291#[cfg(test)]
292mod tests {
293
294 use jomini::{self, text::TokenReader};
295
296 use super::{types::Tape, *};
297
298 fn get_test_obj(contents: &str) -> Result<Tape, jomini::Error> {
299 Ok(Tape::Text(TokenReader::from_slice(contents.as_bytes())))
300 }
301
302 #[test]
303 fn test_save_file() -> Result<(), Box<dyn std::error::Error>> {
304 let mut tape = get_test_obj(
305 "
306 test={
307 test2={
308 test3=1
309 }
310 }
311 ",
312 )?;
313 let mut section = yield_section(&mut tape).unwrap().unwrap();
314 assert_eq!(section.get_name(), "test");
315 let object = section.parse().unwrap();
316 let test2 = object
317 .as_map()?
318 .get("test2")
319 .unwrap()
320 .as_object()?
321 .as_map()?;
322 assert_eq!(test2.get("test3").unwrap().as_integer()?, 1);
323 return Ok(());
324 }
325
326 #[test]
327 fn test_save_file_array() -> Result<(), Box<dyn std::error::Error>> {
328 let mut tape = get_test_obj(
329 "
330 test={
331 test2={
332 1
333 2
334 3
335 }
336 test3={ 1 2 3}
337 }
338 ",
339 )?;
340 let mut section = yield_section(&mut tape).unwrap()?;
341 assert_eq!(section.get_name(), "test");
342 let object = section.parse().unwrap();
343 let test2 = object.as_map()?.get("test2").unwrap().as_object()?;
344 let test2_val = test2.as_array()?;
345 assert_eq!(test2_val.get_index(0)?.as_integer()?, 1);
346 assert_eq!(test2_val.get_index(1)?.as_integer()?, 2);
347 assert_eq!(test2_val.get_index(2)?.as_integer()?, 3);
348 let test3 = object.as_map()?.get("test3").unwrap().as_object()?;
349 let test3_val = test3.as_array()?;
350 assert_eq!(test3_val.get_index(0)?.as_integer()?, 1);
351 assert_eq!(test3_val.get_index(1)?.as_integer()?, 2);
352 assert_eq!(test3_val.get_index(2)?.as_integer()?, 3);
353 Ok(())
354 }
355
356 #[test]
357 fn test_weird_syntax() -> Result<(), Box<dyn std::error::Error>> {
358 let mut tape = get_test_obj(
359 "
360 test={
361 test2={1=2
362 3=4}
363 test3={1 2
364 3}
365 test4={1 2 3}
366 test5=42
367 }
368 ",
369 )
370 .unwrap();
371 let mut section = yield_section(&mut tape).unwrap().unwrap();
372 assert_eq!(section.get_name(), "test");
373 let object = section.parse().unwrap();
374 let test2 = object
375 .as_map()?
376 .get("test2")
377 .unwrap()
378 .as_object()?
379 .as_map()?;
380 assert_eq!(test2.get("1").unwrap().as_integer()?, 2);
381 assert_eq!(test2.get("3").unwrap().as_integer()?, 4);
382 Ok(())
383 }
384
385 #[test]
386 fn test_array_syntax() -> Result<(), Box<dyn std::error::Error>> {
387 let mut tape = get_test_obj(
388 "
389 test={
390 test2={ 1 2 3 }
391 }
392 ",
393 )
394 .unwrap();
395 let mut section = yield_section(&mut tape).unwrap().unwrap();
396 assert_eq!(section.get_name(), "test");
397 let object = section.parse().unwrap();
398 let test2 = object
399 .as_map()?
400 .get("test2")
401 .unwrap()
402 .as_object()?
403 .as_array()?;
404 assert_eq!(test2.get_index(0)?.as_integer()?, 1);
405 assert_eq!(test2.get_index(1)?.as_integer()?, 2);
406 assert_eq!(test2.get_index(2)?.as_integer()?, 3);
407 assert_eq!(test2.len(), 3);
408 Ok(())
409 }
410
411 #[test]
412 fn test_unnamed_obj() -> Result<(), Box<dyn std::error::Error>> {
413 let mut tape = get_test_obj(
414 "
415 3623={
416 name=\"dynn_Sao\"
417 variables={
418 data={
419 {
420 flag=\"ai_random_harm_cooldown\"
421 tick=7818
422 data={
423 type=boolean
424 identity=1
425 }
426 }
427 {
428 something_else=\"test\"
429 }
430 }
431 }
432 }
433 }
434 ",
435 )
436 .unwrap();
437 let object = yield_section(&mut tape).unwrap().unwrap().parse().unwrap();
438 let variables = object
439 .as_map()?
440 .get("variables")
441 .unwrap()
442 .as_object()?
443 .as_map()?;
444 let data = variables.get("data").unwrap().as_object()?.as_array()?;
445 assert_ne!(data.len(), 0);
446 Ok(())
447 }
448
449 #[test]
450 fn test_example_1() -> Result<(), Box<dyn std::error::Error>> {
451 let mut tape = get_test_obj("
452 3623={
453 name=\"dynn_Sao\"
454 variables={
455 data={ {
456 flag=\"ai_random_harm_cooldown\"
457 tick=7818
458 data={
459 type=boolean
460 identity=1
461 }
462
463 }
464 }
465 }
466 found_date=750.1.1
467 head_of_house=83939093
468 dynasty=3623
469 historical={ 4440 5398 6726 10021 33554966 50385988 77977 33583389 50381158 50425637 16880568 83939093 }
470 motto={
471 key=\"motto_with_x_I_seek_y\"
472 variables={ {
473 key=\"1\"
474 value=\"motto_the_sword_word\"
475 }
476 {
477 key=\"2\"
478 value=\"motto_bravery\"
479 }
480 }
481 }
482 artifact_claims={ 83888519 }
483 }").unwrap();
484 let mut section = yield_section(&mut tape).unwrap().unwrap();
485 assert_eq!(section.get_name(), "3623");
486 let object = section.parse().unwrap();
487 assert_eq!(
488 *(object.as_map()?.get("name").unwrap().as_string()?),
489 "dynn_Sao".to_string()
490 );
491 let historical = object
492 .as_map()?
493 .get("historical")
494 .unwrap()
495 .as_object()?
496 .as_array()?;
497 assert_eq!(historical.get_index(0)?.as_integer()?, 4440);
498 assert_eq!(historical.get_index(1)?.as_integer()?, 5398);
499 assert_eq!(historical.get_index(2)?.as_integer()?, 6726);
500 assert_eq!(historical.get_index(3)?.as_integer()?, 10021);
501 assert_eq!(historical.get_index(4)?.as_integer()?, 33554966);
502 assert_eq!(historical.len(), 12);
503 Ok(())
504 }
505
506 #[test]
507 fn test_space() -> Result<(), Box<dyn std::error::Error>> {
508 let mut tape = get_test_obj(
509 "
510 test = {
511 test2 = {
512 test3 = 1
513 }
514 test4 = { a b c}
515 }
516 ",
517 )
518 .unwrap();
519 let mut section = yield_section(&mut tape).unwrap().unwrap();
520 assert_eq!(section.get_name(), "test");
521 let object = section.parse().unwrap();
522 let test2 = object
523 .as_map()?
524 .get("test2")
525 .unwrap()
526 .as_object()?
527 .as_map()?;
528 assert_eq!(test2.get("test3").unwrap().as_integer()?, 1);
529 let test4 = object.as_map()?.get("test4").unwrap().as_object()?;
530 let test4_val = test4.as_array()?;
531 assert_eq!(*(test4_val.get_index(0)?.as_string()?), "a".to_string());
532 assert_eq!(*(test4_val.get_index(1)?.as_string()?), "b".to_string());
533 assert_eq!(*(test4_val.get_index(2)?.as_string()?), "c".to_string());
534 Ok(())
535 }
536
537 #[test]
538 fn test_landed() -> Result<(), Box<dyn std::error::Error>> {
539 let mut tape = get_test_obj(
540 "
541 c_derby = {
542 color = { 255 50 20 }
543
544 cultural_names = {
545 name_list_norwegian = cn_djuraby
546 name_list_danish = cn_djuraby
547 name_list_swedish = cn_djuraby
548 name_list_norse = cn_djuraby
549 }
550
551 b_derby = {
552 province = 1621
553
554 color = { 255 89 89 }
555
556 cultural_names = {
557 name_list_norwegian = cn_djuraby
558 name_list_danish = cn_djuraby
559 name_list_swedish = cn_djuraby
560 name_list_norse = cn_djuraby
561 }
562 }
563 b_chesterfield = {
564 province = 1622
565
566 color = { 255 50 20 }
567 }
568 b_castleton = {
569 province = 1623
570
571 color = { 255 50 20 }
572 }
573 }
574 ",
575 )
576 .unwrap();
577 let mut section = yield_section(&mut tape).unwrap().unwrap();
578 assert_eq!(section.get_name(), "c_derby");
579 let object = section.parse().unwrap();
580 let b_derby = object
581 .as_map()?
582 .get("b_derby")
583 .unwrap()
584 .as_object()?
585 .as_map()?;
586 assert_eq!(b_derby.get("province").unwrap().as_integer()?, 1621);
587 let b_chesterfield = object
588 .as_map()?
589 .get("b_chesterfield")
590 .unwrap()
591 .as_object()?
592 .as_map()?;
593 assert_eq!(b_chesterfield.get("province").unwrap().as_integer()?, 1622);
594 let b_castleton = object
595 .as_map()?
596 .get("b_castleton")
597 .unwrap()
598 .as_object()?
599 .as_map()?;
600 assert_eq!(b_castleton.get("province").unwrap().as_integer()?, 1623);
601 Ok(())
602 }
603
604 #[test]
605 fn test_invalid_line() -> Result<(), Box<dyn std::error::Error>> {
606 let mut tape = get_test_obj(
607 "
608 nonsense=idk
609 test={
610 test2={
611 test3=1
612 }
613 }
614 ",
615 )
616 .unwrap();
617 let mut section = yield_section(&mut tape).unwrap().unwrap();
618 assert_eq!(section.get_name(), "test");
619 let object = section.parse().unwrap();
620 let test2 = object
621 .as_map()?
622 .get("test2")
623 .unwrap()
624 .as_object()?
625 .as_map()?;
626 assert_eq!(test2.get("test3").unwrap().as_integer()?, 1);
627 Ok(())
628 }
629
630 #[test]
631 fn test_empty() {
632 let mut tape = get_test_obj(
633 "
634 test={
635 }
636 ",
637 )
638 .unwrap();
639 let object = yield_section(&mut tape).unwrap().unwrap();
640 assert_eq!(object.get_name(), "test");
641 }
642
643 #[test]
644 fn test_arr_index() {
645 let mut tape = get_test_obj(
646 "
647 duration={ 2 0=7548 1=2096 }
648 ",
649 )
650 .unwrap();
651 let mut section = yield_section(&mut tape).unwrap().unwrap();
652 assert_eq!(section.get_name(), "duration");
653 let object = section.parse().unwrap();
654 let arr = object.as_array().unwrap();
655 assert_eq!(arr.len(), 3);
656 assert_eq!(arr[0].as_id().unwrap(), 7548);
657 }
658
659 #[test]
660 fn test_multi_key() -> Result<(), Box<dyn std::error::Error>> {
661 let mut tape = get_test_obj(
662 "
663 test={
664 a=hello
665 a=world
666 }
667 ",
668 )
669 .unwrap();
670 let object = yield_section(&mut tape).unwrap().unwrap().parse().unwrap();
671 let arr = object.as_map()?.get("a").unwrap().as_object()?.as_array()?;
672 assert_eq!(arr.len(), 2);
673 Ok(())
674 }
675
676 }