path_dsl/
lib.rs

1//! Utility DSL and macro to help deal with Paths.
2//!
3//! PathDSL provides a simple and zero-overhead abstraction for creating
4//! paths and appending to existing `Path`-like things.
5//!
6//! # Overview
7//!
8//! ```rust
9//! use path_dsl::path;
10//! # use std::path::{PathBuf, Path};
11//!
12//! // PathBuf::push() only called once with consecutive literals:
13//! let literals: PathBuf = path!("dir1" | "dir2" | "dir3");
14//! // Type annotation for illustration purposes; not needed
15//!
16//! // Does not copy data if first path segment is a owning value:
17//! let moving = path!(literals | "dir4");
18//! # let mut moving_pb = PathBuf::new();
19//! # moving_pb.push("dir1");
20//! # moving_pb.push("dir2");
21//! # moving_pb.push("dir3");
22//! # moving_pb.push("dir4");
23//! # assert_eq!(moving, moving_pb);
24//!
25//! // Mixing and matching is easy:
26//! let start = path!("some" | "dir");
27//! let end = path!("my_folder" | "my_file.txt");
28//! # let mut end_pb = PathBuf::new();
29//! # end_pb.push("my_folder");
30//! # end_pb.push("my_file.txt");
31//! # assert_eq!(end, end_pb);
32//! // Can borrow as normal
33//! let result = path!(start | "middle_folder" | &end);
34//! # let mut result_pb = PathBuf::new();
35//! # result_pb.push("some");
36//! # result_pb.push("dir");
37//! # result_pb.push("middle_folder");
38//! # result_pb.push(end_pb);
39//! # assert_eq!(result, result_pb);
40//!
41//! // Works with PathBuf, Path, and String-likes
42//! let file = Path::new("file.txt");
43//! let folder = PathBuf::from("folder");
44//! let middle: &str = "other_middle";
45//! let combined = path!(folder | middle | "middle_folder" | file);
46//! # let mut combined_pb = PathBuf::new();
47//! # combined_pb.push("folder");
48//! # combined_pb.push("other_middle");
49//! # combined_pb.push("middle_folder");
50//! # combined_pb.push("file.txt");
51//! # assert_eq!(combined, combined_pb);
52//! ```
53//!
54//! # PathDSL Macro and Type
55//!
56//! PathDSL's [`path!`](macro.path.html) macro allows for the creation of a `PathBuf` in the most efficent way possible in the situation.
57//!
58//! note the use of `|` instead of `/` due to rust's macro rules
59//!
60//! ```rust
61//! use path_dsl::path;
62//! // Type annotation for illustration only, not needed
63//! let path: PathBuf = path!("dir1" | "dir2" | "dir3" | "file.txt");
64//!
65//! # use std::path::PathBuf;
66//! # let mut path2 = PathBuf::new();
67//! # path2.push("dir1");
68//! # path2.push("dir2");
69//! # path2.push("dir3");
70//! # path2.push("file.txt");
71//! # assert_eq!(path, path2);
72//! ```
73//!
74//! ### PathDSL
75//!
76//! You can also generate a PathDSL directly, though this is discouraged. PathDSL will pretend to be
77//! a `PathBuf` as best it can, but it is almost always more efficent to use the `path!` macro to generate
78//! a `PathBuf` directly.
79//!
80//! ```rust
81//! use path_dsl::PathDSL;
82//! let path = PathDSL::from("dir1") / "dir2" / "dir3" / "file.txt";
83//!
84//! # use std::path::PathBuf;
85//! # let mut path2 = PathBuf::new();
86//! # path2.push("dir1");
87//! # path2.push("dir2");
88//! # path2.push("dir3");
89//! # path2.push("file.txt");
90//! # assert_eq!(path, path2);
91//! ```
92//!
93//! ### Adding Path-Like Structures
94//!
95//! As well as using regular string literals, you can use anything that can be passed to `PathBuf::push`
96//! as a part of the DSL.
97//!
98//! Note the borrow on `other`: as these types are not `Copy`, they will be moved
99//! into the path unless you borrow them. This matches behavior with `PathBuf::push`, but can be surprising
100//! when used in a infix expression.
101//!
102//! ```rust
103//! use path_dsl::{path, PathDSL};
104//!
105//! let other = PathBuf::from("some_dir");
106//! let filename: &str = "my_file.txt";
107//!
108//! let mac: PathBuf  = path!("dir1" | "dir2" | &other | filename); // Preferred
109//! let path: PathDSL = PathDSL::from("dir1") / "dir2" / other / filename; // Also works
110//!
111//! # use std::path::PathBuf;
112//! # let mut path2 = PathBuf::new();
113//! # path2.push("dir1");
114//! # path2.push("dir2");
115//! # path2.push("some_dir");
116//! # path2.push("my_file.txt");
117//! # assert_eq!(path, path2);
118//! # assert_eq!(mac, path2);
119//! ```
120//!
121//! ### Moving vs Borrowing
122//!
123//! Both the macro and the DSL type behave the same with regard to borrowing vs moving. If a
124//! reference is provided, it will borrow the provided value. However, if a value is provided
125//! **it will move it**, making the value unusable afterwards. While these are the normal rules
126//! for rust, infix operators are normally used with `Copy` types, so this may be **surprising**.
127//!
128//! Both mutable and immutable borrows are supported, though they will never actually mutate anything.
129//!
130//! ```rust,compile_fail
131//! use path_dsl::path;
132//! # use std::path::PathBuf;
133//!
134//! let value = PathBuf::from("some_dir");
135//! let borrow: &str = "my_file.txt";
136//!
137//! let mac  = path!(value | borrow);
138//! let path = path!(value | borrow); // Will not compile because `value` was moved
139//! ```
140//!
141//! You must manually borrow it:
142//!
143//! ```rust
144//! # use path_dsl::{path, PathDSL};
145//! #
146//! # let value = PathBuf::from("some_dir");
147//! # let borrow: &str = "my_file.txt";
148//! #
149//! let mac  = path!(&value | borrow); // Borrow value so it can be used later
150//! let path = PathDSL::new() / value / borrow; // Not used afterwards, so doesn't need a borrow
151//!
152//! # use std::path::PathBuf;
153//! # let mut path2 = PathBuf::new();
154//! # path2.push("some_dir");
155//! # path2.push("my_file.txt");
156//! # assert_eq!(path, path2);
157//! # assert_eq!(mac, path2);
158//! ```
159//!
160//! ### PathDSL <=> PathBuf
161//!
162//! **The PathDSL type is not meant to be used directly, but exists to allow the macro to work.
163//! It was once intended to be directly used, so it ratains this functionality.**
164//!
165//! `PathDSL` is designed to be a drop-in replacement for `PathBuf`, including trivial conversions
166//! between the two. In any situation where you would be able to use `PathBuf` you can use
167//! `PathDSL`. `PathDSL` includes an implementation of `Deref` to a `PathBuf` (and by proxy `Path`) and re-implements all functions that take `self`, so is fully api compatable.
168//! However there are some situations where you must have a `PathBuf`.
169//! Obtaining a `&PathBuf` is trivial through dereferencing and obtaining a `PathBuf` is possible through the [`PathDSL::into_pathbuf`](struct.PathDSL.html#method.into_pathbuf) function.
170//!
171//! PathDSL is `#[repr(transparent)]` over `PathBuf` and all functions are force-inlined so
172//! conversions and operations should be cost-free compared to the equivalent `PathBuf` operation.
173//! If they aren't, please file a bug.
174//!
175//! Some known issues are:
176//!
177//! **Equality**
178//!
179//! ```rust
180//! use path_dsl::path;
181//! # use std::path::PathBuf;
182//!
183//! let dsl = path!("file.txt");
184//! let buf = PathBuf::from("file.txt");
185//!
186//! assert!(dsl == buf);
187//! // Must de-reference to PathBuf can't implement `Eq` for `PathBuf`
188//! assert!(buf == *dsl);
189//! ```
190//!
191//! **Function Calls**
192//!
193//! ```rust
194//! use path_dsl::path;
195//! # use std::path::PathBuf;
196//!
197//! fn func(p: PathBuf) {
198//! # assert_eq!(p, PathBuf::from("file.txt"));
199//! }
200//!
201//! let dsl = path!("file.txt");
202//! let buf = PathBuf::from("file.txt");
203//!
204//! func(buf);
205//! // Must convert into `PathBuf`
206//! // Dereferencing doesn't work because `func` moves.
207//! func(dsl.to_path_buf());
208//! # let dsl = path!("file.txt");
209//! func(dsl.into()) // also works
210//! ```
211//!
212//! ### Macro Optimizations
213//!
214//! As previously mentioned, the macro contains some optimizations over using raw `PathDSL` and should always
215//! be used over manually using PathDSL. These optimizations happen at compile time, and are guaranteed.
216//! Further details on these can be found on the [`path!`](macro.path.html) macro documentation.
217//!
218//! **String Literal Concatenation:**
219//!
220//! While it is ill-advised to use string literals with slashes in a `Path`, The [`path!`](macro.path.html) macro
221//! takes slashes into account, and automatically constructs a single string literal from multiple
222//! consecutive string literals. This can potentially save an allocation or two in the underlying
223//! `OsString`.
224//!
225//! ```rust
226//! use path_dsl::path;
227//! # use std::path::PathBuf;
228//!
229//! let p = path!("this" | "is" | "combined");
230//! if cfg!(windows) {
231//!     assert_eq!(p, PathBuf::from("this\\is\\combined"));
232//! } else {
233//!     assert_eq!(p, PathBuf::from("this/is/combined"));
234//! }
235//! ```
236//!
237//! **First-Argument Optimization:**
238//!
239//! When the very first argument of the [`path!`](macro.path.html) macro is a owning `PathBuf`, `OsString` or `PathDSL`
240//! passed by value (moved), instead of copying everything into a new `PathDSL`, it will just steal the
241//! buffer from that moved-in value. This allows you to use the [`path!`](macro.path.html) macro fearlessly when
242//! appending to already existing variables.
243//!
244//! ```rust
245//! use path_dsl::path;
246//! # use std::path::PathBuf;
247//!
248//! let first = PathBuf::from("a_very_long_folder_name");
249//! # let dup = first.clone();
250//! let p = path!(first); // Does not copy anything.
251//!
252//! # assert_eq!(p, dup);
253//! ```
254//!
255//! # Why Use A Crate?
256//!
257//! You may be wondering why you should use a crate for this when you can easily wrap `PathBuf` and
258//! add some `Div` implementations. This is basically what I thought as well until I actually went
259//! to go implement this crate. There is a surprising amount of very tedious and particular code to try to emulate
260//! `PathBuf` directly, as well as to test the functionality.
261//!
262//! With this in mind, I have made `path_dsl` completely dependency free, choosing to lean on declarative
263//! macros over proc macros as to not depend on things like `syn`. Additionally, everything is contained within
264//! this one file, I have thorough tests, and I have added `#[deny(unsafe_code)]` for good measure.
265//! Hopefully this makes this crate light enough and easily-auditable enough to be an acceptable dependency.
266
267#![allow(clippy::cognitive_complexity)]
268#![allow(clippy::float_cmp)]
269#![deny(nonstandard_style)]
270#![deny(future_incompatible)]
271#![deny(rust_2018_idioms)]
272#![deny(unsafe_code)]
273#![warn(missing_docs)]
274#![warn(unused)]
275
276use std::borrow::{Borrow, Cow};
277use std::cmp::Ordering;
278use std::convert::Infallible;
279use std::ffi::{OsStr, OsString};
280use std::hash::{Hash, Hasher};
281use std::ops::{Deref, DerefMut, Div};
282use std::path::{Iter, Path, PathBuf};
283use std::rc::Rc;
284use std::str::FromStr;
285use std::sync::Arc;
286
287#[cfg(test)]
288mod tests;
289
290/// A PathBuf wrapper that has support for a Path DSL.
291///
292/// It is usable nearly identically to a PathBuf.
293/// Supports [`Deref`](https://doc.rust-lang.org/stable/core/ops/trait.Deref.html) to
294/// [`PathBuf`](https://doc.rust-lang.org/stable/std/path/struct.PathBuf.html) to cover all edge cases.
295///
296/// Prefer using the [`path!`](macro.path.html) macro.
297///
298/// See [crate documentation](index.html) for usage examples.
299#[derive(Debug, Clone, Default)]
300#[repr(transparent)]
301pub struct PathDSL {
302    path: PathBuf,
303}
304
305impl PathDSL {
306    /// Creates a new PathDSL with a new empty PathBuf inside
307    #[inline(always)]
308    pub fn new() -> Self {
309        PathDSL { path: PathBuf::new() }
310    }
311
312    /// Forwarder function for [`PathBuf::into_os_string`](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.into_os_string)
313    #[inline(always)]
314    pub fn into_os_string(self) -> OsString {
315        self.path.into_os_string()
316    }
317
318    /// Forwarder function for [`PathBuf::into_boxed_path`](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.into_boxed_path)
319    #[inline(always)]
320    pub fn into_boxed_path(self) -> Box<Path> {
321        self.path.into_boxed_path()
322    }
323
324    /// Converts this PathDSL into the underlying PathBuf
325    #[inline(always)]
326    pub fn into_pathbuf(self) -> PathBuf {
327        self.into()
328    }
329}
330
331//////////////////////////////////
332// Pretending to be a Path(Buf) //
333//////////////////////////////////
334
335impl AsRef<Path> for PathDSL {
336    #[inline(always)]
337    fn as_ref(&self) -> &Path {
338        self.path.as_ref()
339    }
340}
341
342impl AsMut<PathBuf> for PathDSL {
343    #[inline(always)]
344    fn as_mut(&mut self) -> &mut PathBuf {
345        &mut self.path
346    }
347}
348
349impl AsRef<OsStr> for PathDSL {
350    #[inline(always)]
351    fn as_ref(&self) -> &OsStr {
352        self.path.as_ref()
353    }
354}
355
356impl Deref for PathDSL {
357    type Target = PathBuf;
358
359    #[inline(always)]
360    fn deref(&self) -> &Self::Target {
361        &self.path
362    }
363}
364
365impl DerefMut for PathDSL {
366    #[inline(always)]
367    fn deref_mut(&mut self) -> &mut Self::Target {
368        &mut self.path
369    }
370}
371
372//////////
373// From //
374//////////
375
376impl<T> From<&T> for PathDSL
377where
378    T: AsRef<Path> + ?Sized,
379{
380    #[inline(always)]
381    fn from(other: &T) -> Self {
382        PathDSL {
383            path: PathBuf::from(other.as_ref()),
384        }
385    }
386}
387
388impl<T> From<&mut T> for PathDSL
389where
390    T: AsRef<Path> + ?Sized,
391{
392    #[inline(always)]
393    fn from(other: &mut T) -> Self {
394        PathDSL {
395            path: PathBuf::from(other.as_ref()),
396        }
397    }
398}
399
400impl From<PathBuf> for PathDSL {
401    #[inline(always)]
402    fn from(other: PathBuf) -> Self {
403        PathDSL { path: other }
404    }
405}
406
407impl From<OsString> for PathDSL {
408    #[inline(always)]
409    fn from(other: OsString) -> Self {
410        PathDSL {
411            path: PathBuf::from(other),
412        }
413    }
414}
415
416impl From<String> for PathDSL {
417    #[inline(always)]
418    fn from(other: String) -> Self {
419        PathDSL {
420            path: PathBuf::from(other),
421        }
422    }
423}
424
425impl From<Box<Path>> for PathDSL {
426    #[inline(always)]
427    fn from(other: Box<Path>) -> Self {
428        PathDSL {
429            path: PathBuf::from(other),
430        }
431    }
432}
433
434impl From<Cow<'_, Path>> for PathDSL {
435    #[inline(always)]
436    fn from(other: Cow<'_, Path>) -> Self {
437        PathDSL {
438            path: PathBuf::from(other),
439        }
440    }
441}
442
443impl From<Cow<'_, OsStr>> for PathDSL {
444    #[inline(always)]
445    fn from(other: Cow<'_, OsStr>) -> Self {
446        PathDSL {
447            path: PathBuf::from(&*other),
448        }
449    }
450}
451
452//////////
453// Into //
454//////////
455// We can't implement from on these types, so the best we can do is Into.
456
457impl Into<PathBuf> for PathDSL {
458    #[inline(always)]
459    fn into(self) -> PathBuf {
460        self.path
461    }
462}
463
464impl Into<OsString> for PathDSL {
465    #[inline(always)]
466    fn into(self) -> OsString {
467        self.into_os_string()
468    }
469}
470
471impl Into<Box<Path>> for PathDSL {
472    #[inline(always)]
473    fn into(self) -> Box<Path> {
474        self.into_boxed_path()
475    }
476}
477
478impl<'a> Into<Cow<'a, Path>> for PathDSL {
479    #[inline(always)]
480    fn into(self) -> Cow<'a, Path> {
481        self.path.into()
482    }
483}
484
485impl<'a> Into<Cow<'a, Path>> for &'a PathDSL {
486    #[inline(always)]
487    fn into(self) -> Cow<'a, Path> {
488        Cow::Borrowed(self.path.as_path())
489    }
490}
491
492impl<'a> Into<Cow<'a, OsStr>> for &'a PathDSL {
493    #[inline(always)]
494    fn into(self) -> Cow<'a, OsStr> {
495        Cow::Borrowed(self.path.as_os_str())
496    }
497}
498
499impl<'a> Into<Arc<Path>> for PathDSL {
500    #[inline(always)]
501    fn into(self) -> Arc<Path> {
502        self.path.into()
503    }
504}
505
506impl<'a> Into<Rc<Path>> for PathDSL {
507    #[inline(always)]
508    fn into(self) -> Rc<Path> {
509        self.path.into()
510    }
511}
512
513////////////////
514// Partial Eq //
515////////////////
516
517impl PartialEq<PathDSL> for PathDSL {
518    #[inline(always)]
519    fn eq(&self, other: &PathDSL) -> bool {
520        self.path == other.path
521    }
522}
523
524impl PartialEq<PathBuf> for PathDSL {
525    #[inline(always)]
526    fn eq(&self, other: &PathBuf) -> bool {
527        self.path == *other
528    }
529}
530
531impl PartialEq<Path> for PathDSL {
532    #[inline(always)]
533    fn eq(&self, other: &Path) -> bool {
534        self.path.as_path() == other
535    }
536}
537
538impl PartialEq<OsStr> for PathDSL {
539    #[inline(always)]
540    fn eq(&self, other: &OsStr) -> bool {
541        self.path.as_path() == other
542    }
543}
544
545impl PartialEq<OsString> for PathDSL {
546    #[inline(always)]
547    fn eq(&self, other: &OsString) -> bool {
548        self.path.as_path() == other
549    }
550}
551
552impl<'a> PartialEq<Cow<'a, Path>> for PathDSL {
553    #[inline(always)]
554    fn eq(&self, other: &Cow<'a, Path>) -> bool {
555        self.path.as_path() == other
556    }
557}
558
559impl<'a> PartialEq<Cow<'a, OsStr>> for PathDSL {
560    #[inline(always)]
561    fn eq(&self, other: &Cow<'a, OsStr>) -> bool {
562        self.path.as_path() == other
563    }
564}
565
566////////
567// Eq //
568////////
569
570impl Eq for PathDSL {}
571
572/////////////////
573// Partial Ord //
574/////////////////
575
576impl PartialOrd<PathDSL> for PathDSL {
577    #[inline(always)]
578    fn partial_cmp(&self, other: &PathDSL) -> Option<Ordering> {
579        self.path.partial_cmp(&other.path)
580    }
581}
582
583impl PartialOrd<PathBuf> for PathDSL {
584    #[inline(always)]
585    fn partial_cmp(&self, other: &PathBuf) -> Option<Ordering> {
586        self.path.partial_cmp(other)
587    }
588}
589
590impl PartialOrd<Path> for PathDSL {
591    #[inline(always)]
592    fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
593        self.path.as_path().partial_cmp(other)
594    }
595}
596
597impl<'a> PartialOrd<Cow<'a, Path>> for PathDSL {
598    #[inline(always)]
599    fn partial_cmp(&self, other: &Cow<'a, Path>) -> Option<Ordering> {
600        self.path.as_path().partial_cmp(other)
601    }
602}
603
604impl<'a> PartialOrd<Cow<'a, OsStr>> for PathDSL {
605    //noinspection RsTypeCheck
606    #[inline(always)]
607    fn partial_cmp(&self, other: &Cow<'a, OsStr>) -> Option<Ordering> {
608        self.path.as_path().partial_cmp(&*other)
609    }
610}
611
612impl PartialOrd<OsStr> for PathDSL {
613    //noinspection RsTypeCheck
614    #[inline(always)]
615    fn partial_cmp(&self, other: &OsStr) -> Option<Ordering> {
616        self.path.as_path().partial_cmp(&*other)
617    }
618}
619
620impl PartialOrd<OsString> for PathDSL {
621    //noinspection RsTypeCheck
622    #[inline(always)]
623    fn partial_cmp(&self, other: &OsString) -> Option<Ordering> {
624        self.path.as_path().partial_cmp(&*other)
625    }
626}
627
628/////////
629// Ord //
630/////////
631
632impl Ord for PathDSL {
633    #[inline(always)]
634    fn cmp(&self, other: &Self) -> Ordering {
635        self.path.cmp(&other.path)
636    }
637}
638
639/////////////
640// FromStr //
641/////////////
642
643impl FromStr for PathDSL {
644    type Err = Infallible;
645
646    #[inline(always)]
647    fn from_str(s: &str) -> Result<Self, Self::Err> {
648        PathBuf::from_str(s).map(|path| PathDSL { path })
649    }
650}
651
652//////////
653// Hash //
654//////////
655
656impl Hash for PathDSL {
657    #[inline(always)]
658    fn hash<H: Hasher>(&self, state: &mut H) {
659        self.path.hash(state)
660    }
661}
662
663////////////
664// Extend //
665////////////
666
667impl<P> Extend<P> for PathDSL
668where
669    P: AsRef<Path>,
670{
671    #[inline(always)]
672    fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
673        self.path.extend(iter)
674    }
675}
676
677//////////////////
678// FromIterator //
679//////////////////
680
681impl<'a> IntoIterator for &'a PathDSL {
682    type Item = &'a OsStr;
683    type IntoIter = Iter<'a>;
684
685    #[inline(always)]
686    fn into_iter(self) -> Self::IntoIter {
687        self.path.as_path().iter()
688    }
689}
690
691/////////////
692// Default //
693/////////////
694
695impl Borrow<Path> for PathDSL {
696    #[inline(always)]
697    fn borrow(&self) -> &Path {
698        self.path.borrow()
699    }
700}
701
702/////////
703// Div //
704/////////
705
706impl Div<PathDSL> for PathDSL {
707    type Output = PathDSL;
708
709    #[inline(always)]
710    fn div(mut self, rhs: PathDSL) -> Self::Output {
711        if self.path.as_os_str().is_empty() {
712            rhs
713        } else {
714            self.path.push(rhs);
715            self
716        }
717    }
718}
719
720impl<T> Div<&T> for PathDSL
721where
722    T: AsRef<Path> + ?Sized,
723{
724    type Output = PathDSL;
725
726    #[inline(always)]
727    fn div(mut self, rhs: &T) -> Self::Output {
728        self.path.push(rhs.as_ref());
729        self
730    }
731}
732
733impl<T> Div<&mut T> for PathDSL
734where
735    T: AsRef<Path> + ?Sized,
736{
737    type Output = PathDSL;
738
739    #[inline(always)]
740    fn div(mut self, rhs: &mut T) -> Self::Output {
741        self.path.push(rhs.as_ref());
742        self
743    }
744}
745
746impl Div<OsString> for PathDSL {
747    type Output = PathDSL;
748
749    #[inline(always)]
750    fn div(mut self, rhs: OsString) -> Self::Output {
751        if self.path.as_os_str().is_empty() {
752            Self::from(rhs)
753        } else {
754            self.path.push(rhs);
755            self
756        }
757    }
758}
759
760impl Div<String> for PathDSL {
761    type Output = PathDSL;
762
763    #[inline(always)]
764    fn div(mut self, rhs: String) -> Self::Output {
765        if self.path.as_os_str().is_empty() {
766            Self::from(rhs)
767        } else {
768            self.path.push(rhs);
769            self
770        }
771    }
772}
773
774impl Div<PathBuf> for PathDSL {
775    type Output = PathDSL;
776
777    #[inline(always)]
778    fn div(mut self, rhs: PathBuf) -> Self::Output {
779        if self.path.as_os_str().is_empty() {
780            Self::from(rhs)
781        } else {
782            self.path.push(rhs);
783            self
784        }
785    }
786}
787
788impl Div<Box<Path>> for PathDSL {
789    type Output = PathDSL;
790
791    #[inline(always)]
792    fn div(mut self, rhs: Box<Path>) -> Self::Output {
793        self.path.push(rhs);
794        self
795    }
796}
797
798impl Div<Cow<'_, Path>> for PathDSL {
799    type Output = PathDSL;
800
801    #[inline(always)]
802    fn div(mut self, rhs: Cow<'_, Path>) -> Self::Output {
803        self.path.push(rhs);
804        self
805    }
806}
807
808impl Div<Cow<'_, OsStr>> for PathDSL {
809    type Output = PathDSL;
810
811    #[inline(always)]
812    fn div(mut self, rhs: Cow<'_, OsStr>) -> Self::Output {
813        self.path.push(rhs);
814        self
815    }
816}
817
818///////////
819// Div & //
820///////////
821
822impl Div<PathDSL> for &PathDSL {
823    type Output = PathDSL;
824
825    #[inline(always)]
826    fn div(self, rhs: PathDSL) -> Self::Output {
827        let mut new_self = (*self).clone();
828        new_self.path.push(rhs);
829        new_self
830    }
831}
832
833impl<T> Div<&T> for &PathDSL
834where
835    T: AsRef<Path> + ?Sized,
836{
837    type Output = PathDSL;
838
839    #[inline(always)]
840    fn div(self, rhs: &T) -> Self::Output {
841        let mut new_self = (*self).clone();
842        new_self.path.push(rhs.as_ref());
843        new_self
844    }
845}
846
847impl<T> Div<&mut T> for &PathDSL
848where
849    T: AsRef<Path> + ?Sized,
850{
851    type Output = PathDSL;
852
853    #[inline(always)]
854    fn div(self, rhs: &mut T) -> Self::Output {
855        let mut new_self = (*self).clone();
856        new_self.path.push(rhs.as_ref());
857        new_self
858    }
859}
860
861impl Div<OsString> for &PathDSL {
862    type Output = PathDSL;
863
864    #[inline(always)]
865    fn div(self, rhs: OsString) -> Self::Output {
866        let mut new_self = (*self).clone();
867        new_self.path.push(rhs);
868        new_self
869    }
870}
871
872impl Div<String> for &PathDSL {
873    type Output = PathDSL;
874
875    #[inline(always)]
876    fn div(self, rhs: String) -> Self::Output {
877        let mut new_self = (*self).clone();
878        new_self.path.push(rhs);
879        new_self
880    }
881}
882
883impl Div<PathBuf> for &PathDSL {
884    type Output = PathDSL;
885
886    #[inline(always)]
887    fn div(self, rhs: PathBuf) -> Self::Output {
888        let mut new_self = (*self).clone();
889        new_self.path.push(rhs);
890        new_self
891    }
892}
893
894impl Div<Box<Path>> for &PathDSL {
895    type Output = PathDSL;
896
897    #[inline(always)]
898    fn div(self, rhs: Box<Path>) -> Self::Output {
899        let mut new_self = (*self).clone();
900        new_self.path.push(rhs);
901        new_self
902    }
903}
904
905impl Div<Cow<'_, Path>> for &PathDSL {
906    type Output = PathDSL;
907
908    #[inline(always)]
909    fn div(self, rhs: Cow<'_, Path>) -> Self::Output {
910        let mut new_self = (*self).clone();
911        new_self.path.push(rhs);
912        new_self
913    }
914}
915
916impl Div<Cow<'_, OsStr>> for &PathDSL {
917    type Output = PathDSL;
918
919    #[inline(always)]
920    fn div(self, rhs: Cow<'_, OsStr>) -> Self::Output {
921        let mut new_self = (*self).clone();
922        new_self.path.push(rhs);
923        new_self
924    }
925}
926
927//////////////
928// Div &mut //
929//////////////
930
931impl Div<PathDSL> for &mut PathDSL {
932    type Output = PathDSL;
933
934    #[inline(always)]
935    fn div(self, rhs: PathDSL) -> Self::Output {
936        let mut new_self = (*self).clone();
937        new_self.path.push(rhs);
938        new_self
939    }
940}
941
942impl<T> Div<&T> for &mut PathDSL
943where
944    T: AsRef<Path> + ?Sized,
945{
946    type Output = PathDSL;
947
948    #[inline(always)]
949    fn div(self, rhs: &T) -> Self::Output {
950        let mut new_self = (*self).clone();
951        new_self.path.push(rhs.as_ref());
952        new_self
953    }
954}
955
956impl<T> Div<&mut T> for &mut PathDSL
957where
958    T: AsRef<Path> + ?Sized,
959{
960    type Output = PathDSL;
961
962    #[inline(always)]
963    fn div(self, rhs: &mut T) -> Self::Output {
964        let mut new_self = (*self).clone();
965        new_self.path.push(rhs.as_ref());
966        new_self
967    }
968}
969
970impl Div<OsString> for &mut PathDSL {
971    type Output = PathDSL;
972
973    #[inline(always)]
974    fn div(self, rhs: OsString) -> Self::Output {
975        let mut new_self = (*self).clone();
976        new_self.path.push(rhs);
977        new_self
978    }
979}
980
981impl Div<String> for &mut PathDSL {
982    type Output = PathDSL;
983
984    #[inline(always)]
985    fn div(self, rhs: String) -> Self::Output {
986        let mut new_self = (*self).clone();
987        new_self.path.push(rhs);
988        new_self
989    }
990}
991
992impl Div<PathBuf> for &mut PathDSL {
993    type Output = PathDSL;
994
995    #[inline(always)]
996    fn div(self, rhs: PathBuf) -> Self::Output {
997        let mut new_self = (*self).clone();
998        new_self.path.push(rhs);
999        new_self
1000    }
1001}
1002
1003impl Div<Box<Path>> for &mut PathDSL {
1004    type Output = PathDSL;
1005
1006    #[inline(always)]
1007    fn div(self, rhs: Box<Path>) -> Self::Output {
1008        let mut new_self = (*self).clone();
1009        new_self.path.push(rhs);
1010        new_self
1011    }
1012}
1013
1014impl Div<Cow<'_, Path>> for &mut PathDSL {
1015    type Output = PathDSL;
1016
1017    #[inline(always)]
1018    fn div(self, rhs: Cow<'_, Path>) -> Self::Output {
1019        let mut new_self = (*self).clone();
1020        new_self.path.push(rhs);
1021        new_self
1022    }
1023}
1024
1025impl Div<Cow<'_, OsStr>> for &mut PathDSL {
1026    type Output = PathDSL;
1027
1028    #[inline(always)]
1029    fn div(self, rhs: Cow<'_, OsStr>) -> Self::Output {
1030        let mut new_self = (*self).clone();
1031        new_self.path.push(rhs);
1032        new_self
1033    }
1034}
1035
1036/////////////////
1037// CopylessDSL //
1038/////////////////
1039
1040/// Implementation struct for the no-copy optimization. Should not ever
1041/// be found in user code.
1042#[derive(Default)]
1043#[doc(hidden)]
1044pub struct CopylessDSL;
1045
1046impl CopylessDSL {
1047    /// Creates a new empty CopylessDSL
1048    #[doc(hidden)]
1049    #[inline(always)]
1050    pub fn new() -> CopylessDSL {
1051        CopylessDSL
1052    }
1053}
1054
1055impl Into<PathDSL> for CopylessDSL {
1056    #[inline(always)]
1057    fn into(self) -> PathDSL {
1058        PathDSL::new()
1059    }
1060}
1061
1062impl Into<PathBuf> for CopylessDSL {
1063    #[inline(always)]
1064    fn into(self) -> PathBuf {
1065        PathBuf::new()
1066    }
1067}
1068
1069impl Div<PathDSL> for CopylessDSL {
1070    type Output = PathDSL;
1071
1072    #[inline(always)]
1073    fn div(self, rhs: PathDSL) -> Self::Output {
1074        rhs
1075    }
1076}
1077
1078impl<T> Div<&T> for CopylessDSL
1079where
1080    T: AsRef<Path> + ?Sized,
1081{
1082    type Output = PathDSL;
1083
1084    #[inline(always)]
1085    fn div(self, rhs: &T) -> Self::Output {
1086        PathDSL::from(rhs)
1087    }
1088}
1089
1090impl<T> Div<&mut T> for CopylessDSL
1091where
1092    T: AsRef<Path> + ?Sized,
1093{
1094    type Output = PathDSL;
1095
1096    #[inline(always)]
1097    fn div(self, rhs: &mut T) -> Self::Output {
1098        PathDSL::from(rhs)
1099    }
1100}
1101
1102impl Div<OsString> for CopylessDSL {
1103    type Output = PathDSL;
1104
1105    #[inline(always)]
1106    fn div(self, rhs: OsString) -> Self::Output {
1107        PathDSL::from(rhs)
1108    }
1109}
1110
1111impl Div<String> for CopylessDSL {
1112    type Output = PathDSL;
1113
1114    #[inline(always)]
1115    fn div(self, rhs: String) -> Self::Output {
1116        PathDSL::from(rhs)
1117    }
1118}
1119
1120impl Div<PathBuf> for CopylessDSL {
1121    type Output = PathDSL;
1122
1123    #[inline(always)]
1124    fn div(self, rhs: PathBuf) -> Self::Output {
1125        PathDSL::from(rhs)
1126    }
1127}
1128
1129impl Div<Box<Path>> for CopylessDSL {
1130    type Output = PathDSL;
1131
1132    #[inline(always)]
1133    fn div(self, rhs: Box<Path>) -> Self::Output {
1134        PathDSL::from(rhs)
1135    }
1136}
1137
1138impl Div<Cow<'_, Path>> for CopylessDSL {
1139    type Output = PathDSL;
1140
1141    #[inline(always)]
1142    fn div(self, rhs: Cow<'_, Path>) -> Self::Output {
1143        PathDSL::from(rhs)
1144    }
1145}
1146
1147impl Div<Cow<'_, OsStr>> for CopylessDSL {
1148    type Output = PathDSL;
1149
1150    #[inline(always)]
1151    fn div(self, rhs: Cow<'_, OsStr>) -> Self::Output {
1152        PathDSL::from(&*rhs)
1153    }
1154}
1155
1156#[cfg(windows)]
1157#[doc(hidden)]
1158#[macro_export]
1159macro_rules! separator {
1160    () => {
1161        "\\"
1162    };
1163}
1164
1165#[cfg(not(windows))]
1166#[doc(hidden)]
1167#[macro_export]
1168macro_rules! separator {
1169    () => {
1170        "/"
1171    };
1172}
1173
1174#[doc(hidden)]
1175#[macro_export]
1176macro_rules! concat_separator {
1177    ( $e:literal, $($other:literal),+ ) => {
1178        concat!($e, $crate::separator!(), $crate::concat_separator!($($other),+))
1179    };
1180    ( $e:literal ) => {
1181        $e
1182    }
1183}
1184
1185#[doc(hidden)]
1186#[macro_export]
1187macro_rules! path_impl {
1188    ( @($($stack:expr),*)@ ($exp:expr) | $($other:tt)+ ) => {
1189        $crate::path_impl!( @($($stack),* / $exp)@ $($other)+ )
1190    };
1191    ( @($($stack:expr),*)@ ($exp:expr) ) => {
1192        $($stack),* / $exp
1193    };
1194    ( @($($stack:expr),*)@ $blk:block | $($other:tt)+ ) => {
1195        $crate::path_impl!( @($($stack),* / $blk)@ $($other)+ )
1196    };
1197    ( @($($stack:expr),*)@ $blk:block ) => {
1198        $($stack),* / $blk
1199    };
1200    ( @($($stack:expr),*)@ $name:path | $($other:tt)+ ) => {
1201        $crate::path_impl!( @($($stack),* / $name)@ $($other)+ )
1202    };
1203    ( @($($stack:expr),*)@ $name:path ) => {
1204        $($stack),* / $name
1205    };
1206    ( @($($stack:expr),*)@ &$name:path | $($other:tt)+ ) => {
1207        $crate::path_impl!( @($($stack),* / &$name)@ $($other)+ )
1208    };
1209    ( @($($stack:expr),*)@ &$name:path ) => {
1210        $($stack),* / &$name
1211    };
1212    ( @($($stack:expr),*)@ &mut $name:path | $($other:tt)+ ) => {
1213        $crate::path_impl!( @($($stack),* / &mut $name)@ $($other)+ )
1214    };
1215    ( @($($stack:expr),*)@ &mut $name:path ) => {
1216        $($stack),* / &mut $name
1217    };
1218    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal | $lit10:literal | $lit11:literal | $lit12:literal | $lit13:literal | $lit14:literal | $lit15:literal | $lit16:literal | $($other:tt)+ ) => {
1219        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9, $lit10, $lit11, $lit12, $lit13, $lit14, $lit15, $lit16))@ $($other)+ )
1220    };
1221    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal | $lit10:literal | $lit11:literal | $lit12:literal | $lit13:literal | $lit14:literal | $lit15:literal | $($other:tt)+ ) => {
1222        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9, $lit10, $lit11, $lit12, $lit13, $lit14, $lit15))@ $($other)+ )
1223    };
1224    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal | $lit10:literal | $lit11:literal | $lit12:literal | $lit13:literal | $lit14:literal | $($other:tt)+ ) => {
1225        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9, $lit10, $lit11, $lit12, $lit13, $lit14))@ $($other)+ )
1226    };
1227    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal | $lit10:literal | $lit11:literal | $lit12:literal | $lit13:literal | $($other:tt)+ ) => {
1228        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9, $lit10, $lit11, $lit12, $lit13))@ $($other)+ )
1229    };
1230    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal | $lit10:literal | $lit11:literal | $lit12:literal | $($other:tt)+ ) => {
1231        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9, $lit10, $lit11, $lit12))@ $($other)+ )
1232    };
1233    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal | $lit10:literal | $lit11:literal | $($other:tt)+ ) => {
1234        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9, $lit10, $lit11))@ $($other)+ )
1235    };
1236    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal | $lit10:literal | $($other:tt)+ ) => {
1237        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9, $lit10))@ $($other)+ )
1238    };
1239    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $lit9:literal| $($other:tt)+ ) => {
1240        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8, $lit9))@ $($other)+ )
1241    };
1242    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $lit8:literal | $($other:tt)+ ) => {
1243        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7, $lit8))@ $($other)+ )
1244    };
1245    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $lit7:literal | $($other:tt)+ ) => {
1246        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6, $lit7))@ $($other)+ )
1247    };
1248    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $lit6:literal | $($other:tt)+ ) => {
1249        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5, $lit6))@ $($other)+ )
1250    };
1251    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $lit5:literal | $($other:tt)+ ) => {
1252        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4, $lit5))@ $($other)+ )
1253    };
1254    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $lit4:literal | $($other:tt)+ ) => {
1255        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3, $lit4))@ $($other)+ )
1256    };
1257    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $lit3:literal | $($other:tt)+ ) => {
1258        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2, $lit3))@ $($other)+ )
1259    };
1260    ( @($($stack:expr),*)@ $lit:literal | $lit2:literal | $($other:tt)+ ) => {
1261        $crate::path_impl!( @($($stack),* / $crate::concat_separator!($lit, $lit2))@ $($other)+ )
1262    };
1263    ( @($($stack:expr),*)@ $lit:literal | $($other:tt)+ ) => {
1264        $crate::path_impl!( @($($stack),* / $lit)@ $($other)+ )
1265    };
1266    ( @($($stack:expr),*)@ $lit:literal ) => {
1267        $($stack),* / $lit
1268    };
1269    ( @($($stack:expr),*)@ ) => {
1270        $($stack),*
1271    };
1272}
1273
1274/// Efficient macro for creating a `PathBuf`.
1275///
1276/// General usage documentation is available at the [crate root](index.html). The following is
1277/// documentation of the optimizations made and internal implementation details.
1278///
1279/// # Expansion
1280///
1281/// The macro is a fairly simple forwarding macro that just matches against the `|` syntax specified
1282/// and forwards it on to the `Div` based implementation. However it does do some small optimizations,
1283/// including the use of a hidden type called `CopylessDSL` which allows for the
1284/// zero-copy optimization be guarenteed. A single `/` operation on `CopylessDSL` always
1285/// produces a `PathDSL`.
1286///
1287/// Some example expansions (on a unix-like system):
1288///
1289/// ```rust
1290/// # use path_dsl::{CopylessDSL, PathDSL, path};
1291/// # use std::path::PathBuf;
1292/// # let ret1 =
1293/// path!("concat" | "optimization");
1294/// # let res2 =
1295/// Into::<PathBuf>::into(CopylessDSL::new() / "concat/optimization");
1296/// # assert_eq!(ret1, res2);
1297/// ```
1298///
1299/// ```rust
1300/// # use path_dsl::{CopylessDSL, PathDSL, path};
1301/// # use std::path::PathBuf;
1302/// // Steals the data from `owning_path`
1303/// # let owning_path = PathBuf::new();
1304/// # let ret1 =
1305/// path!(owning_path | "concat" | "optimization");
1306/// # let owning_path = PathBuf::new();
1307/// # let res2 =
1308/// Into::<PathBuf>::into(CopylessDSL::new() / owning_path / "concat/optimization");
1309/// # assert_eq!(ret1, res2);
1310/// ```
1311///
1312/// ```rust
1313/// # use path_dsl::{CopylessDSL, PathDSL, path};
1314/// # use std::path::PathBuf;
1315/// # let owning_path = PathBuf::new();
1316/// // Copies the data from `owning_path` because we already have a buffer
1317/// # let ret1 =
1318/// path!("concat" | "optimization" | owning_path | "other_thing");
1319/// # let owning_path = PathBuf::new();
1320/// # let res2 =
1321/// Into::<PathBuf>::into(CopylessDSL::new() / "concat/optimization" / owning_path / "other_thing");
1322/// # assert_eq!(ret1, res2);
1323/// ```
1324///
1325/// # String Literal Concatenation
1326///
1327/// One of the optimizations made in the macro is the correct concatenation of multiple string literals in a row, as
1328/// shown above. This is normally not recommended because there are situations where a path with `/`
1329/// will not work on a windows machine. To get around this, I have first actually verified that `\\`
1330/// only happens on windows with a ripgrep of the rust codebase (master as of 2019-08-13):
1331///
1332/// ```text
1333/// $ rg "MAIN_SEP: .*="
1334/// rust\src\libstd\sys\sgx\path.rs
1335/// 19:pub const MAIN_SEP: char = '/';
1336///
1337/// rust\src\libstd\sys\unix\path.rs
1338/// 19:pub const MAIN_SEP: char = '/';
1339///
1340/// rust\src\libstd\sys\wasi\path.rs
1341/// 19:pub const MAIN_SEP: char = '/';
1342///
1343/// rust\src\libstd\sys\vxworks\path.rs
1344/// 19:pub const MAIN_SEP: char = '/';
1345///
1346/// rust\src\libstd\sys\wasm\path.rs
1347/// 19:pub const MAIN_SEP: char = '/';
1348///
1349/// rust\src\libstd\sys\windows\path.rs
1350/// 93:pub const MAIN_SEP: char = '\\';
1351/// ```
1352///
1353/// I then have an internal macro that I define multiple times using `#[cfg(windows)]` etc. to always
1354/// give me the correct separator no matter the platform.
1355///
1356/// Additionally, due to either my inability to write macros well, or an inherent limitation in rust's
1357/// declarative macros, I can't match on a set of `|` separated string literals variadically. As a result
1358/// I have unrolled the combiner out to 16 string literals in a row. This should be enough for basically
1359/// everyone. If you go above this limit, it will combine them into `ceil(N/16)` literals not 1. If you
1360/// need this limit raised, feel free to submit a PR or an issue, but... why? 😃
1361///
1362/// # CopylessDSL
1363///
1364/// `CopylessDSL` is a `#[doc(hidden)]` class that aids in the zero-copy optimization. It is a very limited
1365/// form of `PathDSL` that supports `Div` on all types `PathDSL` supports. It will steal the buffer of any
1366/// moved in owning values. All `Div` operations return a `PathDSL`. Additionally all macro invocations are
1367/// surrounded by a forced conversion to a `PathDSL` so this type should never be seen in user code.
1368///
1369/// If this type shows up in user code at all, this is a bug and should be reported.
1370#[macro_export]
1371macro_rules! path {
1372    ( $($other:tt)* ) => {
1373         ::std::convert::Into::<std::path::PathBuf>::into($crate::path_impl!( @($crate::CopylessDSL::new())@ $($other)* ));
1374    };
1375    () => {  $crate::PathDSL::new() };
1376}