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
use std::{
    fmt::{self, Display},
    str::FromStr,
};

/// Implemented for types that can be the routes of a web application
pub trait Routes: Sized {
    /// A type that knows how to display the url for this route.
    type UrlDisplay: Display;
    /// Get the url
    fn url(&self) -> &Self::UrlDisplay;
    /// Parse a url
    fn parse_url(url: &str) -> Result<Self, NotFound>;
}

#[derive(Debug)]
/// No routes matched the given url
pub struct NotFound;

impl Display for NotFound {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("No route matched the given url")
    }
}

impl std::error::Error for NotFound {}

// Some simple functions to aid the parsing of urls.
pub fn consume_literal<'src>(input: &'src str, literal: &str) -> Result<&'src str, NotFound> {
    if input.starts_with(literal) {
        Ok(&input[literal.len()..])
    } else {
        Err(NotFound)
    }
}

pub fn consume_placeholder<'src, T>(input: &'src str) -> Result<(&'src str, T), NotFound>
where
    T: FromStr,
{
    let end_of_placeholder = match input.find('/') {
        Some(idx) => idx,
        None => input.len(),
    };
    let placeholder_str = &input[..end_of_placeholder];
    let rest = &input[end_of_placeholder..];
    match placeholder_str.parse() {
        Ok(t) => Ok((rest, t)),
        Err(_) => Err(NotFound),
    }
}