#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "README.md" ) ) ]
#![deny(missing_docs)]
use core::fmt;
use std::{net::ToSocketAddrs, time::Duration};
use once_cell::sync::Lazy;
mod server;
pub use server::Server;
mod client;
pub use client::{Client, ConnectionOptions};
#[cfg(feature = "async")]
mod client_async;
#[cfg(feature = "async")]
pub use client_async::ClientAsync;
#[cfg(feature = "locking-default")]
use parking_lot::{Condvar, Mutex, RawMutex};
#[cfg(feature = "locking-rt")]
use parking_lot_rt::{Condvar, Mutex, RawMutex};
#[cfg(feature = "locking-rt-safe")]
use rtsc::pi::{Condvar, Mutex, RawMutex};
const GREETING: &str = "RFLOW";
const HEADERS_TRANSMISSION_END: &str = "---";
const API_VERSION: u8 = 1;
const DEFAULT_INCOMING_QUEUE_SIZE: usize = 128;
const DEFAULT_OUTGOING_QUEUE_SIZE: usize = 128;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
static DEFAULT_SERVER: Lazy<Server> = Lazy::new(|| Server::new(DEFAULT_TIMEOUT));
pub fn serve(addr: impl ToSocketAddrs + std::fmt::Debug) -> Result<(), Error> {
DEFAULT_SERVER.serve(addr)
}
pub fn spawn(addr: impl ToSocketAddrs + std::fmt::Debug) -> Result<server::FrameReceiver, Error> {
let listener = std::net::TcpListener::bind(addr)?;
std::thread::spawn(move || {
DEFAULT_SERVER
.serve_with_listener(listener)
.expect("RFlow server error");
});
DEFAULT_SERVER.take_data_channel()
}
pub fn send(data: impl ToString) {
DEFAULT_SERVER.send(data);
}
pub fn take_data_channel() -> Result<server::FrameReceiver, Error> {
DEFAULT_SERVER.take_data_channel()
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Direction {
ClientToServer,
ServerToClient,
Last,
}
impl Direction {
#[inline]
pub fn as_bytes(self) -> &'static [u8] {
self.as_str().as_bytes()
}
pub fn as_str(self) -> &'static str {
match self {
Self::ClientToServer => ">>>",
Self::ServerToClient => "<<<",
Self::Last => unreachable!(),
}
}
pub fn as_char(self) -> char {
match self {
Self::ClientToServer => '>',
Self::ServerToClient => '<',
Self::Last => unreachable!(),
}
}
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Data channel is already taken")]
DataChannelTaken,
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Unsupported API version: {0}")]
ApiVersion(u8),
#[error("Invalid data")]
InvalidData,
#[error("Invalid address")]
InvalidAddress,
#[error("Timed out")]
Timeout,
}
#[cfg(feature = "async")]
impl From<tokio::time::error::Elapsed> for Error {
fn from(_: tokio::time::error::Elapsed) -> Self {
Self::Timeout
}
}