pub const PATH_PREFIX: &str = "http_expr";
pub const PATH_PREFIX_BYTES: &[u8] = PATH_PREFIX.as_bytes();
pub const PATH_DIR_SEPARATOR: &str = "";
pub const PATH_DIR_SEPARATOR_BYTES: &[u8] = PATH_DIR_SEPARATOR.as_bytes();
pub const EXACT_PATH_TERMINATOR: &str = "<$>";
pub const EXACT_PATH_TERMINATOR_BYTES: &[u8] = EXACT_PATH_TERMINATOR.as_bytes();
pub const WILDCARD_PATH_TERMINATOR: &str = "<*>";
pub const WILDCARD_PATH_TERMINATOR_BYTES: &[u8] = WILDCARD_PATH_TERMINATOR.as_bytes();
pub fn is_wildcard_path_valid_for_request_path(
wildcard_path: &[Vec<u8>],
request_path: &[Vec<u8>],
) -> bool {
if request_path.starts_with(wildcard_path) {
return true;
}
if wildcard_path.ends_with(&[PATH_DIR_SEPARATOR_BYTES.to_vec()]) {
return request_path.starts_with(&wildcard_path[..wildcard_path.len() - 1]);
}
false
}
fn strip_path_affixes(path: &mut Vec<Vec<u8>>) {
if matches!(
path.first(),
Some(first) if first == PATH_PREFIX_BYTES,
) {
path.remove(0);
}
if matches!(path.last(), Some(last) if
last == EXACT_PATH_TERMINATOR_BYTES ||
last == WILDCARD_PATH_TERMINATOR_BYTES)
{
path.pop();
}
if path.len() > 1
&& matches!(
path.first(),
Some(first) if first == PATH_DIR_SEPARATOR_BYTES,
)
{
path.remove(0);
}
}
pub fn more_specific_wildcards_for(
requested_path: &[Vec<u8>],
responding_wildcard_path: &[Vec<u8>],
) -> Vec<Vec<Vec<u8>>> {
let mut valid_wildcards: Vec<Vec<Vec<u8>>> = vec![];
let mut potential_path = requested_path.to_vec();
strip_path_affixes(&mut potential_path);
let mut responding_wildcard_path = responding_wildcard_path.to_vec();
strip_path_affixes(&mut responding_wildcard_path);
if !is_wildcard_path_valid_for_request_path(&responding_wildcard_path, &potential_path) {
responding_wildcard_path = vec![];
}
while potential_path.len() > responding_wildcard_path.len()
|| potential_path.last() != responding_wildcard_path.last()
{
potential_path.push(WILDCARD_PATH_TERMINATOR_BYTES.to_vec());
valid_wildcards.push(potential_path.clone());
potential_path.pop();
if potential_path.ends_with(&[PATH_DIR_SEPARATOR_BYTES.to_vec()]) {
potential_path.pop(); } else {
potential_path.pop(); potential_path.push(PATH_DIR_SEPARATOR_BYTES.to_vec());
}
}
valid_wildcards
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
#[rstest]
#[case(responding_path_a(), more_specific_paths_a())]
#[case(responding_path_b(), more_specific_paths_b())]
#[case(responding_path_c(), more_specific_paths_c())]
#[case(responding_path_d(), more_specific_paths_d())]
#[case(responding_path_e(), more_specific_paths_e())]
#[case(responding_path_f(), more_specific_paths_f())]
#[case(responding_path_g(), more_specific_paths_g())]
#[case(responding_path_h(), more_specific_paths_h())]
fn test_more_specific_wildcards_for(
requested_path: Vec<Vec<u8>>,
#[case] responding_path: Vec<Vec<u8>>,
#[case] expected: Vec<Vec<Vec<u8>>>,
) {
let more_specific_paths = more_specific_wildcards_for(&requested_path, &responding_path);
assert_eq!(more_specific_paths, expected);
}
#[fixture]
fn requested_path() -> Vec<Vec<u8>> {
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]
}
#[fixture]
fn responding_path_a() -> Vec<Vec<u8>> {
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]
}
#[fixture]
fn more_specific_paths_a() -> Vec<Vec<Vec<u8>>> {
vec![]
}
#[fixture]
fn responding_path_b() -> Vec<Vec<u8>> {
vec![
b"a".to_vec(),
b"b".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
]
}
#[fixture]
fn more_specific_paths_b() -> Vec<Vec<Vec<u8>>> {
vec![vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
]]
}
#[fixture]
fn responding_path_c() -> Vec<Vec<u8>> {
vec![b"a".to_vec(), b"b".to_vec()]
}
#[fixture]
fn more_specific_paths_c() -> Vec<Vec<Vec<u8>>> {
vec![
vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
]
}
#[fixture]
fn responding_path_d() -> Vec<Vec<u8>> {
vec![b"a".to_vec(), PATH_DIR_SEPARATOR_BYTES.to_vec()]
}
#[fixture]
fn more_specific_paths_d() -> Vec<Vec<Vec<u8>>> {
vec![
vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
]
}
#[fixture]
fn responding_path_e() -> Vec<Vec<u8>> {
vec![b"a".to_vec()]
}
#[fixture]
fn more_specific_paths_e() -> Vec<Vec<Vec<u8>>> {
vec![
vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
]
}
#[fixture]
fn responding_path_f() -> Vec<Vec<u8>> {
vec![PATH_DIR_SEPARATOR_BYTES.to_vec()]
}
#[fixture]
fn more_specific_paths_f() -> Vec<Vec<Vec<u8>>> {
vec![
vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![b"a".to_vec(), WILDCARD_PATH_TERMINATOR_BYTES.to_vec()],
]
}
#[fixture]
fn responding_path_g() -> Vec<Vec<u8>> {
vec![]
}
#[fixture]
fn more_specific_paths_g() -> Vec<Vec<Vec<u8>>> {
vec![
vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![b"a".to_vec(), WILDCARD_PATH_TERMINATOR_BYTES.to_vec()],
vec![
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
]
}
#[fixture]
fn responding_path_h() -> Vec<Vec<u8>> {
vec![b"d".to_vec(), b"e".to_vec(), b"f".to_vec()]
}
#[fixture]
fn more_specific_paths_h() -> Vec<Vec<Vec<u8>>> {
vec![
vec![
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
b"b".to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![
b"a".to_vec(),
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
vec![b"a".to_vec(), WILDCARD_PATH_TERMINATOR_BYTES.to_vec()],
vec![
PATH_DIR_SEPARATOR_BYTES.to_vec(),
WILDCARD_PATH_TERMINATOR_BYTES.to_vec(),
],
]
}
}