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}