spo_rhai/packages/
time_basic.rs1#![cfg(not(feature = "no_time"))]
2
3use super::arithmetic::make_err as make_arithmetic_err;
4use crate::module::ModuleFlags;
5use crate::plugin::*;
6use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
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 instant::{Duration, Instant};
16
17def_package! {
18 pub BasicTimePackage(lib) {
20 lib.flags |= ModuleFlags::STANDARD_LIB;
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 if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) {
70 return Err(make_arithmetic_err(format!(
71 "Integer overflow for timestamp.elapsed: {seconds}"
72 )));
73 }
74 if timestamp > Instant::now() {
75 return Err(make_arithmetic_err("Time-stamp is later than now"));
76 }
77
78 Ok((seconds as INT).into())
79 }
80 }
81
82 #[rhai_fn(return_raw, name = "-")]
84 #[allow(clippy::unnecessary_wraps)]
85 pub fn time_diff(timestamp1: Instant, timestamp2: Instant) -> RhaiResult {
86 #[cfg(not(feature = "no_float"))]
87 return Ok(if timestamp2 > timestamp1 {
88 -(timestamp2 - timestamp1).as_secs_f64() as FLOAT
89 } else {
90 (timestamp1 - timestamp2).as_secs_f64() as FLOAT
91 }
92 .into());
93
94 #[cfg(feature = "no_float")]
95 if timestamp2 > timestamp1 {
96 let seconds = (timestamp2 - timestamp1).as_secs();
97
98 if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) {
99 return Err(make_arithmetic_err(format!(
100 "Integer overflow for timestamp duration: -{seconds}"
101 )));
102 }
103
104 Ok((-(seconds as INT)).into())
105 } else {
106 let seconds = (timestamp1 - timestamp2).as_secs();
107
108 if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) {
109 return Err(make_arithmetic_err(format!(
110 "Integer overflow for timestamp duration: {seconds}"
111 )));
112 }
113
114 Ok((seconds as INT).into())
115 }
116 }
117
118 #[cfg(not(feature = "no_float"))]
119 pub mod float_functions {
120 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
121 fn add_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
122 if seconds < 0.0 {
123 return subtract_impl(timestamp, -seconds);
124 }
125 if cfg!(not(feature = "unchecked")) {
126 if seconds > (INT::MAX as FLOAT).min(u64::MAX as FLOAT) {
127 return Err(make_arithmetic_err(format!(
128 "Integer overflow for timestamp add: {seconds}"
129 )));
130 }
131
132 timestamp
133 .checked_add(Duration::from_millis((seconds * 1000.0) as u64))
134 .ok_or_else(|| {
135 make_arithmetic_err(format!(
136 "Timestamp overflow when adding {seconds} second(s)"
137 ))
138 })
139 } else {
140 Ok(timestamp + Duration::from_millis((seconds * 1000.0) as u64))
141 }
142 }
143 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
144 fn subtract_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
145 if seconds < 0.0 {
146 return add_impl(timestamp, -seconds);
147 }
148 if cfg!(not(feature = "unchecked")) {
149 if seconds > (INT::MAX as FLOAT).min(u64::MAX as FLOAT) {
150 return Err(make_arithmetic_err(format!(
151 "Integer overflow for timestamp subtract: {seconds}"
152 )));
153 }
154
155 timestamp
156 .checked_sub(Duration::from_millis((seconds * 1000.0) as u64))
157 .ok_or_else(|| {
158 make_arithmetic_err(format!(
159 "Timestamp overflow when subtracting {seconds} second(s)"
160 ))
161 })
162 } else {
163 Ok(timestamp
164 .checked_sub(Duration::from_millis((seconds * 1000.0) as u64))
165 .unwrap())
166 }
167 }
168
169 #[rhai_fn(return_raw, name = "+")]
171 pub fn add(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
172 add_impl(timestamp, seconds)
173 }
174 #[rhai_fn(return_raw, name = "+=")]
176 pub fn add_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
177 *timestamp = add_impl(*timestamp, seconds)?;
178 Ok(())
179 }
180 #[rhai_fn(return_raw, name = "-")]
182 pub fn subtract(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
183 subtract_impl(timestamp, seconds)
184 }
185 #[rhai_fn(return_raw, name = "-=")]
187 pub fn subtract_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
188 *timestamp = subtract_impl(*timestamp, seconds)?;
189 Ok(())
190 }
191 }
192
193 #[inline(always)]
194 fn add_inner(timestamp: Instant, seconds: u64) -> Option<Instant> {
195 if cfg!(not(feature = "unchecked")) {
196 timestamp.checked_add(Duration::from_secs(seconds))
197 } else {
198 Some(timestamp + Duration::from_secs(seconds))
199 }
200 }
201 #[inline]
202 fn add_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
203 if seconds < 0 {
204 subtract_inner(timestamp, seconds.unsigned_abs() as u64)
205 } else {
206 #[allow(clippy::cast_sign_loss)]
207 add_inner(timestamp, seconds as u64)
208 }
209 .ok_or_else(|| {
210 make_arithmetic_err(format!(
211 "Timestamp overflow when adding {seconds} second(s)"
212 ))
213 })
214 }
215 #[inline(always)]
216 fn subtract_inner(timestamp: Instant, seconds: u64) -> Option<Instant> {
217 if cfg!(not(feature = "unchecked")) {
218 timestamp.checked_sub(Duration::from_secs(seconds))
219 } else {
220 Some(timestamp - Duration::from_secs(seconds))
221 }
222 }
223 #[inline]
224 fn subtract_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
225 if seconds < 0 {
226 add_inner(timestamp, seconds.unsigned_abs() as u64)
227 } else {
228 #[allow(clippy::cast_sign_loss)]
229 subtract_inner(timestamp, seconds as u64)
230 }
231 .ok_or_else(|| {
232 make_arithmetic_err(format!(
233 "Timestamp overflow when subtracting {seconds} second(s)"
234 ))
235 })
236 }
237
238 #[rhai_fn(return_raw, name = "+")]
240 pub fn add(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
241 add_impl(timestamp, seconds)
242 }
243 #[rhai_fn(return_raw, name = "+=")]
245 pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
246 *timestamp = add_impl(*timestamp, seconds)?;
247 Ok(())
248 }
249 #[rhai_fn(return_raw, name = "-")]
251 pub fn subtract(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
252 subtract_impl(timestamp, seconds)
253 }
254 #[rhai_fn(return_raw, name = "-=")]
256 pub fn subtract_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
257 *timestamp = subtract_impl(*timestamp, seconds)?;
258 Ok(())
259 }
260
261 #[rhai_fn(name = "==")]
263 pub fn eq(timestamp1: Instant, timestamp2: Instant) -> bool {
264 timestamp1 == timestamp2
265 }
266 #[rhai_fn(name = "!=")]
268 pub fn ne(timestamp1: Instant, timestamp2: Instant) -> bool {
269 timestamp1 != timestamp2
270 }
271 #[rhai_fn(name = "<")]
273 pub fn lt(timestamp1: Instant, timestamp2: Instant) -> bool {
274 timestamp1 < timestamp2
275 }
276 #[rhai_fn(name = "<=")]
278 pub fn lte(timestamp1: Instant, timestamp2: Instant) -> bool {
279 timestamp1 <= timestamp2
280 }
281 #[rhai_fn(name = ">")]
283 pub fn gt(timestamp1: Instant, timestamp2: Instant) -> bool {
284 timestamp1 > timestamp2
285 }
286 #[rhai_fn(name = ">=")]
288 pub fn gte(timestamp1: Instant, timestamp2: Instant) -> bool {
289 timestamp1 >= timestamp2
290 }
291}