accrete 0.2.0

Rust port of Accrete, planetary system generation algorithm. Based on 'Formation of Planetary Systems by Aggregation: A Computer Simulation' by Stephen H. Dole. Improved and extended by many talented people during past ~50 years.
Documentation
use crate::{System, Planetesimal};
use super::accrete_event::AccreteEvent;

#[derive(Debug)]
pub struct AccreteState {
    pub system: System,
}

impl From<&System> for AccreteState  {
    fn from(system: &System) -> Self {
        AccreteState { system: system.clone() }
    }
}

impl TryFrom<&AccreteEvent> for AccreteState {
    type Error = String;

    fn try_from(event: &AccreteEvent) -> Result<Self, Self::Error> {
        match event {
            AccreteEvent::PlanetarySystemSetup(_, system) |
            AccreteEvent::PlanetaryEnvironmentGenerated(_, system) |
            AccreteEvent::PlanetarySystemComplete(_, system) => Ok(AccreteState { system: system.clone() }),
            _ => Err(format!("Failed to restore state from AccreteEvent. No System data present with event."))
        }
    }
}

impl AccreteState {
    pub fn set_from_event(&mut self, event: &AccreteEvent) {
        match event {
            AccreteEvent::PlanetarySystemSetup(_, system) |
            AccreteEvent::PlanetaryEnvironmentGenerated(_, system) => self.system = system.clone(),

            AccreteEvent::OuterBodyInjected(_, planetesimal) |
            AccreteEvent::PlanetesimalCreated(_, planetesimal) => self.system.planets.push(planetesimal.clone()),

            AccreteEvent::PlanetesimalUpdated(_, planetesimal) |
            AccreteEvent::PlanetesimalToGasGiant(_, planetesimal) => {
                let planet = self.find_planetesimal_mut(&planetesimal.id);
                *planet = planetesimal.clone();
            },
    
            AccreteEvent::DustBandsUpdated(_, dust_bands) => self.system.dust_bands = dust_bands.clone(),

            AccreteEvent::PlanetesimalCaptureMoon(_, id1, id2, planetesimal) |
            AccreteEvent::PlanetesimalsCoalesced(_, id1, id2, planetesimal) => {
                self.system.planets = self.system.planets.iter().filter(|p| &p.id != id1 && &p.id != id2).cloned().collect();
                self.system.planets.push(planetesimal.clone());
            },

            AccreteEvent::PlanetesimalMoonToRing(_, planet_id, moon_id, ring) => {
                let planet = self.find_planetesimal_mut(&planet_id);
                planet.moons = planet.moons.iter().filter(|m| &m.id != moon_id).cloned().collect();
                planet.rings.push(ring.clone());
            },
            AccreteEvent::MoonsCoalesced(_, id1, id2, planetesimal) => {
                let mut planet = None;
                for p in self.system.planets.iter_mut() {
                    if planet.is_some() {
                        break;
                    }

                    planet = p.moons.iter_mut().find(|m| m.id == planetesimal.id);
                }
                if let Some(planet) = planet {
                    planet.moons = planet.moons.iter().filter(|m| &m.id != id1 && &m.id != id2).cloned().collect();
                    planet.moons.push(planetesimal.clone());
                }
            },
            AccreteEvent::PostAccretionStarted(_) => (),
            AccreteEvent::PlanetarySystemComplete(_, system) => assert_eq!(format!("{:?}", &self.system), format!("{:?}", system)),
            AccreteEvent::None => (),
        }
        self.system.planets.sort_by(|p1, p2| p1.a.partial_cmp(&p2.a).expect("Failed to sort planetesimals.") )
    }

    pub fn find_planetesimal_mut(&mut self, id: &str) -> &mut Planetesimal {
        self.system.planets.iter_mut().find(|p| p.id == id).expect("Failed to find planet by id")
    }

    pub fn find_planetesimal(&self, id: &str) -> &Planetesimal {
        self.system.planets.iter().find(|p| p.id == id).expect("Failed to find planet by id")
    }

}