1use chrono::{DateTime, Local, TimeDelta, TimeZone, Utc};
2use gettextrs::pgettext;
3use glib;
4use strum::EnumIter;
5
6use std::{
7 ffi::CStr,
8 fmt::{Display, Write},
9};
10
11#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumIter)]
12pub enum TimestampStyle {
13 Pretty,
14 PrettyUsec,
15 Utc,
16 UtcUsec,
17 Unix,
18 UnixUsec,
19}
20
21impl TimestampStyle {
22 pub fn code(&self) -> &str {
23 match self {
24 TimestampStyle::Pretty => "Pretty",
25 TimestampStyle::PrettyUsec => "Pretty usec",
26 TimestampStyle::Utc => "UTC",
27 TimestampStyle::UtcUsec => "UTC usec",
28 TimestampStyle::Unix => "Unix",
29 TimestampStyle::UnixUsec => "Unix usec",
30 }
31 }
32
33 pub fn label(&self) -> String {
34 match self {
35 TimestampStyle::Pretty => pgettext("pref time style", "Pretty"),
37 TimestampStyle::PrettyUsec => pgettext("pref time style", "Pretty usec"),
39 TimestampStyle::Utc => pgettext("pref time style", "UTC"),
41 TimestampStyle::UtcUsec => pgettext("pref time style", "UTC usec"),
43 TimestampStyle::Unix => pgettext("pref time style", "Unix"),
45 TimestampStyle::UnixUsec => pgettext("pref time style", "Unix usec"),
47 }
48 }
49
50 pub fn details(&self) -> String {
51 match self {
52 TimestampStyle::Pretty => pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS TZ"),
54
55 TimestampStyle::PrettyUsec => {
57 pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS.000000 TZ")
58 }
59
60 TimestampStyle::Utc => pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS UTC"),
62
63 TimestampStyle::UtcUsec => {
65 pgettext("pref time style", "Day YYYY-MM-DD HH:MM:SS.000000 UTC")
66 }
67
68 TimestampStyle::Unix => pgettext("pref time style", "Seconds since the epoch"),
70
71 TimestampStyle::UnixUsec => {
73 pgettext("pref time style", "Micro seconds since the epoch")
74 }
75 }
76 }
77
78 pub fn usec_formated(&self, timestamp_usec: u64) -> String {
79 match self {
80 TimestampStyle::Pretty => pretty(timestamp_usec, "%a, %d %b %Y %H:%M:%S"),
81 TimestampStyle::PrettyUsec => pretty(timestamp_usec, "%a, %d %b %Y %H:%M:%S%.6f"),
82 TimestampStyle::Utc => {
83 let since_local = get_date_utc(timestamp_usec);
84 since_local.format("%a, %d %b %Y %H:%M:%S %Z").to_string()
85 }
86 TimestampStyle::UtcUsec => {
87 let since_local = get_date_utc(timestamp_usec);
88 since_local
89 .format("%a, %d %b %Y %H:%M:%S%.6f %Z")
90 .to_string()
91 }
92 TimestampStyle::Unix => {
93 let timestamp_sec = timestamp_usec / USEC_PER_SEC;
94 format!("@{timestamp_sec}")
95 }
96 TimestampStyle::UnixUsec => {
97 format!("@{timestamp_usec}")
98 }
99 }
100 }
101}
102
103impl Display for TimestampStyle {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.write_str(&self.label())
106 }
107}
108
109impl From<glib::GString> for TimestampStyle {
110 fn from(level: glib::GString) -> Self {
111 level.as_str().into()
112 }
113}
114
115impl From<&str> for TimestampStyle {
116 fn from(style: &str) -> Self {
117 match style {
118 "UTC" => TimestampStyle::Utc,
119 "UTC usec" => TimestampStyle::UtcUsec,
120 "Unix" => TimestampStyle::Unix,
121 "Unix usec" => TimestampStyle::UnixUsec,
122 "Pretty usec" => TimestampStyle::PrettyUsec,
123 _ => TimestampStyle::Pretty,
124 }
125 }
126}
127
128impl From<i32> for TimestampStyle {
129 fn from(style: i32) -> Self {
130 match style {
131 1 => TimestampStyle::PrettyUsec,
132 2 => TimestampStyle::Utc,
133 3 => TimestampStyle::UtcUsec,
134 4 => TimestampStyle::Unix,
135 5 => TimestampStyle::UnixUsec,
136 _ => TimestampStyle::Pretty,
137 }
138 }
139}
140
141pub fn get_since_and_passed_time(
142 timestamp_usec: u64,
143 timestamp_style: TimestampStyle,
144) -> (String, String) {
145 let since = get_since_time(timestamp_usec, timestamp_style);
146
147 (since, format_timestamp_relative_full(timestamp_usec))
148}
149
150pub fn get_since_time(timestamp_usec: u64, timestamp_style: TimestampStyle) -> String {
151 timestamp_style.usec_formated(timestamp_usec)
152}
153
154fn pretty(timestamp_usec: u64, format: &str) -> String {
155 let since_local = get_date_local(timestamp_usec);
156
157 let time: libc::tm = localtime_or_gmtime_usec(timestamp_usec as i64, false);
158 let time_zone = unsafe { CStr::from_ptr(time.tm_zone) };
159 let time_zone = match time_zone.to_str() {
160 Ok(s) => s,
161 Err(_e) => &since_local.format("%Z").to_string(),
162 };
163
164 let formated_time = since_local.format(format).to_string();
165 format!("{formated_time} {time_zone}")
166}
167
168fn get_date_local(timestamp_usec: u64) -> DateTime<Local> {
169 let timestamp = get_date_utc(timestamp_usec);
170 DateTime::from(timestamp)
171}
172
173fn get_date_utc(timestamp_usec: u64) -> DateTime<Utc> {
174 match Utc.timestamp_micros(timestamp_usec as i64) {
175 chrono::offset::LocalResult::Single(a) => a,
176 chrono::offset::LocalResult::Ambiguous(a, _b) => a,
177 chrono::offset::LocalResult::None => panic!("timestamp_opt None"),
178 }
179}
180
181macro_rules! plur {
182 ($num:expr, $single:expr, $plur:expr) => {{ if $num > 1 { $plur } else { $single } }};
183}
184
185macro_rules! plur_year {
186 ($num:expr) => {{ plur!($num, "year", "years") }};
187}
188
189macro_rules! plur_month {
190 ($num:expr) => {{ plur!($num, "month", "months") }};
191}
192
193macro_rules! plur_week {
194 ($num:expr) => {{ plur!($num, "week", "weeks") }};
195}
196
197macro_rules! plur_day {
198 ($num:expr) => {{ plur!($num, "day", "days") }};
199}
200
201#[macro_export]
202macro_rules! swrite {
203
204 ($out:expr, $($y:expr),+) => {
205 if let Err(e) = write!($out, $($y), +) {
207 log::warn!("swrite error {:?}", e);
208 let s = format!($($y), +);
210 $out.push_str(&s);
211 }}
212}
213
214const SEC_PER_MONTH: u64 = 2629800;
215const SEC_PER_YEAR: u64 = 31_557_600;
216pub const USEC_PER_SEC: u64 = 1_000_000;
217const USEC_PER_YEAR: u64 = 31_557_600 * USEC_PER_SEC;
218const SEC_PER_DAY: u64 = 24 * SEC_PER_HOUR;
219const SEC_PER_WEEK: u64 = SEC_PER_DAY * 7;
220const SEC_PER_HOUR: u64 = 60 * 60;
221pub const SEC_PER_MINUTE: u64 = 60;
222pub const MSEC_PER_SEC: u64 = 1000;
223
224const USEC_PER_MONTH: u64 = SEC_PER_MONTH * USEC_PER_SEC;
225const USEC_PER_WEEK: u64 = SEC_PER_WEEK * USEC_PER_SEC;
226const USEC_PER_DAY: u64 = SEC_PER_DAY * USEC_PER_SEC;
227const USEC_PER_HOUR: u64 = SEC_PER_HOUR * USEC_PER_SEC;
228const USEC_PER_MINUTE: u64 = SEC_PER_MINUTE * USEC_PER_SEC;
229pub const USEC_PER_MSEC: u64 = 1000;
230pub const NSEC_PER_USEC: u64 = 1_000;
231
232fn format_timestamp_relative_full(timestamp_usec: u64) -> String {
233 let since_time = get_date_local(timestamp_usec);
234
235 let now = Local::now();
236
237 let delta = now.signed_duration_since(since_time);
238
239 format_timestamp_relative_full_delta(delta, true)
240}
241
242pub fn format_timestamp_relative_duration(
243 begin_timestamp_usec: u64,
244 finish_timestamp_usec: u64,
245) -> String {
246 let since_time = get_date_local(begin_timestamp_usec);
247
248 let to_time = get_date_local(finish_timestamp_usec);
249
250 let delta = to_time.signed_duration_since(since_time);
251
252 format_timestamp_relative_full_delta(delta, false)
253}
254
255fn format_timestamp_relative_full_delta(delta: TimeDelta, show_suffix: bool) -> String {
257 let is_ago = delta.num_seconds() >= 0;
258 let suffix = if show_suffix {
259 if is_ago { "ago" } else { "left" }
260 } else {
261 ""
262 };
263
264 let delta = delta.abs();
265
266 let d = delta.num_seconds() as u64;
267
268 let mut out = String::with_capacity(256);
269
270 if d >= SEC_PER_YEAR {
271 let years = d / SEC_PER_YEAR;
272 let months = (d % SEC_PER_YEAR) / SEC_PER_MONTH;
273
274 swrite!(
275 out,
276 "{} {} {} {} {suffix}",
277 years,
278 plur_year!(years),
279 months,
280 plur_month!(months)
281 );
282 } else if d >= SEC_PER_MONTH {
283 let months = d / SEC_PER_MONTH;
284 let days = (d % SEC_PER_MONTH) / SEC_PER_DAY;
285
286 swrite!(
287 out,
288 "{} {} {} {} {suffix}",
289 months,
290 plur_month!(months),
291 days,
292 plur_day!(days)
293 );
294 } else if d >= SEC_PER_WEEK {
295 let weeks = d / SEC_PER_WEEK;
296 let days = (d % SEC_PER_WEEK) / SEC_PER_DAY;
297
298 swrite!(
299 out,
300 "{} {} {} {} {suffix}",
301 weeks,
302 plur_week!(weeks),
303 days,
304 plur_day!(days)
305 );
306 } else if d >= 2 * SEC_PER_DAY {
307 let days = d / SEC_PER_DAY;
308 swrite!(out, "{days} {} {suffix}", plur_day!(days));
309 } else if d >= 25 * SEC_PER_HOUR {
310 let hours = (d - SEC_PER_DAY) / SEC_PER_HOUR;
311 swrite!(out, "1 {} {hours}h {suffix}", plur_day!(1));
312 } else if d >= 6 * SEC_PER_HOUR {
313 let hours = d / SEC_PER_HOUR;
314 swrite!(out, "{hours}h {suffix}");
315 } else if d >= SEC_PER_HOUR {
316 let hours = d / SEC_PER_HOUR;
317 let mins = (d % SEC_PER_HOUR) / SEC_PER_MINUTE;
318 swrite!(out, "{hours}h {mins}min {suffix}");
319 } else if d >= 5 * SEC_PER_MINUTE {
320 let mins = d / SEC_PER_MINUTE;
321 swrite!(out, "{mins}min {suffix}");
322 } else if d >= SEC_PER_MINUTE {
323 let mins = d / SEC_PER_MINUTE;
324 let sec = d % SEC_PER_MINUTE;
325 swrite!(out, "{mins}min {sec}s {suffix}");
326 } else if d > 0 {
327 swrite!(out, "{d}s {suffix}");
328 } else if let Some(d_us) = delta.num_microseconds() {
329 let d_us = d_us as u64;
330 if d_us >= USEC_PER_MSEC {
331 let ms = d_us / USEC_PER_MSEC;
332 swrite!(out, "{ms}ms {suffix}");
333 } else if d_us > 0 {
334 swrite!(out, "{d_us}μs {suffix}");
335 } else {
336 out.push_str("now");
337 }
338 } else {
339 out.push_str("now");
340 }
341
342 out
343}
344
345pub fn now_monotonic() -> u64 {
346 now(libc::CLOCK_MONOTONIC)
347}
348
349pub fn now_realtime() -> u64 {
350 now(libc::CLOCK_REALTIME)
351}
352
353fn now(clock_id: i32) -> u64 {
354 let mut time = libc::timespec {
355 tv_sec: 0,
356 tv_nsec: 0,
357 };
358 let ret = unsafe { libc::clock_gettime(clock_id, &mut time) };
359 assert!(ret == 0);
360
361 time.tv_sec as u64 * USEC_PER_SEC + time.tv_nsec as u64 / NSEC_PER_USEC
362}
363pub const U64MAX: u64 = 18_446_744_073_709_551_615; pub fn format_timespan(mut duration: u64, accuracy: u64) -> String {
365 let mut out = String::with_capacity(64);
366
367 if duration == U64MAX {
368 out.push_str("infinity");
369 return out;
370 }
371
372 if duration == 0 {
373 out.push('0');
374 return out;
375 }
376
377 const TABLE: [(&str, u64); 9] = [
378 ("y", USEC_PER_YEAR),
379 ("month", USEC_PER_MONTH),
380 ("w", USEC_PER_WEEK),
381 ("d", USEC_PER_DAY),
382 ("h", USEC_PER_HOUR),
383 ("min", USEC_PER_MINUTE),
384 ("s", USEC_PER_SEC),
385 ("ms", USEC_PER_MSEC),
386 ("μs", 1),
387 ];
388
389 let mut something = false;
390 let mut done = false;
391
392 for (suffix, unit_magnitute_in_usec) in TABLE {
393 if duration == 0 {
394 break;
395 }
396
397 if duration < accuracy && something {
398 break;
399 }
400
401 if duration < unit_magnitute_in_usec {
402 continue;
403 }
404
405 let a = duration / unit_magnitute_in_usec;
406 let mut b = duration % unit_magnitute_in_usec;
407
408 if duration < USEC_PER_MINUTE && b > 0 {
409 let mut zero_padding = 0;
410
411 let mut cc = unit_magnitute_in_usec;
412 while cc > 1 {
413 zero_padding += 1;
414 cc /= 10;
415 }
416
417 let mut cc = accuracy;
418 while cc > 1 {
419 b /= 10;
420 zero_padding -= 1;
421 cc /= 10;
422 }
423
424 if zero_padding > 0 {
425 let space_padding = if out.is_empty() { "" } else { " " };
426 swrite!(out, "{space_padding}{a}.{:0zero_padding$}{suffix}", b);
427
428 duration = 0;
429 done = true;
430 }
431 }
432
433 if !done {
435 let pad = if out.is_empty() { "" } else { " " };
436 swrite!(out, "{pad}{a}{suffix}");
437 duration = b;
438 }
439
440 something = true;
441 }
442
443 out
444}
445
446fn localtime_or_gmtime_usec(time_usec: i64, utc: bool) -> libc::tm {
447 let layout = std::alloc::Layout::new::<libc::tm>();
448
449 let time_usec_ptr: *const libc::time_t = &(time_usec as libc::time_t);
450
451 unsafe {
452 let returned_time_struct = std::alloc::alloc(layout) as *mut libc::tm;
453
454 if utc {
455 libc::gmtime_r(time_usec_ptr, returned_time_struct);
456 } else {
457 libc::localtime_r(time_usec_ptr, returned_time_struct);
458 }
459
460 *returned_time_struct
461 }
462}
463
464#[cfg(test)]
465mod tests {
466
467 use std::ffi::CStr;
468
469 use chrono::{Duration, TimeDelta};
470
471 use super::*;
472
473 #[test]
474 fn test_since() {
475 let since = get_since_and_passed_time(1727116768682604, TimestampStyle::Pretty);
476 println!("since {since:?}");
477 let since = get_since_and_passed_time(1727116768682442, TimestampStyle::Pretty);
478 println!("since {since:?}");
479 let since = get_since_and_passed_time(1727116768682435, TimestampStyle::Pretty);
480 println!("since {since:?}");
481 let since = get_since_and_passed_time(1727413184243915, TimestampStyle::Pretty);
482 println!("since {since:?}");
483 }
484
485 #[test]
486 fn test_duration() {
487 let now = Local::now();
488
489 let tomorrow_midnight = (now + Duration::days(1))
490 .date_naive()
491 .and_hms_opt(0, 0, 0)
492 .unwrap();
493
494 let tomorrow_midnight_local = tomorrow_midnight
495 .and_local_timezone(Local)
496 .earliest()
497 .unwrap();
498
499 let duration = tomorrow_midnight_local
500 .signed_duration_since(now)
501 .to_std()
502 .unwrap();
503
504 println!("Duration between {now:?} and {tomorrow_midnight:?}: {duration:?}");
505 }
506
507 #[test]
508 fn test_duration2() {
509 let prev = get_date_local(1727116768682604);
510
511 let now = Local::now();
512
513 let duration = now.signed_duration_since(prev);
514
515 println!(
516 "Duration between {:?} and {:?}: {:?}",
517 prev,
518 now,
519 duration.to_std().unwrap()
520 );
521
522 println!(
523 "{} ago",
524 format_timestamp_relative_full_delta(duration, true)
525 )
526 }
527
528 #[test]
529 fn most_significant_duration_test() {
530 let a = TimeDelta::minutes(1) + TimeDelta::seconds(30);
531 println!("{a:?}");
532 println!("{:?}", format_timestamp_relative_full_delta(a, true));
533
534 let b = TimeDelta::minutes(2);
535 println!("{b:?}");
536 println!("{:?}", format_timestamp_relative_full_delta(b, true));
537
538 let a = TimeDelta::minutes(10) + TimeDelta::seconds(30);
539 println!("{a:?}");
540 println!("{:?}", format_timestamp_relative_full_delta(a, true));
541
542 let a = TimeDelta::minutes(9) + TimeDelta::seconds(30);
543 println!("{a:?}");
544 println!("{:?}", format_timestamp_relative_full_delta(a, true));
545 }
546
547 #[test]
548 fn test_duration_() {
549 let enter = get_date_local(1727500436962647);
555 let exit = get_date_local(1727501504134907);
556
557 let d = exit.signed_duration_since(enter);
558 println!(
559 "{:?} {:?}",
560 format_timestamp_relative_full_delta(d, true),
561 d
562 );
563 }
564
565 #[test]
566 fn test_format_timestamp_relative_full() {
567 const SEC_PER_YEAR_I: i64 = SEC_PER_YEAR as i64;
569 const SEC_PER_MONTH_I: i64 = SEC_PER_MONTH as i64;
570 const SEC_PER_DAY_I: i64 = SEC_PER_DAY as i64;
571 const SEC_PER_WEEK_I: i64 = SEC_PER_WEEK as i64;
572
573 const USEC_PER_SEC: i64 = 1_000_000;
574 const USEC_PER_YEAR_I: i64 = SEC_PER_YEAR_I * USEC_PER_SEC;
575 const USEC_PER_MONTH: i64 = SEC_PER_MONTH_I * USEC_PER_SEC;
576 const USEC_PER_DAY: i64 = SEC_PER_DAY_I * USEC_PER_SEC;
577 const USEC_PER_WEEK: i64 = SEC_PER_WEEK_I * USEC_PER_SEC;
578
579 let tests: Vec<(i64, &str)> = vec![
580 ((USEC_PER_YEAR_I + USEC_PER_MONTH), "1 year 1 month ago"),
581 (
582 -(USEC_PER_YEAR_I + (1.5 * USEC_PER_MONTH as f64) as i64),
583 "1 year 1 month left",
584 ),
585 (
586 (USEC_PER_YEAR_I + (2 * USEC_PER_MONTH)),
587 "1 year 2 months ago",
588 ),
589 (
590 (2 * USEC_PER_YEAR_I + USEC_PER_MONTH),
591 "2 years 1 month ago",
592 ),
593 (
594 (2 * USEC_PER_YEAR_I + 2 * USEC_PER_MONTH),
595 "2 years 2 months ago",
596 ),
597 ((USEC_PER_MONTH + USEC_PER_DAY), "1 month 1 day ago"),
598 ((USEC_PER_MONTH + 2 * USEC_PER_DAY), "1 month 2 days ago"),
599 ((2 * USEC_PER_MONTH + USEC_PER_DAY), "2 months 1 day ago"),
600 (
601 (2 * USEC_PER_MONTH + 2 * USEC_PER_DAY),
602 "2 months 2 days ago",
603 ),
604 ((USEC_PER_WEEK + USEC_PER_DAY), "1 week 1 day ago"),
606 ((USEC_PER_WEEK + 2 * USEC_PER_DAY), "1 week 2 days ago"),
607 ((2 * USEC_PER_WEEK + USEC_PER_DAY), "2 weeks 1 day ago"),
608 ((2 * USEC_PER_WEEK + 2 * USEC_PER_DAY), "2 weeks 2 days ago"),
609 (3 * 1000, "3ms ago"),
610 (2, "2μs ago"),
611 (0, "now"),
612 ];
613
614 for (time_us, time_output) in tests {
615 let delta = TimeDelta::new(
616 time_us / USEC_PER_SEC,
617 (time_us % USEC_PER_SEC) as u32 * 1000,
618 )
619 .expect("Time delta not supposed to have bondary issues");
620
621 let value = format_timestamp_relative_full_delta(delta, true);
622 assert_eq!(value, time_output);
623 }
624 }
625
626 #[test]
627 fn test_format_timespan() {
628 println!("{:?}", format_timespan(4 * USEC_PER_YEAR, MSEC_PER_SEC));
629 println!("{:?}", format_timespan(4 * USEC_PER_MONTH, MSEC_PER_SEC));
630 println!("{:?}", format_timespan(4 * USEC_PER_WEEK, MSEC_PER_SEC));
631 println!("{:?}", format_timespan(4 * USEC_PER_DAY, MSEC_PER_SEC));
632 println!("{:?}", format_timespan(4 * USEC_PER_HOUR, MSEC_PER_SEC));
633 println!("{:?}", format_timespan(4 * USEC_PER_MINUTE, MSEC_PER_SEC));
634 println!("{:?}", format_timespan(4 * USEC_PER_SEC, MSEC_PER_SEC));
635 println!("{:?}", format_timespan(4 * USEC_PER_MSEC, MSEC_PER_SEC));
636
637 println!(
638 "{:?}",
639 format_timespan(
640 4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC,
641 MSEC_PER_SEC
642 )
643 );
644 println!(
645 "{:?}",
646 format_timespan(
647 4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 4 * USEC_PER_MSEC,
648 MSEC_PER_SEC
649 )
650 );
651 println!(
652 "{:?}",
653 format_timespan(
654 4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 4 * USEC_PER_MSEC + 4,
655 MSEC_PER_SEC
656 )
657 );
658
659 println!(
660 "{:?}",
661 format_timespan(
662 4 * USEC_PER_DAY + 4 * USEC_PER_HOUR + 4 * USEC_PER_SEC + 50 * USEC_PER_MSEC,
663 MSEC_PER_SEC
664 )
665 );
666 }
667
668 #[test]
669 fn test_localtime_or_gmtime_usec() {
670 let asdf = localtime_or_gmtime_usec(0, false);
671 println!("time {asdf:?}");
672
673 let c_str = unsafe { CStr::from_ptr(asdf.tm_zone) };
674 println!("time zone {c_str:?}");
675
676 let asdf = localtime_or_gmtime_usec(0, true);
677 println!("time {asdf:?}");
678
679 let c_str = unsafe { CStr::from_ptr(asdf.tm_zone) };
680 println!("time zone {c_str:?}");
681 }
682
683 #[test]
684 fn test_date_format() {
685 let date = Local::now();
686
687 println!("{}", date.format("%a, %d %b %Y %H:%M:%S %Z"));
689 println!("{}", date.to_rfc2822());
690 println!("{}", date.to_rfc3339());
691
692 let date = Utc::now();
693
694 println!("{}", date.format("%a, %d %b %Y %H:%M:%S %Z"));
696 println!("{}", date.to_rfc2822());
697 println!("{}", date.to_rfc3339());
698 }
699
700 #[test]
701 fn test_casting() {
702 let a: i64 = 0x0000_FFFF_FFFF_FFFF;
703
704 let b: i32 = a as i32;
705
706 println!("a {a:#x} b {b:#x}");
707 }
708}