use crate::socket_path::{SocketDir, SocketName};
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>) -> io::Result<ServicePaths> {
let 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));
SocketDir::new(&directory, &[CONTROL_SOCKET])?;
Ok(ServicePaths { directory })
}
}
pub const CONTROL_SOCKET: SocketName = SocketName::new("control.sock");
#[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_SOCKET.as_str())
}
pub fn sockets(&self, names: &[SocketName]) -> io::Result<SocketDir> {
let mut all = Vec::with_capacity(names.len() + 1);
all.push(CONTROL_SOCKET);
all.extend_from_slice(names);
SocketDir::new(&self.directory, &all)
}
}