use std::borrow::Cow;
use once_cell::sync::Lazy;
use regex::{Captures, Regex};
use crate::util;
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r#"\{(.*?:*.*?)\}/|:(.+?)/|:(.*)|\*"#).unwrap());
pub fn normalize(path_hint: &str) -> String {
RE.replace_all(path_hint.trim(), |caps: &Captures| {
if &caps[0] == "*" {
return Cow::Borrowed("{wildcard}");
};
if let Some(matched) = util::get_first_capture(caps) {
if caps[0].ends_with('/') {
Cow::Owned(format!("{{{}}}/", matched))
} else {
Cow::Owned(format!("{{{}}}", matched))
}
} else {
Cow::Owned(caps[0].to_string())
}
})
.into_owned()
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
struct Test {
#[allow(dead_code)]
name: &'static str,
path_hint: &'static str,
expected: &'static str,
}
#[test]
fn run() {
let tests = vec![
Test {
name: "simple path",
path_hint: "/hello/world",
expected: "/hello/world",
},
Test {
name: "simple path with wildcard",
path_hint: "/hello/*",
expected: "/hello/{wildcard}",
},
Test {
name: "path with mix formats",
path_hint: "/user/{id}/account/:action",
expected: "/user/{id}/account/{action}",
},
Test {
name: "path with multiple of the same format",
path_hint: "/user/:id/account/:action",
expected: "/user/{id}/account/{action}",
},
Test {
name: "path with multiple of the same format (:)",
path_hint: "/user/{id}/account/{action}",
expected: "/user/{id}/account/{action}",
},
Test {
name: "does not normalize unknown format",
path_hint: "/user/<id>/account/<action>",
expected: "/user/<id>/account/<action>",
},
Test {
name: "keeps trailing slash",
path_hint: "/user/{id}/account/{action}/",
expected: "/user/{id}/account/{action}/",
},
];
for test in tests {
assert_eq!(normalize(test.path_hint), test.expected);
}
}
}