compile-time-macros 0.3.2

Macros for getting compile time information.
Documentation
use once_cell::sync::Lazy;
use proc_macro2::Span;
use syn::{parse::Parse, Ident, Token};
use time::{OffsetDateTime, UtcOffset};

pub static COMPILE_TIME: Lazy<OffsetDateTime> = Lazy::new(OffsetDateTime::now_utc);

pub fn utc() -> OffsetDateTime {
  *COMPILE_TIME
}

fn local() -> Result<OffsetDateTime, syn::Error> {
  let offset = match UtcOffset::local_offset_at(*COMPILE_TIME) {
    Ok(offset) => offset,
    Err(err) => return Err(syn::Error::new(Span::call_site(), err)),
  };

  Ok(COMPILE_TIME.to_offset(offset))
}

pub struct TimeInput {
  local: bool,
}

impl TimeInput {
  pub fn now(&self) -> Result<OffsetDateTime, syn::Error> {
    if self.local {
      local()
    } else {
      Ok(utc())
    }
  }
}

impl Parse for TimeInput {
  fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
    let local = if input.is_empty() {
      false
    } else {
      let arg = input.parse::<Ident>()?;

      arg == "local"
    };

    if !input.is_empty() {
      input.parse::<Token![,]>()?;
    }

    if !input.is_empty() {
      return Err(syn::Error::new(input.span(), "Unexpected argument."));
    }

    Ok(Self { local })
  }
}

#[cfg(test)]
mod tests {
  use proc_macro2::TokenStream;
  use quote::quote;

  use super::TimeInput;

  #[test]
  fn parse_no_args() {
    let tokens: TokenStream = quote!().into();

    let input: TimeInput = syn::parse2(tokens).unwrap();

    assert_eq!(input.local, false);
  }

  #[test]
  fn parse_local() {
    let tokens: TokenStream = quote!(local).into();

    let input: TimeInput = syn::parse2(tokens).unwrap();

    assert_eq!(input.local, true);
  }

  #[test]
  fn parse_local_trailing_comma() {
    let tokens: TokenStream = quote!(local).into();

    let input: TimeInput = syn::parse2(tokens).unwrap();

    assert_eq!(input.local, true);
  }
}