1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::f64::consts::PI;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Sphere {
9 radius: f64,
10}
11
12impl Sphere {
13 #[must_use]
15 pub const fn new(radius: f64) -> Option<Self> {
16 if radius.is_finite() && radius > 0.0 {
17 Some(Self { radius })
18 } else {
19 None
20 }
21 }
22
23 #[must_use]
25 pub const fn radius(self) -> f64 {
26 self.radius
27 }
28
29 #[must_use]
31 pub const fn diameter(self) -> f64 {
32 self.radius * 2.0
33 }
34
35 #[must_use]
37 pub fn surface_area(self) -> f64 {
38 4.0 * PI * self.radius * self.radius
39 }
40
41 #[must_use]
43 pub fn volume(self) -> f64 {
44 (4.0 / 3.0) * PI * self.radius * self.radius * self.radius
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use core::f64::consts::PI;
51
52 use super::Sphere;
53
54 fn approx_eq(left: f64, right: f64) -> bool {
55 (left - right).abs() < 1.0e-10
56 }
57
58 #[test]
59 fn computes_sphere_measurements() {
60 let sphere = Sphere::new(3.0).expect("valid sphere");
61
62 assert_eq!(sphere.radius(), 3.0);
63 assert_eq!(sphere.diameter(), 6.0);
64 assert!(approx_eq(sphere.surface_area(), 36.0 * PI));
65 assert!(approx_eq(sphere.volume(), 36.0 * PI));
66 assert_eq!(Sphere::new(0.0), None);
67 }
68}