Skip to content
21 changes: 20 additions & 1 deletion cot/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,9 @@ pub fn split_view_name(view_name: &str) -> (Option<&str>, &str) {

/// A route that can be used to route requests to their respective views.
///
/// Non-empty route paths may omit the leading slash. Cot normalizes them by
/// prepending `/`, so `"home"` and `"/home"` define the same route.
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -562,7 +565,8 @@ impl Route {
/// # unimplemented!()
/// }
///
/// let route = Route::with_handler("/", home);
/// let route = Route::with_handler("home", home);
/// assert_eq!(route.url(), "/home");
/// ```
#[must_use]
pub fn with_handler<HandlerParams, H>(url: &str, handler: H) -> Self
Expand Down Expand Up @@ -1130,6 +1134,21 @@ mod tests {
assert_eq!(response.status(), StatusCode::OK);
}

#[cot::test]
async fn router_route_without_leading_slash() {
let route = Route::with_handler_and_name("test", MockHandler, "test");
assert_eq!(route.url(), "/test");

let router = Router::with_urls(vec![route]);
let response = router.route(test_request(), "/test").await.unwrap();
assert_eq!(response.status(), StatusCode::OK);

let url = router
.reverse(None, "test", &ReverseParamMap::new())
.unwrap();
assert_eq!(url, "/test");
}

#[cot::test]
async fn router_handle() {
let route = Route::with_handler("/test", MockHandler);
Expand Down
19 changes: 18 additions & 1 deletion cot/src/router/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ impl PathMatcher {
Param { start: usize },
}

let path_pattern = path_pattern.into();
let mut path_pattern = path_pattern.into();
if !path_pattern.is_empty() && !path_pattern.starts_with('/') {
path_pattern.insert(0, '/');
}

let mut parts = Vec::new();
let mut state = State::Literal { start: 0 };
Expand Down Expand Up @@ -349,6 +352,20 @@ mod tests {
assert_eq!(path_parser.capture("/test"), None);
}

#[test]
fn path_parser_adds_missing_leading_slash() {
let path_parser = PathMatcher::new("users/{id}");
let mut params = ReverseParamMap::new();
params.insert("id", "123");

assert_eq!(
path_parser.capture("/users/123"),
Some(CaptureResult::new(vec![PathParam::new("id", "123")], ""))
);
assert_eq!(path_parser.reverse(&params).unwrap(), "/users/123");
assert_eq!(path_parser.to_string(), "/users/{id}");
}

#[test]
fn path_parser_escaped() {
let path_parser = PathMatcher::new("/users/{{{{{{escaped}}}}}}");
Expand Down
Loading