use serde::{Deserialize, Serialize};
use crate::config::{RouteCache, RouteHeader, RoutePlugin, RouteUpstream};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Location {
pub path: String,
#[serde(default)]
pub exact: bool,
#[serde(default)]
pub upstreams: Vec<RouteUpstream>,
pub headers: Option<RouteHeader>,
pub plugins: Option<Vec<RoutePlugin>>,
pub cache: Option<RouteCache>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeouts: Option<LocationTimeouts>,
}
#[allow(clippy::struct_field_names)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LocationTimeouts {
pub read_secs: Option<u64>,
pub write_secs: Option<u64>,
}
impl LocationTimeouts {
pub fn or(self, defaults: Option<Self>) -> Self {
let defaults = defaults.unwrap_or_default();
Self {
read_secs: self.read_secs.or(defaults.read_secs),
write_secs: self.write_secs.or(defaults.write_secs),
}
}
}
impl Location {
pub fn patterns(&self) -> Vec<String> {
if self.exact || self.path.ends_with(['*', '+']) {
return vec![self.path.clone()];
}
let base = self.path.trim_end_matches('/');
vec![self.path.clone(), format!("{base}/*")]
}
}
#[cfg(test)]
mod tests {
use super::Location;
fn location(path: &str, exact: bool) -> Location {
Location {
path: path.to_string(),
exact,
upstreams: vec![],
headers: None,
plugins: None,
cache: None,
timeouts: None,
}
}
#[test]
fn test_location_patterns_match_the_path_and_below() {
assert_eq!(
location("/v1/session", false).patterns(),
vec!["/v1/session", "/v1/session/*"]
);
assert_eq!(location("/", false).patterns(), vec!["/", "/*"]);
assert_eq!(location("/api/", false).patterns(), vec!["/api/", "/api/*"]);
}
#[test]
fn test_location_patterns_with_wildcards_or_exact() {
assert_eq!(location("/*", false).patterns(), vec!["/*"]);
assert_eq!(
location("/files/:path*", false).patterns(),
vec!["/files/:path*"]
);
assert_eq!(
location("/files/:path+", false).patterns(),
vec!["/files/:path+"]
);
assert_eq!(location("/auth", true).patterns(), vec!["/auth"]);
}
}