dtt/macros.rs
1// macros.rs
2//
3// Copyright © 2025 DateTime (DTT) library. All rights reserved.
4// SPDX-License-Identifier: Apache-2.0 OR MIT
5
6//! # Macros for the DTT Crate
7//!
8//! This module bundles all macros used across the DTT crate, facilitating date and time operations.
9//!
10//! ## Overview
11//!
12//! - **Utility Macros**: General purpose macros such as `dtt_print` for logging and `dtt_vec` for vector operations.
13//! - **DateTime Construction**: Macros like `dtt_now` and `dtt_new_with_tz` for creating `DateTime` instances.
14//! - **DateTime Operations**: Macros that perform arithmetic and comparisons on `DateTime` objects.
15//! - **Validation**: Macros like `is_valid` which validate various aspects of date and time inputs.
16
17/// Creates a new `DateTime` instance with the current date and time in UTC.
18///
19/// # Example
20///
21/// ```rust
22/// use dtt::dtt_now;
23///
24/// let now = dtt_now!();
25/// println!("Current date and time: {}", now);
26/// ```
27#[macro_export]
28macro_rules! dtt_now {
29 () => {{
30 $crate::datetime::DateTime::new()
31 }};
32}
33
34/// Prints the arguments to the console.
35///
36/// # Arguments
37///
38/// - `($($arg:tt)*)`: The arguments to be printed.
39///
40/// # Example
41///
42/// ```rust
43/// use dtt::dtt_print;
44///
45/// dtt_print!("Hello, World!");
46/// ```
47#[macro_export]
48macro_rules! dtt_parse {
49 ($input:expr) => {{
50 $crate::datetime::DateTime::parse($input)
51 }};
52}
53
54/// Prints the arguments to the console.
55///
56/// # Example
57///
58/// ```rust
59/// use dtt::dtt_print;
60///
61/// dtt_print!("Hello, World!");
62/// ```
63#[macro_export]
64macro_rules! dtt_print {
65 ($($arg:tt)*) => {
66 println!("{}", format_args!("{}", $($arg)*));
67 };
68}
69
70/// Creates a new vector of the given elements.
71///
72/// # Arguments
73///
74/// - `$($elem:expr),*`: The elements to be added to the vector.
75///
76/// # Example
77///
78/// ```rust
79/// use dtt::dtt_vec;
80///
81/// let v = dtt_vec!(1, 2, 3);
82/// assert_eq!(v, vec![1, 2, 3]);
83/// ```
84#[macro_export]
85macro_rules! dtt_vec {
86 ($($elem:expr),*) => {{
87 let mut v = Vec::new();
88 $(
89 v.push($elem);
90 )*
91 v
92 }};
93}
94
95/// Creates a new map of the given key-value pairs.
96///
97/// # Arguments
98///
99/// - `$($key:expr => $value:expr),*`: The key-value pairs to be added to the map.
100///
101/// # Example
102///
103/// ```rust
104/// use dtt::dtt_map;
105///
106/// let m = dtt_map!("one" => 1, "two" => 2);
107/// assert_eq!(m.get("one"), Some(&1));
108/// assert_eq!(m.get("two"), Some(&2));
109/// ```
110#[macro_export]
111macro_rules! dtt_map {
112 ($($key:expr => $value:expr),* $(,)?) => {{
113 use std::collections::HashMap;
114 let mut m = HashMap::new();
115 $(
116 m.insert($key, $value);
117 )*
118 m
119 }};
120}
121
122/// Asserts that the given expression is true.
123///
124/// If the expression is false, the macro panics with the message "Assertion failed!".
125///
126/// # Arguments
127///
128/// - `$cond:expr`: The condition to be asserted.
129/// - `$msg:expr` (optional): The message to display if the assertion fails.
130///
131/// # Example
132///
133/// ```rust
134/// use dtt::dtt_parse;
135/// use dtt::dtt_assert;
136///
137/// let is_valid = dtt_parse!("2020-02-29").is_ok();
138/// dtt_assert!(is_valid, "The date must be valid.");
139/// ```
140#[macro_export]
141macro_rules! dtt_assert {
142 ($cond:expr $(, $msg:expr)?) => {
143 if cfg!(debug_assertions) {
144 assert!($cond $(, $msg)?);
145 }
146 };
147}
148
149/// Generates a function that validates a given input string based on a specified type.
150///
151/// # Arguments
152///
153/// - `$name:ident`: The name of the validation function.
154/// - `$type:ty`: The type to validate.
155///
156/// # Example
157///
158/// ```rust
159/// use dtt::dtt_is_valid_function;
160///
161/// dtt_is_valid_function!(day, u8);
162/// assert!(is_valid_day("15"));
163/// assert!(!is_valid_day("32"));
164/// ```
165#[macro_export]
166macro_rules! dtt_is_valid_function {
167 ($name:ident, $type:ty) => {
168 ::pastey::paste! {
169 pub fn [<is_valid_ $name>](input: &str) -> bool {
170 if let Ok(parsed_val) = input.parse::<$type>() {
171 $crate::datetime::DateTime::[<is_valid_ $name>](&parsed_val.to_string())
172 } else {
173 false
174 }
175 }
176 }
177 };
178}
179
180/// Returns the minimum of the given values.
181///
182/// # Arguments
183///
184/// - `$x:expr`: The first value to be compared.
185/// - `$(, $y:expr)*`: Additional values to be compared.
186///
187/// # Example
188///
189/// ```rust
190/// use dtt::dtt_min;
191///
192/// assert_eq!(dtt_min!(10, 20, 30), 10);
193/// ```
194#[macro_export]
195macro_rules! dtt_min {
196 ($x:expr $(, $y:expr)*) => {{
197 let mut min = $x;
198 $(
199 let y = $y;
200 // This will cause a compile-time error if the types are not comparable
201 if std::cmp::PartialOrd::lt(&y, &min) { min = y; }
202 )*
203 min
204 }};
205}
206
207/// Returns the maximum of the given values.
208///
209/// # Arguments
210///
211/// - `$x:expr`: The first value to be compared.
212/// - `$(, $y:expr)*`: Additional values to be compared.
213///
214/// # Example
215///
216/// ```rust
217/// use dtt::dtt_max;
218///
219/// assert_eq!(dtt_max!(10, 20, 30), 30);
220/// ```
221#[macro_export]
222macro_rules! dtt_max {
223 ($x:expr $(, $y:expr)*) => {{
224 let mut max = $x;
225 $(
226 if max < $y { max = $y; }
227 )*
228 max
229 }};
230}
231
232/// Joins multiple strings into a single string.
233///
234/// # Arguments
235///
236/// - `$($s:expr),*`: The strings to be joined.
237///
238/// # Example
239///
240/// ```rust
241/// use dtt::dtt_join;
242///
243/// let s = dtt_join!("Hello", " ", "World");
244/// assert_eq!(s, "Hello World");
245/// ```
246#[macro_export]
247macro_rules! dtt_join {
248 ($($s:expr),*) => {{
249 let mut s = String::new();
250 $(
251 s += &$s;
252 )*
253 s
254 }};
255}
256
257/// Prints a vector of elements to the console.
258///
259/// # Arguments
260///
261/// - `$($v:expr),*`: The elements to be printed.
262///
263/// # Example
264///
265/// ```rust
266/// use dtt::dtt_print_vec;
267///
268/// dtt_print_vec!(&[1, 2, 3]);
269/// ```
270#[macro_export]
271macro_rules! dtt_print_vec {
272 ($($v:expr),*) => {{
273 for v in $($v),* {
274 println!("{}", v);
275 }
276 }};
277}
278
279/// Generates a function that validates a given input string based on a specified type.
280///
281/// # Arguments
282///
283/// - `$name:ident`: The name of the validation function.
284/// - `$type:ty`: The type to validate.
285///
286/// # Example
287///
288/// ```rust
289/// use dtt::dtt_is_valid_function;
290///
291/// dtt_is_valid_function!(day, u8);
292/// assert!(is_valid_day("15"));
293/// assert!(!is_valid_day("32"));
294/// ```
295#[macro_export]
296macro_rules! is_valid {
297 ($name:ident, $type:ty) => {
298 pub fn $name(input: &str) -> bool {
299 $crate::datetime::$name(input)
300 }
301 };
302}
303
304/// Creates a new `DateTime` instance with the specified timezone.
305///
306/// # Arguments
307///
308/// - `$tz:expr`: The timezone string.
309///
310/// # Example
311///
312/// ```rust
313/// use dtt::dtt_new_with_tz;
314///
315/// let dt = dtt_new_with_tz!("CET");
316/// assert_eq!(dt.offset().to_string(), "+01:00:00");
317/// ```
318#[macro_export]
319macro_rules! dtt_new_with_tz {
320 ($tz:expr) => {{
321 $crate::datetime::DateTime::new_with_tz($tz).expect(
322 "Failed to create DateTime with the specified timezone",
323 )
324 }};
325}
326
327/// Adds the specified number of days to the given `DateTime` instance.
328///
329/// # Arguments
330///
331/// - `$date:expr`: The `DateTime` instance.
332/// - `$days:expr`: The number of days to be added.
333///
334/// # Example
335///
336/// ```rust
337/// use dtt::{dtt_add_days, dtt_parse};
338///
339/// let dt = dtt_parse!("2023-01-01T12:00:00+00:00").unwrap();
340/// let future_date = dtt_add_days!(dt, 5).unwrap();
341/// assert_eq!(future_date.day(), 6);
342/// ```
343#[macro_export]
344macro_rules! dtt_add_days {
345 ($date:expr, $days:expr) => {
346 $date.add_days($days)
347 };
348}
349
350/// Subtracts the specified number of days from the given `DateTime` instance.
351///
352/// # Arguments
353///
354/// - `$date:expr`: The `DateTime` instance.
355/// - `$days:expr`: The number of days to be subtracted.
356///
357/// # Example
358///
359/// ```rust
360/// use dtt::{dtt_sub_days, dtt_parse};
361///
362/// let dt = dtt_parse!("2023-01-06T12:00:00+00:00").unwrap();
363/// let past_date = dtt_sub_days!(dt, 5).unwrap();
364/// assert_eq!(past_date.day(), 1);
365/// ```
366#[macro_export]
367macro_rules! dtt_sub_days {
368 ($date:expr, $days:expr) => {
369 $date.add_days(-$days)
370 };
371}
372
373/// A helper macro to calculate the difference between two `DateTime` instances.
374///
375/// # Parameters
376///
377/// - `$dt1:expr`: The first `DateTime` instance, as a string parseable as `i64`.
378/// - `$dt2:expr`: The second `DateTime` instance, as a string parseable as `i64`.
379/// - `$unit:expr`: The unit for the difference (seconds, days, etc.).
380///
381/// # Returns
382///
383/// `Some(i64)` containing the absolute difference in the specified unit, or
384/// `None` if either input cannot be parsed as `i64`.
385///
386/// # Example
387///
388/// ```rust
389/// use dtt::{dtt_diff, dtt_parse};
390///
391/// let dt1 = "1609459200"; // 2021-01-01 00:00:00 UTC
392/// let dt2 = "1609459230"; // 2021-01-01 00:00:30 UTC
393/// let seconds_difference = dtt_diff!(dt1, dt2, 1);
394/// assert_eq!(seconds_difference, Some(30i64));
395/// ```
396#[macro_export]
397macro_rules! dtt_diff {
398 ($dt1:expr, $dt2:expr, $unit:expr) => {{
399 match ($dt1.parse::<i64>(), $dt2.parse::<i64>()) {
400 (Ok(dt1), Ok(dt2)) => {
401 let difference =
402 if dt1 <= dt2 { dt2 - dt1 } else { dt1 - dt2 };
403 Some((difference / $unit).abs())
404 }
405 _ => None,
406 }
407 }};
408}
409
410/// Calculates the difference in seconds between two `DateTime` instances.
411///
412/// # Arguments
413///
414/// - `$dt1:expr`: The first `DateTime` instance.
415/// - `$dt2:expr`: The second `DateTime` instance.
416///
417/// # Example
418///
419/// ```rust
420/// use dtt::dtt_diff_seconds;
421/// use dtt::dtt_diff;
422///
423/// let dt1 = "1609459200"; // 2021-01-01 00:00:00 UTC
424/// let dt2 = "1609459230"; // 2021-01-01 00:00:30 UTC
425/// let seconds_difference = dtt_diff_seconds!(dt1, dt2);
426/// assert_eq!(seconds_difference, Some(30i64));
427/// ```
428#[macro_export]
429macro_rules! dtt_diff_seconds {
430 ($dt1:expr, $dt2:expr) => {
431 dtt_diff!($dt1, $dt2, 1)
432 };
433}
434
435/// Calculates the difference in days between two `DateTime` instances.
436///
437/// # Arguments
438///
439/// - `$dt1:expr`: The first `DateTime` instance.
440/// - `$dt2:expr`: The second `DateTime` instance.
441///
442/// # Example
443///
444/// ```rust
445/// use dtt::dtt_diff_days;
446/// use dtt::dtt_diff;
447///
448/// let dt1 = "1609459200"; // 2021-01-01 00:00:00 UTC
449/// let dt2 = "1609545600"; // 2021-01-02 00:00:00 UTC
450/// let days_difference = dtt_diff_days!(dt1, dt2);
451/// assert_eq!(days_difference, Some(1i64));
452/// ```
453#[macro_export]
454macro_rules! dtt_diff_days {
455 ($dt1:expr, $dt2:expr) => {
456 dtt_diff!($dt1, $dt2, 86400) // 86400 seconds in a day
457 };
458}
459
460/// Creates a copy of the provided `DateTime` object.
461///
462/// # Arguments
463///
464/// - `$dt:expr`: The `DateTime` object to be cloned.
465///
466/// # Example
467///
468/// ```rust
469/// use dtt::{dtt_clone, dtt_parse};
470///
471/// let dt = dtt_parse!("2023-01-01T12:00:00+00:00").unwrap();
472/// let cloned = dtt_clone!(dt);
473/// assert_eq!(dt.year(), cloned.year());
474/// ```
475#[macro_export]
476macro_rules! dtt_clone {
477 ($dt:expr) => {{
478 let dt = $dt;
479 dt.clone()
480 }};
481}
482
483/// Formats a `DateTime` object using the provided format string.
484///
485/// # Arguments
486///
487/// - `$dt:expr`: The `DateTime` object to be formatted.
488/// - `$format:expr`: The format string to use for formatting the `DateTime` object.
489///
490/// # Example
491///
492/// ```rust
493/// use dtt::{dtt_format, dtt_parse};
494/// use time::Month;
495///
496/// let dt = dtt_parse!("2023-01-01T12:00:00+00:00").unwrap();
497/// let formatted = dtt_format!(
498/// dt,
499/// "{year}-{month}-{day}T{hour}:{minute}:{second}.{microsecond}{offset_sign}{offset_hour}:{offset_minute}"
500/// );
501/// assert_eq!(formatted, "2023-01-01T12:00:00.000000+00:00");
502/// ```
503#[macro_export]
504macro_rules! dtt_format {
505 ($dt:expr, $format:expr) => {{
506 // Convert the month to a numeric value manually
507 let month_number = match $dt.month() {
508 Month::January => 1,
509 Month::February => 2,
510 Month::March => 3,
511 Month::April => 4,
512 Month::May => 5,
513 Month::June => 6,
514 Month::July => 7,
515 Month::August => 8,
516 Month::September => 9,
517 Month::October => 10,
518 Month::November => 11,
519 Month::December => 12,
520 };
521
522 format!(
523 $format,
524 day = format!("{:02}", $dt.day()),
525 hour = format!("{:02}", $dt.hour()),
526 microsecond = format!("{:06}", $dt.microsecond()),
527 minute = format!("{:02}", $dt.minute()),
528 month = format!("{:02}", month_number), // Use the numeric month
529 offset_sign = if $dt.offset().whole_seconds() >= 0 { "+" } else { "-" },
530 offset_hour = format!("{:02}", $dt.offset().whole_hours().abs()),
531 offset_minute = format!("{:02}", ($dt.offset().whole_seconds().abs() % 3600) / 60),
532 second = format!("{:02}", $dt.second()),
533 year = $dt.year(),
534 )
535 }};
536}