extern crate proc_macro;
use proc_macro::{Literal, TokenStream, TokenTree};
mod utils;
use self::utils::describe_cwd;
#[proc_macro]
pub fn git_version(input: TokenStream) -> TokenStream {
let fallback = parse_fallback(input);
let version = resolve_version(describe_cwd(), fallback);
TokenTree::Literal(Literal::string(&version)).into()
}
fn resolve_version(described: std::io::Result<String>, fallback: Option<String>) -> String {
match described {
Ok(version) => version,
Err(e) => match fallback {
Some(fallback) => fallback,
None => panic!("git describe failed and no `fallback = \"...\"` was provided: {}", e),
},
}
}
fn parse_fallback(input: TokenStream) -> Option<String> {
let mut iter = input.into_iter();
while let Some(tok) = iter.next() {
let is_fallback = matches!(&tok, TokenTree::Ident(id) if id.to_string() == "fallback");
if !is_fallback {
continue;
}
match iter.next() {
Some(TokenTree::Punct(p)) if p.as_char() == '=' => {}
other => panic!("expected `=` after `fallback`, got {:?}", other),
}
match iter.next() {
Some(TokenTree::Literal(lit)) => return Some(unquote(&lit.to_string())),
other => panic!("expected a string literal after `fallback =`, got {:?}", other),
}
}
None
}
fn unquote(lit: &str) -> String {
let bytes = lit.as_bytes();
if bytes.len() >= 2 && bytes[0] == b'"' && bytes[bytes.len() - 1] == b'"' {
lit[1..lit.len() - 1].to_string()
} else {
lit.to_string()
}
}
#[cfg(test)]
mod tests {
use super::{resolve_version, unquote};
fn err() -> std::io::Result<String> {
Err(std::io::Error::new(std::io::ErrorKind::Other, "boom"))
}
#[test]
fn fallback_used_when_git_fails() {
assert_eq!(resolve_version(err(), Some("unknown".into())), "unknown");
}
#[test]
fn ok_wins_over_fallback() {
assert_eq!(resolve_version(Ok("v1.2-3-gabc".into()), Some("unknown".into())), "v1.2-3-gabc");
}
#[test]
#[should_panic(expected = "no `fallback")]
fn no_fallback_still_panics_on_err() {
resolve_version(err(), None);
}
#[test]
fn unquote_strips_quotes() {
assert_eq!(unquote("\"unknown\""), "unknown");
assert_eq!(unquote("bare"), "bare");
}
}