version_consts_git_impl/
lib.rs

1extern 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        // No initial commit
27        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    // Guard against repositories in initial state
49    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}