supercode_interchange/orchestration/
route.rs1use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::ontology::{Residue, SurfaceKey};
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
10pub struct RouteMatch {
11 pub platform: String,
13 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub guild_id: Option<String>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
18 pub chat_id: Option<String>,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub thread_id: Option<String>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
26pub struct Route {
27 #[serde(default)]
29 pub name: Option<String>,
30 #[serde(rename = "match")]
32 pub matches: RouteMatch,
33 pub profile: String,
35 #[serde(default)]
37 pub residue: Residue,
38}
39
40pub fn route_specificity(route: &Route) -> u8 {
42 let m = &route.matches;
43 (if m.thread_id.is_some() { 8 } else { 0 })
44 + (if m.chat_id.is_some() { 4 } else { 0 })
45 + (if m.guild_id.is_some() { 2 } else { 0 })
46 + 1
47}
48
49pub fn resolve_route<'a>(
52 routes: &'a [Route],
53 key: &SurfaceKey,
54 guild_id: Option<&str>,
55) -> Option<&'a str> {
56 let mut best: Option<&Route> = None;
57 for route in routes {
58 let m = &route.matches;
59 if Some(m.platform.as_str()) != key.platform.as_deref() {
60 continue;
61 }
62 if m.guild_id.is_some() && m.guild_id.as_deref() != guild_id {
63 continue;
64 }
65 if m.chat_id.is_some() && m.chat_id != key.chat_id {
66 continue;
67 }
68 if m.thread_id.is_some() && m.thread_id != key.thread_id {
69 continue;
70 }
71 if best.is_none_or(|b| route_specificity(route) > route_specificity(b)) {
72 best = Some(route);
73 }
74 }
75 best.map(|r| r.profile.as_str())
76}