rhai/packages/
time_basic.rs1#![cfg(not(feature = "no_time"))]
2
3use super::arithmetic::make_err as make_arithmetic_err;
4use crate::plugin::*;
5use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
6use std::convert::TryFrom;
7
8#[cfg(not(feature = "no_float"))]
9use crate::FLOAT;
10
11#[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
12use std::time::{Duration, Instant};
13
14#[cfg(all(target_family = "wasm", target_os = "unknown"))]
15use web_time::{Duration, Instant};
16
17def_package! {
18 pub BasicTimePackage(lib) {
20 lib.set_standard_lib(true);
21
22 combine_with_exported_module!(lib, "time", time_functions);
24 }
25}
26
27#[export_module]
28mod time_functions {
29 #[rhai_fn(volatile)]
41 pub fn timestamp() -> Instant {
42 Instant::now()
43 }
44
45 #[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
57 pub fn elapsed(timestamp: Instant) -> RhaiResult {
58 #[cfg(not(feature = "no_float"))]
59 if timestamp > Instant::now() {
60 Err(make_arithmetic_err("Time-stamp is later than now"))
61 } else {
62 Ok((timestamp.elapsed().as_secs_f64() as FLOAT).into())
63 }
64
65 #[cfg(feature = "no_float")]
66 {
67 let seconds = timestamp.elapsed().as_secs();
68
69 let Ok(seconds) = INT::try_from(seconds) else {
70 return Err(make_arithmetic_err(format!(
71 "Integer overflow for timestamp.elapsed: {seconds}"
72 )));
73 };
74
75 if timestamp > Instant::now() {
76 return Err(make_arithmetic_err("Time-stamp is later than now"));
77 }
78
79 Ok(seconds.into())
80 }
81 }
82
83 #[rhai_fn(return_raw, name = "-")]
85 #[allow(clippy::unnecessary_wraps)]
86 pub fn time_diff(timestamp1: Instant, timestamp2: Instant) -> RhaiResult {
87 #[cfg(not(feature = "no_float"))]
88 return Ok(if timestamp2 > timestamp1 {
89 -(timestamp2 - timestamp1).as_secs_f64() as FLOAT
90 } else {
91 (timestamp1 - timestamp2).as_secs_f64() as FLOAT
92 }
93 .into());
94
95 #[cfg(feature = "no_float")]
96 if timestamp2 > timestamp1 {
97 let seconds = (timestamp2 - timestamp1).as_secs();
98
99 let Ok(seconds) = INT::try_from(seconds) else {
100 return Err(make_arithmetic_err(format!(
101 "Integer overflow for timestamp.elapsed: {seconds}"
102 )));
103 };
104
105 Ok((-seconds).into())
106 } else {
107 let seconds = (timestamp1 - timestamp2).as_secs();
108
109 let Ok(seconds) = INT::try_from(seconds) else {
110 return Err(make_arithmetic_err(format!(
111 "Integer overflow for timestamp.elapsed: {seconds}"
112 )));
113 };
114
115 Ok(seconds.into())
116 }
117 }
118
119 #[cfg(not(feature = "no_float"))]
120 pub mod float_functions {
121 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
122 fn add_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
123 if seconds < 0.0 {
124 return subtract_impl(timestamp, -seconds);
125 }
126 if cfg!(not(feature = "unchecked")) {
127 #[allow(clippy::cast_precision_loss)]
128 if seconds > (INT::MAX as FLOAT).min(u64::MAX as FLOAT) {
129 return Err(make_arithmetic_err(format!(
130 "Integer overflow for timestamp add: {seconds}"
131 )));
132 }
133
134 timestamp
135 .checked_add(Duration::from_millis((seconds * 1000.0) as u64))
136 .ok_or_else(|| {
137 make_arithmetic_err(format!(
138 "Timestamp overflow when adding {seconds} second(s)"
139 ))
140 })
141 } else {
142 Ok(timestamp + Duration::from_millis((seconds * 1000.0) as u64))
143 }
144 }
145 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
146 fn subtract_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
147 if seconds < 0.0 {
148 return add_impl(timestamp, -seconds);
149 }
150 if cfg!(not(feature = "unchecked")) {
151 #[allow(clippy::cast_precision_loss)]
152 if seconds > (INT::MAX as FLOAT).min(u64::MAX as FLOAT) {
153 return Err(make_arithmetic_err(format!(
154 "Integer overflow for timestamp subtract: {seconds}"
155 )));
156 }
157
158 timestamp
159 .checked_sub(Duration::from_millis((seconds * 1000.0) as u64))
160 .ok_or_else(|| {
161 make_arithmetic_err(format!(
162 "Timestamp overflow when subtracting {seconds} second(s)"
163 ))
164 })
165 } else {
166 Ok(timestamp
167 .checked_sub(Duration::from_millis((seconds * 1000.0) as u64))
168 .unwrap())
169 }
170 }
171
172 #[rhai_fn(return_raw, name = "+")]
174 pub fn add(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
175 add_impl(timestamp, seconds)
176 }
177 #[rhai_fn(return_raw, name = "+=")]
179 pub fn add_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
180 *timestamp = add_impl(*timestamp, seconds)?;
181 Ok(())
182 }
183 #[rhai_fn(return_raw, name = "-")]
185 pub fn subtract(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
186 subtract_impl(timestamp, seconds)
187 }
188 #[rhai_fn(return_raw, name = "-=")]
190 pub fn subtract_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
191 *timestamp = subtract_impl(*timestamp, seconds)?;
192 Ok(())
193 }
194 }
195
196 #[inline(always)]
197 fn add_inner(timestamp: Instant, seconds: u64) -> Option<Instant> {
198 if cfg!(not(feature = "unchecked")) {
199 timestamp.checked_add(Duration::from_secs(seconds))
200 } else {
201 Some(timestamp + Duration::from_secs(seconds))
202 }
203 }
204 #[inline]
205 fn add_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
206 if seconds < 0 {
207 #[allow(clippy::useless_conversion)]
208 subtract_inner(timestamp, u64::try_from(seconds.unsigned_abs()).unwrap())
209 } else {
210 add_inner(timestamp, u64::try_from(seconds).unwrap())
211 }
212 .ok_or_else(|| {
213 make_arithmetic_err(format!(
214 "Timestamp overflow when adding {seconds} second(s)"
215 ))
216 })
217 }
218 #[inline(always)]
219 fn subtract_inner(timestamp: Instant, seconds: u64) -> Option<Instant> {
220 #[cfg(not(feature = "unchecked"))]
221 return timestamp.checked_sub(Duration::from_secs(seconds));
222
223 #[cfg(feature = "unchecked")]
224 return Some(timestamp - Duration::from_secs(seconds));
225 }
226 #[inline]
227 fn subtract_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
228 if seconds < 0 {
229 add_inner(timestamp, u64::try_from(seconds.unsigned_abs()).unwrap())
230 } else {
231 subtract_inner(timestamp, u64::try_from(seconds).unwrap())
232 }
233 .ok_or_else(|| {
234 make_arithmetic_err(format!(
235 "Timestamp overflow when subtracting {seconds} second(s)"
236 ))
237 })
238 }
239
240 #[rhai_fn(return_raw, name = "+")]
242 pub fn add(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
243 add_impl(timestamp, seconds)
244 }
245 #[rhai_fn(return_raw, name = "+=")]
247 pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
248 *timestamp = add_impl(*timestamp, seconds)?;
249 Ok(())
250 }
251 #[rhai_fn(return_raw, name = "-")]
253 pub fn subtract(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
254 subtract_impl(timestamp, seconds)
255 }
256 #[rhai_fn(return_raw, name = "-=")]
258 pub fn subtract_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
259 *timestamp = subtract_impl(*timestamp, seconds)?;
260 Ok(())
261 }
262
263 #[rhai_fn(name = "==")]
265 pub fn eq(timestamp1: Instant, timestamp2: Instant) -> bool {
266 timestamp1 == timestamp2
267 }
268 #[rhai_fn(name = "!=")]
270 pub fn ne(timestamp1: Instant, timestamp2: Instant) -> bool {
271 timestamp1 != timestamp2
272 }
273 #[rhai_fn(name = "<")]
275 pub fn lt(timestamp1: Instant, timestamp2: Instant) -> bool {
276 timestamp1 < timestamp2
277 }
278 #[rhai_fn(name = "<=")]
280 pub fn lte(timestamp1: Instant, timestamp2: Instant) -> bool {
281 timestamp1 <= timestamp2
282 }
283 #[rhai_fn(name = ">")]
285 pub fn gt(timestamp1: Instant, timestamp2: Instant) -> bool {
286 timestamp1 > timestamp2
287 }
288 #[rhai_fn(name = ">=")]
290 pub fn gte(timestamp1: Instant, timestamp2: Instant) -> bool {
291 timestamp1 >= timestamp2
292 }
293}