auto_version/lib.rs
1use proc_macro::TokenStream;
2use quote::quote;
3use syn;
4use syn::{ItemFn};
5
6/// This attribute macro which prints the value of CARGO_PKG_VERSION and exits with status code 0
7/// if the command line arguments include either `-v` or `--version`
8///
9/// In the case where the code is not compiled with cargo, the version will be replaced with the message
10/// "`auto_version` macro only works for projects compiled with cargo".
11///
12/// Example:
13/// ```rust
14/// use auto_version::auto_version;
15///
16/// #[auto_version]
17/// fn main() {
18/// // executed code
19/// }
20/// ```
21/// Then, when this binary is called with `binary -v` or `binary --version`, it will output the `Cargo.toml`
22/// version without any specific formatting:
23/// ```shell
24/// $ ./binary -v
25/// $ 0.1.0
26/// ```
27
28#[proc_macro_attribute]
29pub fn auto_version(_: TokenStream, input: TokenStream) -> TokenStream {
30
31 let mut item: syn::Item = syn::parse(input).unwrap();
32 let fn_item = match &mut item {
33 syn::Item::Fn(fn_item) => fn_item,
34 _ => panic!("expected fn")
35 };
36
37 let version_tokens = quote! {
38 {
39 // use this inner block so that the local imports don't affect the user's code
40 use std::ffi::{OsString, OsStr};
41 use std::process::exit;
42
43 if std::env::args_os()
44 .find(|arg| arg == "-v" || arg == "--version" || arg == "-V")
45 .is_some()
46 {
47 match option_env!("CARGO_PKG_VERSION") {
48 Some(version) => println!("{}", version),
49 None => println!("`auto_version` macro only works for projects compiled with cargo")
50 }
51 exit(0);
52 }
53 }
54 };
55
56 let ItemFn { attrs, vis, sig, block } = fn_item;
57 let stmts = &block.stmts;
58 let stream = quote! {
59 #(#attrs)* #vis #sig {
60 #version_tokens
61 #(#stmts)*
62 }
63 };
64 stream.into()
65}