Documentation
type Result = std::result::Result<(), Box<dyn std::error::Error>>;
use std::{iter::FromIterator, str::FromStr};

use routefinder::*;

#[test]
fn it_works() -> Result {
    let mut router = Router::new();
    router.add("/*", 1)?;
    router.add("/hello", 2)?;
    router.add("/:greeting", 3)?;
    router.add("/hey/:world", 4)?;
    router.add("/hey/earth", 5)?;
    router.add("/:greeting/:world/*", 6)?;

    assert_eq!(
        &format!("{:#?}", &router),
        r#"{
    /hello,
    /hey/earth,
    /hey/:world,
    /:greeting,
    /:greeting/:world/*,
    /*,
}"#
    );

    let matches = router.matches("/hello");
    assert_eq!(matches.len(), 3);
    assert_eq!(router.matches("/").len(), 1);
    assert_eq!(*router.best_match("/hey/earth").unwrap(), 5);

    assert_eq!(
        router
            .best_match("/hey/mars")
            .unwrap()
            .captures()
            .get("world"),
        Some("mars")
    );

    let m = router.best_match("/hey/earth/wildcard/stuff").unwrap();

    assert_eq!(*m, 6);
    let captures = m.captures();
    assert_eq!(captures.wildcard(), Some("wildcard/stuff"));
    assert_eq!(captures.get("greeting"), Some("hey"));
    assert_eq!(captures.get("world"), Some("earth"));

    Ok(())
}

#[test]
fn several_params() -> Result {
    let mut router = Router::new();
    router.add("/:a", 1)?;
    router.add("/:a/:b", 2)?;
    router.add("/:a/:b/:c", 3)?;
    router.add("/:param1/specific/:param2", 4)?;
    assert_eq!(*router.best_match("/hi").unwrap(), 1);
    assert_eq!(*router.best_match("/hi/there").unwrap(), 2);
    assert_eq!(*router.best_match("/hi/there/hey").unwrap(), 3);

    assert_eq!(router.matches("/hi/specific/anything").len(), 2);

    assert_eq!(*router.best_match("/hi/specific/anything").unwrap(), 4);

    assert!(router.matches("/").is_empty());
    assert!(router.matches("/a/b/c/d").is_empty());

    Ok(())
}

#[test]
fn wildcard_matches_root() -> Result {
    let mut router = Router::new();
    router.add("*", ())?;
    assert!(router.best_match("/").is_some());

    let mut router = Router::new();
    router.add("/something/:anything/*", ())?;
    assert!(router.best_match("/something/1/").is_some());

    let mut router = Router::new();
    router.add("/something/:anything/*", ())?;
    assert!(router.best_match("/something/1").is_some());

    Ok(())
}

#[test]
fn trailing_slashes_are_ignored() -> Result {
    let mut router = Router::new();
    router.add("/a", ())?;
    assert!(router.best_match("/a/").is_some());
    assert!(router.best_match("/a").is_some());

    let mut router = Router::new();
    router.add("/a/", ())?;
    assert!(router.best_match("/a").is_some());
    assert!(router.best_match("/a/").is_some());

    Ok(())
}

#[test]
fn captures() -> Result {
    let mut router = Router::new();
    router.add("/:a/:b/:c", ())?;
    let best_match = router.best_match("/aaa/bbb/ccc").unwrap();
    let captures = best_match.captures();
    assert_eq!(captures.get("a"), Some("aaa"));
    assert_eq!(captures.get("b"), Some("bbb"));
    assert_eq!(captures.get("c"), Some("ccc"));
    assert_eq!(captures.get("not-present"), None);

    let mut router = Router::new();
    router.add("/*", ())?;
    let best_match = router.best_match("/hello/world").unwrap();
    assert_eq!(best_match.captures().wildcard(), Some("hello/world"));

    Ok(())
}

#[test]
fn errors_on_add() {
    let mut router = Router::new();

    assert!(router
        .add("*named_star", ())
        .unwrap_err()
        .contains("replace `*named_star` with `*`"));

    assert_eq!(router.add(":", ()).unwrap_err(), "params must be named");
}

#[test]
fn dots() -> Result {
    let mut router = Router::new();
    router.add("/:a.:b", 1)?;
    router.add("/:a/:b.:c", 2)?;
    router.add("/:a/:b", 3)?;
    router.add("/:a/:b.txt", 4)?;
    assert_eq!(*router.best_match("/hello.world").unwrap(), 1);
    assert_eq!(*router.best_match("/hi/there.world").unwrap(), 2);
    assert_eq!(*router.best_match("/hi/yep").unwrap(), 3);
    assert_eq!(*router.best_match("/hi/planet.txt").unwrap(), 4);

    assert_eq!(
        router
            .matches("/hi/planet.txt")
            .into_iter()
            .map(|x| *x)
            .collect::<Vec<_>>(),
        vec![4, 2, 3]
    );

    assert!(router.matches("/").is_empty());
    assert!(router.matches("/a/b/c/d").is_empty());

    Ok(())
}

#[test]
fn parse() -> Result {
    assert_eq!(
        RouteSpec::from_str("a.:b")?.matches("a.hello"),
        Some(vec!["hello"])
    );

    assert_eq!(
        RouteSpec::from_str(":a.:b")?.matches("a.hello"),
        Some(vec!["a", "hello"])
    );
    Ok(())
}

#[test]
fn templating() -> Result {
    assert_eq!(
        RouteSpec::from_str(":a/:b.:c")?
            .template(&[("a", "users"), ("b", "jbr"), ("c", "txt")].into())
            .unwrap()
            .to_string(),
        "/users/jbr.txt"
    );

    Ok(())
}

#[test]
fn specific_matches() -> Result {
    assert_eq!(
        RouteSpec::from_str(":param")?.matches("/a.b.c.d").unwrap(),
        vec!["a.b.c.d"]
    );

    assert_eq!(
        RouteSpec::from_str(":a.:b")?.matches("/a.b.c.d").unwrap(),
        vec!["a", "b.c.d"]
    );

    assert_eq!(
        RouteSpec::from_str(":a.:b.:c")?
            .matches("/a.b.c.d")
            .unwrap(),
        vec!["a", "b", "c.d"]
    );

    assert_eq!(
        RouteSpec::from_str(":a.:b.:c.:d")?
            .matches("/a.b.c.d")
            .unwrap(),
        vec!["a", "b", "c", "d"]
    );

    assert!(RouteSpec::from_str(":a.:b")?.matches("/a").is_none());

    Ok(())
}

#[test]
fn priority() -> Result {
    assert!(RouteSpec::from_str("exact")? < RouteSpec::from_str(":param")?);
    assert!(RouteSpec::from_str("a")? < RouteSpec::from_str("a/b")?);
    assert!(RouteSpec::from_str(":a.:b")? < RouteSpec::from_str(":a")?);
    Ok(())
}

#[test]
fn extend_captures() {
    let mut captures = Captures::from_iter([("key", "value")]);
    let other_captures = Captures::from_iter([("key2", "value2")]);

    captures.extend(other_captures);

    assert_eq!(
        captures.iter().collect::<Vec<_>>(),
        [("key", "value"), ("key2", "value2")]
    );
}

#[test]
fn append_captures() {
    let mut captures = Captures::from_iter([("key", "value")]);
    captures.set_wildcard("something");

    let mut other_captures = Captures::from_iter([("key2", "value2")]);
    other_captures.set_wildcard("other");

    captures.append(other_captures);

    assert_eq!(
        captures.iter().collect::<Vec<_>>(),
        [("key", "value"), ("key2", "value2")]
    );

    assert_eq!(Some("other"), captures.wildcard());
}