use std::{
io,
path::{Path, PathBuf},
};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ServiceIdentity {
id: [u8; 16],
application: String,
profile: String,
instance: String,
}
impl ServiceIdentity {
pub fn new(
id: [u8; 16],
application: impl Into<String>,
profile: impl Into<String>,
instance: impl Into<String>,
) -> io::Result<Self> {
if id == [0; 16] {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"service UUID must not be nil",
));
}
let identity = Self {
id,
application: application.into(),
profile: profile.into(),
instance: instance.into(),
};
for component in [&identity.application, &identity.profile, &identity.instance] {
if component.is_empty()
|| component.len() > 64
|| !component
.bytes()
.all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-' || b == b'.')
|| component.split('.').any(|part| {
part.is_empty()
|| !part.as_bytes()[0].is_ascii_alphanumeric()
|| !part.as_bytes()[part.len() - 1].is_ascii_alphanumeric()
})
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid service identity component",
));
}
}
Ok(identity)
}
pub fn id(&self) -> &[u8; 16] {
&self.id
}
pub fn application(&self) -> &str {
&self.application
}
pub fn profile(&self) -> &str {
&self.profile
}
pub fn instance(&self) -> &str {
&self.instance
}
pub fn paths(&self, user_root: impl AsRef<Path>) -> ServicePaths {
ServicePaths {
directory: user_root
.as_ref()
.join(format!("service-{:032x}", u128::from_be_bytes(self.id)))
.join(format!("profile-{}", self.profile))
.join(format!("instance-{}", self.instance)),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ServicePaths {
directory: PathBuf,
}
impl ServicePaths {
pub fn directory(&self) -> &Path {
&self.directory
}
pub fn ownership_lock(&self) -> PathBuf {
self.directory.join("owner.lock")
}
pub fn upgrade_lock(&self) -> PathBuf {
self.directory.join("upgrade.lock")
}
pub fn discovery(&self) -> PathBuf {
self.directory.join("discovery")
}
pub fn control_socket(&self) -> PathBuf {
self.directory.join("control.sock")
}
}