pushwire_core/channel.rs
1use serde::{Serialize, de::DeserializeOwned};
2use std::fmt::Debug;
3use std::hash::Hash;
4
5/// Trait for defining multiplexed channel types.
6///
7/// Consumers implement this trait to define their own channel taxonomy.
8/// Each channel has a priority, a wire ID for binary encoding, and
9/// string representations for JSON/SSE serialization.
10///
11/// # Example
12///
13/// ```rust
14/// use pushwire_core::ChannelKind;
15///
16/// #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
17/// #[serde(rename_all = "lowercase")]
18/// enum MyChannel { Events, Control, System }
19///
20/// impl ChannelKind for MyChannel {
21/// fn priority(&self) -> u8 {
22/// match self {
23/// MyChannel::System | MyChannel::Events => 0,
24/// MyChannel::Control => 1,
25/// }
26/// }
27/// fn wire_id(&self) -> u8 {
28/// match self {
29/// MyChannel::Events => 0x01,
30/// MyChannel::Control => 0x02,
31/// MyChannel::System => 0x05,
32/// }
33/// }
34/// fn from_wire_id(id: u8) -> Option<Self> {
35/// match id {
36/// 0x01 => Some(MyChannel::Events),
37/// 0x02 => Some(MyChannel::Control),
38/// 0x05 => Some(MyChannel::System),
39/// _ => None,
40/// }
41/// }
42/// fn from_name(s: &str) -> Option<Self> {
43/// match s {
44/// "events" => Some(MyChannel::Events),
45/// "control" => Some(MyChannel::Control),
46/// "system" => Some(MyChannel::System),
47/// _ => None,
48/// }
49/// }
50/// fn name(&self) -> &'static str {
51/// match self {
52/// MyChannel::Events => "events",
53/// MyChannel::Control => "control",
54/// MyChannel::System => "system",
55/// }
56/// }
57/// fn is_system(&self) -> bool { matches!(self, MyChannel::System) }
58/// fn all() -> &'static [Self] { &[Self::Events, Self::Control, Self::System] }
59/// }
60/// ```
61pub trait ChannelKind:
62 Debug + Clone + Copy + Hash + Eq + Send + Sync + Serialize + DeserializeOwned + 'static
63{
64 /// Priority for outbound queue ordering. Lower values = higher priority.
65 /// Typical mapping: 0 = high (system, critical UI), 1 = normal, 2 = low (telemetry).
66 fn priority(&self) -> u8;
67
68 /// Numeric ID for the binary wire format. Must be unique per channel.
69 fn wire_id(&self) -> u8;
70
71 /// Decode from binary wire ID. Returns `None` for unknown IDs.
72 fn from_wire_id(id: u8) -> Option<Self>;
73
74 /// Parse from canonical string name (for SSE query params, JSON fields).
75 fn from_name(s: &str) -> Option<Self>;
76
77 /// Canonical string name for this channel.
78 fn name(&self) -> &'static str;
79
80 /// Whether this channel is the system/control channel.
81 /// The system channel handles auth, ping/pong, ACK, and subscription management.
82 /// Exactly one channel should return `true`.
83 fn is_system(&self) -> bool;
84
85 /// All valid channel variants. Used for cursor initialization and enumeration.
86 fn all() -> &'static [Self];
87}
88
89/// Parse a comma-separated channel list (e.g. from SSE query params).
90pub fn parse_channels<C: ChannelKind>(raw: Option<&str>) -> Vec<C> {
91 raw.map(|list| {
92 list.split(',')
93 .filter_map(|s| C::from_name(s.trim()))
94 .collect()
95 })
96 .unwrap_or_default()
97}