compile_time/
lib.rs

1//! This crate provides macros for getting compile time information.
2//!
3//! You can get the compile time either as
4//! [`time::Date`](time::Date), [`time::Time`](time::Time),
5//! [`time::OffsetDateTime`](time::OffsetDateTime), string, or UNIX timestamp.
6//!
7//! You can get the Rust compiler version either as
8//! [`semver::Version`](semver::Version) or string,
9//! and the individual version parts as integer literals or strings, respectively.
10//!
11//! # Example
12//!
13//! ```
14//! let compile_datetime = compile_time::datetime_str!();
15//! let rustc_version = compile_time::rustc_version_str!();
16//!
17//! println!("Compiled using Rust {rustc_version} on {compile_datetime}.");
18//! ```
19
20extern crate proc_macro;
21
22use once_cell::sync::Lazy;
23use proc_macro::TokenStream;
24use quote::{format_ident, quote, ToTokens};
25use time::{macros::format_description, OffsetDateTime};
26
27static COMPILE_TIME: Lazy<OffsetDateTime> = Lazy::new(OffsetDateTime::now_utc);
28static RUSTC_VERSION: Lazy<rustc_version::Result<rustc_version::Version>> = Lazy::new(rustc_version::version);
29
30/// Compile date as `time::Date`.
31///
32/// # Example
33///
34/// ```
35/// const COMPILE_DATE: time::Date = compile_time::date!();
36///
37/// let year = COMPILE_DATE.year();
38/// let month = COMPILE_DATE.month();
39/// let day = COMPILE_DATE.day();
40/// println!("Compiled on {month} {day}, {year}.");
41/// ```
42#[proc_macro]
43pub fn date(_item: TokenStream) -> TokenStream {
44  let date = COMPILE_TIME.date();
45
46  let year = date.year();
47  let month = format_ident!("{}", format!("{:?}", date.month()));
48  let day = date.day();
49
50  quote! {
51    match ::time::Date::from_calendar_date(#year, ::time::Month::#month, #day) {
52      Ok(date) => date,
53      _ => ::core::unreachable!(),
54    }
55  }
56  .into()
57}
58
59/// Compile date as `&'static str` in `yyyy-MM-dd` format.
60///
61/// # Example
62///
63/// ```
64/// const COMPILE_DATE: time::Date = compile_time::date!();
65///
66/// let year = COMPILE_DATE.year();
67/// let month: u8 = COMPILE_DATE.month().into();
68/// let day = COMPILE_DATE.day();
69/// let date_string = format!("{year:04}-{month:02}-{day:02}");
70///
71/// assert_eq!(compile_time::date_str!(), date_string);
72/// ```
73#[proc_macro]
74pub fn date_str(_item: TokenStream) -> TokenStream {
75  let date = COMPILE_TIME.date();
76
77  let fmt = format_description!("[year]-[month]-[day]");
78  let date_str = date.format(&fmt).unwrap();
79
80  quote! { #date_str }.into()
81}
82
83/// Compile time as `time::Time`.
84///
85/// # Example
86///
87/// ```
88/// const COMPILE_TIME: time::Time = compile_time::time!();
89///
90/// let hour = COMPILE_TIME.hour();
91/// let minute = COMPILE_TIME.minute();
92/// let second = COMPILE_TIME.second();
93/// println!("Compiled at {hour:02}:{minute:02}:{second:02}.");
94/// ```
95#[proc_macro]
96pub fn time(_item: TokenStream) -> TokenStream {
97  let time = COMPILE_TIME.time();
98
99  let hour = time.hour();
100  let minute = time.minute();
101  let second = time.second();
102
103  quote! {
104    match ::time::Time::from_hms(#hour, #minute, #second) {
105      Ok(time) => time,
106      _ => ::core::unreachable!(),
107    }
108  }
109  .into()
110}
111
112/// Compile time as `&'static str` in `hh:mm:ss` format.
113///
114/// # Example
115///
116/// ```
117/// const COMPILE_TIME: time::Time = compile_time::time!();
118///
119/// let hour = COMPILE_TIME.hour();
120/// let minute = COMPILE_TIME.minute();
121/// let second = COMPILE_TIME.second();
122/// let time_string = format!("{hour:02}:{minute:02}:{second:02}");
123///
124/// assert_eq!(compile_time::time_str!(), time_string);
125/// ```
126#[proc_macro]
127pub fn time_str(_item: TokenStream) -> TokenStream {
128  let time = COMPILE_TIME.time();
129
130  let fmt = format_description!("[hour]:[minute]:[second]");
131  let time_str = time.format(&fmt).unwrap();
132
133  quote! { #time_str }.into()
134}
135
136/// Compile date and time as `time::OffsetDateTime`.
137///
138/// # Example
139///
140/// ```
141/// const COMPILE_DATETIME: time::OffsetDateTime = compile_time::datetime!();
142///
143/// let year = COMPILE_DATETIME.year();
144/// let month = COMPILE_DATETIME.month();
145/// let day = COMPILE_DATETIME.day();
146/// let hour = COMPILE_DATETIME.hour();
147/// let minute = COMPILE_DATETIME.minute();
148/// let second = COMPILE_DATETIME.second();
149/// println!("Compiled at {hour:02}:{minute:02}:{second:02} on {month} {day}, {year}.");
150/// #
151/// # // Evaluation is only done once.
152/// # std::thread::sleep(std::time::Duration::from_secs(1));
153/// # assert_eq!(COMPILE_DATETIME, compile_time::datetime!());
154/// #
155/// # // Additional sanity check.
156/// # let now = time::OffsetDateTime::now_utc();
157/// # let yesterday = now.saturating_sub(time::Duration::days(1));
158/// # assert!(COMPILE_DATETIME > yesterday);
159/// # assert!(COMPILE_DATETIME < now);
160/// ```
161#[proc_macro]
162pub fn datetime(_item: TokenStream) -> TokenStream {
163  let datetime = *COMPILE_TIME;
164
165  let year = datetime.year();
166  let month = format_ident!("{}", format!("{:?}", datetime.month()));
167  let day = datetime.day();
168
169  let hour = datetime.hour();
170  let minute = datetime.minute();
171  let second = datetime.second();
172
173  let date = quote! {
174    match ::time::Date::from_calendar_date(#year, ::time::Month::#month, #day) {
175      Ok(date) => date,
176      _ => ::core::unreachable!(),
177    }
178  };
179
180  let time = quote! {
181    match ::time::Time::from_hms(#hour, #minute, #second) {
182      Ok(time) => time,
183      _ => ::core::unreachable!(),
184    }
185  };
186
187  quote! {
188    ::time::PrimitiveDateTime::new(#date, #time).assume_utc()
189  }
190  .into()
191}
192
193/// Compile time as `&'static str` in `yyyy-MM-ddThh:mm:ssZ` format.
194///
195/// # Example
196///
197/// ```
198/// const COMPILE_DATE_STRING: &str = compile_time::date_str!();
199/// const COMPILE_TIME_STRING: &str = compile_time::time_str!();
200///
201/// let datetime_string = format!("{COMPILE_DATE_STRING}T{COMPILE_TIME_STRING}Z");
202/// assert_eq!(compile_time::datetime_str!(), datetime_string);
203/// ```
204#[proc_macro]
205pub fn datetime_str(_item: TokenStream) -> TokenStream {
206  let datetime = *COMPILE_TIME;
207
208  let fmt = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z");
209  let datetime_str = datetime.format(&fmt).unwrap();
210
211  quote! { #datetime_str }.into()
212}
213
214/// Compile date and time as UNIX timestamp in seconds.
215///
216/// # Example
217///
218/// ```
219/// const COMPILE_DATETIME: time::OffsetDateTime = compile_time::datetime!();
220///
221/// assert_eq!(compile_time::unix!(), COMPILE_DATETIME.unix_timestamp());
222/// ```
223#[proc_macro]
224pub fn unix(_item: TokenStream) -> TokenStream {
225  let datetime = *COMPILE_TIME;
226
227  let unix_timestamp = proc_macro2::Literal::i64_unsuffixed(datetime.unix_timestamp());
228
229  quote! {
230    #unix_timestamp
231  }
232  .into()
233}
234
235/// Rust compiler version as `semver::Version`.
236///
237/// # Example
238///
239/// ```
240/// let rustc_version: semver::Version = compile_time::rustc_version!();
241/// assert_eq!(rustc_version, rustc_version::version().unwrap());
242/// ```
243#[proc_macro]
244pub fn rustc_version(_item: TokenStream) -> TokenStream {
245  let rustc_version::Version { major, minor, patch, pre, build } = match &*RUSTC_VERSION {
246    Ok(rustc_version) => rustc_version,
247    Err(err) => panic!("Failed to get version: {}", err),
248  };
249
250  let pre = if pre.is_empty() {
251    quote! { ::semver::Prerelease::EMPTY }
252  } else {
253    let pre = pre.as_str();
254    quote! {
255      if let Ok(pre) = ::semver::Prerelease::new(#pre) {
256        pre
257      } else {
258        ::core::unreachable!()
259      }
260    }
261  };
262
263  let build = if build.is_empty() {
264    quote! { ::semver::BuildMetadata::EMPTY }
265  } else {
266    let build = build.as_str();
267    quote! {
268      if let Ok(build) = ::semver::BuildMetadata::new(#build) {
269        build
270      } else {
271        ::core::unreachable!()
272      }
273    }
274  };
275
276  quote! {
277    ::semver::Version {
278      major: #major,
279      minor: #minor,
280      patch: #patch,
281      pre: #pre,
282      build: #build,
283    }
284  }
285  .into()
286}
287
288/// Rust compiler version as `&'static str`.
289///
290/// # Example
291///
292/// ```
293/// const RUSTC_VERSION_STRING: &str = compile_time::rustc_version_str!();
294/// assert_eq!(RUSTC_VERSION_STRING, compile_time::rustc_version_str!());
295/// ```
296#[proc_macro]
297pub fn rustc_version_str(_item: TokenStream) -> TokenStream {
298  let rustc_version = match &*RUSTC_VERSION {
299    Ok(rustc_version) => rustc_version,
300    Err(err) => panic!("Failed to get version: {}", err),
301  };
302
303  let rustc_version_string = rustc_version.to_string();
304  quote! { #rustc_version_string }.into()
305}
306
307/// Rust compiler major version as integer literal.
308///
309/// # Example
310///
311/// ```
312/// let rustc_version: semver::Version = compile_time::rustc_version!();
313/// assert_eq!(rustc_version.major, compile_time::rustc_version_major!());
314/// ```
315#[proc_macro]
316pub fn rustc_version_major(_item: TokenStream) -> TokenStream {
317  let major = match &*RUSTC_VERSION {
318    Ok(rustc_version) => rustc_version.major,
319    Err(err) => panic!("Failed to get version: {}", err),
320  };
321
322  proc_macro2::Literal::u64_unsuffixed(major).to_token_stream().into()
323}
324
325/// Rust compiler minor version as integer literal.
326///
327/// # Example
328///
329/// ```
330/// let rustc_version: semver::Version = compile_time::rustc_version!();
331/// assert_eq!(rustc_version.minor, compile_time::rustc_version_minor!());
332/// ```
333#[proc_macro]
334pub fn rustc_version_minor(_item: TokenStream) -> TokenStream {
335  let minor = match &*RUSTC_VERSION {
336    Ok(rustc_version) => rustc_version.minor,
337    Err(err) => panic!("Failed to get version: {}", err),
338  };
339
340  proc_macro2::Literal::u64_unsuffixed(minor).to_token_stream().into()
341}
342
343/// Rust compiler patch version as integer literal.
344///
345/// # Example
346///
347/// ```
348/// let rustc_version: semver::Version = compile_time::rustc_version!();
349/// assert_eq!(rustc_version.minor, compile_time::rustc_version_minor!());
350/// ```
351#[proc_macro]
352pub fn rustc_version_patch(_item: TokenStream) -> TokenStream {
353  let patch = match &*RUSTC_VERSION {
354    Ok(rustc_version) => rustc_version.patch,
355    Err(err) => panic!("Failed to get version: {}", err),
356  };
357
358  proc_macro2::Literal::u64_unsuffixed(patch).to_token_stream().into()
359}
360
361/// Rust compiler pre version as `&'static str`.
362///
363/// # Example
364///
365/// ```
366/// let rustc_version: semver::Version = compile_time::rustc_version!();
367/// assert_eq!(rustc_version.pre.as_str(), compile_time::rustc_version_pre!());
368/// ```
369#[proc_macro]
370pub fn rustc_version_pre(_item: TokenStream) -> TokenStream {
371  let pre = match &*RUSTC_VERSION {
372    Ok(rustc_version) => rustc_version.pre.as_str(),
373    Err(err) => panic!("Failed to get version: {}", err),
374  };
375
376  quote! { #pre }.into()
377}
378
379/// Rust compiler build version as `&'static str`.
380///
381/// # Example
382///
383/// ```
384/// let rustc_version: semver::Version = compile_time::rustc_version!();
385/// assert_eq!(rustc_version.build.as_str(), compile_time::rustc_version_build!());
386/// ```
387#[proc_macro]
388pub fn rustc_version_build(_item: TokenStream) -> TokenStream {
389  let build = match &*RUSTC_VERSION {
390    Ok(rustc_version) => rustc_version.build.as_str(),
391    Err(err) => panic!("Failed to get version: {}", err),
392  };
393
394  quote! { #build }.into()
395}