manifest_dir_macros/
lib.rs1mod functions;
57mod join_builder;
58
59use std::{env, path::PathBuf};
60
61use functions::*;
62use join_builder::*;
63use once_cell::sync::Lazy;
64use proc_macro::TokenStream;
65#[cfg(feature = "mime_guess")]
66use quote::quote;
67use syn::parse_macro_input;
68
69static MANIFEST_DIR: Lazy<PathBuf> = Lazy::new(|| {
70 let s = env::var_os("CARGO_MANIFEST_DIR").expect("we need CARGO_MANIFEST_DIR");
71
72 #[cfg(all(windows, feature = "replace-separator"))]
73 let s = beautify_windows_path_os(s).expect("a UTF8-encodable CARGO_MANIFEST_DIR");
74
75 PathBuf::from(s)
76});
77
78#[proc_macro]
82pub fn path(input: TokenStream) -> TokenStream {
83 let original_path: PathBuf = syn::parse_macro_input!(input as JoinBuilder).into();
84
85 let p =
86 if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) };
87
88 output_path(p)
89}
90
91#[proc_macro]
95pub fn exist_path(input: TokenStream) -> TokenStream {
96 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
97
98 let p =
99 if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) };
100
101 if p.exists() {
102 output_path(p)
103 } else {
104 compile_error_not_exist(p)
105 }
106}
107
108#[proc_macro]
112pub fn directory_path(input: TokenStream) -> TokenStream {
113 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
114
115 let p =
116 if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) };
117
118 if p.is_dir() {
119 output_path(p)
120 } else {
121 compile_error_not_directory(p)
122 }
123}
124
125#[proc_macro]
129pub fn not_directory_path(input: TokenStream) -> TokenStream {
130 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
131
132 let p =
133 if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) };
134
135 if p.metadata().map(|m| !m.is_dir()).unwrap_or(false) {
136 output_path(p)
137 } else {
138 compile_error_directory(p)
139 }
140}
141
142#[proc_macro]
146pub fn file_path(input: TokenStream) -> TokenStream {
147 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
148
149 let p =
150 if original_path.is_absolute() { original_path } else { MANIFEST_DIR.join(original_path) };
151
152 if p.is_file() {
153 output_path(p)
154 } else {
155 compile_error_not_file(p)
156 }
157}
158
159#[proc_macro]
163pub fn relative_path(input: TokenStream) -> TokenStream {
164 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
165
166 if original_path.is_relative() {
167 output_path(MANIFEST_DIR.join(original_path))
168 } else {
169 compile_error_not_relative(original_path)
170 }
171}
172
173#[proc_macro]
177pub fn exist_relative_path(input: TokenStream) -> TokenStream {
178 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
179
180 if original_path.is_relative() {
181 let p = MANIFEST_DIR.join(original_path);
182
183 if p.exists() {
184 output_path(p)
185 } else {
186 compile_error_not_exist(p)
187 }
188 } else {
189 compile_error_not_relative(original_path)
190 }
191}
192
193#[proc_macro]
197pub fn directory_relative_path(input: TokenStream) -> TokenStream {
198 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
199
200 if original_path.is_relative() {
201 let p = MANIFEST_DIR.join(original_path);
202
203 if p.is_dir() {
204 output_path(p)
205 } else {
206 compile_error_not_directory(p)
207 }
208 } else {
209 compile_error_not_relative(original_path)
210 }
211}
212
213#[proc_macro]
217pub fn not_directory_relative_path(input: TokenStream) -> TokenStream {
218 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
219
220 if original_path.is_relative() {
221 let p = MANIFEST_DIR.join(original_path);
222
223 if p.metadata().map(|m| !m.is_dir()).unwrap_or(false) {
224 output_path(p)
225 } else {
226 compile_error_directory(p)
227 }
228 } else {
229 compile_error_not_relative(original_path)
230 }
231}
232
233#[proc_macro]
237pub fn file_relative_path(input: TokenStream) -> TokenStream {
238 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
239
240 if original_path.is_relative() {
241 let p = MANIFEST_DIR.join(original_path);
242
243 if p.is_file() {
244 output_path(p)
245 } else {
246 compile_error_not_file(p)
247 }
248 } else {
249 compile_error_not_relative(original_path)
250 }
251}
252
253#[proc_macro]
257pub fn absolute_path(input: TokenStream) -> TokenStream {
258 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
259
260 if original_path.is_absolute() {
261 output_path(original_path)
262 } else {
263 compile_error_not_absolute(original_path)
264 }
265}
266
267#[proc_macro]
271pub fn exist_absolute_path(input: TokenStream) -> TokenStream {
272 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
273
274 if original_path.is_absolute() {
275 if original_path.exists() {
276 output_path(original_path)
277 } else {
278 compile_error_not_exist(original_path)
279 }
280 } else {
281 compile_error_not_absolute(original_path)
282 }
283}
284
285#[proc_macro]
289pub fn directory_absolute_path(input: TokenStream) -> TokenStream {
290 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
291
292 if original_path.is_absolute() {
293 if original_path.is_dir() {
294 output_path(original_path)
295 } else {
296 compile_error_not_directory(original_path)
297 }
298 } else {
299 compile_error_not_absolute(original_path)
300 }
301}
302
303#[proc_macro]
307pub fn not_directory_absolute_path(input: TokenStream) -> TokenStream {
308 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
309
310 if original_path.is_absolute() {
311 if original_path.metadata().map(|m| !m.is_dir()).unwrap_or(false) {
312 output_path(original_path)
313 } else {
314 compile_error_directory(original_path)
315 }
316 } else {
317 compile_error_not_absolute(original_path)
318 }
319}
320
321#[proc_macro]
325pub fn file_absolute_path(input: TokenStream) -> TokenStream {
326 let original_path: PathBuf = parse_macro_input!(input as JoinBuilder).into();
327
328 if original_path.is_absolute() {
329 if original_path.is_file() {
330 output_path(original_path)
331 } else {
332 compile_error_not_file(original_path)
333 }
334 } else {
335 compile_error_not_absolute(original_path)
336 }
337}
338
339#[proc_macro]
343pub fn get_file_name(input: TokenStream) -> TokenStream {
344 let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue);
345
346 match jb.0.file_name() {
347 Some(file_name) => output_os_str(file_name),
348 None => match jb.1 {
349 Some(expr) => output_expr(&expr),
350 None => compile_error(format!("The path {:?} has no file name", jb.0)),
351 },
352 }
353}
354
355#[proc_macro]
359pub fn get_file_stem(input: TokenStream) -> TokenStream {
360 let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue);
361
362 match jb.0.file_stem() {
363 Some(file_stem) => output_os_str(file_stem),
364 None => match jb.1 {
365 Some(expr) => output_expr(&expr),
366 None => compile_error(format!("The path {:?} has no file stem", jb.0)),
367 },
368 }
369}
370
371#[proc_macro]
375pub fn get_extension(input: TokenStream) -> TokenStream {
376 let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue);
377
378 match jb.0.extension() {
379 Some(extension) => output_os_str(extension),
380 None => match jb.1 {
381 Some(expr) => output_expr(&expr),
382 None => compile_error(format!("The path {:?} has no file extension", jb.0)),
383 },
384 }
385}
386
387#[proc_macro]
391pub fn get_parent(input: TokenStream) -> TokenStream {
392 let jb = parse_macro_input!(input as JoinBuilderWithDefaultValue);
393
394 match jb.0.parent() {
395 Some(parent) => output_path(parent),
396 None => match jb.1 {
397 Some(expr) => output_expr(&expr),
398 None => compile_error(format!("The path {:?} has no parent", jb.0)),
399 },
400 }
401}
402
403#[cfg(feature = "mime_guess")]
404#[proc_macro]
408pub fn mime_guess(input: TokenStream) -> TokenStream {
409 let jb = parse_macro_input!(input as JoinBuilderNoBeautifyWithDefaultValue);
410
411 match jb
412 .0
413 .extension()
414 .and_then(|ext| ext.to_str())
415 .and_then(|ext| mime_guess::from_ext(ext).first())
416 .map(|mime| mime.to_string())
417 {
418 Some(mime) => {
419 let code = quote! {
420 #mime
421 };
422
423 code.into()
424 },
425 None => match jb.1 {
426 Some(expr) => output_expr(&expr),
427 None => {
428 compile_error(format!("The path {:?} can not be guessed for its mime type", jb.0))
429 },
430 },
431 }
432}