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