//! Newtypes used by [`NotifyState`](super::NotifyState) use core::{fmt::Display, time::Duration}; use super::error; /// Allowed File descriptor name. /// /// A name is allowed when it : /// /// * Has less than 255 characters. /// * Is ASCII. /// * Doesn't contains control characters. /// * Doesn't contains a colon (`:`). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct FdName<'a>(&'a str); impl<'a> TryFrom<&'a str> for FdName<'a> { type Error = error::FdNameError; fn try_from(name: &'a str) -> Result { if name.len() > 255 { return Err(error::FdNameError::TooLong { length: name.len(), name: name.into(), }); } for c in name.chars() { if !c.is_ascii() || c.is_ascii_control() { return Err(error::FdNameError::NotAsciiNonControl { disallowed_char: c, name: name.into(), }); } if c == ':' { return Err(error::FdNameError::ContainColon(name.into())); } } Ok(Self(name)) } } impl AsRef for FdName<'_> { fn as_ref(&self) -> &str { self.0 } } impl Display for FdName<'_> { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(self.0, f) } } /// A status line for [`NotifyState::Status`](super::NotifyState::Status). /// /// As the name explains it needs to be a single line. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct StatusLine<'a>(&'a str); impl<'a> TryFrom<&'a str> for StatusLine<'a> { type Error = error::StatusLineError; fn try_from(value: &'a str) -> Result { if value.lines().nth(1).is_some() { return Err(Self::Error::TooManyLines); } Ok(Self(value)) } } impl AsRef for StatusLine<'_> { fn as_ref(&self) -> &str { self.0 } } impl Display for StatusLine<'_> { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(self.0, f) } } /// Semantic type representing a number of microseconds. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Microseconds(Duration); impl TryFrom for Microseconds { type Error = error::MicrosecondsFromDurationError; fn try_from(value: Duration) -> Result { if value.as_micros() == 0 { return Err(Self::Error::NoMicroseconds); } Ok(Self(value)) } } impl Display for Microseconds { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0.as_micros()) } } /// Timeout for the [`Notifier::barrier()`](super::Notifier::barrier). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BarrierTimeout { /// Will block indefinitely. Infinite, /// Will not block at all and return immediately even if no event has happened. Immediate, /// Will block for a number of milliseconds. NonZero(PollTimeout), } impl BarrierTimeout { pub(crate) const fn to_raw(self) -> i32 { match self { Self::Immediate => 0_i32, Self::Infinite => -1_i32, Self::NonZero(PollTimeout(timeout)) => timeout.get(), } } } /// Variant of [`BarrierTimeout`] for positive timeout. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PollTimeout(core::num::NonZeroI32); impl TryFrom for PollTimeout { type Error = error::PollTimeoutFromIntError; fn try_from(milliseconds: core::num::NonZeroI32) -> Result { if milliseconds.is_negative() { return Err(Self::Error::Negative); } Ok(Self(milliseconds)) } } impl From for core::num::NonZeroI32 { #[inline] fn from(value: PollTimeout) -> Self { value.0 } } /// A D-Bus error-style error code. /// /// Right now it doesn't impose any additional constraint on [`str`] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BusError<'a>(&'a str); impl<'a> From<&'a str> for BusError<'a> { fn from(value: &'a str) -> Self { Self(value) } } impl AsRef for BusError<'_> { #[inline] fn as_ref(&self) -> &str { self.0 } } impl Display for BusError<'_> { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(self.0, f) } } /// An arbitrary custom state other than `BARRIER=1`. /// /// `BARRIER=1` is blocked as it result in special expectations by the protocol. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct OtherState<'a>(&'a str); impl<'a> TryFrom<&'a str> for OtherState<'a> { type Error = error::OtherStateError; fn try_from(value: &'a str) -> Result { if value.contains('\n') { return Err(Self::Error::TooManyLines); } if !value.contains('=') { return Err(Self::Error::NoAssignement); } if value == "BARRIER=1" { return Err(Self::Error::DisallowedState(String::from(value))); } Ok(Self(value)) } } impl AsRef for OtherState<'_> { #[inline] fn as_ref(&self) -> &str { self.0 } } impl OtherState<'_> { pub(super) const fn barrier() -> Self { Self("BARRIER=1") } }