compile_time_macros/lib.rs
1//! This crate provides macros for getting compile time information.
2//!
3//! This crate should not be used directly; use [`compile-time`](https://docs.rs/compile-time) instead.
4
5extern crate proc_macro;
6
7use proc_macro::TokenStream;
8use quote::quote;
9
10#[cfg(feature = "command")]
11mod command;
12#[cfg(feature = "time")]
13mod time;
14#[cfg(feature = "version")]
15mod version;
16
17/// Returns the compile date as [`time::Date`](time03::Date).
18///
19/// By default, the returned date is in UTC, call `date!(local)`
20/// to return the local date.
21///
22/// # Example
23///
24/// Get the UTC date:
25///
26/// ```
27/// # use time03 as time;
28/// const COMPILE_DATE: time::Date = compile_time::date!();
29///
30/// let year = COMPILE_DATE.year();
31/// let month = COMPILE_DATE.month();
32/// let day = COMPILE_DATE.day();
33/// println!("Compiled on {month} {day}, {year}.");
34/// ```
35///
36/// Get the date for the local time zone:
37///
38/// ```
39/// # use time03 as time;
40/// const COMPILE_DATE: time::Date = compile_time::date!(local);
41///
42/// let year = COMPILE_DATE.year();
43/// let month = COMPILE_DATE.month();
44/// let day = COMPILE_DATE.day();
45/// println!("Compiled on {month} {day}, {year}.");
46/// ```
47#[cfg(feature = "time")]
48#[proc_macro]
49pub fn date(input: TokenStream) -> TokenStream {
50 use quote::format_ident;
51 use syn::parse_macro_input;
52 use time::TimeInput;
53
54 let input = parse_macro_input!(input as TimeInput);
55 let now = match input.now() {
56 Ok(now) => now,
57 Err(err) => return err.into_compile_error().into(),
58 };
59
60 let date = now.date();
61
62 let year = date.year();
63 let month = format_ident!("{}", format!("{:?}", date.month()));
64 let day = date.day();
65
66 let time_prefix = quote! { ::compile_time::__re_exports::time };
67 quote! {
68 const {
69 match #time_prefix::Date::from_calendar_date(#year, #time_prefix::Month::#month, #day) {
70 Ok(date) => date,
71 _ => ::core::unreachable!(),
72 }
73 }
74 }
75 .into()
76}
77
78/// Returns the compile date as `&'static str` in `yyyy-MM-dd` format.
79///
80/// By default, the returned date is in UTC, call `date_str!(local)`
81/// to return the local date.
82///
83/// # Example
84///
85/// Get the UTC date:
86///
87/// ```
88/// # use time03 as time;
89/// const COMPILE_DATE: time::Date = compile_time::date!();
90///
91/// let year = COMPILE_DATE.year();
92/// let month: u8 = COMPILE_DATE.month().into();
93/// let day = COMPILE_DATE.day();
94/// let date_string = format!("{year:04}-{month:02}-{day:02}");
95///
96/// assert_eq!(compile_time::date_str!(), date_string);
97/// ```
98///
99/// Get the date for the local time zone:
100///
101/// ```
102/// # use time03 as time;
103/// const COMPILE_DATE: time::Date = compile_time::date!(local);
104///
105/// let year = COMPILE_DATE.year();
106/// let month: u8 = COMPILE_DATE.month().into();
107/// let day = COMPILE_DATE.day();
108/// let date_string = format!("{year:04}-{month:02}-{day:02}");
109///
110/// assert_eq!(compile_time::date_str!(local), date_string);
111/// ```
112#[cfg(feature = "time")]
113#[proc_macro]
114pub fn date_str(input: TokenStream) -> TokenStream {
115 use syn::parse_macro_input;
116 use time03::format_description;
117
118 use time::TimeInput;
119
120 let input = parse_macro_input!(input as TimeInput);
121 let now = match input.now() {
122 Ok(now) => now,
123 Err(err) => return err.into_compile_error().into(),
124 };
125
126 let date = now.date();
127
128 let fmt = format_description::parse_borrowed::<3>("[year]-[month]-[day]").unwrap();
129 let date_str = date.format(&fmt).unwrap();
130
131 quote! { #date_str }.into()
132}
133
134/// Returns the compile time as [`time::Time`](time03::Time).
135///
136/// By default, the returned time is in UTC, call `time!(local)`
137/// to return the local time.
138///
139/// # Example
140///
141/// Get the UTC time:
142///
143/// ```
144/// # use time03 as time;
145/// const COMPILE_TIME: time::Time = compile_time::time!();
146///
147/// let hour = COMPILE_TIME.hour();
148/// let minute = COMPILE_TIME.minute();
149/// let second = COMPILE_TIME.second();
150/// println!("Compiled at {hour:02}:{minute:02}:{second:02}.");
151/// ```
152///
153/// Get the time for the local time zone:
154///
155/// ```
156/// # use time03 as time;
157/// const COMPILE_TIME: time::Time = compile_time::time!(local);
158///
159/// let hour = COMPILE_TIME.hour();
160/// let minute = COMPILE_TIME.minute();
161/// let second = COMPILE_TIME.second();
162/// println!("Compiled at {hour:02}:{minute:02}:{second:02}.");
163/// ```
164#[cfg(feature = "time")]
165#[proc_macro]
166pub fn time(input: TokenStream) -> TokenStream {
167 use syn::parse_macro_input;
168
169 use time::TimeInput;
170
171 let input = parse_macro_input!(input as TimeInput);
172 let now = match input.now() {
173 Ok(now) => now,
174 Err(err) => return err.into_compile_error().into(),
175 };
176
177 let time = now.time();
178
179 let hour = time.hour();
180 let minute = time.minute();
181 let second = time.second();
182
183 let time_prefix = quote! { ::compile_time::__re_exports::time };
184 quote! {
185 const {
186 match #time_prefix::Time::from_hms(#hour, #minute, #second) {
187 Ok(time) => time,
188 _ => ::core::unreachable!(),
189 }
190 }
191 }
192 .into()
193}
194
195/// Returns the compile time as `&'static str` in `hh:mm:ss` format.
196///
197/// By default, the returned time is in UTC, call `time_str!(local)`
198/// to return the local time.
199///
200/// # Example
201///
202/// Get the UTC time:
203///
204/// ```
205/// # use time03 as time;
206/// const COMPILE_TIME: time::Time = compile_time::time!();
207///
208/// let hour = COMPILE_TIME.hour();
209/// let minute = COMPILE_TIME.minute();
210/// let second = COMPILE_TIME.second();
211/// let time_string = format!("{hour:02}:{minute:02}:{second:02}");
212///
213/// assert_eq!(compile_time::time_str!(), time_string);
214/// ```
215///
216/// Get the time for the local time zone:
217///
218/// ```
219/// # use time03 as time;
220/// const COMPILE_TIME: time::Time = compile_time::time!(local);
221///
222/// let hour = COMPILE_TIME.hour();
223/// let minute = COMPILE_TIME.minute();
224/// let second = COMPILE_TIME.second();
225/// let time_string = format!("{hour:02}:{minute:02}:{second:02}");
226///
227/// assert_eq!(compile_time::time_str!(local), time_string);
228/// ```
229#[cfg(feature = "time")]
230#[proc_macro]
231pub fn time_str(input: TokenStream) -> TokenStream {
232 use syn::parse_macro_input;
233 use time03::format_description;
234
235 use time::TimeInput;
236
237 let input = parse_macro_input!(input as TimeInput);
238 let now = match input.now() {
239 Ok(now) => now,
240 Err(err) => return err.into_compile_error().into(),
241 };
242
243 let time = now.time();
244
245 let fmt = format_description::parse_borrowed::<3>("[hour]:[minute]:[second]").unwrap();
246 let time_str = time.format(&fmt).unwrap();
247
248 quote! { #time_str }.into()
249}
250
251/// Returns the compile date and time as [`time::OffsetDateTime`](time03::OffsetDateTime).
252///
253/// By default, the returned datetime is in UTC, call `datetime!(local)`
254/// to return the local date and time.
255///
256/// # Example
257///
258/// Get the UTC date and time:
259///
260/// ```
261/// # use time03 as time;
262/// const COMPILE_DATETIME: time::OffsetDateTime = compile_time::datetime!();
263///
264/// let year = COMPILE_DATETIME.year();
265/// let month = COMPILE_DATETIME.month();
266/// let day = COMPILE_DATETIME.day();
267/// let hour = COMPILE_DATETIME.hour();
268/// let minute = COMPILE_DATETIME.minute();
269/// let second = COMPILE_DATETIME.second();
270/// println!("Compiled at {hour:02}:{minute:02}:{second:02} on {month} {day}, {year}.");
271/// #
272/// # // Evaluation is only done once.
273/// # std::thread::sleep(std::time::Duration::from_secs(1));
274/// # assert_eq!(COMPILE_DATETIME, compile_time::datetime!());
275/// #
276/// # // Additional sanity check.
277/// # let now = time::OffsetDateTime::now_utc();
278/// # let yesterday = now.saturating_sub(time::Duration::days(1));
279/// # assert!(COMPILE_DATETIME > yesterday);
280/// # assert!(COMPILE_DATETIME < now);
281/// ```
282///
283/// Get the date and time for the local time zone:
284///
285/// ```
286/// # use time03 as time;
287/// const COMPILE_DATETIME: time::OffsetDateTime = compile_time::datetime!(local);
288///
289/// let year = COMPILE_DATETIME.year();
290/// let month = COMPILE_DATETIME.month();
291/// let day = COMPILE_DATETIME.day();
292/// let hour = COMPILE_DATETIME.hour();
293/// let minute = COMPILE_DATETIME.minute();
294/// let second = COMPILE_DATETIME.second();
295/// println!("Compiled at {hour:02}:{minute:02}:{second:02} on {month} {day}, {year}.");
296/// ```
297#[cfg(feature = "time")]
298#[proc_macro]
299pub fn datetime(input: TokenStream) -> TokenStream {
300 use quote::format_ident;
301 use syn::parse_macro_input;
302 use time::TimeInput;
303
304 let input = parse_macro_input!(input as TimeInput);
305 let now = match input.now() {
306 Ok(now) => now,
307 Err(err) => return err.into_compile_error().into(),
308 };
309
310 let datetime = now;
311
312 let year = datetime.year();
313 let month = format_ident!("{}", format!("{:?}", datetime.month()));
314 let day = datetime.day();
315
316 let hour = datetime.hour();
317 let minute = datetime.minute();
318 let second = datetime.second();
319
320 let time_prefix = quote! { ::compile_time::__re_exports::time };
321 let date = quote! {
322 match #time_prefix::Date::from_calendar_date(#year, #time_prefix::Month::#month, #day) {
323 Ok(date) => date,
324 _ => ::core::unreachable!(),
325 }
326 };
327
328 let time = quote! {
329 match #time_prefix::Time::from_hms(#hour, #minute, #second) {
330 Ok(time) => time,
331 _ => ::core::unreachable!(),
332 }
333 };
334
335 quote! {
336 const { #time_prefix::PrimitiveDateTime::new(#date, #time).assume_utc() }
337 }
338 .into()
339}
340
341/// Returns the compile date and time as `&'static str` in `yyyy-MM-ddThh:mm:ssZ` format.
342///
343/// By default, the returned datetime is in UTC, call `datetime_str!(local)`
344/// to return the local date and time.
345///
346/// # Example
347///
348/// Get the UTC date and time:
349///
350/// ```
351/// const COMPILE_DATE_STRING: &str = compile_time::date_str!();
352/// const COMPILE_TIME_STRING: &str = compile_time::time_str!();
353///
354/// let datetime_string = format!("{COMPILE_DATE_STRING}T{COMPILE_TIME_STRING}Z");
355/// assert_eq!(compile_time::datetime_str!(), datetime_string);
356/// ```
357///
358/// Get the date and time for the local time zone:
359///
360/// ```
361/// const COMPILE_DATE_STRING: &str = compile_time::date_str!(local);
362/// const COMPILE_TIME_STRING: &str = compile_time::time_str!(local);
363///
364/// let datetime_string = format!("{COMPILE_DATE_STRING}T{COMPILE_TIME_STRING}Z");
365/// assert_eq!(compile_time::datetime_str!(local), datetime_string);
366/// ```
367#[cfg(feature = "time")]
368#[proc_macro]
369pub fn datetime_str(input: TokenStream) -> TokenStream {
370 use syn::parse_macro_input;
371 use time03::format_description;
372
373 use time::TimeInput;
374
375 let input = parse_macro_input!(input as TimeInput);
376 let now = match input.now() {
377 Ok(now) => now,
378 Err(err) => return err.into_compile_error().into(),
379 };
380
381 let datetime = now;
382
383 let fmt = format_description::parse_borrowed::<3>("[year]-[month]-[day]T[hour]:[minute]:[second]Z").unwrap();
384 let datetime_str = datetime.format(&fmt).unwrap();
385
386 quote! { #datetime_str }.into()
387}
388
389/// Returns the compile date and time as UNIX timestamp in seconds.
390///
391/// # Example
392///
393/// ```
394/// # use time03 as time;
395/// const COMPILE_DATETIME: time::OffsetDateTime = compile_time::datetime!();
396///
397/// assert_eq!(compile_time::unix!(), COMPILE_DATETIME.unix_timestamp());
398/// ```
399#[cfg(feature = "time")]
400#[proc_macro]
401pub fn unix(_input: TokenStream) -> TokenStream {
402 let datetime = time::utc();
403
404 let unix_timestamp = proc_macro2::Literal::i64_unsuffixed(datetime.unix_timestamp());
405
406 quote! {
407 #unix_timestamp
408 }
409 .into()
410}
411
412/// Returns the Rust compiler version as [`semver::Version`].
413///
414/// # Example
415///
416/// ```
417/// const RUSTC_VERSION: semver::Version = compile_time::rustc_version!();
418/// assert_eq!(RUSTC_VERSION, rustc_version::version().unwrap());
419/// ```
420#[cfg(feature = "version")]
421#[proc_macro]
422pub fn rustc_version(_item: TokenStream) -> TokenStream {
423 match version::rustc() {
424 Ok(rustc_version::Version { major, minor, patch, pre, build }) => {
425 let semver_prefix = quote! { ::compile_time::__re_exports::semver };
426 let pre = if pre.is_empty() {
427 quote! { #semver_prefix::Prerelease::EMPTY }
428 } else {
429 let pre = pre.as_str();
430 quote! {
431 if let Ok(pre) = #semver_prefix::Prerelease::new(#pre) {
432 pre
433 } else {
434 ::core::unreachable!()
435 }
436 }
437 };
438
439 let build = if build.is_empty() {
440 quote! { #semver_prefix::BuildMetadata::EMPTY }
441 } else {
442 let build = build.as_str();
443 quote! {
444 if let Ok(build) = #semver_prefix::BuildMetadata::new(#build) {
445 build
446 } else {
447 ::core::unreachable!()
448 }
449 }
450 };
451
452 quote! {
453 #semver_prefix::Version {
454 major: #major,
455 minor: #minor,
456 patch: #patch,
457 pre: #pre,
458 build: #build,
459 }
460 }
461 },
462 Err(err) => err.into_compile_error(),
463 }
464 .into()
465}
466
467/// Returns the Rust compiler version as `&'static str`.
468///
469/// # Example
470///
471/// ```
472/// const RUSTC_VERSION_STRING: &str = compile_time::rustc_version_str!();
473/// assert_eq!(RUSTC_VERSION_STRING, compile_time::rustc_version_str!());
474/// ```
475#[cfg(feature = "version")]
476#[proc_macro]
477pub fn rustc_version_str(_item: TokenStream) -> TokenStream {
478 match version::rustc() {
479 Ok(rustc_version) => {
480 let rustc_version_string = rustc_version.to_string();
481 quote! { #rustc_version_string }
482 },
483 Err(err) => err.into_compile_error(),
484 }
485 .into()
486}
487
488/// Returns the Rust compiler major version as integer literal.
489///
490/// # Example
491///
492/// ```
493/// const RUSTC_VERSION: semver::Version = compile_time::rustc_version!();
494/// assert_eq!(RUSTC_VERSION.major, compile_time::rustc_version_major!());
495/// ```
496#[cfg(feature = "version")]
497#[proc_macro]
498pub fn rustc_version_major(_item: TokenStream) -> TokenStream {
499 use quote::ToTokens;
500
501 match version::rustc() {
502 Ok(rustc_version) => {
503 let major = rustc_version.major;
504 proc_macro2::Literal::u64_unsuffixed(major).to_token_stream()
505 },
506 Err(err) => err.into_compile_error(),
507 }
508 .into()
509}
510
511/// Returns the Rust compiler minor version as integer literal.
512///
513/// # Example
514///
515/// ```
516/// const RUSTC_VERSION: semver::Version = compile_time::rustc_version!();
517/// assert_eq!(RUSTC_VERSION.minor, compile_time::rustc_version_minor!());
518/// ```
519#[cfg(feature = "version")]
520#[proc_macro]
521pub fn rustc_version_minor(_item: TokenStream) -> TokenStream {
522 use quote::ToTokens;
523
524 match version::rustc() {
525 Ok(rustc_version) => {
526 let minor = rustc_version.minor;
527 proc_macro2::Literal::u64_unsuffixed(minor).to_token_stream()
528 },
529 Err(err) => err.into_compile_error(),
530 }
531 .into()
532}
533
534/// Returns the Rust compiler patch version as integer literal.
535///
536/// # Example
537///
538/// ```
539/// const RUSTC_VERSION: semver::Version = compile_time::rustc_version!();
540/// assert_eq!(RUSTC_VERSION.minor, compile_time::rustc_version_minor!());
541/// ```
542#[cfg(feature = "version")]
543#[proc_macro]
544pub fn rustc_version_patch(_item: TokenStream) -> TokenStream {
545 use quote::ToTokens;
546
547 match version::rustc() {
548 Ok(rustc_version) => {
549 let patch = rustc_version.patch;
550 proc_macro2::Literal::u64_unsuffixed(patch).to_token_stream()
551 },
552 Err(err) => err.into_compile_error(),
553 }
554 .into()
555}
556
557/// Returns the Rust compiler pre version as `&'static str`.
558///
559/// # Example
560///
561/// ```
562/// const RUSTC_VERSION: semver::Version = compile_time::rustc_version!();
563/// assert_eq!(RUSTC_VERSION.pre.as_str(), compile_time::rustc_version_pre!());
564/// ```
565#[cfg(feature = "version")]
566#[proc_macro]
567pub fn rustc_version_pre(_item: TokenStream) -> TokenStream {
568 match version::rustc() {
569 Ok(rustc_version) => {
570 let pre = rustc_version.pre.as_str();
571 quote! { #pre }
572 },
573 Err(err) => err.into_compile_error(),
574 }
575 .into()
576}
577
578/// Returns the Rust compiler build version as a `&'static str`.
579///
580/// # Example
581///
582/// ```
583/// const RUSTC_VERSION: semver::Version = compile_time::rustc_version!();
584/// assert_eq!(RUSTC_VERSION.build.as_str(), compile_time::rustc_version_build!());
585/// ```
586#[cfg(feature = "version")]
587#[proc_macro]
588pub fn rustc_version_build(_input: TokenStream) -> TokenStream {
589 match version::rustc() {
590 Ok(rustc_version) => {
591 let build = rustc_version.build.as_str();
592 quote! { #build }
593 },
594 Err(err) => err.into_compile_error(),
595 }
596 .into()
597}
598
599/// Runs the given command and returns its standard output as a `&'static [u8]`.
600///
601/// Produces a compile error if the command fails or of the output is not valid UTF-8.
602///
603/// # Example
604///
605/// ```
606/// const MAGIC_NUMBER: &[u8] = compile_time::command_bytes!("echo", "42");
607///
608/// assert_eq!(MAGIC_NUMBER, b"42\n");
609/// ```
610#[proc_macro]
611#[cfg(feature = "command")]
612pub fn command_bytes(input: TokenStream) -> TokenStream {
613 use proc_macro2::Span;
614 use syn::{parse_macro_input, LitByteStr};
615
616 use command::CommandInput;
617
618 let input = parse_macro_input!(input as CommandInput);
619 match command::output(input) {
620 Ok(output) => {
621 let output = LitByteStr::new(&output, Span::call_site());
622 quote! { #output }
623 },
624 Err(err) => err.into_compile_error(),
625 }
626 .into()
627}
628
629/// Runs the given command and returns its standard output as a `&'static str`.
630///
631/// Produces a compile error if the command fails or of the output is not valid UTF-8.
632///
633/// # Example
634///
635/// ```
636/// const MAGIC_NUMBER: &str = compile_time::command_str!("echo", "42");
637///
638/// assert_eq!(MAGIC_NUMBER, "42\n");
639/// ```
640#[proc_macro]
641#[cfg(feature = "command")]
642pub fn command_str(input: TokenStream) -> TokenStream {
643 use proc_macro2::Span;
644 use syn::{parse_macro_input, LitStr};
645
646 use command::CommandInput;
647
648 let input = parse_macro_input!(input as CommandInput);
649 match command::output(input) {
650 Ok(output) => match String::from_utf8(output) {
651 Ok(output_string) => {
652 let output = LitStr::new(&output_string, Span::call_site());
653 quote! { #output }
654 },
655 Err(_) => quote! {
656 ::core::compile_error!("Command output is not valid UTF-8.")
657 },
658 },
659 Err(err) => err.into_compile_error(),
660 }
661 .into()
662}