macron_path/
lib.rs

1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6
7/// Creates a new instance of PathBuf
8#[proc_macro]
9pub fn path(input: TokenStream) -> TokenStream {
10    // empty string:
11    if input.is_empty() {
12        return quote! { ::std::path::PathBuf::new() }.into();
13    }
14    
15    // string format:
16    let Format { expr, args } = syn::parse_macro_input!(input as Format);
17    
18    let path_expr = match expr {
19        syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(fmt), .. }) => {
20            if args.is_some() || fmt.value().contains(&['{', '}'][..]) {
21                quote! { ::std::path::PathBuf::from(::std::format!(#fmt #args)) }
22            } else {
23                quote! { ::std::path::PathBuf::from(#fmt) }
24            }
25        }
26        _ => quote! { ::std::path::PathBuf::from(#expr) }
27    };
28
29    quote! {{
30        let path = #path_expr;
31        if path.to_string_lossy().starts_with("/") || path.to_string_lossy().starts_with("\\") {
32            let exe_path = ::std::env::current_exe().expect("Failed to get exe path");
33            let exe_dir = exe_path.parent().expect("Failed to get exe dir");
34
35            let rel_str = path.to_str().expect("Invalid path");
36            let rel_str = if rel_str.starts_with('/') || rel_str.starts_with('\\') {
37                &rel_str[1..]
38            } else {
39                rel_str
40            };
41            exe_dir.join(rel_str)
42        } else {
43            path
44        }
45    }}.into()
46}
47
48/// The string formatter
49struct Format {
50    pub expr: syn::Expr,
51    pub args: Option<TokenStream2>,
52}
53
54impl syn::parse::Parse for Format {
55    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
56        // parse expression:
57        let expr = input.parse()?;
58
59        // parse arguments:
60        let args = if input.peek(syn::token::Comma) {
61            Some(input.parse()?)
62        } else {
63            None
64        };
65        
66        Ok(Self { expr, args })
67    }
68}