use core::fmt;
use core::num::NonZeroU16;
use core::sync::atomic::{AtomicU16, Ordering};
use musli::alloc::Global;
use musli::mode::{Binary, Text};
use musli::{Decode, Encode};
#[doc(inline)]
pub use musli_web_macros::define;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum Format {
Packed,
Storage,
Wire,
Descriptive,
Json,
}
impl Format {
pub const DEFAULT: Self = Self::Wire;
pub const ALL: &'static [Format] = &[
Format::Packed,
Format::Storage,
Format::Wire,
Format::Descriptive,
Format::Json,
];
#[inline]
pub const fn to_u8(self) -> u8 {
match self {
Format::Packed => 1,
Format::Storage => 2,
Format::Wire => 3,
Format::Descriptive => 4,
Format::Json => 5,
}
}
#[inline]
pub const fn from_u8(id: u8) -> Option<Self> {
match id {
1 => Some(Format::Packed),
2 => Some(Format::Storage),
3 => Some(Format::Wire),
4 => Some(Format::Descriptive),
5 => Some(Format::Json),
_ => None,
}
}
#[inline]
pub const fn name(self) -> &'static str {
match self {
Format::Packed => "packed",
Format::Storage => "storage",
Format::Wire => "wire",
Format::Descriptive => "descriptive",
Format::Json => "json",
}
}
#[inline]
pub const fn is_upgrade_safe(self) -> bool {
matches!(self, Format::Wire | Format::Descriptive | Format::Json)
}
#[inline]
pub const fn is_self_descriptive(self) -> bool {
matches!(self, Format::Descriptive | Format::Json)
}
#[inline]
pub const fn is_human_readable(self) -> bool {
matches!(self, Format::Json)
}
}
impl Default for Format {
#[inline]
fn default() -> Self {
Self::DEFAULT
}
}
impl fmt::Display for Format {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
pub trait EncodeBody
where
Self: Encode<Binary> + Encode<Text>,
{
}
impl<T> EncodeBody for T where T: ?Sized + Encode<Binary> + Encode<Text> {}
pub trait DecodeBody<'de>
where
Self: Decode<'de, Binary, Global> + Decode<'de, Text, Global>,
{
}
impl<'de, T> DecodeBody<'de> for T where T: Decode<'de, Binary, Global> + Decode<'de, Text, Global> {}
pub trait Id
where
Self: 'static + Send + Sized + fmt::Debug,
{
fn id(&self) -> MessageId;
fn from_id(id: MessageId) -> Self;
#[doc(hidden)]
fn __do_not_implement_id();
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
#[musli(transparent)]
pub struct ChannelId {
repr: u16,
}
impl ChannelId {
pub const NONE: Self = Self::from_u16(0);
#[inline]
pub const fn from_u16(repr: u16) -> Self {
Self { repr }
}
#[inline]
#[cfg(feature = "ws")]
pub(crate) const fn raw(&self) -> u16 {
self.repr
}
}
impl fmt::Debug for ChannelId {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.repr == 0 {
f.write_str("NONE")
} else {
write!(f, "{:04x}", self.repr)
}
}
}
#[repr(transparent)]
pub struct AtomicChannelId {
repr: AtomicU16,
}
impl AtomicChannelId {
#[allow(clippy::declare_interior_mutable_const)]
pub const NONE: Self = Self::new(ChannelId::NONE);
#[inline]
pub const fn new(id: ChannelId) -> Self {
Self {
repr: AtomicU16::new(id.repr),
}
}
#[inline]
pub fn load(&self, ordering: Ordering) -> ChannelId {
ChannelId::from_u16(self.repr.load(ordering))
}
#[inline]
pub fn store(&self, id: ChannelId, ordering: Ordering) {
self.repr.store(id.repr, ordering);
}
#[inline]
pub fn replace(&self, id: ChannelId, ordering: Ordering) -> ChannelId {
ChannelId::from_u16(self.repr.swap(id.repr, ordering))
}
#[inline]
pub fn take(&self, ordering: Ordering) -> ChannelId {
self.replace(ChannelId::NONE, ordering)
}
#[inline]
pub fn into_inner(self) -> ChannelId {
ChannelId::from_u16(self.repr.into_inner())
}
}
impl Default for AtomicChannelId {
#[inline]
fn default() -> Self {
Self::NONE
}
}
impl From<ChannelId> for AtomicChannelId {
#[inline]
fn from(id: ChannelId) -> Self {
Self::new(id)
}
}
impl fmt::Debug for AtomicChannelId {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.load(Ordering::Relaxed).fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
#[repr(transparent)]
#[musli(transparent)]
pub struct MessageId(NonZeroU16);
impl fmt::Display for MessageId {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl MessageId {
pub const ERROR_MESSAGE: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 1) };
pub const CONNECT: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 2) };
pub const DISCONNECT: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 3) };
pub const SERVER_HELLO: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 4) };
pub const NEGOTIATE: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 5) };
pub const EMPTY: Self = unsafe { Self::new_unchecked(u16::MAX) };
#[inline]
pub const fn new(id: u16) -> Option<Self> {
let Some(value) = NonZeroU16::new(id) else {
return None;
};
Some(Self(value))
}
#[inline]
pub const fn get(&self) -> u16 {
self.0.get()
}
#[inline]
pub const unsafe fn new_unchecked(id: u16) -> Self {
Self(unsafe { NonZeroU16::new_unchecked(id) })
}
}
pub trait Decodable {
type Type<'de>: DecodeBody<'de>;
#[doc(hidden)]
fn __do_not_implement_decodable();
}
pub trait Endpoint
where
Self: 'static,
for<'de> Self: Decodable<Type<'de> = Self::Response<'de>>,
{
const ID: MessageId;
type Response<'de>: DecodeBody<'de>;
#[doc(hidden)]
fn __do_not_implement_endpoint();
}
pub trait Broadcast
where
Self: 'static,
{
const ID: MessageId;
#[doc(hidden)]
fn __do_not_implement_broadcast();
}
pub trait BroadcastWithEvent
where
Self: Broadcast,
for<'de> Self: Decodable<Type<'de> = Self::Event<'de>>,
{
type Event<'de>: Event<Broadcast = Self> + DecodeBody<'de>
where
Self: 'de;
#[doc(hidden)]
fn __do_not_implement_broadcast_with_event();
}
pub trait Request
where
Self: EncodeBody,
{
type Endpoint: Endpoint;
#[doc(hidden)]
fn __do_not_implement_request();
}
pub trait Event
where
Self: EncodeBody,
{
type Broadcast: Broadcast;
#[doc(hidden)]
fn __do_not_implement_event();
}
#[derive(Debug, Clone, Copy, Encode, Decode)]
#[doc(hidden)]
#[musli(packed)]
pub struct Connect;
#[derive(Debug, Clone, Encode, Decode)]
#[doc(hidden)]
#[musli(packed)]
pub struct ResponseHeader {
pub serial: u32,
pub broadcast: u16,
pub error: u16,
pub format: u8,
pub channel: ChannelId,
}
#[derive(Debug, Clone, Encode, Decode)]
#[doc(hidden)]
#[musli(packed)]
pub struct ErrorMessage<'de> {
pub message: &'de str,
}
#[derive(Debug, Clone, Copy, Encode, Decode)]
#[doc(hidden)]
#[musli(packed)]
pub struct RequestHeader {
pub serial: u32,
pub id: u16,
pub format: u8,
pub channel: ChannelId,
}