1#![warn(missing_docs)]
23#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
24
25extern crate proc_macro;
26
27use proc_macro::TokenStream;
28
29use std::process::Command;
30use core::fmt;
31
32#[cold]
33#[inline(never)]
34fn compile_error(args: fmt::Arguments<'_>) -> TokenStream {
35 format!("compile_error!(\"{args}\")").parse().expect("To generate compile error")
36}
37
38#[inline(always)]
39fn run_git(args: &[&str]) -> Result<String, TokenStream> {
40 match Command::new("git").args(args).output() {
41 Ok(output) => match output.status.success() {
42 true => match String::from_utf8(output.stdout) {
43 Ok(output) => Ok(output),
44 Err(error) => Err(compile_error(format_args!("git output is not valid utf-8: {error}"))),
45 },
46 false => {
47 let status = output.status.code().unwrap_or(1);
48 let stderr = core::str::from_utf8(&output.stderr).unwrap_or("<invalid utf-8>");
49 Err(compile_error(format_args!("git failed with status {status}:\n {stderr}")))
50 }
51 },
52 Err(error) => Err(compile_error(format_args!("git execution error: {error}"))),
53 }
54}
55
56#[proc_macro]
57pub fn git_hash(input: TokenStream) -> TokenStream {
62 let input = input.to_string();
63 let revision = match input.trim() {
64 "" => "HEAD",
65 input => input,
66 };
67
68 let output = match run_git(&["rev-parse", revision]) {
69 Ok(output) => output,
70 Err(error) => return error,
71 };
72
73 let output = output.trim();
74 format!("\"{output}\"").parse().expect("generate hash string")
75}
76
77#[proc_macro]
78pub fn git_short_hash(input: TokenStream) -> TokenStream {
83 let input = input.to_string();
84 let revision = match input.trim() {
85 "" => "HEAD",
86 input => input,
87 };
88
89 let output = match run_git(&["rev-parse", "--short", revision]) {
90 Ok(output) => output,
91 Err(error) => return error,
92 };
93
94 let output = output.trim();
95 format!("\"{output}\"").parse().expect("generate hash string")
96}
97
98#[proc_macro]
99pub fn git_root(_input: TokenStream) -> TokenStream {
101 let output = match run_git(&["rev-parse", "--show-toplevel"]) {
102 Ok(output) => output,
103 Err(error) => return error,
104 };
105
106 let output = output.trim();
107 format!("\"{output}\"").parse().expect("generate hash string")
108}