Add a Newtype for custom states

This allow us to do some sanity checks on them.
This commit is contained in:
Mathieu Trossevin 2024-01-04 14:31:02 +01:00
parent cbfa648c4a
commit 7d930c3e42
Signed by: mtrossevin
GPG key ID: D1DBB7EA828374E9
3 changed files with 53 additions and 5 deletions

View file

@ -77,9 +77,9 @@ pub enum FdNameError {
impl Display for FdNameError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FdNameError::TooLong { length, name } => write!(f, "the file descriptor name {name:?} is too long (is {length} characters and should be less than 255)"),
FdNameError::NotAsciiNonControl { disallowed_char, name } => write!(f, "the file descriptor name {name:?} contains invalid character '{disallowed_char}' (only ASCII allowed)"),
FdNameError::ContainColon(name) => write!(f, "the file descriptor name {name:?} contains a colon (':') which isn't allowed"),
Self::TooLong { length, name } => write!(f, "the file descriptor name {name:?} is too long (is {length} characters and should be less than 255)"),
Self::NotAsciiNonControl { disallowed_char, name } => write!(f, "the file descriptor name {name:?} contains invalid character '{disallowed_char}' (only ASCII allowed)"),
Self::ContainColon(name) => write!(f, "the file descriptor name {name:?} contains a colon (':') which isn't allowed"),
}
}
}
@ -102,3 +102,22 @@ impl Display for StatusLineError {
}
impl Error for StatusLineError {}
#[derive(Debug)]
pub enum OtherStateError {
TooManyLines,
NoAssignement,
DisallowedState(String),
}
impl Display for OtherStateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DisallowedState(state) => write!(f, "The state assignement {state:?} isn't allowed."),
Self::NoAssignement => write!(f, "No assignement was found in provided custom state."),
Self::TooManyLines => write!(f, "States cannot contains newlines (they are newline separated in the underlying protocol).")
}
}
}
impl Error for OtherStateError {}

View file

@ -216,7 +216,7 @@ pub enum NotifyState<'a> {
/// The main process ID of the service, in case of forking applications.
Mainpid(libc::pid_t),
/// Custom state change, as a `KEY=VALUE` string.
Other(&'a str),
Other(types::OtherState<'a>),
/// Service startup is finished.
Ready,
/// Service is reloading.
@ -247,7 +247,7 @@ impl<'a> Display for NotifyState<'a> {
NotifyState::FdStoreRemove => f.write_str("FDSTOREREMOVE=1"),
NotifyState::FdpollDisable => f.write_str("FDPOLL=0"),
NotifyState::Mainpid(pid) => write!(f, "MAINPID={pid}"),
NotifyState::Other(message) => Display::fmt(message, f),
NotifyState::Other(types::OtherState(message)) => f.write_str(message),
NotifyState::Ready => f.write_str("READY=1"),
NotifyState::Reloading => f.write_str("RELOADING=1"),
NotifyState::Status(types::StatusLine(status)) => write!(f, "STATUS={status}"),

View file

@ -102,3 +102,32 @@ impl AsRef<str> for BusError<'_> {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct OtherState<'a>(pub(super) &'a str);
impl<'a> TryFrom<&'a str> for OtherState<'a> {
type Error = error::OtherStateError;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
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<str> for OtherState<'_> {
fn as_ref(&self) -> &str {
self.0
}
}