manifest-dir-macros-0.1.16/.cargo_vcs_info.json0000644000000001360000000000100147650ustar { "git": { "sha1": "268c993742c49a4deb75650a45d9d1a83f04222a" }, "path_in_vcs": "" }manifest-dir-macros-0.1.16/Cargo.lock0000644000000042140000000000100127410ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "manifest-dir-macros" version = "0.1.16" dependencies = [ "mime_guess", "once_cell", "proc-macro2", "quote", "syn", ] [[package]] name = "mime" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mime_guess" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" dependencies = [ "mime", "unicase", ] [[package]] name = "once_cell" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "proc-macro2" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "unicase" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ "version_check", ] [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" manifest-dir-macros-0.1.16/Cargo.toml0000644000000027000000000000100127620ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "manifest-dir-macros" version = "0.1.16" authors = ["Magic Len "] include = [ "src/**/*", "Cargo.toml", "README.md", "LICENSE", ] description = "This crate provides function-like macros to check or operate paths relative to CARGO_MANIFEST_DIR at compile time." homepage = "https://magiclen.org/manifest-dir-macros" readme = "README.md" keywords = [ "CARGO_MANIFEST_DIR", "directory", "project", "root", ] categories = [ "no-std", "rust-patterns", ] license = "MIT" repository = "https://github.com/magiclen/manifest-dir-macros" resolver = "2" [package.metadata.docs.rs] all-features = true [lib] proc-macro = true [dependencies.mime_guess] version = "2" optional = true [dependencies.once_cell] version = "1" [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "1" [features] default = ["replace-separator"] replace-separator = [] tuple = ["syn/full"] manifest-dir-macros-0.1.16/Cargo.toml.orig000064400000000000000000000015250072674642500164770ustar 00000000000000[package] name = "manifest-dir-macros" version = "0.1.16" authors = ["Magic Len "] edition = "2021" repository = "https://github.com/magiclen/manifest-dir-macros" homepage = "https://magiclen.org/manifest-dir-macros" keywords = ["CARGO_MANIFEST_DIR", "directory", "project", "root"] categories = ["no-std", "rust-patterns"] description = "This crate provides function-like macros to check or operate paths relative to CARGO_MANIFEST_DIR at compile time." readme = "README.md" license = "MIT" include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] [lib] proc-macro = true [dependencies] once_cell = "1" proc-macro2 = "1" syn = "1" quote = "1" mime_guess = { version = "2", optional = true } [features] default = ["replace-separator"] replace-separator = [] tuple = ["syn/full"] [package.metadata.docs.rs] all-features = truemanifest-dir-macros-0.1.16/LICENSE000064400000000000000000000020660072674642500146160ustar 00000000000000MIT License Copyright (c) 2021 magiclen.org (Ron Li) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. manifest-dir-macros-0.1.16/README.md000064400000000000000000000041140072674642500150640ustar 00000000000000Manifest Dir Macros ==================== [![CI](https://github.com/magiclen/manifest-dir-macros/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/manifest-dir-macros/actions/workflows/ci.yml) This crate provides function-like macros to check or operate paths relative to **CARGO_MANIFEST_DIR** at compile time. ## Examples ```rust #[macro_use] extern crate manifest_dir_macros; println!(path!("Cargo.toml")); println!(path!("src/lib.rs")); println!(path!("src", "lib.rs")); println!(path!("src", "lib.rs", "/bin")); println!(path!("/usr")); println!(exist_path!("Cargo.toml")); println!(directory_path!("src")); println!(not_directory_path!("Cargo.toml")); println!(file_path!("Cargo.toml")); println!(relative_path!("Cargo.toml")); println!(directory_relative_path!("src")); println!(not_directory_relative_path!("Cargo.toml")); println!(file_relative_path!("Cargo.toml")); println!(get_file_name!("src/lib.rs")); println!(get_file_name!(default = "main.rs", "/")); println!(get_file_stem!("src/lib.rs")); println!(get_file_stem!(default = "lib", "/")); println!(get_extension!("src/lib.rs")); println!(get_extension!(default = "rs", "src/lib")); println!(get_parent!("src/lib.rs")); println!(get_parent!(default = "/home", "/")); #[cfg(feature = "mime_guess")] { println!(mime_guess!("src/lib.rs")); println!(mime_guess!(default = "application/octet-stream", "Cargo.lock")); } // The `tuple` feature lets these macros above support to input nested literal string tuples, which is useful when you want to use these macros inside a `macro_rule!` macro and concatenate with other literal strings. // `$x:expr` matchers can be used in these macros thus. #[cfg(feature = "tuple")] { println!(path!(("foo",))); println!(path!(("foo", "bar"))); println!(path!("a", ("foo", "bar"))); println!(path!(("foo", "bar"), "a")); println!(path!(("foo", "bar"), ("a", "b"))); println!(path!(("foo", "bar", ("a", "b")), ("c", "d"))); } ``` ## Crates.io https://crates.io/crates/manifest-dir-macros ## Documentation https://docs.rs/manifest-dir-macros ## License [MIT](LICENSE) manifest-dir-macros-0.1.16/src/functions.rs000064400000000000000000000056520072674642500167620ustar 00000000000000#[cfg(all(windows, feature = "replace-separator"))] use std::ffi::OsString; use std::ffi::OsStr; use std::path::Path; use quote::quote; use syn::Expr; use crate::TokenStream; #[cfg(all(windows, feature = "replace-separator"))] // On Windows, `/` or `\` could be used as the path separator. We would prefer customarily using `/` as the separator in our hard code. This replacement is not necessary but can make the path look good. #[inline] pub fn beautify_windows_path(mut s: String) -> String { let bytes = unsafe { s.as_mut_vec() }; for b in bytes.iter_mut() { if *b == b'/' { *b = std::path::MAIN_SEPARATOR as u8; } } s } #[cfg(all(windows, feature = "replace-separator"))] #[inline] pub fn beautify_windows_path_os(s: OsString) -> Result { let s = s.into_string()?; Ok(beautify_windows_path(s)) } #[inline] pub fn compile_error>(s: S) -> TokenStream { let s = s.as_ref(); let code = quote! { compile_error!(#s) }; code.into() } #[inline] pub fn compile_error_not_exist>(p: P) -> TokenStream { compile_error(format!("The path {:?} does not exist", p.as_ref())) } #[inline] pub fn compile_error_not_directory>(p: P) -> TokenStream { compile_error(format!("The path {:?} is not a directory", p.as_ref())) } #[inline] pub fn compile_error_directory>(p: P) -> TokenStream { let p = p.as_ref(); if p.exists() { compile_error(format!("The path {:?} is a directory", p)) } else { compile_error_not_exist(p) } } #[inline] pub fn compile_error_not_file>(p: P) -> TokenStream { compile_error(format!("The path {:?} is not a file", p.as_ref())) } #[inline] pub fn compile_error_not_relative>(p: P) -> TokenStream { compile_error(format!("The path {:?} is not relative", p.as_ref())) } #[inline] pub fn compile_error_not_absolute>(p: P) -> TokenStream { compile_error(format!("The path {:?} is not absolute", p.as_ref())) } #[inline] pub fn output_os_str>(s: S) -> TokenStream { let s = s.as_ref(); match s.to_str() { Some(utf8_str) => { let code = quote! { #utf8_str }; code.into() } None => { compile_error(format!("The OsStr {:?} cannot be canonicalized to a UTF-8 string.", s)) } } } #[inline] pub fn output_path>(p: P) -> TokenStream { let p = p.as_ref(); match p.to_str() { Some(utf8_str) => { let code = quote! { #utf8_str }; code.into() } None => { compile_error(format!("The path {:?} cannot be canonicalized to a UTF-8 string.", p)) } } } #[inline] pub fn output_expr(expr: &Expr) -> TokenStream { let code = quote! { #expr }; code.into() } manifest-dir-macros-0.1.16/src/join_builder.rs000064400000000000000000000120320072674642500174050ustar 00000000000000use std::path::PathBuf; use syn::parse::{Parse, ParseStream}; use syn::{Expr, LitStr, Token}; #[cfg(feature = "tuple")] use syn::Lit; #[cfg(feature = "tuple")] use syn::spanned::Spanned; #[cfg(feature = "tuple")] use quote::ToTokens; pub struct JoinBuilder(pub PathBuf); pub struct JoinBuilderNoBeautify(pub PathBuf); pub struct JoinBuilderWithDefaultValue(pub PathBuf, pub Option); pub struct JoinBuilderNoBeautifyWithDefaultValue(pub PathBuf, pub Option); #[cfg(not(feature = "tuple"))] fn parse( input: ParseStream, default_value: bool, _beautify: bool, ) -> Result<(PathBuf, Option), syn::Error> { let default_value = if default_value && input.lookahead1().peek(Token!(default)) { input.parse::()?; input.parse::()?; let expr = input.parse::()?; input.parse::()?; Some(expr) } else { None }; let s = input.parse::()?.value(); #[cfg(all(windows, feature = "replace-separator"))] let s = if _beautify { crate::functions::beautify_windows_path(s) } else { s }; let mut path = PathBuf::from(s); loop { if input.is_empty() { return Ok((path, default_value)); } input.parse::()?; if input.is_empty() { return Ok((path, default_value)); } let s = input.parse::()?.value(); #[cfg(all(windows, feature = "replace-separator"))] let s = crate::functions::beautify_windows_path(s); path.push(s); } } #[cfg(feature = "tuple")] fn handle_expr(expr: Expr, path: &mut PathBuf, _beautify: bool) -> Result<(), syn::Error> { match expr { Expr::Lit(lit) => { if let Lit::Str(s) = lit.lit { let s = s.value(); #[cfg(all(windows, feature = "replace-separator"))] let s = if _beautify { crate::functions::beautify_windows_path(s) } else { s }; path.push(s); } else { return Err(syn::Error::new(lit.span(), "not a literal string")); } } Expr::Tuple(tuple) => { for expr in tuple.elems { handle_expr(expr, path, _beautify)?; } } Expr::Group(group) => { // In order to use the `expr` matcher in this macro. I don't know why it ends up here. let expr = syn::parse2::(group.expr.into_token_stream())?; return handle_expr(expr, path, _beautify); } Expr::Paren(paren) => { let expr = syn::parse2::(paren.expr.into_token_stream())?; return handle_expr(expr, path, _beautify); } _ => { return Err(syn::Error::new( expr.span(), "not a literal string or a literal string tuple", )); } } Ok(()) } #[cfg(feature = "tuple")] fn parse( input: ParseStream, default_value: bool, _beautify: bool, ) -> Result<(PathBuf, Option), syn::Error> { if input.is_empty() { // to hint developers that they must input some arguments let _ = input.parse::()?; } let default_value = if default_value && input.lookahead1().peek(Token!(default)) { input.parse::()?; input.parse::()?; let expr = input.parse::()?; input.parse::()?; Some(expr) } else { None }; let mut path = PathBuf::new(); while !input.is_empty() { let expr = input.parse::()?; handle_expr(expr, &mut path, _beautify)?; if input.lookahead1().peek(Token!(,)) { input.parse::()?; } else { break; } } Ok((path, default_value)) } impl Parse for JoinBuilder { #[inline] fn parse(input: ParseStream) -> Result { let result = parse(input, false, true)?; Ok(JoinBuilder(result.0)) } } impl Parse for JoinBuilderNoBeautify { #[inline] fn parse(input: ParseStream) -> Result { let result = parse(input, false, false)?; Ok(JoinBuilderNoBeautify(result.0)) } } impl Parse for JoinBuilderWithDefaultValue { #[inline] fn parse(input: ParseStream) -> Result { let result = parse(input, true, true)?; Ok(JoinBuilderWithDefaultValue(result.0, result.1)) } } impl Parse for JoinBuilderNoBeautifyWithDefaultValue { #[inline] fn parse(input: ParseStream) -> Result { let result = parse(input, true, false)?; Ok(JoinBuilderNoBeautifyWithDefaultValue(result.0, result.1)) } } impl From for PathBuf { #[inline] fn from(jb: JoinBuilder) -> Self { jb.0 } } impl From for PathBuf { #[inline] fn from(jb: JoinBuilderNoBeautify) -> Self { jb.0 } } manifest-dir-macros-0.1.16/src/lib.rs000064400000000000000000000365260072674642500155240ustar 00000000000000/*! # Manifest Dir Macros This crate provides function-like macros to check or operate paths relative to **CARGO_MANIFEST_DIR** at compile time. ## Examples ```rust #[macro_use] extern crate manifest_dir_macros; println!(path!("Cargo.toml")); println!(path!("src/lib.rs")); println!(path!("src", "lib.rs")); println!(path!("src", "lib.rs", "/bin")); println!(path!("/usr")); println!(exist_path!("Cargo.toml")); println!(directory_path!("src")); println!(not_directory_path!("Cargo.toml")); println!(file_path!("Cargo.toml")); println!(relative_path!("Cargo.toml")); println!(directory_relative_path!("src")); println!(not_directory_relative_path!("Cargo.toml")); println!(file_relative_path!("Cargo.toml")); println!(get_file_name!("src/lib.rs")); println!(get_file_name!(default = "main.rs", "/")); println!(get_file_stem!("src/lib.rs")); println!(get_file_stem!(default = "lib", "/")); println!(get_extension!("src/lib.rs")); println!(get_extension!(default = "rs", "src/lib")); println!(get_parent!("src/lib.rs")); println!(get_parent!(default = "/home", "/")); #[cfg(feature = "mime_guess")] { println!(mime_guess!("src/lib.rs")); println!(mime_guess!(default = "application/octet-stream", "Cargo.lock")); } // The `tuple` feature lets these macros above support to input nested literal string tuples, which is useful when you want to use these macros inside a `macro_rule!` macro and concatenate with other literal strings. // `$x:expr` matchers can be used in these macros thus. #[cfg(feature = "tuple")] { println!(path!(("foo",))); println!(path!(("foo", "bar"))); println!(path!("a", ("foo", "bar"))); println!(path!(("foo", "bar"), "a")); println!(path!(("foo", "bar"), ("a", "b"))); println!(path!(("foo", "bar", ("a", "b")), ("c", "d"))); } ``` */ mod functions; mod join_builder; use std::env; use std::path::PathBuf; use syn::parse_macro_input; #[cfg(feature = "mime_guess")] use quote::quote; use once_cell::sync::Lazy; use proc_macro::TokenStream; use join_builder::*; use functions::*; static MANIFEST_DIR: Lazy = Lazy::new(|| { let s = env::var_os("CARGO_MANIFEST_DIR").expect("we need CARGO_MANIFEST_DIR"); #[cfg(all(windows, feature = "replace-separator"))] let s = beautify_windows_path_os(s).expect("a UTF8-encodable CARGO_MANIFEST_DIR"); PathBuf::from(s) }); /// Allows input an absolute path, or a relative path. If a relative path is input, it will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn path(input: TokenStream) -> TokenStream { let original_path: PathBuf = syn::parse_macro_input!(input as JoinBuilder).into(); let p = if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) }; output_path(p) } /// Allows input an absolute path, or a relative path. (multiple components are supported) If a relative path is input, it will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must exist. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn exist_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); let p = if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) }; if p.exists() { output_path(p) } else { compile_error_not_exist(p) } } /// Allows input an absolute path, or a relative path. If a relative path is input, it will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must be an existing directory. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn directory_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); let p = if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) }; if p.is_dir() { output_path(p) } else { compile_error_not_directory(p) } } /// Allows input an absolute path, or a relative path. If a relative path is input, it will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must not be an existing directory. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn not_directory_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); let p = if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) }; if p.metadata().map(|m| !m.is_dir()).unwrap_or(false) { output_path(p) } else { compile_error_directory(p) } } /// Allows input an absolute path, or a relative path. If a relative path is input, it will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must be an existing file. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn file_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); let p = if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) }; if p.is_file() { output_path(p) } else { compile_error_not_file(p) } } /// Allows input a relative path. It will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn relative_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_relative() { output_path(MANIFEST_DIR.join(original_path)) } else { compile_error_not_relative(original_path) } } /// Allows input a relative path. It will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must exist. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn exist_relative_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_relative() { let p = MANIFEST_DIR.join(original_path); if p.exists() { output_path(p) } else { compile_error_not_exist(p) } } else { compile_error_not_relative(original_path) } } /// Allows input a relative path. It will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must be a directory. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn directory_relative_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_relative() { let p = MANIFEST_DIR.join(original_path); if p.is_dir() { output_path(p) } else { compile_error_not_directory(p) } } else { compile_error_not_relative(original_path) } } /// Allows input a relative path. It will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must not be a directory. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn not_directory_relative_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_relative() { let p = MANIFEST_DIR.join(original_path); if p.metadata().map(|m| !m.is_dir()).unwrap_or(false) { output_path(p) } else { compile_error_directory(p) } } else { compile_error_not_relative(original_path) } } /// Allows input a relative path. It will be relative to the CARGO_MANIFEST_DIR (a directory where your `Cargo.toml` located). Returns an absolute path, and it must be a file. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn file_relative_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_relative() { let p = MANIFEST_DIR.join(original_path); if p.is_file() { output_path(p) } else { compile_error_not_file(p) } } else { compile_error_not_relative(original_path) } } /// Allows input a absolute path. Checks and returns the absolute path. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn absolute_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_absolute() { output_path(original_path) } else { compile_error_not_absolute(original_path) } } /// Allows input a absolute path. Checks whether it exists and returns the absolute path. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn exist_absolute_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_absolute() { if original_path.exists() { output_path(original_path) } else { compile_error_not_exist(original_path) } } else { compile_error_not_absolute(original_path) } } /// Allows input a absolute path. Checks whether it is a directory and returns the absolute path. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn directory_absolute_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_absolute() { if original_path.is_dir() { output_path(original_path) } else { compile_error_not_directory(original_path) } } else { compile_error_not_absolute(original_path) } } /// Allows input a absolute path. Checks whether it is not a directory and returns the absolute path. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn not_directory_absolute_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_absolute() { if original_path.metadata().map(|m| !m.is_dir()).unwrap_or(false) { output_path(original_path) } else { compile_error_directory(original_path) } } else { compile_error_not_absolute(original_path) } } /// Allows input a absolute path. Checks whether it is a file and returns the absolute path. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn file_absolute_path(input: TokenStream) -> TokenStream { let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into(); if original_path.is_absolute() { if original_path.is_file() { output_path(original_path) } else { compile_error_not_file(original_path) } } else { compile_error_not_absolute(original_path) } } /// Gets the file name for other purposes. If there is no file name, the default value will be used, or a compile error will be shown. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn get_file_name(input: TokenStream) -> TokenStream { let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue); match jb.0.file_name() { Some(file_name) => output_os_str(file_name), None => { match jb.1 { Some(expr) => output_expr(&expr), None => compile_error(format!("The path {:?} has no file name", jb.0)), } } } } /// Gets the file stem for other purposes. If there is no file stem, the default value will be used, or a compile error will be shown. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn get_file_stem(input: TokenStream) -> TokenStream { let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue); match jb.0.file_stem() { Some(file_stem) => output_os_str(file_stem), None => { match jb.1 { Some(expr) => output_expr(&expr), None => compile_error(format!("The path {:?} has no file stem", jb.0)), } } } } /// Gets the file extension for other purposes. If there is no file extension, the default value will be used, or a compile error will be shown. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn get_extension(input: TokenStream) -> TokenStream { let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue); match jb.0.extension() { Some(extension) => output_os_str(extension), None => { match jb.1 { Some(expr) => output_expr(&expr), None => compile_error(format!("The path {:?} has no file extension", jb.0)), } } } } /// Gets the parent for other purposes. If there is no parent, the default value will be used, or a compile error will be shown. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn get_parent(input: TokenStream) -> TokenStream { let jb = parse_macro_input!(input as JoinBuilderWithDefaultValue); match jb.0.parent() { Some(parent) => output_path(parent), None => { match jb.1 { Some(expr) => output_expr(&expr), None => compile_error(format!("The path {:?} has no parent", jb.0)), } } } } #[cfg(feature = "mime_guess")] /// Guesses the mime type by the path. If the guess fails, the default value will be used, or a compile error will be shown. /// /// Multiple components can be input by using commas to separate them. #[proc_macro] pub fn mime_guess(input: TokenStream) -> TokenStream { let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue); match jb .0 .extension() .and_then(|ext| ext.to_str()) .and_then(|ext| mime_guess::from_ext(ext).first()) .map(|mime| mime.to_string()) { Some(mime) => { let code = quote! { #mime }; code.into() } None => { match jb.1 { Some(expr) => output_expr(&expr), None => { compile_error(format!( "The path {:?} can not be guessed for its mime type", jb.0 )) } } } } }