Skip to main content

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}