use std::io;
use std::path::{Path, PathBuf};
use crate::pal::error::{PalError, PalErrorKind};
use crate::pal::session_store::{self, FsSessionStore, RecordFile, SessionStore, StoredSession};
use crate::pal::store_root::resolve_store_root;
use crate::protocol::PROTOCOL_VERSION;
use crate::session_record::{ProcessIdentity, SessionRecord};
use crate::{AppCommand, SessionId};
#[derive(Clone, Debug)]
pub struct Store(FsSessionStore);
impl Store {
#[must_use]
pub fn new(root: PathBuf) -> Self {
Self(FsSessionStore::new(root))
}
pub fn allocate_id(&self, owner: &Identity) -> io::Result<SessionId> {
self.0.allocate_id(&owner.0).map_err(io::Error::other)
}
pub fn install_claim(&self, staging: &Path) -> io::Result<SessionId> {
self.0.install_claim(staging).map_err(io::Error::other)
}
pub fn publish(&self, record: &Record) -> io::Result<()> {
self.0.publish(&record.0).map_err(io::Error::other)
}
pub fn read(&self, id: SessionId) -> io::Result<Option<Record>> {
self.0
.read(id)
.map(|record| record.map(Record))
.map_err(io::Error::other)
}
pub fn list(&self) -> io::Result<Vec<Record>> {
self.0
.list()
.map(|records| records.into_iter().map(Record).collect())
.map_err(io::Error::other)
}
pub fn list_reservations(&self) -> io::Result<Vec<(SessionId, Identity)>> {
self.0
.list_reservations()
.map(|claims| {
claims
.into_iter()
.map(|(id, owner)| (id, Identity(owner)))
.collect()
})
.map_err(io::Error::other)
}
pub fn delete_owned_by(&self, id: SessionId, owner: &Identity) -> io::Result<()> {
self.0
.delete_owned_by(id, &owner.0)
.map_err(io::Error::other)
}
pub fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
self.0.canonicalize(path).map_err(io::Error::other)
}
pub fn current_dir(&self) -> io::Result<PathBuf> {
self.0.current_dir().map_err(io::Error::other)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Record(SessionRecord);
impl Record {
#[must_use]
pub fn new(id: SessionId, directory: &Path) -> Self {
Self(SessionRecord {
id,
supervisor: Identity::for_test(1).0,
pipe_name: "pipe".to_string(),
launch_directory: directory.to_path_buf(),
command: AppCommand::from_argv(vec!["app.exe".to_string()]).unwrap(),
started_at_unix_ms: 1,
attached: false,
protocol_version: PROTOCOL_VERSION,
})
}
#[must_use]
pub fn supervisor(&self) -> Identity {
Identity(self.0.supervisor)
}
pub fn set_supervisor(&mut self, owner: Identity) {
self.0.supervisor = owner.0;
}
pub fn set_command(&mut self, command: AppCommand) {
self.0.command = command;
}
#[must_use]
pub fn stored_bytes(&self) -> Vec<u8> {
serde_json::to_vec(&StoredSession::Published(self.0.clone())).unwrap()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Identity(ProcessIdentity);
impl Identity {
#[must_use]
pub fn new(pid: u32, creation_time: u64) -> Self {
Self(ProcessIdentity {
pid: pid.try_into().unwrap(),
creation_time,
})
}
#[must_use]
pub fn for_test(pid: u32) -> Self {
Self::new(pid, u64::from(pid))
}
}
pub struct InspectedRecord(RecordFile);
impl std::fmt::Debug for InspectedRecord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(std::any::type_name::<Self>())
.finish_non_exhaustive()
}
}
impl InspectedRecord {
pub fn open(path: &Path) -> io::Result<Self> {
RecordFile::open(path).map(Self)
}
pub fn delete(&self) -> io::Result<()> {
self.0.delete()
}
}
pub fn discard_failed_claim(path: &Path) {
session_store::remove_failed_staging(path, &Err(PalError::new(PalErrorKind::Other)));
}
pub fn read_if_present(path: &Path) -> io::Result<Option<Vec<u8>>> {
session_store::read_if_present(path)
}
pub fn reservation_owner(bytes: &[u8]) -> io::Result<Identity> {
match serde_json::from_slice(bytes).map_err(io::Error::other)? {
StoredSession::Reserved { owner } => Ok(Identity(owner)),
StoredSession::Published(_) => Err(io::Error::other("expected a reservation")),
}
}
pub fn default_store_root() -> io::Result<PathBuf> {
resolve_store_root(None).map_err(io::Error::other)
}