supercode-interchange 0.4.18

Canonical, provider-neutral session interchange primitives for Supercode
Documentation
//! Routes: which profile answers a surface (ยง2.3, Hermes `gateway.profile_routes`).

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::ontology::{Residue, SurfaceKey};

/// What a route matches; most specific wins.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct RouteMatch {
    /// The platform.
    pub platform: String,
    /// Discord-style guild id.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub guild_id: Option<String>,
    /// The chat.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub chat_id: Option<String>,
    /// The thread.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub thread_id: Option<String>,
}

/// One route.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Route {
    /// Optional name.
    #[serde(default)]
    pub name: Option<String>,
    /// What it matches.
    #[serde(rename = "match")]
    pub matches: RouteMatch,
    /// The profile that answers.
    pub profile: String,
    /// Unmodeled fields of the route, verbatim.
    #[serde(default)]
    pub residue: Residue,
}

/// Most-specific-first: thread > chat > guild > platform.
pub fn route_specificity(route: &Route) -> u8 {
    let m = &route.matches;
    (if m.thread_id.is_some() { 8 } else { 0 })
        + (if m.chat_id.is_some() { 4 } else { 0 })
        + (if m.guild_id.is_some() { 2 } else { 0 })
        + 1
}

/// The profile a surface routes to, or `None` for the adapter's own profile.
/// Ties are broken by declaration order, as Hermes does.
pub fn resolve_route<'a>(
    routes: &'a [Route],
    key: &SurfaceKey,
    guild_id: Option<&str>,
) -> Option<&'a str> {
    let mut best: Option<&Route> = None;
    for route in routes {
        let m = &route.matches;
        if Some(m.platform.as_str()) != key.platform.as_deref() {
            continue;
        }
        if m.guild_id.is_some() && m.guild_id.as_deref() != guild_id {
            continue;
        }
        if m.chat_id.is_some() && m.chat_id != key.chat_id {
            continue;
        }
        if m.thread_id.is_some() && m.thread_id != key.thread_id {
            continue;
        }
        if best.is_none_or(|b| route_specificity(route) > route_specificity(b)) {
            best = Some(route);
        }
    }
    best.map(|r| r.profile.as_str())
}