mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-22 19:34:03 +02:00
221 lines
6.8 KiB
Rust
221 lines
6.8 KiB
Rust
use crate::models::HttpUrlParameter;
|
|
|
|
pub fn apply_path_placeholders(
|
|
url: &str,
|
|
parameters: &Vec<HttpUrlParameter>,
|
|
) -> (String, Vec<HttpUrlParameter>) {
|
|
let mut new_parameters = Vec::new();
|
|
|
|
let mut url = url.to_string();
|
|
for p in parameters {
|
|
if !p.enabled || p.name.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
// Replace path parameters with values from URL parameters
|
|
let old_url_string = url.clone();
|
|
url = replace_path_placeholder(&p, url.as_str());
|
|
|
|
// Remove as param if it modified the URL
|
|
if old_url_string == *url {
|
|
new_parameters.push(p.to_owned());
|
|
}
|
|
}
|
|
|
|
(url, new_parameters)
|
|
}
|
|
|
|
fn replace_path_placeholder(p: &HttpUrlParameter, url: &str) -> String {
|
|
if !p.enabled {
|
|
return url.to_string();
|
|
}
|
|
|
|
if !p.name.starts_with(":") {
|
|
return url.to_string();
|
|
}
|
|
|
|
// A placeholder is `/` followed by the parameter's name (which starts with `:`), and it
|
|
// ends at `/`, `?`, `#`, a literal `:`, or the end of the URL. The `:` boundary is what
|
|
// lets `/:id:increment-importance` substitute the `:id` placeholder while leaving
|
|
// `:increment-importance` as literal text. `/:foooo` is not a match for `:foo`.
|
|
//
|
|
// A plain scan rather than a regex: the name is matched literally, so a name containing
|
|
// `.` or `+` means exactly that, and nothing else in the model layer needs a regex engine.
|
|
let name = p.name.as_str();
|
|
let value = urlencoding::encode(p.value.as_str());
|
|
let mut result = String::with_capacity(url.len());
|
|
let mut rest = url;
|
|
while let Some(slash) = rest.find('/') {
|
|
let after_slash = &rest[slash + 1..];
|
|
let is_placeholder = after_slash.starts_with(name)
|
|
&& after_slash[name.len()..]
|
|
.chars()
|
|
.next()
|
|
.is_none_or(|c| matches!(c, '/' | '?' | '#' | ':'));
|
|
if is_placeholder {
|
|
result.push_str(&rest[..=slash]);
|
|
result.push_str(&value);
|
|
rest = &after_slash[name.len()..];
|
|
} else {
|
|
result.push_str(&rest[..=slash]);
|
|
rest = after_slash;
|
|
}
|
|
}
|
|
result.push_str(rest);
|
|
result
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod placeholder_tests {
|
|
use crate::models::{HttpRequest, HttpUrlParameter};
|
|
use crate::path_placeholders::{apply_path_placeholders, replace_path_placeholder};
|
|
|
|
#[test]
|
|
fn placeholder_middle() {
|
|
let p =
|
|
HttpUrlParameter { name: ":foo".into(), value: "xxx".into(), enabled: true, id: None };
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:foo/bar"),
|
|
"https://example.com/xxx/bar",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_end() {
|
|
let p =
|
|
HttpUrlParameter { name: ":foo".into(), value: "xxx".into(), enabled: true, id: None };
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:foo"),
|
|
"https://example.com/xxx",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_query() {
|
|
let p =
|
|
HttpUrlParameter { name: ":foo".into(), value: "xxx".into(), enabled: true, id: None };
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:foo?:foo"),
|
|
"https://example.com/xxx?:foo",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_followed_by_literal_colon() {
|
|
// AIP-136-style custom method: `:id` is the placeholder, `:increment-importance`
|
|
// is literal text in the same path segment.
|
|
let p =
|
|
HttpUrlParameter { name: ":id".into(), value: "42".into(), enabled: true, id: None };
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/tasks/:id:increment-importance"),
|
|
"https://example.com/tasks/42:increment-importance",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_name_is_matched_literally() {
|
|
// `.` in a name is a dot, not "any character".
|
|
let p = HttpUrlParameter {
|
|
name: ":id.v2".into(),
|
|
value: "xxx".into(),
|
|
enabled: true,
|
|
id: None,
|
|
};
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:id.v2/:idXv2"),
|
|
"https://example.com/xxx/:idXv2",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_repeated() {
|
|
let p = HttpUrlParameter { name: ":id".into(), value: "7".into(), enabled: true, id: None };
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:id/:id"),
|
|
"https://example.com/7/7",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_missing() {
|
|
let p = HttpUrlParameter {
|
|
enabled: true,
|
|
name: "".to_string(),
|
|
value: "".to_string(),
|
|
id: None,
|
|
};
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:missing"),
|
|
"https://example.com/:missing",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_disabled() {
|
|
let p = HttpUrlParameter {
|
|
enabled: false,
|
|
name: ":foo".to_string(),
|
|
value: "xxx".to_string(),
|
|
id: None,
|
|
};
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:foo"),
|
|
"https://example.com/:foo",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_prefix() {
|
|
let p =
|
|
HttpUrlParameter { name: ":foo".into(), value: "xxx".into(), enabled: true, id: None };
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:foooo"),
|
|
"https://example.com/:foooo",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_encode() {
|
|
let p = HttpUrlParameter {
|
|
name: ":foo".into(),
|
|
value: "Hello World".into(),
|
|
enabled: true,
|
|
id: None,
|
|
};
|
|
assert_eq!(
|
|
replace_path_placeholder(&p, "https://example.com/:foo"),
|
|
"https://example.com/Hello%20World",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn apply_placeholder() {
|
|
let req = HttpRequest {
|
|
url: "example.com/:a/bar".to_string(),
|
|
url_parameters: vec![
|
|
HttpUrlParameter {
|
|
name: "b".to_string(),
|
|
value: "bbb".to_string(),
|
|
enabled: true,
|
|
id: None,
|
|
},
|
|
HttpUrlParameter {
|
|
name: ":a".to_string(),
|
|
value: "aaa".to_string(),
|
|
enabled: true,
|
|
id: None,
|
|
},
|
|
],
|
|
..Default::default()
|
|
};
|
|
|
|
let (url, url_parameters) = apply_path_placeholders(&req.url, &req.url_parameters);
|
|
|
|
// Pattern match back to access it
|
|
assert_eq!(url, "example.com/aaa/bar");
|
|
assert_eq!(url_parameters.len(), 1);
|
|
assert_eq!(url_parameters[0].name, "b");
|
|
assert_eq!(url_parameters[0].value, "bbb");
|
|
}
|
|
}
|