diff --git a/src/display/map.rs b/src/display/map.rs index 013b8e9..db523d7 100644 --- a/src/display/map.rs +++ b/src/display/map.rs @@ -62,8 +62,8 @@ fn create_title_province_map(game_path: &str) -> HashMap { let path = game_path.to_owned() + "/common/landed_titles/00_landed_titles.txt"; let file = SaveFile::open(&path).unwrap(); let mut map = HashMap::default(); - for mut title in file { - let title_object = title.parse().unwrap(); + for title in file { + let title_object = title.unwrap().parse().unwrap(); //DFS in the structure let mut stack = vec![title_object.as_map()]; while let Some(o) = stack.pop() { diff --git a/src/main.rs b/src/main.rs index 419c1d7..29a2f69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,12 @@ impl Debug for UserError { } } +impl From for UserError { + fn from(value: SaveFileError) -> Self { + return UserError::FileError(value); + } +} + /// Main function. This is the entry point of the program. /// /// # Process @@ -132,19 +138,17 @@ fn main() -> Result<(), UserError> { } localizer.resolve(); //initialize the save file - let save = match SaveFile::open(args.filename.as_str()) { - Ok(s) => s, - Err(e) => return Err(UserError::FileError(e)), - }; + let save = SaveFile::open(args.filename.as_str())?; // this is sort of like the first round of filtering where we store the objects we care about let mut game_state: GameState = GameState::new(); let mut players: Vec = Vec::new(); let progress_bar = ProgressBar::new(save.len() as u64); progress_bar.set_style(bar_style.clone()); - for mut i in progress_bar.wrap_iter(save.into_iter()) { - progress_bar.set_message(i.get_name().to_owned()); - // TODO make this return a Result - process_section(&mut i, &mut game_state, &mut players); + for i in progress_bar.wrap_iter(save.into_iter()) { + let mut section = i.unwrap(); + progress_bar.set_message(section.get_name().to_owned()); + // if an error occured somewhere here, there's nothing we can do + process_section(&mut section, &mut game_state, &mut players).unwrap(); } progress_bar.finish_with_message("Save parsing complete"); //prepare things for rendering diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 72132c1..cfc41b7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12,6 +12,7 @@ pub use save_file::{SaveFile, SaveFileError}; /// A submodule that provides the [Section] object, which is used to store the parsed data of a section of the save file. mod section; use section::Section; +pub use section::SectionError; /// A submodule that provides the [GameState] object, which is used as a sort of a dictionary. /// CK3 save files have a myriad of different objects that reference each other, and in order to allow for centralized storage and easy access, the [GameState] object is used. @@ -26,21 +27,25 @@ use super::{ /// A function that processes a section of the save file. /// Based on the given section, it will update the [GameState] object and the [Player] vector. /// The [GameState] object is used to store all the data from the save file, while the [Player] vector is used to store the player data. -pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mut Vec) { +pub fn process_section( + i: &mut Section, + game_state: &mut GameState, + players: &mut Vec, +) -> Result<(), SectionError> { match i.get_name() { "meta_data" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let r = r.as_map(); game_state.set_current_date(r.get_string_ref("meta_date")); game_state.set_offset_date(r.get_string_ref("meta_real_date")); } //the order is kept consistent with the order in the save file "traits_lookup" => { - let r = i.parse().unwrap(); + let r = i.parse()?; game_state.add_lookup(r.as_array().into_iter().map(|x| x.as_string()).collect()); } "landed_titles" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let landed = r.as_map().get_object_ref("landed_titles").as_map(); for (_, v) in landed.into_iter() { match v { @@ -55,7 +60,7 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "county_manager" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let counties = r.as_map().get_object_ref("counties").as_map(); // we create an association between the county key and the faith and culture of the county // this is so that we can easily add the faith and culture to the title, so O(n) instead of O(n^2) @@ -82,7 +87,7 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "dynasties" => { - let r = i.parse().unwrap(); + let r = i.parse()?; for (_, d) in r.as_map().into_iter() { match d.as_object() { SaveFileObject::Map(o) => { @@ -106,7 +111,7 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "living" => { - let r = i.parse().unwrap(); + let r = i.parse()?; for (_, l) in r.as_map().into_iter() { match l { SaveFileValue::Object(o) => { @@ -120,7 +125,7 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "dead_unprunable" => { - let r = i.parse().unwrap(); + let r = i.parse()?; for (_, d) in r.as_map().into_iter() { match d { SaveFileValue::Object(o) => { @@ -134,7 +139,7 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "characters" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let dead_prunable = r.as_map().get("dead_prunable"); if dead_prunable.is_some() { for (_, d) in dead_prunable.unwrap().as_object().as_map().into_iter() { @@ -151,7 +156,7 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "vassal_contracts" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let r = r.as_map(); // if version <= 1.12 then the key is active, otherwise it is database, why paradox? let active = r @@ -173,21 +178,21 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "religion" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let faiths = r.as_map().get_object_ref("faiths").as_map(); for (_, f) in faiths.into_iter() { game_state.add_faith(f.as_object().as_map()); } } "culture_manager" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let cultures = r.as_map().get_object_ref("cultures").as_map(); for (_, c) in cultures.into_iter() { game_state.add_culture(c.as_object().as_map()); } } "character_memory_manager" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let database = r.as_map().get_object_ref("database").as_map(); for (_, d) in database.into_iter() { match d { @@ -201,12 +206,12 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu } } "played_character" => { - let r = i.parse().unwrap(); + let r = i.parse()?; let p = Player::from_game_object(r.as_map(), game_state); players.push(p); } "artifacts" => { - let artifacts = i.parse().unwrap(); + let artifacts = i.parse()?; let arr = artifacts.as_map().get_object_ref("artifacts").as_map(); for (_, a) in arr.into_iter() { match a { @@ -223,4 +228,5 @@ pub fn process_section(i: &mut Section, game_state: &mut GameState, players: &mu i.skip(); } } + return Ok(()); } diff --git a/src/parser/save_file.rs b/src/parser/save_file.rs index b3e61c6..7aa16e0 100644 --- a/src/parser/save_file.rs +++ b/src/parser/save_file.rs @@ -151,17 +151,19 @@ impl SaveFile { } impl Iterator for SaveFile { - type Item = Section; + type Item = Result; /// Get the next object in the save file /// If the file pointer has reached the end of the save file then it will return None. - fn next(&mut self) -> Option
{ + fn next(&mut self) -> Option { let mut key = String::new(); let off = self.offset.get_internal_mut(); for c in self.contents[*off..].chars() { match c { '}' | '"' => { - panic!("Unexpected character at {}", *off); + return Some(Err(SaveFileError::ParseError( + "Unexpected character encountered in-between sections", + ))); } '{' => { break; @@ -180,11 +182,11 @@ impl Iterator for SaveFile { if key.is_empty() { return None; } - return Some(Section::new( + return Some(Ok(Section::new( key, self.contents.clone(), self.offset.clone(), - )); + ))); } } @@ -214,7 +216,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "test".to_string()); let test2 = object.as_map().get_object_ref("test2").as_map(); let test3 = test2.get_string_ref("test3"); @@ -235,7 +237,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "test".to_string()); let test2 = object.as_map().get_object_ref("test2"); let test2_val = test2.as_array(); @@ -281,7 +283,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "test".to_string()); let test2 = object.as_map().get_object_ref("test2").as_map(); assert_eq!(*(test2.get_string_ref("1")), "2".to_string()); @@ -297,7 +299,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "test".to_string()); let test2 = object.as_map().get_object_ref("test2").as_array(); assert_eq!(*(test2.get_index(0).unwrap().as_string()), "1".to_string()); @@ -331,7 +333,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); let variables = object.as_map().get_object_ref("variables").as_map(); let data = variables.get_object_ref("data").as_array(); assert_ne!(data.len(), 0) @@ -372,7 +374,7 @@ mod tests { } artifact_claims={ 83888519 } }"); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "3623".to_string()); assert_eq!( *(object.as_map().get_string_ref("name")), @@ -414,7 +416,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "test".to_string()); let test2 = object.as_map().get_object_ref("test2").as_map(); let test3 = test2.get_string_ref("test3"); @@ -474,7 +476,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "c_derby".to_string()); let b_derby = object.as_map().get_object_ref("b_derby").as_map(); assert_eq!(*(b_derby.get_string_ref("province")), "1621".to_string()); @@ -504,7 +506,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "test".to_string()); let test2 = object.as_map().get_object_ref("test2").as_map(); let test3 = test2.get_string_ref("test3"); @@ -519,7 +521,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "test".to_string()); } @@ -530,7 +532,7 @@ mod tests { duration={ 2 0=7548 1=2096 } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); assert_eq!(object.get_name(), "duration".to_string()); assert_eq!(object.as_array().len(), 3); } @@ -545,7 +547,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse().unwrap(); + let object = save_file.next().unwrap().unwrap().parse().unwrap(); let arr = object.as_map().get_object_ref("a").as_array(); assert_eq!(arr.len(), 2); } @@ -560,7 +562,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse(); + let object = save_file.next().unwrap().unwrap().parse(); assert!(object.is_err()) } @@ -574,7 +576,7 @@ mod tests { } ", ); - let object = save_file.next().unwrap().parse(); + let object = save_file.next().unwrap().unwrap().parse(); assert!(object.is_err()) } #[test] @@ -594,7 +596,7 @@ mod tests { b={ ", ); - let object = save_file.next().unwrap().parse(); + let object = save_file.next().unwrap().unwrap().parse(); assert!(object.is_err()) } }