//! Path Filters
//!
//! The filters here work on the "path" of requests.
//!
//! - [`path`](./fn.path.html) matches a specific segment, like `/foo`.
//! - [`param`](./fn.param.html) tries to parse a segment into a type, like `/:u16`.
//! - [`end`](./fn.end.html) matches when the path end is found.
//! - [`path!`](../../macro.path.html) eases combining multiple `path` and `param` filters.
//!
//! # Routing
//!
//! Routing in warp is simple yet powerful.
//!
//! First up, matching a single segment:
//!
//! ```
//! use warp::Filter;
//!
//! // GET /hi
//! let hi = warp::path("hi").map(|| {
//!     "Hello, World!"
//! });
//! ```
//!
//! How about multiple segments? It's easiest with the `path!` macro:
//!
//! ```
//! # use warp::Filter;
//! // GET /hello/from/warp
//! let hello_from_warp = warp::path!("hello" / "from" / "warp").map(|| {
//!     "Hello from warp!"
//! });
//! ```
//!
//! Neat! But how do I handle **parameters** in paths?
//!
//! ```
//! # use warp::Filter;
//! // GET /sum/:u32/:u32
//! let sum = warp::path!("sum" / u32 / u32).map(|a, b| {
//!     format!("{} + {} = {}", a, b, a + b)
//! });
//! ```
//!
//! In fact, any type that implements `FromStr` can be used, in any order:
//!
//! ```
//! # use warp::Filter;
//! // GET /:u16/times/:u16
//! let times = warp::path!(u16 / "times" / u16).map(|a, b| {
//!     format!("{} times {} = {}", a, b, a * b)
//! });
//! ```
//!
//! Oh shoot, those math routes should be **mounted** at a different path,
//! is that possible? Yep!
//!
//! ```
//! # use warp::Filter;
//! # let sum = warp::any().map(warp::reply);
//! # let times = sum.clone();
//! // GET /math/sum/:u32/:u32
//! // GET /math/:u16/times/:u16
//! let math = warp::path("math");
//! let math_sum = math.and(sum);
//! let math_times = math.and(times);
//! ```
//!
//! What! `and`? What's that do?
//!
//! It combines the filters in a sort of "this and then that" order. In fact,
//! it's exactly what the `path!` macro has been doing internally.
//!
//! ```
//! # use warp::Filter;
//! // GET /bye/:string
//! let bye = warp::path("bye")
//!     .and(warp::path::param())
//!     .map(|name: String| {
//!         format!("Good bye, {}!", name)
//!     });
//! ```
//!
//! Ah, so, can filters do things besides `and`?
//!
//! Why, yes they can! They can also `or`! As you might expect, `or` creates a
//! "this or else that" chain of filters. If the first doesn't succeed, then
//! it tries the other.
//!
//! So, those `math` routes could have been **mounted** all as one, with `or`.
//!
//!
//! ```
//! # use warp::Filter;
//! # let sum = warp::path("sum");
//! # let times = warp::path("times");
//! // GET /math/sum/:u32/:u32
//! // GET /math/:u16/times/:u16
//! let math = warp::path("math")
//!     .and(sum.or(times));
//! ```
//!
//! It turns out, using `or` is how you combine everything together into a
//! single API.
//!
//! ```
//! # use warp::Filter;
//! # let hi = warp::path("hi");
//! # let hello_from_warp = hi.clone();
//! # let bye = hi.clone();
//! # let math = hi.clone();
//! // GET /hi
//! // GET /hello/from/warp
//! // GET /bye/:string
//! // GET /math/sum/:u32/:u32
//! // GET /math/:u16/times/:u16
//! let routes = hi
//!     .or(hello_from_warp)
//!     .or(bye)
//!     .or(math);
//! ```
//!
//! Note that you will generally want path filters to come **before** other filters
//! like `body` or `headers`. If a different type of filter comes first, a request
//! with an invalid body for route `/right-path-wrong-body` may try matching against `/wrong-path`
//! and return the error from `/wrong-path` instead of the correct body-related error.

use std::convert::Infallible;
use std::fmt;
use std::str::FromStr;

use futures_util::future;
use http::uri::PathAndQuery;

use self::internal::Opaque;
use crate::filter::{filter_fn, one, Filter, FilterBase, Internal, One, Tuple};
use crate::reject::{self, Rejection};
use crate::route::{self, Route};

/// Create an exact match path segment `Filter`.
///
/// This will try to match exactly to the current request path segment.
///
/// # Note
///
/// - [`end()`](./fn.end.html) should be used to match the end of a path to avoid having
///   filters for shorter paths like `/math` unintentionally match a longer
///   path such as `/math/sum`
/// - Path-related filters should generally come **before** other types of filters, such
///   as those checking headers or body types. Including those other filters before
///   the path checks may result in strange errors being returned because a given request
///   does not match the parameters for a completely separate route.
///
/// # Panics
///
/// Exact path filters cannot be empty, or contain slashes.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Matches '/hello'
/// let hello = warp::path("hello")
///     .map(|| "Hello, World!");
/// ```
pub fn path<P>(p: P) -> Exact<Opaque<P>>
where
    P: AsRef<str>,
{
    let s = p.as_ref();
    assert!(!s.is_empty(), "exact path segments should not be empty");
    assert!(
        !s.contains('/'),
        "exact path segments should not contain a slash: {:?}",
        s
    );

    Exact(Opaque(p))
    /*
    segment(move |seg| {
        tracing::trace!("{:?}?: {:?}", p, seg);
        if seg == p {
            Ok(())
        } else {
            Err(reject::not_found())
        }
    })
    */
}

/// A `Filter` matching an exact path segment.
///
/// Constructed from `path()` or `path!()`.
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
pub struct Exact<P>(P);

impl<P> FilterBase for Exact<P>
where
    P: AsRef<str>,
{
    type Extract = ();
    type Error = Rejection;
    type Future = future::Ready<Result<Self::Extract, Self::Error>>;

    #[inline]
    fn filter(&self, _: Internal) -> Self::Future {
        route::with(|route| {
            let p = self.0.as_ref();
            future::ready(with_segment(route, |seg| {
                tracing::trace!("{:?}?: {:?}", p, seg);

                if seg == p {
                    Ok(())
                } else {
                    Err(reject::not_found())
                }
            }))
        })
    }
}

/// Matches the end of a route.
///
/// Note that _not_ including `end()` may result in shorter paths like
/// `/math` unintentionally matching `/math/sum`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Matches '/'
/// let hello = warp::path::end()
///     .map(|| "Hello, World!");
/// ```
pub fn end() -> impl Filter<Extract = (), Error = Rejection> + Copy {
    filter_fn(move |route| {
        if route.path().is_empty() {
            future::ok(())
        } else {
            future::err(reject::not_found())
        }
    })
}

/// Extract a parameter from a path segment.
///
/// This will try to parse a value from the current request path
/// segment, and if successful, the value is returned as the `Filter`'s
/// "extracted" value.
///
/// If the value could not be parsed, rejects with a `404 Not Found`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path::param()
///     .map(|id: u32| {
///         format!("You asked for /{}", id)
///     });
/// ```
pub fn param<T: FromStr + Send + 'static>(
) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
    filter_segment(|seg| {
        tracing::trace!("param?: {:?}", seg);
        if seg.is_empty() {
            return Err(reject::not_found());
        }
        T::from_str(seg).map(one).map_err(|_| reject::not_found())
    })
}

/// Extract the unmatched tail of the path.
///
/// This will return a `Tail`, which allows access to the rest of the path
/// that previous filters have not already matched.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path("foo")
///     .and(warp::path::tail())
///     .map(|tail| {
///         // GET /foo/bar/baz would return "bar/baz".
///         format!("The tail after foo is {:?}", tail)
///     });
/// ```
pub fn tail() -> impl Filter<Extract = One<Tail>, Error = Infallible> + Copy {
    filter_fn(move |route| {
        let path = path_and_query(route);
        let idx = route.matched_path_index();

        // Giving the user the full tail means we assume the full path
        // has been matched now.
        let end = path.path().len() - idx;
        route.set_unmatched_path(end);

        future::ok(one(Tail {
            path,
            start_index: idx,
        }))
    })
}

/// Represents the tail part of a request path, returned by the [`tail()`] filter.
pub struct Tail {
    path: PathAndQuery,
    start_index: usize,
}

impl Tail {
    /// Get the `&str` representation of the remaining path.
    pub fn as_str(&self) -> &str {
        &self.path.path()[self.start_index..]
    }
}

impl fmt::Debug for Tail {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self.as_str(), f)
    }
}

/// Peek at the unmatched tail of the path, without affecting the matched path.
///
/// This will return a `Peek`, which allows access to the rest of the path
/// that previous filters have not already matched. This differs from `tail`
/// in that `peek` will **not** set the entire path as matched.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path("foo")
///     .and(warp::path::peek())
///     .map(|peek| {
///         // GET /foo/bar/baz would return "bar/baz".
///         format!("The path after foo is {:?}", peek)
///     });
/// ```
pub fn peek() -> impl Filter<Extract = One<Peek>, Error = Infallible> + Copy {
    filter_fn(move |route| {
        let path = path_and_query(route);
        let idx = route.matched_path_index();

        future::ok(one(Peek {
            path,
            start_index: idx,
        }))
    })
}

/// Represents the tail part of a request path, returned by the [`peek()`] filter.
pub struct Peek {
    path: PathAndQuery,
    start_index: usize,
}

impl Peek {
    /// Get the `&str` representation of the remaining path.
    pub fn as_str(&self) -> &str {
        &self.path.path()[self.start_index..]
    }

    /// Get an iterator over the segments of the peeked path.
    pub fn segments(&self) -> impl Iterator<Item = &str> {
        self.as_str().split('/').filter(|seg| !seg.is_empty())
    }
}

impl fmt::Debug for Peek {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self.as_str(), f)
    }
}

/// Returns the full request path, irrespective of other filters.
///
/// This will return a `FullPath`, which can be stringified to return the
/// full path of the request.
///
/// This is more useful in generic pre/post-processing filters, and should
/// probably not be used for request matching/routing.
///
/// # Example
///
/// ```
/// use warp::{Filter, path::FullPath};
/// use std::{collections::HashMap, sync::{Arc, Mutex}};
///
/// let counts = Arc::new(Mutex::new(HashMap::new()));
/// let access_counter = warp::path::full()
///     .map(move |path: FullPath| {
///         let mut counts = counts.lock().unwrap();
///
///         *counts.entry(path.as_str().to_string())
///             .and_modify(|c| *c += 1)
///             .or_insert(0)
///     });
///
/// let route = warp::path("foo")
///     .and(warp::path("bar"))
///     .and(access_counter)
///     .map(|count| {
///         format!("This is the {}th visit to this URL!", count)
///     });
/// ```
pub fn full() -> impl Filter<Extract = One<FullPath>, Error = Infallible> + Copy {
    filter_fn(move |route| future::ok(one(FullPath(path_and_query(route)))))
}

/// Represents the full request path, returned by the [`full()`] filter.
pub struct FullPath(PathAndQuery);

impl FullPath {
    /// Get the `&str` representation of the request path.
    pub fn as_str(&self) -> &str {
        self.0.path()
    }
}

impl fmt::Debug for FullPath {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self.as_str(), f)
    }
}

fn filter_segment<F, U>(func: F) -> impl Filter<Extract = U, Error = Rejection> + Copy
where
    F: Fn(&str) -> Result<U, Rejection> + Copy,
    U: Tuple + Send + 'static,
{
    filter_fn(move |route| future::ready(with_segment(route, func)))
}

fn with_segment<F, U>(route: &mut Route, func: F) -> Result<U, Rejection>
where
    F: Fn(&str) -> Result<U, Rejection>,
{
    let seg = segment(route);
    let ret = func(seg);
    if ret.is_ok() {
        let idx = seg.len();
        route.set_unmatched_path(idx);
    }
    ret
}

fn segment(route: &Route) -> &str {
    route
        .path()
        .splitn(2, '/')
        .next()
        .expect("split always has at least 1")
}

fn path_and_query(route: &Route) -> PathAndQuery {
    route
        .uri()
        .path_and_query()
        .cloned()
        .unwrap_or_else(|| PathAndQuery::from_static(""))
}

/// Convenient way to chain multiple path filters together.
///
/// Any number of either type identifiers or string expressions can be passed,
/// each separated by a forward slash (`/`). Strings will be used to match
/// path segments exactly, and type identifiers are used just like
/// [`param`](crate::path::param) filters.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Match `/sum/:a/:b`
/// let route = warp::path!("sum" / u32 / u32)
///     .map(|a, b| {
///         format!("{} + {} = {}", a, b, a + b)
///     });
/// ```
///
/// The equivalent filter chain without using the `path!` macro looks this:
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path("sum")
///     .and(warp::path::param::<u32>())
///     .and(warp::path::param::<u32>())
///     .and(warp::path::end())
///     .map(|a, b| {
///         format!("{} + {} = {}", a, b, a + b)
///     });
/// ```
///
/// # Path Prefixes
///
/// The `path!` macro automatically assumes the path should include an `end()`
/// filter. To build up a path filter *prefix*, such that the `end()` isn't
/// included, use the `/ ..` syntax.
///
///
/// ```
/// use warp::Filter;
///
/// let prefix = warp::path!("math" / "sum" / ..);
///
/// let sum = warp::path!(u32 / u32)
///     .map(|a, b| {
///         format!("{} + {} = {}", a, b, a + b)
///     });
///
/// let help = warp::path::end()
///     .map(|| "This API returns the sum of two u32's");
///
/// let api = prefix.and(sum.or(help));
/// ```
#[macro_export]
macro_rules! path {
    ($($pieces:tt)*) => ({
        $crate::__internal_path!(@start $($pieces)*)
    });
}

#[doc(hidden)]
#[macro_export]
// not public API
macro_rules! __internal_path {
    (@start) => (
        $crate::path::end()
    );
    (@start ..) => ({
        compile_error!("'..' cannot be the only segment")
    });
    (@start $first:tt $(/ $tail:tt)*) => ({
        $crate::__internal_path!(@munch $crate::any(); [$first] [$(/ $tail)*])
    });

    (@munch $sum:expr; [$cur:tt] [/ $next:tt $(/ $tail:tt)*]) => ({
        $crate::__internal_path!(@munch $crate::Filter::and($sum, $crate::__internal_path!(@segment $cur)); [$next] [$(/ $tail)*])
    });
    (@munch $sum:expr; [$cur:tt] []) => ({
        $crate::__internal_path!(@last $sum; $cur)
    });

    (@last $sum:expr; ..) => (
        $sum
    );
    (@last $sum:expr; $end:tt) => (
        $crate::Filter::and(
            $crate::Filter::and($sum, $crate::__internal_path!(@segment $end)),
            $crate::path::end()
        )
    );

    (@segment ..) => (
        compile_error!("'..' must be the last segment")
    );
    (@segment $param:ty) => (
        $crate::path::param::<$param>()
    );
    // Constructs a unique ZST so the &'static str pointer doesn't need to
    // be carried around.
    (@segment $s:literal) => ({
        #[derive(Clone, Copy)]
        struct __StaticPath;
        impl ::std::convert::AsRef<str> for __StaticPath {
            fn as_ref(&self) -> &str {
                static S: &str = $s;
                S
            }
        }
        $crate::path(__StaticPath)
    });
}

// path! compile fail tests

/// ```compile_fail
/// warp::path!("foo" / .. / "bar");
/// ```
///
/// ```compile_fail
/// warp::path!(.. / "bar");
/// ```
///
/// ```compile_fail
/// warp::path!("foo" ..);
/// ```
///
/// ```compile_fail
/// warp::path!("foo" / .. /);
/// ```
///
/// ```compile_fail
/// warp::path!(..);
/// ```
fn _path_macro_compile_fail() {}

mod internal {
    // Used to prevent users from naming this type.
    //
    // For instance, `Exact<Opaque<String>>` means a user cannot depend
    // on it being `Exact<String>`.
    #[allow(missing_debug_implementations)]
    #[derive(Clone, Copy)]
    pub struct Opaque<T>(pub(super) T);

    impl<T: AsRef<str>> AsRef<str> for Opaque<T> {
        #[inline]
        fn as_ref(&self) -> &str {
            self.0.as_ref()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_path_exact_size() {
        use std::mem::{size_of, size_of_val};

        assert_eq!(
            size_of_val(&path("hello")),
            size_of::<&str>(),
            "exact(&str) is size of &str"
        );

        assert_eq!(
            size_of_val(&path(String::from("world"))),
            size_of::<String>(),
            "exact(String) is size of String"
        );

        assert_eq!(
            size_of_val(&path!("zst")),
            size_of::<()>(),
            "path!(&str) is ZST"
        );
    }
}
