use std::{any::TypeId, fmt, str::FromStr, sync::Arc, time::SystemTime};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ChannelConf {
pub enabled: bool,
pub subscriber_queue: usize,
pub replay_limit: usize,
pub retention_events: usize,
pub max_message_bytes: usize,
pub long_poll_timeout_ms: u64,
pub sse_keepalive_ms: u64,
pub slow_subscriber_policy: SlowSubscriberPolicy,
}
impl Default for ChannelConf {
fn default() -> Self {
Self {
enabled: true,
subscriber_queue: 256,
replay_limit: 256,
retention_events: 10_000,
max_message_bytes: 1024 * 1024,
long_poll_timeout_ms: 25_000,
sse_keepalive_ms: 15_000,
slow_subscriber_policy: SlowSubscriberPolicy::Disconnect,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SlowSubscriberPolicy {
Disconnect,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct UserKey(String);
impl UserKey {
pub fn new(key: impl Into<String>) -> Result<Self, ChannelError> {
let key = key.into();
validate_key("user key", &key)?;
Ok(Self(key))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for UserKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct ChannelKey(String);
impl ChannelKey {
pub fn new(key: impl Into<String>) -> Result<Self, ChannelError> {
let key = key.into();
validate_key("channel key", &key)?;
Ok(Self(key))
}
pub(crate) fn generated() -> Self {
Self(uuid::Uuid::now_v7().to_string())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for ChannelKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
fn validate_key(name: &str, key: &str) -> Result<(), ChannelError> {
if key.is_empty() {
return Err(ChannelError::InvalidKey(format!("{name} cannot be empty")));
}
if key.len() > 512 {
return Err(ChannelError::InvalidKey(format!(
"{name} cannot exceed 512 bytes"
)));
}
if key.chars().any(char::is_control) {
return Err(ChannelError::InvalidKey(format!(
"{name} cannot contain control characters"
)));
}
Ok(())
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
)]
pub struct ChannelEventId(u64);
impl ChannelEventId {
pub(crate) fn new(value: u64) -> Self {
Self(value)
}
pub fn as_u64(self) -> u64 {
self.0
}
}
impl fmt::Display for ChannelEventId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ChannelCursor(ChannelEventId);
impl ChannelCursor {
pub fn new(id: ChannelEventId) -> Self {
Self(id)
}
pub fn event_id(self) -> ChannelEventId {
self.0
}
}
impl fmt::Display for ChannelCursor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for ChannelCursor {
type Err = ChannelError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let id = value
.parse::<u64>()
.map_err(|_| ChannelError::InvalidCursor(value.to_string()))?;
Ok(Self(ChannelEventId(id)))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ChannelEvent {
pub id: ChannelEventId,
#[serde(rename = "type")]
pub event_type: String,
pub data: serde_json::Value,
pub created_at: u64,
}
impl ChannelEvent {
pub(crate) fn new(id: ChannelEventId, event_type: String, data: serde_json::Value) -> Self {
Self {
id,
event_type,
data,
created_at: unix_now(),
}
}
}
fn unix_now() -> u64 {
match SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
Ok(duration) => duration.as_secs(),
Err(_) => 0,
}
}
pub type ChannelTransport = u8;
pub const WS: ChannelTransport = 0b001;
pub const SSE: ChannelTransport = 0b010;
pub const POLL: ChannelTransport = 0b100;
pub const ALL_TRANSPORTS: ChannelTransport = WS | SSE | POLL;
pub type DeliveryPredicate = Arc<dyn Fn(&dyn std::any::Any) -> bool + Send + Sync>;
#[derive(Clone)]
pub struct DeliverySpec {
pub type_id: TypeId,
pub type_key: String,
pub event_type: String,
pub predicate: DeliveryPredicate,
}
#[derive(Clone)]
pub(crate) struct DeliveryRule {
pub(crate) predicate: DeliveryPredicate,
}
#[derive(Debug, thiserror::Error)]
pub enum ChannelError {
#[error("invalid channel key: {0}")]
InvalidKey(String),
#[error("invalid channel cursor: {0}")]
InvalidCursor(String),
#[error("channel message is too large: maximum is {max} bytes, got {got}")]
MessageTooLarge { max: usize, got: usize },
#[error("channel backend unavailable")]
BackendUnavailable,
#[error("channel serialization failed: {0}")]
Serialization(String),
#[error("channel transport failed: {0}")]
Transport(String),
#[error("channel transport is not allowed")]
TransportNotAllowed,
}