#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
#[cfg(not(all(
target_pointer_width = "64",
target_endian = "little",
target_has_atomic = "64"
)))]
compile_error!("Protocol v3 requires a little-endian 64-bit target with native 64-bit atomics");
mod platform;
mod queue;
use std::{fmt, io, path::PathBuf};
pub use queue::{Publisher, Subscriber, MAX_PUBLISHERS};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Options {
pub name: String,
pub path: PathBuf,
pub capacity: usize,
}
impl Options {
pub fn new(name: impl Into<String>, capacity: usize) -> Self {
Self {
name: name.into(),
path: std::env::temp_dir(),
capacity,
}
}
pub fn with_path(mut self, path: impl Into<PathBuf>) -> Self {
self.path = path.into();
self
}
fn validate(&self) -> Result<()> {
if cfg!(target_os = "macos") && self.name.len() > 24 {
return Err(Error::Invalid(
"queue name exceeds the macOS limit of 24 UTF-8 bytes",
));
}
if cfg!(target_os = "linux") && self.name.len() > 245 {
return Err(Error::Invalid(
"queue name exceeds the Linux limit of 245 UTF-8 bytes",
));
}
if self.name.is_empty()
|| self.name.contains(['\0', '/'])
|| (cfg!(windows) && self.name.contains('\\'))
|| self.name == "."
|| self.name == ".."
{
return Err(Error::Invalid("queue name must be a nonempty file name"));
}
if self.capacity <= 16 || !self.capacity.is_multiple_of(8) {
return Err(Error::Invalid(
"capacity must exceed 16 bytes and be a multiple of 8",
));
}
if self
.capacity
.checked_add(queue::BUFFER_OFFSET)
.is_none_or(|n| n > isize::MAX as usize)
{
return Err(Error::Invalid("queue mapping is too large"));
}
Ok(())
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Full,
Invalid(&'static str),
CapacityMismatch,
PublisherLimit,
Exhausted,
Corrupt,
Io(io::Error),
}
impl Error {
pub fn is_full(&self) -> bool {
matches!(self, Self::Full)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Full => f.write_str("queue is full or temporarily unavailable during recovery"),
Self::Invalid(message) => f.write_str(message),
Self::CapacityMismatch => f.write_str("capacity does not match the existing queue"),
Self::PublisherLimit => f.write_str("the queue already has 2048 connected publishers"),
Self::Exhausted => f.write_str("queue lifetime counter exhausted; use a fresh queue"),
Self::Corrupt => f.write_str("corrupt or inconsistent shared queue state"),
Self::Io(error) => error.fmt(f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(error) => Some(error),
_ => None,
}
}
}
impl From<io::Error> for Error {
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
pub type Result<T> = std::result::Result<T, Error>;