Skip to main content

zenops_safe_relative_path_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use relative_path::RelativePath;
4use syn::{Ident, LitStr, parse_macro_input};
5
6/// Creates a `SafeRelativePath` without run-time cost by validating the string literal at compile time
7#[proc_macro]
8pub fn srpath(input: TokenStream) -> TokenStream {
9    let arg = parse_macro_input!(input as LitStr).value();
10
11    match RelativePath::from_path(&arg) {
12        Ok(path) => {
13            if zenops_safe_relative_path_validator::is_safe_relative_path(path) {
14                quote! {
15                    unsafe {
16                        ::zenops_safe_relative_path::SafeRelativePath::new_unchecked_from_str(#arg)
17                    }
18                }
19                .into()
20            } else {
21                syn::Error::new_spanned(&arg, "The relative path uses traversal")
22                    .to_compile_error()
23                    .into()
24            }
25        }
26        Err(e) => syn::Error::new_spanned(&arg, e).to_compile_error().into(),
27    }
28}
29
30#[proc_macro]
31pub fn generate_is_valid_path_code(input: TokenStream) -> TokenStream {
32    let ident = parse_macro_input!(input as Ident);
33
34    quote! {
35        ::std::convert::AsRef<::relative_path::RelativePath>
36            .as_ref(#ident)
37            .components()
38            .scan(0, |level, c| {
39                match c {
40                    relative_path::Component::CurDir => (),
41                    relative_path::Component::ParentDir => *level -= 1,
42                    relative_path::Component::Normal(_) => *level += 1,
43                }
44                Some(*level)
45            })
46            .all(|level| level >= 0)
47    }
48    .into()
49}