mod exec;
mod file;
mod parse;
mod pipe;
mod stdio;
mod stream;
mod sys;
mod tcp;
mod udp;
mod unix;
use std::num::NonZeroUsize;
use serde::{Deserialize, Serialize};
pub use self::{
exec::{Exec, System},
file::File,
parse::ParseEndpointError,
pipe::Pipe,
stdio::Stdio,
stream::{
BoxRead, BoxWrite, Connection, DatagramSocket, EndpointStream, ReadHalf, SyncHalves,
SyncRead, SyncWrite, WriteHalf,
},
sys::{PathGuard, size_if_pipe},
tcp::{Tcp, TcpListen},
udp::{Udp, UdpListen},
unix::{Unix, UnixListen},
};
const DEFAULT_HOST: &str = "127.0.0.1";
const DEFAULT_PORT: u16 = 8000;
const DEFAULT_MAX_CONNECTIONS: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Endpoint {
Raw(String),
Spec(EndpointSpec),
}
impl Endpoint {
pub fn into_spec(self) -> Result<EndpointSpec, ParseEndpointError> {
match self {
Endpoint::Raw(raw) => raw.parse(),
Endpoint::Spec(spec) => Ok(spec),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Direction {
Source,
Sink,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum EndpointSpec {
#[serde(
alias = "TCP",
alias = "tcp-connect",
alias = "connect",
alias = "TCP-CONNECT",
alias = "CONNECT"
)]
Tcp(Tcp),
#[serde(
alias = "TCP-LISTEN",
alias = "tcplisten",
alias = "TCPLISTEN",
alias = "listen",
alias = "LISTEN"
)]
TcpListen(TcpListen),
Stdio(Stdio),
#[serde(alias = "UNIX", alias = "unix-connect", alias = "UNIX-CONNECT")]
Unix(Unix),
#[serde(alias = "UNIX-LISTEN", alias = "unixlisten", alias = "UNIXLISTEN")]
UnixListen(UnixListen),
#[serde(alias = "fifo", alias = "FIFO", alias = "PIPE")]
Pipe(Pipe),
#[serde(alias = "open", alias = "FILE", alias = "OPEN")]
File(File),
Exec(Exec),
System(System),
#[serde(alias = "UDP", alias = "udp-connect", alias = "UDP-CONNECT")]
Udp(Udp),
#[serde(alias = "UDP-LISTEN", alias = "udplisten", alias = "UDPLISTEN")]
UdpListen(UdpListen),
}
impl EndpointSpec {
pub fn is_listen(&self) -> bool {
matches!(
self,
Self::TcpListen(_) | Self::UnixListen(_) | Self::UdpListen(_)
)
}
pub fn is_datagram(&self) -> bool {
matches!(self, Self::Udp(_) | Self::UdpListen(_))
}
pub fn is_fork(&self) -> bool {
match self {
Self::TcpListen(e) => e.fork,
Self::UnixListen(e) => e.fork,
_ => false,
}
}
pub fn name(&self) -> String {
match self {
Self::Tcp(e) => e.label(),
Self::TcpListen(e) => e.label(),
Self::Stdio(e) => e.label(),
Self::Unix(e) => e.label(),
Self::UnixListen(e) => e.label(),
Self::Pipe(e) => e.label(),
Self::File(e) => e.label(),
Self::Exec(e) => e.label(),
Self::System(e) => e.label(),
Self::Udp(e) => e.label(),
Self::UdpListen(e) => e.label(),
}
}
pub fn max_connections(&self) -> NonZeroUsize {
match self {
Self::TcpListen(TcpListen {
max_connections, ..
})
| Self::UnixListen(UnixListen {
max_connections, ..
}) => max_connections.unwrap_or(DEFAULT_MAX_CONNECTIONS),
_ => DEFAULT_MAX_CONNECTIONS,
}
}
pub fn is_blocking_backed(&self) -> bool {
matches!(self, Self::File(_) | Self::Stdio(_) | Self::Pipe(_))
}
pub fn connect_sync(&self, dir: Direction, buffer: usize) -> anyhow::Result<SyncHalves> {
match self {
Self::Stdio(e) => Ok(e.connect_sync(buffer)),
Self::Pipe(e) => e.connect_sync(dir),
Self::File(e) => e.connect_sync(dir),
other => anyhow::bail!("{} has no synchronous form", other.name()),
}
}
pub async fn connect(&self, dir: Direction, buffer: usize) -> anyhow::Result<Connection> {
match self {
Self::Tcp(e) => e.connect().await,
Self::TcpListen(e) => e.connect().await,
Self::Stdio(e) => e.connect(buffer),
Self::Unix(e) => e.connect().await,
Self::UnixListen(e) => e.connect().await,
Self::Pipe(e) => e.connect(dir).await,
Self::File(e) => e.connect(dir).await,
Self::Exec(e) => e.connect(buffer).await,
Self::System(e) => e.connect(buffer).await,
Self::Udp(e) => e.connect().await,
Self::UdpListen(e) => e.connect().await,
}
}
}