-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
550 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,40 @@ | ||
[package] | ||
name = "pkg" | ||
version = "3.0.0" | ||
authors = ["James Whaley <[email protected]>"] | ||
version.workspace = true | ||
authors.workspace = true | ||
description = "Utility macros and functions for crates." | ||
edition = "2021" | ||
license = "MIT OR Apache-2.0" | ||
edition.workspace = true | ||
license.workspace = true | ||
rust-version.workspace = true | ||
|
||
documentation = "https://docs.rs/pkg" | ||
repository = "https://github.com/Juici/pkg-rs" | ||
repository.workspace = true | ||
|
||
keywords = ["cargo", "package"] | ||
categories = ["development-tools"] | ||
|
||
[features] | ||
default = ["std", "proc-macros", "git"] | ||
std = [] | ||
proc-macros = ["pkg-macros"] | ||
git = ["proc-macros", "pkg-macros?/git"] | ||
|
||
[dependencies] | ||
once_cell = { version = "1", optional = true } | ||
pkg-macros = { version = "=4.0.0", path = "macros", optional = true } | ||
|
||
[features] | ||
default = ["semver", "rt_bin_name"] | ||
[workspace] | ||
members = ["macros"] | ||
resolver = "2" | ||
|
||
std = [] | ||
semver = [] | ||
rt_bin_name = ["once_cell", "std"] | ||
[workspace.package] | ||
version = "4.0.0" | ||
authors = ["James Whaley <[email protected]>"] | ||
edition = "2021" | ||
license = "MIT OR Apache-2.0" | ||
rust-version = "1.71" | ||
|
||
repository = "https://github.com/Juici/pkg-rs" | ||
|
||
[package.metadata.docs.rs] | ||
all-features = true | ||
targets = ["x86_64-unknown-linux-gnu"] | ||
rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
[package] | ||
name = "pkg-macros" | ||
version.workspace = true | ||
authors.workspace = true | ||
description = "Procedural macros for the `pkg` crate." | ||
edition.workspace = true | ||
license.workspace = true | ||
rust-version.workspace = true | ||
|
||
repository.workspace = true | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[features] | ||
default = ["git"] | ||
git = ["gix"] | ||
|
||
[dependencies] | ||
proc-macro2 = "1.0.78" | ||
quote = "1.0.35" | ||
syn = { version = "2.0.48", default-features = false } | ||
|
||
thiserror = "1.0.56" | ||
|
||
gix = { version = "0.58.0", default-features = false, optional = true } | ||
|
||
[package.metadata.docs.rs] | ||
all-features = true | ||
targets = ["x86_64-unknown-linux-gnu"] | ||
rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../LICENSE-APACHE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../LICENSE-MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
use std::ffi::{OsStr, OsString}; | ||
use std::fmt::Display; | ||
use std::num::ParseIntError; | ||
use std::sync::OnceLock; | ||
use std::time::{SystemTime, UNIX_EPOCH}; | ||
use std::{env, fmt}; | ||
|
||
use proc_macro2::{Span, TokenStream}; | ||
use quote::quote; | ||
use thiserror::Error; | ||
|
||
pub fn build_time() -> syn::Result<TokenStream> { | ||
static BUILD_TIME: OnceLock<Result<i64, BuildTimeError>> = OnceLock::new(); | ||
|
||
let timestamp = BUILD_TIME | ||
.get_or_init(|| match get_reproducible_time() { | ||
Ok(Some(timestamp)) => Ok(timestamp), | ||
Ok(None) => get_system_time(), | ||
Err(err) => Err(BuildTimeError::SourceDate(err)), | ||
}) | ||
.as_ref() | ||
.copied() | ||
.map_err(|err| syn::Error::new(Span::call_site(), err))?; | ||
|
||
Ok(quote! { #timestamp }) | ||
} | ||
|
||
// https://reproducible-builds.org/docs/source-date-epoch/ | ||
fn get_reproducible_time() -> Result<Option<i64>, SourceDateError> { | ||
match env::var_os("SOURCE_DATE_EPOCH") { | ||
Some(timestamp) => Ok(Some(timestamp.into_string()?.parse::<i64>()?)), | ||
None => Ok(None), | ||
} | ||
} | ||
|
||
fn get_system_time() -> Result<i64, BuildTimeError> { | ||
let now = SystemTime::now(); | ||
|
||
let timestamp = match now.duration_since(UNIX_EPOCH) { | ||
Ok(since) => i128::from(since.as_secs()), | ||
Err(err) => -i128::from(err.duration().as_secs()), | ||
}; | ||
|
||
i64::try_from(timestamp).map_err(|_| BuildTimeError::Overflow(timestamp)) | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
enum BuildTimeError { | ||
#[error("system clock overflows unix timestamp: {0}")] | ||
Overflow(i128), | ||
#[error(transparent)] | ||
SourceDate(#[from] SourceDateError), | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
enum SourceDateError { | ||
#[error("invalid unicode in $SOURCE_DATE_EPOCH")] | ||
NotUnicode(OsString), | ||
#[error("failed to parse $SOURCE_DATE_EPOCH timestamp")] | ||
Parse(#[from] ParseIntError), | ||
} | ||
|
||
impl From<OsString> for SourceDateError { | ||
fn from(s: OsString) -> Self { | ||
Self::NotUnicode(s) | ||
} | ||
} | ||
|
||
struct InvalidUtf8<'a>(&'a OsStr); | ||
|
||
impl Display for InvalidUtf8<'_> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
// Hack to make use of unstable Utf8Chunks to output lossy string. | ||
std::path::Path::new(&self.0).display().fmt(f) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use std::env; | ||
use std::error::Error; | ||
use std::path::PathBuf; | ||
|
||
use gix::Repository; | ||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
use thiserror::Error; | ||
|
||
fn get_manifest_dir() -> Result<PathBuf, GitError> { | ||
env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from).ok_or(GitError::MissingManifestDir) | ||
} | ||
|
||
fn get_repo() -> Result<Repository, GitError> { | ||
let manifest_dir = get_manifest_dir()?; | ||
let repo = gix::discover(manifest_dir).map_err(GitError::boxed)?; | ||
|
||
Ok(repo) | ||
} | ||
|
||
pub fn commit_hash() -> Result<TokenStream, GitError> { | ||
let repo = get_repo()?; | ||
let head_id = repo.head_id().map_err(GitError::boxed)?; | ||
|
||
let commit_hash = head_id.to_hex().to_string(); | ||
|
||
Ok(quote!(#commit_hash)) | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
pub enum GitError { | ||
#[error("missing CARGO_MANIFEST_DIR environment variable")] | ||
MissingManifestDir, | ||
#[error(transparent)] | ||
Boxed(#[from] Box<dyn Error>), | ||
} | ||
|
||
impl GitError { | ||
fn boxed<E: Error + 'static>(err: E) -> Self { | ||
Self::Boxed(Box::new(err)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#![cfg_attr(doc_cfg, feature(doc_cfg))] | ||
#![allow(clippy::module_name_repetitions)] | ||
|
||
extern crate proc_macro; | ||
|
||
use crate::traits::MacroResult; | ||
|
||
#[macro_use] | ||
mod macros; | ||
mod build_time; | ||
mod traits; | ||
|
||
#[proc_macro] | ||
pub fn build_time(input: proc_macro::TokenStream) -> proc_macro::TokenStream { | ||
parse_nothing!(input); | ||
|
||
build_time::build_time().macro_result() | ||
} | ||
|
||
cfg_git! { | ||
mod git; | ||
|
||
#[proc_macro] | ||
pub fn git_commit_hash(input: proc_macro::TokenStream) -> proc_macro::TokenStream { | ||
parse_nothing!(input); | ||
|
||
git::commit_hash().macro_result() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#![allow(unused_macros)] | ||
|
||
macro_rules! cfg_git { | ||
($($item:item)*) => { | ||
$( | ||
#[cfg(feature = "git")] | ||
#[cfg_attr(doc_cfg, doc(cfg(feature = "git")))] | ||
$item | ||
)* | ||
}; | ||
} | ||
|
||
macro_rules! parse_nothing { | ||
($input:expr) => {{ | ||
let input = ::proc_macro2::TokenStream::from($input); | ||
|
||
if let Some(token) = input.into_iter().next() { | ||
let err = ::syn::Error::new(token.span(), "unexpected token"); | ||
|
||
return err.into_compile_error().into(); | ||
} | ||
}}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use std::any::Any; | ||
use std::fmt::Display; | ||
|
||
use proc_macro2::{Span, TokenStream}; | ||
|
||
pub trait MacroResult { | ||
fn macro_result(self) -> proc_macro::TokenStream; | ||
} | ||
|
||
impl<E> MacroResult for Result<TokenStream, E> | ||
where | ||
E: Display + 'static, | ||
{ | ||
fn macro_result(self) -> proc_macro::TokenStream { | ||
let result = match self { | ||
Ok(tokens) => tokens, | ||
Err(err) => match <dyn Any>::downcast_ref::<syn::Error>(&err) { | ||
Some(err) => err.to_compile_error(), | ||
None => syn::Error::new(Span::call_site(), err).to_compile_error(), | ||
}, | ||
}; | ||
result.into() | ||
} | ||
} | ||
|
||
impl MacroResult for TokenStream { | ||
fn macro_result(self) -> proc_macro::TokenStream { | ||
self.into() | ||
} | ||
} | ||
|
||
impl MacroResult for proc_macro::TokenStream { | ||
fn macro_result(self) -> proc_macro::TokenStream { | ||
self | ||
} | ||
} |
Oops, something went wrong.