1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
7
8pub mod prelude;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum DensityError {
12 NonPositiveVolume,
13 NonPositiveDensity,
14}
15
16impl fmt::Display for DensityError {
17 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 Self::NonPositiveVolume => formatter.write_str("volume must be greater than zero"),
20 Self::NonPositiveDensity => formatter.write_str("density must be greater than zero"),
21 }
22 }
23}
24
25impl std::error::Error for DensityError {}
26
27#[must_use]
28pub const fn mass(density: f64, volume: f64) -> f64 {
29 density * volume
30}
31
32pub fn density(mass: f64, volume: f64) -> Result<f64, DensityError> {
38 if volume <= 0.0 {
39 Err(DensityError::NonPositiveVolume)
40 } else {
41 Ok(mass / volume)
42 }
43}
44
45pub fn volume(mass: f64, density: f64) -> Result<f64, DensityError> {
51 if density <= 0.0 {
52 Err(DensityError::NonPositiveDensity)
53 } else {
54 Ok(mass / density)
55 }
56}
57
58#[cfg(test)]
59#[allow(clippy::float_cmp)]
60mod tests {
61 use super::{DensityError, density, mass, volume};
62
63 #[test]
64 fn density_helpers_cover_mass_volume_relationships() -> Result<(), DensityError> {
65 assert_eq!(density(10.0, 2.0)?, 5.0);
66 assert_eq!(mass(5.0, 2.0), 10.0);
67 assert_eq!(volume(10.0, 5.0)?, 2.0);
68 Ok(())
69 }
70
71 #[test]
72 fn density_validation_requires_positive_inputs() {
73 assert_eq!(density(10.0, 0.0), Err(DensityError::NonPositiveVolume));
74 assert_eq!(volume(10.0, 0.0), Err(DensityError::NonPositiveDensity));
75 }
76}