mod uri_pattern_score;
mod pattern_part;
use core::cmp::Ordering;
use crate::pattern_part::PatternPart;
use crate::uri_pattern_score::UriPatternScore;
#[derive(Debug, Clone)]
pub struct UriPattern<'a> {
value: &'a str,
pub(crate) parts: Vec<PatternPart<'a>>,
}
impl<'a> From<&'a str> for UriPattern<'a> {
fn from(pattern: &'a str) -> Self {
let parts = pattern.split('/').map(|part| part.into()).collect();
Self { value : pattern, parts }
}
}
impl UriPattern<'_> {
pub fn is_match(&self, candidate: &str) -> bool {
!candidate.split('/').enumerate().map(|(key, value)| {
match self.parts[key] {
PatternPart::Joker => true,
PatternPart::Value(s) => if s == value {true} else {false},
}
})
.collect::<Vec<bool>>()
.contains(&false)
}
}
impl PartialEq for UriPattern<'_> {
fn eq(&self, other: &Self) -> bool {
let score: UriPatternScore = self.into();
let other_score: UriPatternScore = other.into();
score == other_score
}
}
impl PartialOrd for UriPattern<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let score: UriPatternScore = self.into();
let other_score: UriPatternScore = other.into();
score.partial_cmp(&other_score)
}
}
impl Ord for UriPattern<'_> {
fn cmp(&self, other: &Self) -> Ordering {
let score: UriPatternScore = self.into();
let other_score: UriPatternScore = other.into();
score.cmp(&other_score)
}
}
impl Eq for UriPattern<'_> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parsing_works() {
let pattern: UriPattern = "/a/{b}/{c}/d".into();
assert!(pattern.is_match("/a/resource/test/d"));
}
#[test]
fn non_equality_works() {
let pattern: UriPattern = "/a/{b}/{c}/d".into();
let pattern2: UriPattern = "/a/{b}/c/{d}".into();
assert_ne!(pattern, pattern2);
}
#[test]
fn equality_works() {
let pattern: UriPattern = "/a/{b}/{c}/d".into();
let pattern2: UriPattern = "/api/{resource}/{id}/details".into();
assert_eq!(pattern, pattern2);
}
#[test]
fn inequality_works() {
let pattern: UriPattern = "/a/{b}/{c}/d".into();
let pattern2: UriPattern = "/a/{b}/c/{d}".into();
assert!(pattern > pattern2);
}
#[test]
fn best_match_with_ord() {
let patterns: Vec<UriPattern> = vec![
"/api/{foo}/bar/{zzz}".into(),
"/api/{foo}/{bar}/zzz".into(),
"/{api}/{foo}/foo/{zzz}".into()
];
let candidate = "/api/resource/bar/zzz";
let best_match = patterns.iter()
.filter(|p| p.is_match(candidate))
.max();
assert_eq!(best_match.unwrap(), &UriPattern::from("/api/{foo}/{bar}/zzz"));
}
}