Skip to main content

onlyne_client/session/
slice.rs

1//! Role-slice hot reload after `Event::SpecReloaded`.
2//!
3//! `reconfigure` currently only runs on `welcome`. A live connection that
4//! stays up across `onlyne reload` never sees that frame, so this module
5//! compares the `query_roles` row against the dispatcher's current slice
6//! and calls `reconfigure` only when `max_sessions`,
7//! `session_command`, or the relay policy changed.
8
9use onlyne_proto::{RoleInfo, Welcome};
10
11/// The fields `reconfigure` consumes.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct RoleSlice {
14    pub command: Vec<String>,
15    pub max_sessions: u32,
16    /// Downstream handoffs a session of this role owes (`relay_required`).
17    pub relay_required: Vec<String>,
18    /// The count form of the same policy (`relay_count`); a non-empty list wins
19    /// when both are present, which is the guard's own precedence.
20    pub relay_count: Option<u32>,
21}
22
23impl RoleSlice {
24    pub fn from_welcome(welcome: &Welcome) -> Self {
25        Self {
26            command: welcome.session_command.clone().unwrap_or_default(),
27            max_sessions: welcome.max_sessions,
28            relay_required: welcome.relay_required.clone().unwrap_or_default(),
29            relay_count: welcome.relay_count,
30        }
31    }
32
33    pub fn from_role_info(info: &RoleInfo, _current: &RoleSlice) -> Self {
34        Self {
35            command: info.session_command.clone(),
36            max_sessions: info.max_sessions,
37            relay_required: info.relay_required.clone().unwrap_or_default(),
38            relay_count: info.relay_count,
39        }
40    }
41}
42
43/// Fields that would change if `next` were applied.
44pub fn slice_diff(current: &RoleSlice, next: &RoleSlice) -> Vec<&'static str> {
45    let mut fields = Vec::new();
46    if current.command != next.command {
47        fields.push("session_command");
48    }
49    if current.max_sessions != next.max_sessions {
50        fields.push("max_sessions");
51    }
52    if current.relay_required != next.relay_required {
53        fields.push("relay_required");
54    }
55    if current.relay_count != next.relay_count {
56        fields.push("relay_count");
57    }
58    fields
59}
60
61/// Apply `next` when it differs; return the fields that changed.
62pub fn apply_if_changed(
63    current: &RoleSlice,
64    next: RoleSlice,
65) -> Option<(RoleSlice, Vec<&'static str>)> {
66    let fields = slice_diff(current, &next);
67    if fields.is_empty() {
68        None
69    } else {
70        Some((next, fields))
71    }
72}
73
74#[cfg(test)]
75mod tests;