ticktock/lib.rs
1//! Timing module for frame-based applications
2//!
3//! Contains methods for slowing down to a fixed framerate, as well as
4//! measuring actual frames per second.
5//!
6//! An example game loop:
7//!
8//! ```ignore
9//! use std::time;
10//! use ticktock::{Clock, Timer};
11//!
12//! let now = time::Instant::now();
13//!
14//! // initialize game
15//! // ...
16//!
17//! // show some fps measurements every 5 seconds
18//! let mut fps_counter = Timer::apply(|delta_t, prev_tick| (delta_t, *prev_tick), 0)
19//! .every(time::Duration::from_secs(5))
20//! .start(now);
21//!
22//! // run with a constant framerate of 30 fps
23//! for (tick, now) in Clock::framerate(30.0).iter() {
24//! // this loop will run approx. every 33.3333 ms
25//!
26//! // update, render, etc
27//! // ...
28//!
29//! // update or display fps count
30//! if let Some((delta_t, prev_tick)) = fps_counter.update(now) {
31//! fps_counter.set_value(tick);
32//!
33//! let fps = (tick - prev_tick) as f64 / delta_t.as_secs_f64();
34//! println!("FPS: {}", fps);
35//! }
36//! break; // ignore, for doctests
37//! }
38//! ```
39
40pub mod clock;
41pub mod delay;
42pub mod throttled_io;
43pub mod timer;
44
45pub use crate::clock::Clock;
46pub use crate::timer::Timer;
47
48/// Iterator attempt
49///
50/// Given an iterator of outcomes, iterates returning either
51///
52/// * the first successful outcome
53/// * the last unsuccessful outcome
54/// * `None` if the iterator was empty
55///
56/// `Result` is often used as an outcome, e.g. when trying to reconnect multiple times:
57///
58/// ```rust
59/// use std::net::TcpStream;
60/// use std::time::Duration;
61/// use ticktock::delay::Delay;
62/// use ticktock::Attempt;
63///
64/// const RETRY_DELAY: Duration = Duration::from_millis(250);
65///
66/// // attempt to connect to localhost:12348 three times, before giving up.
67/// // in total, 500 ms of delay will be inserted
68/// let conn = Delay::new(RETRY_DELAY)
69/// .map(|_| TcpStream::connect("localhost:12348"))
70/// .take(3)
71/// .attempt()
72/// .unwrap();
73///
74/// # // our test will fail, because there is noting listening at 12348
75/// # assert!(conn.is_err());
76/// ```
77///
78/// `Option` is also a valid outcome:
79///
80/// ```ignore
81/// let credentials = vec![("Bob", "secret"), ("Jeff", "hunter2"), ("John", "swordfish")];
82///
83/// fn try_login(username: &str, password: &str) -> Option<(String, String)> { ... }
84///
85/// // brute-force our way in
86/// let valid_credentials: Option<(String, String)> = credentials
87/// .map(|(u, p)| try_login(u, p))
88/// .attempt()
89/// .unwrap();
90/// ```
91
92// note: this could probably be expressed more cleanly by using associated types
93// (i.e. `type Outcome = ...`), but a bug in the rust compiler at the time of this writing
94// did not allow for it https://github.com/rust-lang/rust/issues/20400
95
96pub trait Attempt<O> {
97 /// Consumes until the successful outcome is encountered. In case of failure, returns the last
98 /// unsuccessful outcome.
99 fn attempt(self) -> Option<O>;
100}
101
102impl<T, E, I> Attempt<Result<T, E>> for I
103where
104 I: Iterator<Item = Result<T, E>>,
105{
106 fn attempt(self) -> Option<Result<T, E>> {
107 let mut rv = None;
108
109 for res in self {
110 rv = Some(res);
111
112 // do not keep going if we got an Ok
113 if let Some(Ok(_)) = rv {
114 break;
115 }
116 }
117
118 rv
119 }
120}
121
122impl<T, I> Attempt<Option<T>> for I
123where
124 I: Iterator<Item = Option<T>>,
125{
126 fn attempt(self) -> Option<Option<T>> {
127 let mut rv = None;
128
129 for res in self {
130 rv = Some(res);
131
132 // do not keep going if we got an Ok
133 if let Some(Some(_)) = rv {
134 break;
135 }
136 }
137
138 rv
139 }
140}
141
142#[cfg(test)]
143mod test {
144 use super::Attempt;
145
146 #[test]
147 fn attempt_works_on_ok_results() {
148 let rs = vec![Err(1), Err(2), Err(3), Ok(4), Ok(5)];
149
150 assert_eq!(Some(Ok(4)), rs.into_iter().attempt());
151 }
152
153 #[test]
154 fn attempt_works_on_err_results() {
155 let rs: Vec<Result<(), _>> = vec![Err(1), Err(2), Err(3)];
156
157 assert_eq!(Some(Err(3)), rs.into_iter().attempt());
158 }
159
160 #[test]
161 fn attempt_works_on_empty_result_vecs() {
162 let rs: Vec<Result<(), ()>> = Vec::new();
163
164 assert_eq!(None, rs.into_iter().attempt());
165 }
166
167 #[test]
168 fn attempt_works_on_some_options() {
169 let rs = vec![None, None, None, Some(4), Some(5)];
170
171 assert_eq!(Some(Some(4)), rs.into_iter().attempt());
172 }
173
174 #[test]
175 fn attempt_works_on_none_options() {
176 let rs: Vec<Option<()>> = vec![None, None, None];
177
178 assert_eq!(Some(None), rs.into_iter().attempt());
179 }
180
181 #[test]
182 fn attempt_works_on_empty_option_vecs() {
183 let rs: Vec<Option<()>> = Vec::new();
184
185 assert_eq!(None, rs.into_iter().attempt());
186 }
187}