use transport_core::Endpoint;
use super::selection::weighted_pick;
use super::{RouteHint, RouteRule};
impl RouteRule {
fn wildcard_match(pattern: &str, target: &str) -> bool {
if pattern == "*" {
return true;
}
if let Some(star) = pattern.find('*') {
let prefix = &pattern[..star];
let suffix = &pattern[star + 1..];
return target.starts_with(prefix) && target.ends_with(suffix);
}
pattern == target
}
pub fn matches(&self, endpoint: &Endpoint, hint: &RouteHint) -> bool {
let label_match = self.required_labels.iter().all(|required| {
endpoint
.labels
.iter()
.any(|candidate| candidate == required)
});
if !label_match {
return false;
}
if !self.match_kinds.is_empty() {
let Some(kind) = hint.traffic_kind else {
return false;
};
if !self.match_kinds.iter().any(|allowed| allowed == &kind) {
return false;
}
}
if !self.name_patterns.is_empty() {
let Some(target) = &hint.target_name else {
return false;
};
let pattern_match = self
.name_patterns
.iter()
.any(|pattern| Self::wildcard_match(pattern, target));
if !pattern_match {
return false;
}
}
if self.preferred_schemes.is_empty() {
return true;
}
self.preferred_schemes
.iter()
.any(|scheme| scheme == &endpoint.scheme)
}
}
pub fn resolve_with_rules(
endpoints: &[Endpoint],
rules: &[RouteRule],
hint: &RouteHint,
) -> Option<Endpoint> {
for rule in rules {
let domain_match = match rule.preferred_domain {
Some(domain) => domain == hint.preferred_domain,
None => true,
};
if !domain_match {
continue;
}
let matching: Vec<&Endpoint> = endpoints
.iter()
.filter(|ep| rule.matches(ep, hint))
.collect();
if matching.is_empty() {
continue;
}
let best_scheme_rank = matching
.iter()
.map(|ep| scheme_rank(rule, ep))
.min()
.unwrap_or(usize::MAX);
let prioritized: Vec<&Endpoint> = matching
.into_iter()
.filter(|ep| scheme_rank(rule, ep) == best_scheme_rank)
.collect();
if let Some(endpoint) = weighted_pick(&prioritized, hint) {
return Some(endpoint.clone());
}
}
None
}
fn scheme_rank(rule: &RouteRule, endpoint: &Endpoint) -> usize {
if rule.preferred_schemes.is_empty() {
return 0;
}
rule.preferred_schemes
.iter()
.position(|scheme| scheme == &endpoint.scheme)
.unwrap_or(usize::MAX)
}