mod external;
mod median;
pub mod milliseconds;
mod round_robin;
use std::{
cell::RefCell,
fmt::{self, Display, Formatter},
fs, io,
net::{SocketAddr, ToSocketAddrs},
path::{Path, PathBuf},
};
use datasize::DataSize;
use hyper::server::{conn::AddrIncoming, Builder, Server};
use libc::{c_long, sysconf, _SC_PAGESIZE};
use once_cell::sync::Lazy;
use serde::Serialize;
use thiserror::Error;
use tracing::warn;
#[cfg(test)]
pub use external::RESOURCES_PATH;
pub use external::{External, LoadError, Loadable};
pub(crate) use median::weighted_median;
pub(crate) use round_robin::WeightedRoundRobin;
const DEFAULT_PAGE_SIZE: usize = 4096;
pub static OS_PAGE_SIZE: Lazy<usize> = Lazy::new(|| {
let value: c_long = unsafe { sysconf(_SC_PAGESIZE) };
if value <= 0 {
DEFAULT_PAGE_SIZE
} else {
value as usize
}
});
pub(crate) fn resolve_address(address: &str) -> io::Result<SocketAddr> {
address.to_socket_addrs()?.next().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
format!("could not resolve `{}`", address),
)
})
}
#[derive(Debug, Error)]
pub enum ListeningError {
#[error("failed to resolve network address: {0}")]
ResolveAddress(io::Error),
#[error("failed to listen on {address}: {error}")]
Listen {
address: SocketAddr,
error: hyper::Error,
},
}
pub(crate) fn start_listening(address: &str) -> Result<Builder<AddrIncoming>, ListeningError> {
let address = resolve_address(address).map_err(|error| {
warn!(%error, %address, "failed to start HTTP server, cannot parse address");
ListeningError::ResolveAddress(error)
})?;
Server::try_bind(&address).map_err(|error| {
warn!(%error, %address, "failed to start HTTP server");
ListeningError::Listen { address, error }
})
}
#[inline]
pub(crate) fn leak<T>(value: T) -> &'static T {
Box::leak(Box::new(value))
}
#[derive(Debug)]
pub(crate) struct DisplayIter<T>(RefCell<Option<T>>);
impl<T> DisplayIter<T> {
pub(crate) fn new(item: T) -> Self {
DisplayIter(RefCell::new(Some(item)))
}
}
impl<I, T> Display for DisplayIter<I>
where
I: IntoIterator<Item = T>,
T: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(src) = self.0.borrow_mut().take() {
let mut first = true;
for item in src.into_iter().take(f.width().unwrap_or(usize::MAX)) {
if first {
first = false;
write!(f, "{}", item)?;
} else {
write!(f, ", {}", item)?;
}
}
Ok(())
} else {
write!(f, "DisplayIter:GONE")
}
}
}
#[derive(Debug, Error)]
#[error("could not read '{0}': {error}", .path.display())]
pub struct ReadFileError {
path: PathBuf,
#[source]
error: io::Error,
}
#[derive(Debug, Error)]
#[error("could not write to '{0}': {error}", .path.display())]
pub struct WriteFileError {
path: PathBuf,
#[source]
error: io::Error,
}
pub fn read_file<P: AsRef<Path>>(filename: P) -> Result<Vec<u8>, ReadFileError> {
let path = filename.as_ref();
fs::read(path).map_err(|error| ReadFileError {
path: path.to_owned(),
error,
})
}
pub(crate) fn write_file<P: AsRef<Path>, B: AsRef<[u8]>>(
filename: P,
data: B,
) -> Result<(), WriteFileError> {
let path = filename.as_ref();
fs::write(path, data.as_ref()).map_err(|error| WriteFileError {
path: path.to_owned(),
error,
})
}
#[derive(Clone, DataSize, Debug)]
pub struct WithDir<T> {
dir: PathBuf,
value: T,
}
impl<T> WithDir<T> {
pub fn new<P: Into<PathBuf>>(path: P, value: T) -> Self {
WithDir {
dir: path.into(),
value,
}
}
pub(crate) fn dir(&self) -> &Path {
self.dir.as_ref()
}
pub(crate) fn into_parts(self) -> (PathBuf, T) {
(self.dir, self.value)
}
pub(crate) fn map_ref<U, F: FnOnce(&T) -> U>(&self, f: F) -> WithDir<U> {
WithDir {
dir: self.dir.clone(),
value: f(&self.value),
}
}
pub(crate) fn value(&self) -> &T {
&self.value
}
pub(crate) fn with_dir(&self, path: PathBuf) -> PathBuf {
if path.is_relative() {
self.dir.join(path)
} else {
path
}
}
}
#[derive(Clone, Debug, Serialize)]
pub enum Source<I> {
Peer(I),
Client,
}
impl<I> Source<I> {
pub(crate) fn from_client(&self) -> bool {
matches!(self, Source::Client)
}
}
impl<I: Clone> Source<I> {
pub(crate) fn node_id(&self) -> Option<I> {
match self {
Source::Peer(node_id) => Some(node_id.clone()),
Source::Client => None,
}
}
}
impl<I: Display> Display for Source<I> {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
match self {
Source::Peer(node_id) => Display::fmt(node_id, formatter),
Source::Client => write!(formatter, "client"),
}
}
}