celestial_core/angle/
validate.rs1use super::core::Angle;
2use crate::constants::{HALF_PI, PI};
3use crate::{AstroError, MathErrorKind};
4
5pub fn validate_right_ascension(angle: Angle) -> Result<Angle, AstroError> {
6 let rad = angle.radians();
7 if rad.is_finite() {
8 let normalized = super::normalize::wrap_0_2pi(rad);
9 return Ok(Angle::from_radians(normalized));
10 }
11
12 Err(AstroError::math_error(
13 "validate_right_ascension",
14 MathErrorKind::NotFinite,
15 "RA Not Finite",
16 ))
17}
18
19pub fn validate_declination(angle: Angle, beyond_pole: bool) -> Result<Angle, AstroError> {
27 let rad = angle.radians();
28 if !rad.is_finite() {
29 return Err(AstroError::math_error(
30 "validate_declination",
31 MathErrorKind::NotFinite,
32 "Dec not Finite",
33 ));
34 }
35
36 let (limit, range_desc) = if beyond_pole {
37 (PI, "[-180°, +180°]")
38 } else {
39 (HALF_PI, "[-90°, +90°]")
40 };
41
42 if (-limit..=limit).contains(&rad) {
43 return Ok(angle);
44 }
45
46 Err(AstroError::math_error(
47 "validate_declination",
48 MathErrorKind::OutOfRange,
49 &format!("Dec {:.2}° out of range {}", angle.degrees(), range_desc),
50 ))
51}
52
53pub fn validate_latitude(angle: Angle) -> Result<Angle, AstroError> {
54 validate_declination(angle, false)
55}
56
57pub fn validate_longitude(angle: Angle, normalize: bool) -> Result<Angle, AstroError> {
58 let rad = angle.radians();
59 if !rad.is_finite() {
60 return Err(AstroError::math_error(
61 "validate_longitude",
62 MathErrorKind::NotFinite,
63 "Lon not finite",
64 ));
65 }
66
67 if normalize {
68 let normalized = super::normalize::wrap_0_2pi(rad);
69 return Ok(Angle::from_radians(normalized));
70 }
71
72 if (-PI..=PI).contains(&rad) {
73 return Ok(angle);
74 }
75
76 Err(AstroError::math_error(
77 "validate_longitude",
78 MathErrorKind::OutOfRange,
79 "Lon out of range",
80 ))
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::constants::TWOPI;
87
88 #[test]
89 fn test_validate_right_ascension_valid() {
90 let angle = Angle::from_degrees(45.0);
91 let result = validate_right_ascension(angle);
92 assert!(result.is_ok());
93 }
94
95 #[test]
96 fn test_validate_right_ascension_not_finite() {
97 let angle = Angle::from_radians(f64::NAN);
98 let result = validate_right_ascension(angle);
99 assert!(result.is_err());
100 if let Err(AstroError::MathError { kind, .. }) = result {
101 assert_eq!(kind, MathErrorKind::NotFinite);
102 } else {
103 panic!("Expected MathError with NotFinite");
104 }
105 }
106
107 #[test]
108 fn test_validate_right_ascension_infinite() {
109 let angle = Angle::from_radians(f64::INFINITY);
110 let result = validate_right_ascension(angle);
111 assert!(result.is_err());
112 if let Err(AstroError::MathError { kind, .. }) = result {
113 assert_eq!(kind, MathErrorKind::NotFinite);
114 } else {
115 panic!("Expected MathError with NotFinite");
116 }
117 }
118
119 #[test]
120 fn test_validate_declination() {
121 assert!(validate_declination(Angle::from_degrees(45.0), false).is_ok());
123 assert!(validate_declination(Angle::from_degrees(-90.0), false).is_ok());
124
125 assert!(validate_declination(Angle::from_degrees(95.0), false).is_err());
127
128 assert!(validate_declination(Angle::from_degrees(120.0), true).is_ok());
130
131 assert!(validate_declination(Angle::from_radians(f64::NAN), false).is_err());
133 }
134
135 #[test]
136 fn test_validate_latitude_delegates_to_declination() {
137 let valid = Angle::from_degrees(45.0);
138 assert!(validate_latitude(valid).is_ok());
139
140 let invalid = Angle::from_degrees(95.0);
141 assert!(validate_latitude(invalid).is_err());
142 }
143
144 #[test]
145 fn test_validate_longitude_valid() {
146 let angle = Angle::from_degrees(45.0);
147 let result = validate_longitude(angle, false);
148 assert!(result.is_ok());
149 }
150
151 #[test]
152 fn test_validate_longitude_not_finite() {
153 let angle = Angle::from_radians(f64::NAN);
154 let result = validate_longitude(angle, false);
155 assert!(result.is_err());
156 if let Err(AstroError::MathError { kind, .. }) = result {
157 assert_eq!(kind, MathErrorKind::NotFinite);
158 } else {
159 panic!("Expected MathError with NotFinite");
160 }
161 }
162
163 #[test]
164 fn test_validate_longitude_normalized() {
165 let angle = Angle::from_degrees(370.0);
166 let result = validate_longitude(angle, true);
167 assert!(result.is_ok());
168 let normalized = result.unwrap();
169 assert!(normalized.radians() >= 0.0 && normalized.radians() < TWOPI);
170 }
171
172 #[test]
173 fn test_validate_longitude_out_of_range() {
174 let angle = Angle::from_degrees(190.0);
175 let result = validate_longitude(angle, false);
176 assert!(result.is_err());
177 if let Err(AstroError::MathError { kind, .. }) = result {
178 assert_eq!(kind, MathErrorKind::OutOfRange);
179 } else {
180 panic!("Expected MathError with OutOfRange");
181 }
182 }
183}