1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::str::FromStr;
5
6use crate::address::GalacticAddress;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[non_exhaustive]
11pub enum Discovery {
12 Planet,
13 SolarSystem,
14 Sector,
15 Animal,
16 Flora,
17 Mineral,
18}
19
20impl fmt::Display for Discovery {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 Self::Planet => write!(f, "Planet"),
24 Self::SolarSystem => write!(f, "SolarSystem"),
25 Self::Sector => write!(f, "Sector"),
26 Self::Animal => write!(f, "Animal"),
27 Self::Flora => write!(f, "Flora"),
28 Self::Mineral => write!(f, "Mineral"),
29 }
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct DiscoveryParseError(pub String);
36
37impl fmt::Display for DiscoveryParseError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(f, "unknown discovery type: {}", self.0)
40 }
41}
42
43impl std::error::Error for DiscoveryParseError {}
44
45impl FromStr for Discovery {
46 type Err = DiscoveryParseError;
47
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 match s.to_lowercase().as_str() {
50 "planet" => Ok(Self::Planet),
51 "solarsystem" | "solar_system" => Ok(Self::SolarSystem),
52 "sector" => Ok(Self::Sector),
53 "animal" => Ok(Self::Animal),
54 "flora" => Ok(Self::Flora),
55 "mineral" => Ok(Self::Mineral),
56 _ => Err(DiscoveryParseError(s.to_string())),
57 }
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66#[non_exhaustive]
67pub struct DiscoveryRecord {
68 pub discovery_type: Discovery,
69 pub universe_address: GalacticAddress,
70 pub timestamp: Option<DateTime<Utc>>,
71 pub name: Option<String>,
72 pub discoverer: Option<String>,
73 pub is_uploaded: bool,
74}
75
76impl DiscoveryRecord {
77 pub fn new(
78 discovery_type: Discovery,
79 universe_address: GalacticAddress,
80 timestamp: Option<DateTime<Utc>>,
81 name: Option<String>,
82 discoverer: Option<String>,
83 is_uploaded: bool,
84 ) -> Self {
85 Self {
86 discovery_type,
87 universe_address,
88 timestamp,
89 name,
90 discoverer,
91 is_uploaded,
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn discovery_display_fromstr_roundtrip() {
102 for d in [
103 Discovery::Planet,
104 Discovery::SolarSystem,
105 Discovery::Sector,
106 Discovery::Animal,
107 Discovery::Flora,
108 Discovery::Mineral,
109 ] {
110 let s = d.to_string();
111 let parsed: Discovery = s.parse().unwrap();
112 assert_eq!(d, parsed);
113 }
114 }
115
116 #[test]
117 fn discovery_solar_system_alternate() {
118 assert_eq!(
119 "solar_system".parse::<Discovery>().unwrap(),
120 Discovery::SolarSystem
121 );
122 }
123
124 #[test]
125 fn discovery_record_constructor() {
126 let addr = GalacticAddress::new(0, 0, 0, 0x123, 0, 5);
127 let rec = DiscoveryRecord::new(
128 Discovery::Planet,
129 addr,
130 None,
131 Some("TestPlanet".to_string()),
132 None,
133 false,
134 );
135 assert_eq!(rec.discovery_type, Discovery::Planet);
136 assert_eq!(rec.universe_address.reality_index, 5);
137 assert!(!rec.is_uploaded);
138 }
139}