use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::data::{ConversationId, EntryId, FieldValue};
use crate::runtime::{Variables, Visits, visits::VisitCount};
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct DialogueSave {
#[serde(default)]
pub variables: HashMap<String, FieldValue>,
#[serde(default)]
pub visits: HashMap<(ConversationId, EntryId), VisitCount>,
}
impl DialogueSave {
pub fn record(variables: &Variables, visits: &Visits) -> Self {
Self {
variables: variables.0.clone(),
visits: visits.0.clone(),
}
}
pub fn apply(self, variables: &mut Variables, visits: &mut Visits) {
variables.0.extend(self.variables);
visits.0.extend(self.visits);
}
}
pub fn save_to_ron(save: &DialogueSave) -> Result<String, ron::Error> {
ron::ser::to_string_pretty(save, ron::ser::PrettyConfig::default())
}
pub fn save_from_ron(text: &str) -> Result<DialogueSave, ron::error::SpannedError> {
ron::de::from_str(text)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::{DialogueDatabase, Variable};
use rstest::{fixture, rstest};
#[fixture]
fn db() -> DialogueDatabase {
DialogueDatabase {
variables: vec![
Variable {
name: "AcceptedJob".to_owned(),
initial: FieldValue::Boolean(false),
fields: vec![],
},
Variable {
name: "Gold".to_owned(),
initial: FieldValue::Number(10.0),
fields: vec![],
},
],
..Default::default()
}
}
#[test]
fn ron_roundtrip() {
let mut variables = Variables::default();
variables.set("AcceptedJob", true);
variables.set("PlayerName", "Feri");
let mut visits = Visits::default();
visits.record_displayed((ConversationId(1), EntryId(2)));
visits.record_offered((ConversationId(1), EntryId(3)));
let text = save_to_ron(&DialogueSave::record(&variables, &visits)).unwrap();
let save = save_from_ron(&text).unwrap();
let mut restored_variables = Variables::default();
let mut restored_visits = Visits::default();
save.apply(&mut restored_variables, &mut restored_visits);
assert!(restored_variables.truthy("AcceptedJob"));
assert_eq!(restored_variables.text("PlayerName"), "Feri");
assert_eq!(
restored_visits.displayed((ConversationId(1), EntryId(2))),
1
);
assert_eq!(restored_visits.offered((ConversationId(1), EntryId(3))), 1);
}
#[rstest]
fn saved_values_overwrite_seeded_ones(db: DialogueDatabase) {
let mut variables = Variables::default();
variables.seed(&db);
let save = DialogueSave {
variables: HashMap::from([("Gold".to_owned(), FieldValue::Number(99.0))]),
visits: HashMap::new(),
};
save.apply(&mut variables, &mut Visits::default());
assert_eq!(variables.number("Gold"), 99.0);
assert!(!variables.truthy("AcceptedJob"));
}
#[rstest]
fn seeding_after_restore_fills_only_missing_variables(db: DialogueDatabase) {
let mut variables = Variables::default();
let save = DialogueSave {
variables: HashMap::from([("Gold".to_owned(), FieldValue::Number(99.0))]),
visits: HashMap::new(),
};
save.apply(&mut variables, &mut Visits::default());
variables.seed(&db);
assert_eq!(variables.number("Gold"), 99.0);
assert!(!variables.truthy("AcceptedJob"));
}
}