compile_time_macros/
lib.rs1extern crate proc_macro;
6
7#[cfg(any(feature = "time", feature = "version"))]
8use once_cell::sync::Lazy;
9use proc_macro::TokenStream;
10use quote::{format_ident, quote, ToTokens};
11#[cfg(feature = "time")]
12use time::{macros::format_description, OffsetDateTime};
13
14mod command;
15
16#[cfg(feature = "time")]
17static COMPILE_TIME: Lazy<OffsetDateTime> = Lazy::new(OffsetDateTime::now_utc);
18#[cfg(feature = "version")]
19static RUSTC_VERSION: Lazy<rustc_version::Result<rustc_version::Version>> = Lazy::new(rustc_version::version);
20
21#[cfg(feature = "time")]
34#[proc_macro]
35pub fn date(_input: TokenStream) -> TokenStream {
36 let date = COMPILE_TIME.date();
37
38 let year = date.year();
39 let month = format_ident!("{}", format!("{:?}", date.month()));
40 let day = date.day();
41
42 let time_prefix = quote! { ::compile_time::__re_exports::time };
43 quote! {
44 const {
45 match #time_prefix::Date::from_calendar_date(#year, #time_prefix::Month::#month, #day) {
46 Ok(date) => date,
47 _ => ::core::unreachable!(),
48 }
49 }
50 }
51 .into()
52}
53
54#[cfg(feature = "time")]
69#[proc_macro]
70pub fn date_str(_input: TokenStream) -> TokenStream {
71 let date = COMPILE_TIME.date();
72
73 let fmt = format_description!("[year]-[month]-[day]");
74 let date_str = date.format(&fmt).unwrap();
75
76 quote! { #date_str }.into()
77}
78
79#[cfg(feature = "time")]
92#[proc_macro]
93pub fn time(_input: TokenStream) -> TokenStream {
94 let time = COMPILE_TIME.time();
95
96 let hour = time.hour();
97 let minute = time.minute();
98 let second = time.second();
99
100 let time_prefix = quote! { ::compile_time::__re_exports::time };
101 quote! {
102 const {
103 match #time_prefix::Time::from_hms(#hour, #minute, #second) {
104 Ok(time) => time,
105 _ => ::core::unreachable!(),
106 }
107 }
108 }
109 .into()
110}
111
112#[cfg(feature = "time")]
127#[proc_macro]
128pub fn time_str(_input: TokenStream) -> TokenStream {
129 let time = COMPILE_TIME.time();
130
131 let fmt = format_description!("[hour]:[minute]:[second]");
132 let time_str = time.format(&fmt).unwrap();
133
134 quote! { #time_str }.into()
135}
136
137#[cfg(feature = "time")]
163#[proc_macro]
164pub fn datetime(_input: TokenStream) -> TokenStream {
165 let datetime = *COMPILE_TIME;
166
167 let year = datetime.year();
168 let month = format_ident!("{}", format!("{:?}", datetime.month()));
169 let day = datetime.day();
170
171 let hour = datetime.hour();
172 let minute = datetime.minute();
173 let second = datetime.second();
174
175 let time_prefix = quote! { ::compile_time::__re_exports::time };
176 let date = quote! {
177 match #time_prefix::Date::from_calendar_date(#year, #time_prefix::Month::#month, #day) {
178 Ok(date) => date,
179 _ => ::core::unreachable!(),
180 }
181 };
182
183 let time = quote! {
184 match #time_prefix::Time::from_hms(#hour, #minute, #second) {
185 Ok(time) => time,
186 _ => ::core::unreachable!(),
187 }
188 };
189
190 quote! {
191 const { #time_prefix::PrimitiveDateTime::new(#date, #time).assume_utc() }
192 }
193 .into()
194}
195
196#[cfg(feature = "time")]
208#[proc_macro]
209pub fn datetime_str(_input: TokenStream) -> TokenStream {
210 let datetime = *COMPILE_TIME;
211
212 let fmt = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z");
213 let datetime_str = datetime.format(&fmt).unwrap();
214
215 quote! { #datetime_str }.into()
216}
217
218#[cfg(feature = "time")]
228#[proc_macro]
229pub fn unix(_input: TokenStream) -> TokenStream {
230 let datetime = *COMPILE_TIME;
231
232 let unix_timestamp = proc_macro2::Literal::i64_unsuffixed(datetime.unix_timestamp());
233
234 quote! {
235 #unix_timestamp
236 }
237 .into()
238}
239
240#[cfg(feature = "version")]
249#[proc_macro]
250pub fn rustc_version(_item: TokenStream) -> TokenStream {
251 let rustc_version::Version { major, minor, patch, pre, build } = match &*RUSTC_VERSION {
252 Ok(rustc_version) => rustc_version,
253 Err(err) => panic!("Failed to get version: {}", err),
254 };
255
256 let semver_prefix = quote! { ::compile_time::__re_exports::semver };
257 let pre = if pre.is_empty() {
258 quote! { #semver_prefix::Prerelease::EMPTY }
259 } else {
260 let pre = pre.as_str();
261 quote! {
262 if let Ok(pre) = #semver_prefix::Prerelease::new(#pre) {
263 pre
264 } else {
265 ::core::unreachable!()
266 }
267 }
268 };
269
270 let build = if build.is_empty() {
271 quote! { #semver_prefix::BuildMetadata::EMPTY }
272 } else {
273 let build = build.as_str();
274 quote! {
275 if let Ok(build) = #semver_prefix::BuildMetadata::new(#build) {
276 build
277 } else {
278 ::core::unreachable!()
279 }
280 }
281 };
282
283 quote! {
284 #semver_prefix::Version {
285 major: #major,
286 minor: #minor,
287 patch: #patch,
288 pre: #pre,
289 build: #build,
290 }
291 }
292 .into()
293}
294
295#[cfg(feature = "version")]
304#[proc_macro]
305pub fn rustc_version_str(_item: TokenStream) -> TokenStream {
306 let rustc_version = match &*RUSTC_VERSION {
307 Ok(rustc_version) => rustc_version,
308 Err(err) => panic!("Failed to get version: {}", err),
309 };
310
311 let rustc_version_string = rustc_version.to_string();
312 quote! { #rustc_version_string }.into()
313}
314
315#[cfg(feature = "version")]
324#[proc_macro]
325pub fn rustc_version_major(_item: TokenStream) -> TokenStream {
326 let major = match &*RUSTC_VERSION {
327 Ok(rustc_version) => rustc_version.major,
328 Err(err) => panic!("Failed to get version: {}", err),
329 };
330
331 proc_macro2::Literal::u64_unsuffixed(major).to_token_stream().into()
332}
333
334#[cfg(feature = "version")]
343#[proc_macro]
344pub fn rustc_version_minor(_item: TokenStream) -> TokenStream {
345 let minor = match &*RUSTC_VERSION {
346 Ok(rustc_version) => rustc_version.minor,
347 Err(err) => panic!("Failed to get version: {}", err),
348 };
349
350 proc_macro2::Literal::u64_unsuffixed(minor).to_token_stream().into()
351}
352
353#[cfg(feature = "version")]
362#[proc_macro]
363pub fn rustc_version_patch(_item: TokenStream) -> TokenStream {
364 let patch = match &*RUSTC_VERSION {
365 Ok(rustc_version) => rustc_version.patch,
366 Err(err) => panic!("Failed to get version: {}", err),
367 };
368
369 proc_macro2::Literal::u64_unsuffixed(patch).to_token_stream().into()
370}
371
372#[cfg(feature = "version")]
381#[proc_macro]
382pub fn rustc_version_pre(_item: TokenStream) -> TokenStream {
383 let pre = match &*RUSTC_VERSION {
384 Ok(rustc_version) => rustc_version.pre.as_str(),
385 Err(err) => panic!("Failed to get version: {}", err),
386 };
387
388 quote! { #pre }.into()
389}
390
391#[cfg(feature = "version")]
400#[proc_macro]
401pub fn rustc_version_build(_item: TokenStream) -> TokenStream {
402 let build = match &*RUSTC_VERSION {
403 Ok(rustc_version) => rustc_version.build.as_str(),
404 Err(err) => panic!("Failed to get version: {}", err),
405 };
406
407 quote! { #build }.into()
408}
409
410#[proc_macro]
419#[cfg(feature = "version")]
420pub fn command_bytes(input: TokenStream) -> TokenStream {
421 use proc_macro2::Span;
422 use syn::{parse_macro_input, LitByteStr};
423
424 use command::CommandInput;
425
426 let input = parse_macro_input!(input as CommandInput);
427 match command::output(input) {
428 Ok(output) => {
429 let output = LitByteStr::new(&output, Span::call_site());
430 quote! { #output }.into()
431 },
432 Err(e) => e,
433 }
434}
435
436#[proc_macro]
445#[cfg(feature = "version")]
446pub fn command_str(input: TokenStream) -> TokenStream {
447 use proc_macro2::Span;
448 use syn::{parse_macro_input, LitStr};
449
450 use command::CommandInput;
451
452 let input = parse_macro_input!(input as CommandInput);
453 match command::output(input) {
454 Ok(output) => {
455 let output_string = match String::from_utf8(output) {
456 Ok(string) => string,
457 Err(_) => {
458 return quote! {
459 ::core::compile_error!("Command output is not valid UTF-8.")
460 }
461 .into()
462 },
463 };
464
465 let output = LitStr::new(&output_string, Span::call_site());
466 quote! { #output }.into()
467 },
468 Err(e) => e,
469 }
470}