1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Build cross-platform paths at compile time.
//!
//! ## Installation
//!
//! First, add [`trail`] to the dependencies section of your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! trail = "0.1"
//! ```
//!
//! Next, add the following snippet to the entry point of your crate (`lib.rs` or `main.rs`):
//!
//! ```rust
//! #[macro_use]
//! extern crate trail;
//! #
//! # fn main() {}
//! ```
//!
//! ## Usage
//!
//! You can also use [`trail!`] anywhere else an expression is expected. The expanded output
//! is functionally equivelant to calling [`Path::new`] with a hard coded literal.
//!
//! ```rust
//! # #[macro_use]
//! # extern crate trail;
//! #
//! # use std::path::Path;
//! #
//! # fn main() {
//! let absolute = &trail!("", "hello", "world");
//! let relative = &trail!("hello", "world");
//!
//! if cfg!(windows) {
//!     assert_eq!(*absolute, Path::new("\\hello\\world"));
//!     assert_eq!(*relative, Path::new("hello\\world"));
//! } else {
//!     assert_eq!(*absolute, Path::new("/hello/world"));
//!     assert_eq!(*relative, Path::new("hello/world"));
//! }
//! # }
//! ```
//!
//! [`trail!`]: ./macro.trail.html
//! [`trail`]: https://crates.io/crates/trail
//!
//! [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html
//! [`Path::new`]: https://doc.rust-lang.org/std/path/struct.Path.html#method.new

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

/// A macro for building a cross-platform [`Path`] at compile time.
///
/// ## Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate trail;
/// #
/// # use std::path::Path;
/// #
/// # fn main() {
/// let absolute = &trail!("", "hello", "world");
/// let relative = &trail!("hello", "world");
///
/// if cfg!(windows) {
///     assert_eq!(*absolute, Path::new("\\hello\\world"));
///     assert_eq!(*relative, Path::new("hello\\world"));
/// } else {
///     assert_eq!(*absolute, Path::new("/hello/world"));
///     assert_eq!(*relative, Path::new("hello/world"));
/// }
/// # }
/// ```
///
/// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html
#[macro_export]
macro_rules! trail {
    ( $($segment:expr),* ) => ( ::std::path::Path::new(__trail!($($segment),*)) );
}

#[doc(hidden)]
#[cfg(not(windows))]
#[macro_export]
macro_rules! __trail {
    () => ( "" );
    ( $base:expr ) => ( $base );
    ( $base:expr, $($segment:expr),+ ) => ( concat!($base, $("/", $segment),+) );
}

#[doc(hidden)]
#[cfg(windows)]
#[macro_export]
macro_rules! __trail {
    () => ( "" );
    ( $base:expr ) => ( $base );
    ( $base:expr, $($segment:expr),+ ) => ( concat!($base, $("\\", $segment),+) );
}

#[cfg(test)]
mod tests {
    use std::path::Path;

    #[test]
    fn trail() {
        if cfg!(windows) {
            assert_eq!(trail!(), Path::new(""));
            assert_eq!(trail!(""), Path::new(""));
            assert_eq!(trail!("hello"), Path::new("hello"));
            assert_eq!(trail!("hello", "world"), Path::new("hello\\world"));
            assert_eq!(trail!("", "hello", "world"), Path::new("\\hello\\world"));
        } else {
            assert_eq!(trail!(), Path::new(""));
            assert_eq!(trail!(""), Path::new(""));
            assert_eq!(trail!("hello"), Path::new("hello"));
            assert_eq!(trail!("hello", "world"), Path::new("hello/world"));
            assert_eq!(trail!("", "hello", "world"), Path::new("/hello/world"));
        }
    }
}