1use super::*;
2
3impl Game {
4 pub fn merge(&mut self, other: &Game) -> Result<()> {
6 if self.mode() != other.mode()
7 || self.start().position().position() != other.start().position().position()
8 {
9 return Err(Error::DifferentStart);
10 }
11 self.merge_position(START, other.start())
12 }
13
14 fn merge_position(&mut self, slot: PositionSlot, other: PositionRef<'_>) -> Result<()> {
15 let comment = other.comment();
16 self.position_mut(slot)?.update_public(|public| merge_text(&mut public.comment, comment));
17
18 for incoming in other.iter() {
19 let play = incoming.play;
20 let exists = self.position(slot)?.has_option(play);
21 let mut position = self.position_mut(slot)?;
22 let mut target = position.option_mut_or_push(play)?;
23 if exists {
24 target.update_public(|public| {
25 merge_text(&mut public.affixes.intro, incoming.public.affixes.intro.as_ref());
26 merge_text(&mut public.affixes.outro, incoming.public.affixes.outro.as_ref());
27 });
28 } else {
29 target.update_public(|public| *public = incoming.public.clone());
30 }
31 let target_slot = target.slot();
32 self.merge_position(target_slot.into(), incoming.position())?;
33 }
34 Ok(())
35 }
36}
37
38fn merge_text(target: &mut Option<Text>, incoming: Option<&Text>) {
39 if let Some(incoming) = incoming {
40 match target {
41 Some(existing) if existing != incoming => existing.merge(incoming),
42 Some(_) => {}
43 None => *target = Some(incoming.clone()),
44 }
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::formats::pgn::Pgn;
52
53 #[test]
54 fn merges_moves_and_distinct_comments() {
55 let first: Pgn = "1. e4 {First} e5 *".parse().unwrap();
56 let second: Pgn = "1. e4 {Second} c5 *".parse().unwrap();
57 let mut game = Game::from_pgn(first).unwrap();
58 game.merge(&Game::from_pgn(second).unwrap()).unwrap();
59
60 assert_eq!(game.start().iter().count(), 1);
61 let e4 = game.start().main().unwrap();
62 assert_eq!(e4.comment().unwrap().as_ref(), "First\n\nSecond");
63 assert_eq!(e4.position().iter().count(), 2);
64 }
65}