robotrt-middleware-core 0.1.0-beta.2

RobotRT modular robotics runtime and middleware components.
Documentation
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)
}