1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
extern crate proc_macro;
use std::env;
use git2::Repository;
use proc_macro::TokenStream;
use proc_macro_hack::proc_macro_hack;
use quote::quote;
struct Version {
commit: [u8; 20],
dirty: bool,
}
#[proc_macro_hack]
pub fn version(tokens: TokenStream) -> TokenStream {
assert!(tokens.is_empty(), "no arguments expected");
let none = TokenStream::from(quote! { None::<version_consts_git::Version> });
let repo = match Repository::discover(env::var("CARGO_MANIFEST_DIR").unwrap()) {
Ok(x) => x,
Err(_) => return none,
};
let version = from_repo(&repo);
let dep_path = repo.path().join("logs/HEAD");
if !dep_path.exists() {
return none;
}
let dep_path = dep_path.to_str();
let version = match version {
None => quote! { None },
Some(Version { commit, dirty }) => quote! {
Some(version_consts_git::Version {
commit: version_consts_git::Commit([#(#commit),*]),
dirty: #dirty,
})
},
};
TokenStream::from(quote! {
{
const _GIT_HEAD: &[u8] = include_bytes!(#dep_path);
#version
}
})
}
fn from_repo(repo: &Repository) -> Option<Version> {
if repo.is_empty().ok()? {
return None;
}
let head = repo.head().ok()?;
let commit = head.resolve().ok()?.target().unwrap();
let diff = repo
.diff_tree_to_workdir_with_index(
Some(
&repo
.find_tree(repo.revparse_single("HEAD^{tree}").ok()?.id())
.ok()?,
),
None,
)
.ok()?;
let mut commit_bytes = [0; 20];
commit_bytes.copy_from_slice(commit.as_bytes());
Some(Version {
commit: commit_bytes,
dirty: diff.deltas().len() != 0,
})
}