use crate::orbit::OrbitValidationError;
use holding_kronos::Calendar;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
use holding_color::Color;
use crate::orbit::Orbit;
#[derive(Copy, Clone, Deserialize, Serialize, PartialEq, Eq, Debug)]
pub struct PlanetId(Uuid);
#[derive(Clone, Deserialize, Serialize, Debug)]
pub struct CelestialBody {
pub id: PlanetId,
pub name: String,
pub orbit: Option<Orbit>,
pub color: Color,
pub children: Vec<PlanetId>,
pub rotational_period: usize,
pub temperature: i32,
}
impl CelestialBody {
pub fn new(name: String, temperature: i32, rotational_period: usize, color: Color) -> Self {
Self {
id: PlanetId(Uuid::new_v4()),
name,
color,
temperature,
rotational_period,
orbit: None,
children: vec![],
}
}
pub fn is_luminous(&self) -> bool {
self.temperature > 3500
}
pub fn with_moon(&mut self, moon: &mut CelestialBody, period: usize) -> &mut Self {
let orbit = Orbit::from_period(moon, self.id, period, 0);
moon.orbit = Some(orbit);
self.children.push(moon.id);
self
}
pub fn with_parent(&mut self, parent: &mut CelestialBody, period: usize) -> &mut Self {
let orbit = Orbit::from_period(self, parent.id, period, 0);
self.orbit = Some(orbit);
parent.children.push(self.id);
self
}
pub fn validate_calendar(&self, calendar: &Calendar) -> Result<bool, ValidationError> {
let planet_period = self.rotational_period as u32;
let calendar_period = calendar.days_to_seconds(1);
let x = if planet_period != calendar_period {
Err(ValidationError::InconsistentRotationalPeriod(
planet_period,
calendar_period,
))
} else {
Ok(true)
};
if let Some(y) = self.orbit.as_ref().map(|o| o.validate_calendar(calendar)) {
x.and(y.map_err(Into::into))
} else {
x
}
}
}
#[derive(Error, Debug, Copy, Clone)]
pub enum ValidationError {
#[error("the rotational period is inconsistent. planet: {0}, calendar: {1}")]
InconsistentRotationalPeriod(u32, u32),
#[error("invalid orbit: {0}")]
OrbitValidationError(OrbitValidationError),
}
impl From<OrbitValidationError> for ValidationError {
fn from(e: OrbitValidationError) -> Self {
Self::OrbitValidationError(e)
}
}
pub trait PlanetStore {
fn get_planet(&self, id: PlanetId) -> Option<&CelestialBody>;
fn get_planet_mut(&mut self, id: PlanetId) -> Option<&mut CelestialBody>;
fn create_planet(
&mut self,
name: String,
temperature: i32,
rotational_period: usize,
color: Color,
) -> &CelestialBody;
fn add_orbit(&mut self, parent_id: PlanetId, child_id: PlanetId, period: usize) {
let mut child = self.get_planet_mut(child_id).unwrap();
let orbit = Orbit::from_period(child, parent_id, period, 0);
child.orbit = Some(orbit);
let parent = self.get_planet_mut(parent_id).unwrap();
parent.children.push(child_id);
}
}