Skip to main content

chess/game/
merge.rs

1use super::*;
2
3impl Game {
4    /// Merges another game with the same starting position, retaining this game's metadata.
5    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}