pub(crate) mod errors {
pub fn io_error_invalid_data<E: Into<Box<dyn std::error::Error + Send + Sync>>>(
error: E,
) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::InvalidData, error)
}
}
macro_rules! make_command {
($command:literal) => {{
$crate::Command::from($command)
}};
($format:literal $(, $arg:ident)* $(; text: $text:ident)? $(; options: $options:ident)?) => {{
use std::fmt::Write as _;
$(let $arg: &str = $arg.as_ref();)*
$(let $text: &str = $text.as_ref();)?
let mut message: String = String::with_capacity(
$format.len()
$(+ $arg.len())*
$(+ ($text.len() * 2))?
$(+ ($options.len() * 16))?
);
write!(&mut message, $format$(, $arg)*).unwrap();
#[allow(unused_mut, unused_assignments)]
let (mut prefix_len, mut suffix_start) = (message.len(), message.len());
$(
message.push(' ');
message.push('"');
prefix_len = message.len();
message.push_str($crate::util::escape_str($text).as_str());
suffix_start = message.len();
message.push('"');
)?
$(for option in $options {
write!(&mut message, " {option}").map_err(std::io::Error::other)?;
})?
let suffix_len = message.len() - suffix_start;
$crate::Command::new(message.into_boxed_str(), prefix_len, suffix_len)
}};
}
pub(crate) use make_command;
pub(crate) fn escape_str(str: &str) -> String {
let mut res = String::with_capacity(str.len() * 2);
for c in str.chars() {
match c {
'"' => res.push_str("\\\""),
'\r' => res.push_str("\\r"),
'\n' => res.push_str("\\n"),
'\\' => res.push_str("\\\\"),
_ => res.push(c),
}
}
res
}
macro_rules! impl_channel_structs {
($mode:ident($mode_lowercase:literal): $low_level_ty:ident / $blocking_ty:ident / $async_ty:ident) => {
type LowLevelChannel = $low_level_ty;
#[doc = concat!("A synchronous but non-blocking way to interact with a Sonic Channel in ", stringify!($mode), " mode.")]
#[doc = concat!("\n\nShared logic for [`", stringify!($blocking_ty), "`] and [`", stringify!($async_ty),"`], which you should use instead.")]
#[repr(transparent)]
pub struct $low_level_ty {
inner: SonicChannel<self::Mode>,
}
impl $low_level_ty {
#[doc(alias = "new")]
pub fn connect(
addr: impl Into<std::net::SocketAddr>,
pass: impl AsRef<str>,
multiplexer: &$crate::SonicMultiplexer,
) -> std::io::Result<Self> {
SonicChannel::<self::Mode>::connect::<crate::transport::SonicStream>(addr, pass, multiplexer)
.map(|inner| Self { inner })
}
pub fn connect_custom<T: crate::transport::Transport + 'static>(
addr: impl Into<std::net::SocketAddr>,
pass: impl AsRef<str>,
multiplexer: &$crate::SonicMultiplexer,
) -> std::io::Result<Self> {
SonicChannel::<self::Mode>::connect::<T>(addr, pass, multiplexer)
.map(|inner| Self { inner })
}
pub fn server_info(&self) -> &crate::events::ServerInfo {
&self.inner.server_info
}
pub fn channel_info(&self) -> &crate::events::ChannelInfo {
&self.inner.channel_info
}
fn quit_blocking_(&mut self) -> std::io::Result<()> {
self.quit()?
.recv_timeout($crate::RECV_TIMEOUT)
.map_err(|error| std::io::Error::new(std::io::ErrorKind::BrokenPipe, error))?
}
}
impl Drop for $low_level_ty {
#[inline]
fn drop(&mut self) {
if !self.inner.is_closed() {
$crate::logging::log_trace!(concat!("[Drop] Quitting ", stringify!($low_level_ty)));
self.quit_blocking_().unwrap_or_else(|error| crate::logging::log_error!("{error:?}"));
}
}
}
#[cfg(feature = "sync")]
type BlockingChannel = $blocking_ty;
#[doc = concat!("A blocking way to interact with a Sonic Channel in ", stringify!($mode), " mode.")]
#[doc = concat!("\n\nWhen in an asynchronous context (e.g. using `tokio`), use [`", stringify!($async_ty), "`] instead.")]
#[cfg(feature = "sync")]
#[repr(transparent)]
pub struct $blocking_ty {
inner: $low_level_ty,
}
#[cfg(feature = "sync")]
impl $blocking_ty {
#[doc(alias = "new")]
pub fn connect(
addr: impl Into<std::net::SocketAddr>,
pass: impl AsRef<str>,
multiplexer: &$crate::SonicMultiplexer,
) -> std::io::Result<Self> {
$low_level_ty::connect(addr, pass, multiplexer).map(|inner| Self { inner })
}
pub fn connect_custom<T: crate::transport::Transport + 'static>(
addr: impl Into<std::net::SocketAddr>,
pass: impl AsRef<str>,
multiplexer: &$crate::SonicMultiplexer,
) -> std::io::Result<Self> {
$low_level_ty::connect_custom::<T>(addr, pass, multiplexer).map(|inner| Self { inner })
}
pub fn server_info(&self) -> &crate::events::ServerInfo {
self.inner.server_info()
}
pub fn channel_info(&self) -> &crate::events::ChannelInfo {
self.inner.channel_info()
}
}
#[cfg(feature = "async")]
type AsyncChannel = $async_ty;
#[doc = concat!("An asynchronous way to interact with a Sonic Channel in ", stringify!($mode), " mode.")]
#[doc = concat!("\n\nIf you can’t be in an asynchronous context, use [`", stringify!($blocking_ty), "`] instead.")]
#[cfg(feature = "async")]
#[repr(transparent)]
pub struct $async_ty {
inner: $low_level_ty,
}
#[cfg(feature = "async")]
impl $async_ty {
#[doc(alias = "new")]
pub fn connect(
addr: impl Into<std::net::SocketAddr>,
pass: impl AsRef<str>,
multiplexer: &$crate::SonicMultiplexer,
) -> std::io::Result<Self> {
$low_level_ty::connect(addr, pass, multiplexer).map(|inner| Self { inner })
}
pub fn connect_custom<T: crate::transport::Transport + 'static>(
addr: impl Into<std::net::SocketAddr>,
pass: impl AsRef<str>,
multiplexer: &$crate::SonicMultiplexer,
) -> std::io::Result<Self> {
$low_level_ty::connect_custom::<T>(addr, pass, multiplexer).map(|inner| Self { inner })
}
pub fn server_info(&self) -> &crate::events::ServerInfo {
self.inner.server_info()
}
pub fn channel_info(&self) -> &crate::events::ChannelInfo {
self.inner.channel_info()
}
}
};
}
pub(crate) use impl_channel_structs;
macro_rules! impl_fns {
(
$(#[$meta:meta])*
fn $fn:ident $(<$($lifetime:lifetime),+>)? (&mut $self:ident $(, $arg_name:ident: $arg_ty:ty)* $(,)?) -> $ret_ty:ty $main:block
) => {
impl self::LowLevelChannel {
$(#[$meta])*
pub fn $fn$(<$($lifetime),+>)?(
&mut $self,
$($arg_name: $arg_ty,)*
) -> std::io::Result<oneshot::Receiver<$ret_ty>> {
$main
}
}
#[cfg(feature = "sync")]
impl self::BlockingChannel {
$(#[$meta])*
pub fn $fn$(<$($lifetime),+>)?(
&mut $self,
$($arg_name: $arg_ty,)*
) -> $ret_ty {
$self.inner.$fn($($arg_name),*)?
.recv_timeout($crate::RECV_TIMEOUT)
.map_err(|error| std::io::Error::new(std::io::ErrorKind::BrokenPipe, error))?
}
}
#[cfg(feature = "async")]
impl self::AsyncChannel {
pub async fn $fn$(<$($lifetime),+>)?(
&mut $self,
$($arg_name: $arg_ty,)*
) -> $ret_ty {
$self.inner.$fn($($arg_name),*)?
.await
.map_err(|error| std::io::Error::new(std::io::ErrorKind::BrokenPipe, error.to_string()))?
}
}
};
(
$(#[$meta:meta])*
fn $fn:ident $(<$($lifetime:lifetime),+>)? (&$self:ident $(, $arg_name:ident: $arg_ty:ty)* $(,)?) -> $ret_ty:ty $main:block
) => {
impl self::LowLevelChannel {
$(#[$meta])*
pub fn $fn$(<$($lifetime),+>)?(
&$self,
$($arg_name: $arg_ty,)*
) -> std::io::Result<oneshot::Receiver<$ret_ty>> {
$main
}
}
#[cfg(feature = "sync")]
impl self::BlockingChannel {
$(#[$meta])*
pub fn $fn$(<$($lifetime),+>)?(
&$self,
$($arg_name: $arg_ty,)*
) -> $ret_ty {
$self.inner.$fn($($arg_name),*)?
.recv_timeout($crate::RECV_TIMEOUT)
.map_err(|error| std::io::Error::new(std::io::ErrorKind::BrokenPipe, error))?
}
}
#[cfg(feature = "async")]
impl self::AsyncChannel {
pub async fn $fn$(<$($lifetime),+>)?(
&$self,
$($arg_name: $arg_ty,)*
) -> $ret_ty {
$self.inner.$fn($($arg_name),*)?
.await
.map_err(|error| std::io::Error::new(std::io::ErrorKind::BrokenPipe, error.to_string()))?
}
}
};
}
pub(crate) use impl_fns;