pushwire-core 0.1.1

Shared types and codecs for push-wire multiplexed push protocol
Documentation
use serde::{Serialize, de::DeserializeOwned};
use std::fmt::Debug;
use std::hash::Hash;

/// Trait for defining multiplexed channel types.
///
/// Consumers implement this trait to define their own channel taxonomy.
/// Each channel has a priority, a wire ID for binary encoding, and
/// string representations for JSON/SSE serialization.
///
/// # Example
///
/// ```rust
/// use pushwire_core::ChannelKind;
///
/// #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
/// #[serde(rename_all = "lowercase")]
/// enum MyChannel { Events, Control, System }
///
/// impl ChannelKind for MyChannel {
///     fn priority(&self) -> u8 {
///         match self {
///             MyChannel::System | MyChannel::Events => 0,
///             MyChannel::Control => 1,
///         }
///     }
///     fn wire_id(&self) -> u8 {
///         match self {
///             MyChannel::Events => 0x01,
///             MyChannel::Control => 0x02,
///             MyChannel::System => 0x05,
///         }
///     }
///     fn from_wire_id(id: u8) -> Option<Self> {
///         match id {
///             0x01 => Some(MyChannel::Events),
///             0x02 => Some(MyChannel::Control),
///             0x05 => Some(MyChannel::System),
///             _ => None,
///         }
///     }
///     fn from_name(s: &str) -> Option<Self> {
///         match s {
///             "events" => Some(MyChannel::Events),
///             "control" => Some(MyChannel::Control),
///             "system" => Some(MyChannel::System),
///             _ => None,
///         }
///     }
///     fn name(&self) -> &'static str {
///         match self {
///             MyChannel::Events => "events",
///             MyChannel::Control => "control",
///             MyChannel::System => "system",
///         }
///     }
///     fn is_system(&self) -> bool { matches!(self, MyChannel::System) }
///     fn all() -> &'static [Self] { &[Self::Events, Self::Control, Self::System] }
/// }
/// ```
pub trait ChannelKind:
    Debug + Clone + Copy + Hash + Eq + Send + Sync + Serialize + DeserializeOwned + 'static
{
    /// Priority for outbound queue ordering. Lower values = higher priority.
    /// Typical mapping: 0 = high (system, critical UI), 1 = normal, 2 = low (telemetry).
    fn priority(&self) -> u8;

    /// Numeric ID for the binary wire format. Must be unique per channel.
    fn wire_id(&self) -> u8;

    /// Decode from binary wire ID. Returns `None` for unknown IDs.
    fn from_wire_id(id: u8) -> Option<Self>;

    /// Parse from canonical string name (for SSE query params, JSON fields).
    fn from_name(s: &str) -> Option<Self>;

    /// Canonical string name for this channel.
    fn name(&self) -> &'static str;

    /// Whether this channel is the system/control channel.
    /// The system channel handles auth, ping/pong, ACK, and subscription management.
    /// Exactly one channel should return `true`.
    fn is_system(&self) -> bool;

    /// All valid channel variants. Used for cursor initialization and enumeration.
    fn all() -> &'static [Self];
}

/// Parse a comma-separated channel list (e.g. from SSE query params).
pub fn parse_channels<C: ChannelKind>(raw: Option<&str>) -> Vec<C> {
    raw.map(|list| {
        list.split(',')
            .filter_map(|s| C::from_name(s.trim()))
            .collect()
    })
    .unwrap_or_default()
}