1pub mod approx;
4pub(crate) mod compat;
5pub mod parse;
6pub mod show;
7pub mod tm;
8
9use show::{parse_date_format, DateMode, DateModeType};
10use std::mem::size_of;
11use tm::{atoi_bytes, parse_timestamp_prefix, Timestamp};
12
13pub enum TestToolDateResult {
15 Output(Vec<String>),
16 Exit(i32),
17}
18
19pub fn test_tool_date(args: &[String]) -> Result<TestToolDateResult, String> {
21 if std::env::var_os("GIT_TEST_DATE_NOW").is_some() && std::env::var_os("TZ").is_none() {
24 std::env::set_var("TZ", "UTC");
25 unsafe extern "C" {
27 fn tzset();
28 }
29 unsafe {
30 tzset();
31 }
32 }
33 if args.is_empty() {
34 return Err("test-tool date: missing subcommand".to_string());
35 }
36 let sub = args[0].as_str();
37 let rest = &args[1..];
38
39 match sub {
40 "is64bit" => {
41 let code = if size_of::<Timestamp>() == 8 { 0 } else { 1 };
43 Ok(TestToolDateResult::Exit(code))
44 }
45 "time_t-is64bit" => {
46 let code = if size_of::<compat::time_t>() == 8 {
47 0
48 } else {
49 1
50 };
51 Ok(TestToolDateResult::Exit(code))
52 }
53 "relative" => {
54 let mut lines = Vec::new();
55 for a in rest {
56 let t: u64 = a
57 .parse()
58 .map_err(|_| format!("test-tool date relative: bad integer {a}"))?;
59 let s = show::show_date_relative(t, tm::get_time_sec());
60 lines.push(format!("{a} -> {s}"));
61 }
62 Ok(TestToolDateResult::Output(lines))
63 }
64 "human" => {
65 let mut lines = Vec::new();
66 for a in rest {
67 let t: u64 = a
68 .parse()
69 .map_err(|_| format!("test-tool date human: bad integer {a}"))?;
70 let mut mode = DateMode::from_type(DateModeType::Human);
71 let s = show::show_date(t, 0, &mut mode);
72 show::date_mode_release(&mut mode);
73 lines.push(format!("{a} -> {s}"));
74 }
75 Ok(TestToolDateResult::Output(lines))
76 }
77 "parse" => {
78 let mut lines = Vec::new();
79 for a in rest {
80 match parse::parse_date(a) {
81 Ok(ds) => {
82 let parts: Vec<&str> = ds.split_whitespace().collect();
83 if parts.len() >= 2 {
84 let t: u64 = parts[0].parse().map_err(|_| "bad parse output")?;
85 let tz = atoi_bytes(parts[1].as_bytes());
86 let mut mode = DateMode::from_type(DateModeType::Iso8601);
87 let out = show::show_date(t, tz, &mut mode);
88 show::date_mode_release(&mut mode);
89 lines.push(format!("{a} -> {out}"));
90 } else {
91 lines.push(format!("{a} -> bad"));
92 }
93 }
94 Err(()) => lines.push(format!("{a} -> bad")),
95 }
96 }
97 Ok(TestToolDateResult::Output(lines))
98 }
99 "approxidate" => {
100 let mut lines = Vec::new();
101 for a in rest {
102 let mut err = 0;
103 let t = approx::approxidate_careful(a, Some(&mut err));
104 let mut mode = DateMode::from_type(DateModeType::Iso8601);
105 let out = show::show_date(t, 0, &mut mode);
106 show::date_mode_release(&mut mode);
107 lines.push(format!("{a} -> {out}"));
108 }
109 Ok(TestToolDateResult::Output(lines))
110 }
111 "timestamp" => {
112 let mut lines = Vec::new();
113 for a in rest {
114 let mut err = 0;
115 let t = approx::approxidate_careful(a, Some(&mut err));
116 lines.push(if err == 0 {
117 format!("{a} -> {t}")
118 } else {
119 format!("{a} -> bad")
120 });
121 }
122 Ok(TestToolDateResult::Output(lines))
123 }
124 s if s.starts_with("show:") => {
125 let format = s.strip_prefix("show:").unwrap_or("");
126 let mut mode = parse_date_format(format).map_err(|e| e.to_string())?;
127 let mut lines = Vec::new();
128 for a in rest {
129 let b = a.as_bytes();
130 let (t, n) = parse_timestamp_prefix(b);
131 let mut rest_b = &b[n..];
132 while rest_b.first() == Some(&b' ') {
133 rest_b = &rest_b[1..];
134 }
135 let tz = atoi_bytes(rest_b);
136 let s = show::show_date(t, tz, &mut mode);
137 lines.push(format!("{a} -> {s}"));
138 }
139 show::date_mode_release(&mut mode);
140 Ok(TestToolDateResult::Output(lines))
141 }
142 _ => Err(format!("test-tool date: unknown subcommand '{sub}'")),
143 }
144}