Skip to main content

pleiades_types/
bodies.rs

1//! Celestial body identifiers: [`CelestialBodyClass`] and [`CelestialBody`].
2
3use core::fmt;
4
5use crate::custom_bodies::{CustomBodyId, CustomDefinitionValidationError};
6
7/// A coarse classification for a celestial body.
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
10#[non_exhaustive]
11pub enum CelestialBodyClass {
12    /// The Sun or Moon.
13    Luminary,
14    /// Mercury through Pluto.
15    MajorPlanet,
16    /// Lunar nodes and apsides.
17    LunarPoint,
18    /// Ceres, Pallas, Juno, and Vesta.
19    BuiltInAsteroid,
20    /// A hypothetical/fictitious body defined by osculating orbital elements
21    /// (Uranian planets, Transpluto, Vulcan, historical pre-discovery predictions).
22    Fictitious,
23    /// A structured custom body identifier.
24    Custom,
25}
26
27impl CelestialBodyClass {
28    /// Returns a stable human-readable label for the class.
29    pub const fn label(self) -> &'static str {
30        match self {
31            Self::Luminary => "luminary",
32            Self::MajorPlanet => "major planet",
33            Self::LunarPoint => "lunar point",
34            Self::BuiltInAsteroid => "built-in asteroid",
35            Self::Fictitious => "fictitious body",
36            Self::Custom => "custom body",
37        }
38    }
39}
40
41impl fmt::Display for CelestialBodyClass {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        f.write_str(self.label())
44    }
45}
46
47/// The built-in and custom body identifiers recognized by the shared API.
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49#[derive(Clone, Debug, PartialEq, Eq, Hash)]
50#[non_exhaustive]
51pub enum CelestialBody {
52    /// The Sun.
53    Sun,
54    /// The Moon.
55    Moon,
56    /// Mercury.
57    Mercury,
58    /// Venus.
59    Venus,
60    /// Mars.
61    Mars,
62    /// Jupiter.
63    Jupiter,
64    /// Saturn.
65    Saturn,
66    /// Uranus.
67    Uranus,
68    /// Neptune.
69    Neptune,
70    /// Pluto.
71    Pluto,
72    /// The mean lunar node.
73    MeanNode,
74    /// The true lunar node.
75    TrueNode,
76    /// The mean lunar apogee.
77    MeanApogee,
78    /// The true lunar apogee.
79    TrueApogee,
80    /// The mean lunar perigee.
81    MeanPerigee,
82    /// The true lunar perigee.
83    TruePerigee,
84    /// Ceres.
85    Ceres,
86    /// Pallas.
87    Pallas,
88    /// Juno.
89    Juno,
90    /// Vesta.
91    Vesta,
92    /// Cupido (Uranian/Hamburg, Witte) — SE fictitious body 40.
93    Cupido,
94    /// Hades (Uranian/Hamburg, Witte) — SE 41.
95    Hades,
96    /// Zeus (Uranian/Hamburg, Sieggrün) — SE 42.
97    Zeus,
98    /// Kronos (Uranian/Hamburg, Sieggrün) — SE 43.
99    Kronos,
100    /// Apollon (Uranian/Hamburg, Sieggrün) — SE 44.
101    Apollon,
102    /// Admetos (Uranian/Hamburg, Sieggrün) — SE 45.
103    Admetos,
104    /// Vulkanus (Uranian/Hamburg, Sieggrün) — SE 46.
105    Vulkanus,
106    /// Poseidon (Uranian/Hamburg, Sieggrün) — SE 47.
107    Poseidon,
108    /// Isis / Transpluto — SE 48.
109    Transpluto,
110    /// Nibiru — SE 49.
111    Nibiru,
112    /// Harrington — SE 50.
113    Harrington,
114    /// Neptune (Leverrier historical prediction) — SE 51.
115    NeptuneLeverrier,
116    /// Neptune (Adams historical prediction) — SE 52.
117    NeptuneAdams,
118    /// Pluto (Lowell historical prediction) — SE 53.
119    PlutoLowell,
120    /// Pluto (Pickering historical prediction) — SE 54.
121    PlutoPickering,
122    /// Vulcan (intramercurial) — SE 55.
123    Vulcan,
124    /// White Moon / Selena (geocentric orbit) — SE 56.
125    WhiteMoon,
126    /// Proserpina — SE 57.
127    Proserpina,
128    /// Waldemath (hypothetical second Earth moon, geocentric orbit) — SE 58.
129    Waldemath,
130    /// A body that is not yet one of the built-in identifiers.
131    Custom(CustomBodyId),
132}
133
134impl CelestialBody {
135    /// Returns the coarse class for this body.
136    pub const fn class(&self) -> CelestialBodyClass {
137        match self {
138            Self::Sun | Self::Moon => CelestialBodyClass::Luminary,
139            Self::Mercury
140            | Self::Venus
141            | Self::Mars
142            | Self::Jupiter
143            | Self::Saturn
144            | Self::Uranus
145            | Self::Neptune
146            | Self::Pluto => CelestialBodyClass::MajorPlanet,
147            Self::MeanNode
148            | Self::TrueNode
149            | Self::MeanApogee
150            | Self::TrueApogee
151            | Self::MeanPerigee
152            | Self::TruePerigee => CelestialBodyClass::LunarPoint,
153            Self::Ceres | Self::Pallas | Self::Juno | Self::Vesta => {
154                CelestialBodyClass::BuiltInAsteroid
155            }
156            Self::Cupido
157            | Self::Hades
158            | Self::Zeus
159            | Self::Kronos
160            | Self::Apollon
161            | Self::Admetos
162            | Self::Vulkanus
163            | Self::Poseidon
164            | Self::Transpluto
165            | Self::Nibiru
166            | Self::Harrington
167            | Self::NeptuneLeverrier
168            | Self::NeptuneAdams
169            | Self::PlutoLowell
170            | Self::PlutoPickering
171            | Self::Vulcan
172            | Self::WhiteMoon
173            | Self::Proserpina
174            | Self::Waldemath => CelestialBodyClass::Fictitious,
175            Self::Custom(_) => CelestialBodyClass::Custom,
176        }
177    }
178
179    /// Returns a stable human-readable name for built-in bodies.
180    pub const fn built_in_name(&self) -> Option<&'static str> {
181        match self {
182            Self::Sun => Some("Sun"),
183            Self::Moon => Some("Moon"),
184            Self::Mercury => Some("Mercury"),
185            Self::Venus => Some("Venus"),
186            Self::Mars => Some("Mars"),
187            Self::Jupiter => Some("Jupiter"),
188            Self::Saturn => Some("Saturn"),
189            Self::Uranus => Some("Uranus"),
190            Self::Neptune => Some("Neptune"),
191            Self::Pluto => Some("Pluto"),
192            Self::MeanNode => Some("Mean Node"),
193            Self::TrueNode => Some("True Node"),
194            Self::MeanApogee => Some("Mean Apogee"),
195            Self::TrueApogee => Some("True Apogee"),
196            Self::MeanPerigee => Some("Mean Perigee"),
197            Self::TruePerigee => Some("True Perigee"),
198            Self::Ceres => Some("Ceres"),
199            Self::Pallas => Some("Pallas"),
200            Self::Juno => Some("Juno"),
201            Self::Vesta => Some("Vesta"),
202            Self::Cupido => Some("Cupido"),
203            Self::Hades => Some("Hades"),
204            Self::Zeus => Some("Zeus"),
205            Self::Kronos => Some("Kronos"),
206            Self::Apollon => Some("Apollon"),
207            Self::Admetos => Some("Admetos"),
208            Self::Vulkanus => Some("Vulkanus"),
209            Self::Poseidon => Some("Poseidon"),
210            Self::Transpluto => Some("Transpluto"),
211            Self::Nibiru => Some("Nibiru"),
212            Self::Harrington => Some("Harrington"),
213            Self::NeptuneLeverrier => Some("Neptune (Leverrier)"),
214            Self::NeptuneAdams => Some("Neptune (Adams)"),
215            Self::PlutoLowell => Some("Pluto (Lowell)"),
216            Self::PlutoPickering => Some("Pluto (Pickering)"),
217            Self::Vulcan => Some("Vulcan"),
218            Self::WhiteMoon => Some("White Moon"),
219            Self::Proserpina => Some("Proserpina"),
220            Self::Waldemath => Some("Waldemath"),
221            Self::Custom(_) => None,
222        }
223    }
224
225    /// Validates the custom body identifier when this is a custom body.
226    ///
227    /// Built-in bodies are always valid; only the structured custom body
228    /// identifier is checked. This keeps user-defined catalog entries from
229    /// drifting into request or profile surfaces with blank or padded fields.
230    pub fn validate(&self) -> Result<(), CustomDefinitionValidationError> {
231        match self {
232            Self::Custom(custom) => custom.validate(),
233            _ => Ok(()),
234        }
235    }
236}
237
238impl fmt::Display for CelestialBody {
239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240        match self {
241            Self::Sun => f.write_str("Sun"),
242            Self::Moon => f.write_str("Moon"),
243            Self::Mercury => f.write_str("Mercury"),
244            Self::Venus => f.write_str("Venus"),
245            Self::Mars => f.write_str("Mars"),
246            Self::Jupiter => f.write_str("Jupiter"),
247            Self::Saturn => f.write_str("Saturn"),
248            Self::Uranus => f.write_str("Uranus"),
249            Self::Neptune => f.write_str("Neptune"),
250            Self::Pluto => f.write_str("Pluto"),
251            Self::MeanNode => f.write_str("Mean Node"),
252            Self::TrueNode => f.write_str("True Node"),
253            Self::MeanApogee => f.write_str("Mean Apogee"),
254            Self::TrueApogee => f.write_str("True Apogee"),
255            Self::MeanPerigee => f.write_str("Mean Perigee"),
256            Self::TruePerigee => f.write_str("True Perigee"),
257            Self::Ceres => f.write_str("Ceres"),
258            Self::Pallas => f.write_str("Pallas"),
259            Self::Juno => f.write_str("Juno"),
260            Self::Vesta => f.write_str("Vesta"),
261            Self::Cupido => f.write_str("Cupido"),
262            Self::Hades => f.write_str("Hades"),
263            Self::Zeus => f.write_str("Zeus"),
264            Self::Kronos => f.write_str("Kronos"),
265            Self::Apollon => f.write_str("Apollon"),
266            Self::Admetos => f.write_str("Admetos"),
267            Self::Vulkanus => f.write_str("Vulkanus"),
268            Self::Poseidon => f.write_str("Poseidon"),
269            Self::Transpluto => f.write_str("Transpluto"),
270            Self::Nibiru => f.write_str("Nibiru"),
271            Self::Harrington => f.write_str("Harrington"),
272            Self::NeptuneLeverrier => f.write_str("Neptune (Leverrier)"),
273            Self::NeptuneAdams => f.write_str("Neptune (Adams)"),
274            Self::PlutoLowell => f.write_str("Pluto (Lowell)"),
275            Self::PlutoPickering => f.write_str("Pluto (Pickering)"),
276            Self::Vulcan => f.write_str("Vulcan"),
277            Self::WhiteMoon => f.write_str("White Moon"),
278            Self::Proserpina => f.write_str("Proserpina"),
279            Self::Waldemath => f.write_str("Waldemath"),
280            Self::Custom(custom) => fmt::Display::fmt(custom, f),
281        }
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn fictitious_bodies_map_to_fictitious_class() {
291        for body in [
292            CelestialBody::Cupido,
293            CelestialBody::Transpluto,
294            CelestialBody::Vulcan,
295            CelestialBody::WhiteMoon,
296            CelestialBody::Waldemath,
297        ] {
298            assert_eq!(body.class(), CelestialBodyClass::Fictitious);
299            assert!(body.built_in_name().is_some());
300            assert!(!body.to_string().is_empty());
301        }
302        assert_eq!(
303            CelestialBody::Transpluto.built_in_name(),
304            Some("Transpluto")
305        );
306        assert_eq!(CelestialBody::WhiteMoon.to_string(), "White Moon");
307    }
308}