1mod duration;
4mod iteration;
5mod mode;
6mod utils;
7
8pub use duration::{Delay, Duration};
9pub use iteration::IterationCount;
10pub use mode::Direction;
11
12pub use lilt::Easing;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
30pub struct Timing {
31 duration: Duration,
33 delay: Delay,
35 direction: Direction,
37 easing: Easing,
39 iterations: IterationCount,
41}
42
43impl Timing {
44 #[must_use]
46 pub fn new(duration_ms: f64) -> Self {
47 Self {
48 duration: Duration::from_millis(duration_ms),
49 ..Self::default()
50 }
51 }
52
53 #[must_use]
55 pub fn linear(duration_ms: f64) -> Self {
56 Self::new(duration_ms)
57 }
58
59 #[must_use]
61 pub fn ease_in(duration_ms: f64) -> Self {
62 Self::new(duration_ms).with_easing(Easing::EaseIn)
63 }
64
65 #[must_use]
67 pub fn ease_out(duration_ms: f64) -> Self {
68 Self::new(duration_ms).with_easing(Easing::EaseOut)
69 }
70
71 #[must_use]
73 pub fn ease_in_out(duration_ms: f64) -> Self {
74 Self::new(duration_ms).with_easing(Easing::EaseInOut)
75 }
76
77 #[must_use]
79 pub const fn duration(&self) -> Duration {
80 self.duration
81 }
82
83 #[must_use]
85 pub const fn delay(&self) -> Delay {
86 self.delay
87 }
88
89 #[must_use]
91 pub const fn direction(&self) -> Direction {
92 self.direction
93 }
94
95 #[must_use]
97 pub const fn easing(&self) -> Easing {
98 self.easing
99 }
100
101 #[must_use]
103 pub const fn iterations(&self) -> IterationCount {
104 self.iterations
105 }
106
107 #[must_use]
109 pub const fn with_delay(mut self, delay: Delay) -> Self {
110 self.delay = delay;
111 self
112 }
113
114 #[must_use]
116 pub const fn with_direction(mut self, direction: Direction) -> Self {
117 self.direction = direction;
118 self
119 }
120
121 #[must_use]
123 pub const fn with_easing(mut self, easing: Easing) -> Self {
124 self.easing = easing;
125 self
126 }
127
128 #[must_use]
130 pub fn with_iterations(mut self, iterations: impl Into<IterationCount>) -> Self {
131 self.iterations = iterations.into();
132 self
133 }
134
135 #[must_use]
137 pub fn active_duration(self) -> Option<Duration> {
138 let count = self.iterations.finite_count()?;
139
140 self.duration.checked_mul(count)
141 }
142
143 #[must_use]
145 pub fn total_duration(self) -> Option<Duration> {
146 let active = self.active_duration()?;
147
148 active.checked_add_delay(self.delay)
149 }
150
151 pub(crate) fn with_duration(mut self, duration: Duration) -> Self {
152 self.duration = duration;
153 self
154 }
155
156 pub(crate) fn with_rate(mut self, rate: f64) -> Self {
157 self.duration = self.duration.divided_by(rate);
158 self
159 }
160}
161
162impl Default for Timing {
163 fn default() -> Self {
164 Self {
165 duration: Duration::ZERO,
166 delay: Delay::ZERO,
167 direction: Direction::default(),
168 easing: Easing::Linear,
169 iterations: IterationCount::default(),
170 }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::{Delay, Timing};
177 use float_cmp::assert_approx_eq;
178
179 #[test]
180 fn rate_scales_active_duration_without_changing_delay() {
181 let faster = Timing::new(200.0)
182 .with_delay(Delay::from_millis(40.0))
183 .with_iterations(3)
184 .with_rate(2.0);
185 let slower = Timing::new(200.0).with_rate(0.5);
186
187 assert_approx_eq!(f64, faster.duration().as_millis(), 100.0);
188 assert_approx_eq!(f64, faster.delay().as_millis(), 40.0);
189 assert_approx_eq!(f64, faster.total_duration().unwrap().as_millis(), 340.0);
190 assert_approx_eq!(f64, slower.duration().as_millis(), 400.0);
191 }
192
193 #[test]
194 fn invalid_rate_leaves_duration_unchanged() {
195 let timing = Timing::new(200.0);
196
197 assert_eq!(timing.with_rate(0.0).duration(), timing.duration());
198 assert_eq!(timing.with_rate(-1.0).duration(), timing.duration());
199 assert_eq!(timing.with_rate(f64::NAN).duration(), timing.duration());
200 assert_eq!(
201 timing.with_rate(f64::INFINITY).duration(),
202 timing.duration()
203 );
204 }
205}