comfy_git_version_macro/
lib.rs1extern crate proc_macro;
2
3use proc_macro::{Literal, TokenStream, TokenTree};
4
5mod utils;
6use self::utils::describe_cwd;
7
8#[proc_macro]
19pub fn git_version(input: TokenStream) -> TokenStream {
20 let fallback = parse_fallback(input);
21 let version = resolve_version(describe_cwd(), fallback);
22
23 TokenTree::Literal(Literal::string(&version)).into()
24}
25
26fn resolve_version(described: std::io::Result<String>, fallback: Option<String>) -> String {
30 match described {
31 Ok(version) => version,
32 Err(e) => match fallback {
33 Some(fallback) => fallback,
34 None => panic!("git describe failed and no `fallback = \"...\"` was provided: {}", e),
35 },
36 }
37}
38
39fn parse_fallback(input: TokenStream) -> Option<String> {
41 let mut iter = input.into_iter();
42 while let Some(tok) = iter.next() {
43 let is_fallback = matches!(&tok, TokenTree::Ident(id) if id.to_string() == "fallback");
44 if !is_fallback {
45 continue;
46 }
47 match iter.next() {
48 Some(TokenTree::Punct(p)) if p.as_char() == '=' => {}
49 other => panic!("expected `=` after `fallback`, got {:?}", other),
50 }
51 match iter.next() {
52 Some(TokenTree::Literal(lit)) => return Some(unquote(&lit.to_string())),
53 other => panic!("expected a string literal after `fallback =`, got {:?}", other),
54 }
55 }
56 None
57}
58
59fn unquote(lit: &str) -> String {
61 let bytes = lit.as_bytes();
62 if bytes.len() >= 2 && bytes[0] == b'"' && bytes[bytes.len() - 1] == b'"' {
63 lit[1..lit.len() - 1].to_string()
64 } else {
65 lit.to_string()
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::{resolve_version, unquote};
72
73 fn err() -> std::io::Result<String> {
74 Err(std::io::Error::new(std::io::ErrorKind::Other, "boom"))
75 }
76
77 #[test]
78 fn fallback_used_when_git_fails() {
79 assert_eq!(resolve_version(err(), Some("unknown".into())), "unknown");
80 }
81
82 #[test]
83 fn ok_wins_over_fallback() {
84 assert_eq!(resolve_version(Ok("v1.2-3-gabc".into()), Some("unknown".into())), "v1.2-3-gabc");
85 }
86
87 #[test]
88 #[should_panic(expected = "no `fallback")]
89 fn no_fallback_still_panics_on_err() {
90 resolve_version(err(), None);
91 }
92
93 #[test]
94 fn unquote_strips_quotes() {
95 assert_eq!(unquote("\"unknown\""), "unknown");
96 assert_eq!(unquote("bare"), "bare");
97 }
98}