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