qtty_core/units/acceleration.rs
1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4//! Acceleration unit aliases and named units (`Length / Time²`).
5//!
6//! This module provides both a **parametric alias** and a **named SI unit** for
7//! acceleration:
8//!
9//! - [`Accel<L, T>`] — a generic alias over [`Per`] that works with any length
10//! and time units, mirroring the [`Velocity`](super::velocity::Velocity)
11//! pattern.
12//! - [`MeterPerSecondSquared`] — the SI coherent unit (m/s²) for contexts
13//! where a concrete named type is more convenient.
14//!
15//! ## Examples
16//!
17//! ```rust
18//! use qtty_core::acceleration::Accel;
19//! use qtty_core::length::Meter;
20//! use qtty_core::time::Second;
21//!
22//! let a: Accel<Meter, Second> = Accel::new(9.806_65);
23//! assert!((a.value() - 9.806_65).abs() < 1e-12);
24//! ```
25//!
26//! ```rust
27//! use qtty_core::acceleration::{MeterPerSecondSquared, MetersPerSecondSquared};
28//! use qtty_core::velocity::Velocity;
29//! use qtty_core::length::{Meter, Meters};
30//! use qtty_core::time::{Second, Seconds};
31//!
32//! let v = Meters::new(100.0) / Seconds::new(10.0); // Velocity<Meter, Second>
33//! let a: MetersPerSecondSquared = MetersPerSecondSquared::new(5.0);
34//! assert!((a.value() - 5.0).abs() < 1e-12);
35//! ```
36
37use crate::{Per, Prod, Quantity, Unit};
38use qtty_derive::Unit;
39
40/// Re-export the acceleration dimension from the dimension module.
41pub use crate::dimension::Acceleration;
42
43/// Marker trait for any unit whose dimension is [`Acceleration`].
44pub trait AccelerationUnit: Unit<Dim = Acceleration> {}
45impl<T: Unit<Dim = Acceleration>> AccelerationUnit for T {}
46
47/// An acceleration quantity parameterized by length and time units.
48///
49/// `Accel<L, T>` represents `L / T²` — the natural result of dividing a
50/// velocity by a time quantity, or dividing a length by the square of a time.
51///
52/// # Examples
53///
54/// ```rust
55/// use qtty_core::acceleration::Accel;
56/// use qtty_core::length::{Kilometer, Meter};
57/// use qtty_core::time::{Hour, Second};
58///
59/// let a1: Accel<Meter, Second> = Accel::new(9.8);
60/// let a2: Accel<Kilometer, Hour> = a1.to();
61/// ```
62pub type Accel<L, T> = Quantity<Per<L, Prod<T, T>>>;
63
64// ─────────────────────────────────────────────────────────────────────────────
65// SI named unit
66// ─────────────────────────────────────────────────────────────────────────────
67
68/// Metre per second squared — SI coherent unit of acceleration.
69#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
70#[unit(symbol = "m/s²", dimension = Acceleration, ratio = 1.0)]
71pub struct MeterPerSecondSquared;
72/// A quantity measured in metres per second squared.
73pub type MetersPerSecondSquared = Quantity<MeterPerSecondSquared>;
74/// One metre per second squared.
75pub const METER_PER_SECOND_SQUARED: MetersPerSecondSquared = MetersPerSecondSquared::new(1.0);
76
77/// Standard gravity (g₀ = 9.806 65 m/s², exact by definition).
78#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
79#[unit(symbol = "g₀", dimension = Acceleration, ratio = 9.80665)]
80pub struct StandardGravity;
81/// A quantity measured in standard gravities.
82pub type StandardGravities = Quantity<StandardGravity>;
83/// One standard gravity.
84pub const STANDARD_GRAVITY: StandardGravities = StandardGravities::new(1.0);
85
86// ─────────────────────────────────────────────────────────────────────────────
87// Unit inventory macro
88// ─────────────────────────────────────────────────────────────────────────────
89
90/// Canonical list of always-available acceleration units.
91#[macro_export]
92#[doc(hidden)]
93macro_rules! acceleration_units {
94 ($cb:path) => {
95 $cb!(MeterPerSecondSquared, StandardGravity);
96 };
97}
98
99// Generate bidirectional From impls.
100acceleration_units!(crate::impl_unit_from_conversions);
101
102// Cross-unit ops.
103#[cfg(feature = "cross-unit-ops")]
104acceleration_units!(crate::impl_unit_cross_unit_ops);
105
106// Compile-time check.
107#[cfg(test)]
108acceleration_units!(crate::assert_units_are_builtin);
109
110#[cfg(all(test, feature = "std"))]
111mod tests {
112 use super::*;
113 use approx::assert_abs_diff_eq;
114
115 #[test]
116 fn standard_gravity_to_mps2() {
117 let g = StandardGravities::new(1.0);
118 let mps2: MetersPerSecondSquared = g.to();
119 assert_abs_diff_eq!(mps2.value(), 9.806_65, epsilon = 1e-10);
120 }
121
122 #[test]
123 fn mps2_to_standard_gravity() {
124 let a = MetersPerSecondSquared::new(9.806_65);
125 let g: StandardGravities = a.to();
126 assert_abs_diff_eq!(g.value(), 1.0, epsilon = 1e-10);
127 }
128
129 #[test]
130 fn unit_derives_are_sound() {
131 // Force vtable dispatch to prevent inlining of the proc-macro-generated
132 // Display/LowerExp/UpperExp impls, so LLVM records a hit at the #[derive]
133 // source line that those impls map back to.
134 let q1 = MetersPerSecondSquared::new(1.0);
135 let q2 = StandardGravities::new(1.0);
136 let _ = format!("{}", &q1 as &dyn std::fmt::Display);
137 let _ = format!("{:e}", &q1 as &dyn std::fmt::LowerExp);
138 let _ = format!("{}", &q2 as &dyn std::fmt::Display);
139 let _ = format!("{:e}", &q2 as &dyn std::fmt::LowerExp);
140 }
141}