Files
smartproxy/rust/crates/rustproxy-routing/src/matchers/path.rs

66 lines
1.8 KiB
Rust

/// Match a URL path against a pattern supporting wildcards.
///
/// Supported patterns:
/// - `/api/*` matches `/api/anything` (single level)
/// - `/api/**` matches `/api/any/depth/here`
/// - `/exact/path` exact match
/// - `/prefix*` prefix match
pub fn path_matches(pattern: &str, path: &str) -> bool {
// Exact match
if pattern == path {
return true;
}
// Double-star: match any depth
if pattern.ends_with("/**") {
let prefix = &pattern[..pattern.len() - 3];
return path == prefix || path.starts_with(&format!("{}/", prefix));
}
// Single-star at end: match single path segment
if pattern.ends_with("/*") {
let prefix = &pattern[..pattern.len() - 2];
if path == prefix {
return true;
}
if path.starts_with(&format!("{}/", prefix)) {
let rest = &path[prefix.len() + 1..];
// Single level means no more slashes
return !rest.contains('/');
}
return false;
}
// Star anywhere: use glob matching
if pattern.contains('*') {
return glob_match::glob_match(pattern, path);
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exact_path() {
assert!(path_matches("/api/users", "/api/users"));
assert!(!path_matches("/api/users", "/api/posts"));
}
#[test]
fn test_single_wildcard() {
assert!(path_matches("/api/*", "/api/users"));
assert!(path_matches("/api/*", "/api/posts"));
assert!(!path_matches("/api/*", "/api/users/123"));
}
#[test]
fn test_double_wildcard() {
assert!(path_matches("/api/**", "/api/users"));
assert!(path_matches("/api/**", "/api/users/123"));
assert!(path_matches("/api/**", "/api/users/123/posts"));
}
}