use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use serde::Serialize;
use super::{Message, Micros, Reacting, Rig, Shell};
use crate::failure::{Doing, Failure};
#[derive(Clone, Debug)]
pub struct Layout {
dir: String,
}
const BASH_ENV: &str = "bash_env.bash";
const PRELUDE: &str = "prelude.bash";
const RIG: &str = "rig.bash";
const JOIN: &str = "join";
const LOCK: &str = "lock";
impl Layout {
pub(super) fn new(dir: PathBuf) -> Result<Self, Failure> {
let display = dir.display().to_string();
let dir = dir
.into_os_string()
.into_string()
.ok()
.filter(|dir| !dir.contains('\n'))
.ok_or_else(|| {
Failure::new(
format!("opening the workspace {display}"),
"the path is not one line of text",
)
})?;
Ok(Self { dir })
}
pub fn path(&self) -> &Path {
Path::new(&self.dir)
}
pub fn text(&self) -> &str {
&self.dir
}
pub(crate) fn prelude(&self) -> String {
self.file(PRELUDE)
}
pub(crate) fn rig(&self) -> String {
self.file(RIG)
}
pub(crate) fn join(&self) -> String {
self.file(JOIN)
}
pub(crate) fn lock(&self) -> String {
self.file(LOCK)
}
pub(crate) fn up(&self, token: &str) -> String {
self.file(&format!("up.{token}"))
}
pub(crate) fn rep(&self, token: &str) -> String {
self.file(&format!("rep.{token}"))
}
fn file(&self, name: &str) -> String {
format!("{}/{name}", self.dir)
}
pub fn bash_env(&self, provision: Provision<'_>) -> Result<(OsString, OsString), Failure> {
let file = self.file(BASH_ENV);
let mut content = format!(
"source {}\nsource {}\n",
bash_strings::emit_scalar(&self.prelude()),
bash_strings::emit_scalar(&self.rig()),
);
if let Provision::Joining(line) = provision {
content.push_str(line);
}
std::fs::write(&file, content).doing(|| format!("provisioning {file}"))?;
Ok((OsString::from("BASH_ENV"), file.into()))
}
}
#[derive(Copy, Clone, Debug)]
pub enum Provision<'a> {
Joining(&'a str),
Definitions,
}
pub type Kept<R> = <<R as Rig>::Reaction as Reacting>::Kept;
#[derive(Debug)]
pub struct Attended<K> {
pub shell: Arc<Shell>,
pub kept: K,
pub parted: Option<Micros>,
}
#[derive(Copy, Clone, Debug, Serialize)]
pub struct Said<'a> {
pub shell: &'a Arc<Shell>,
pub message: &'a Message,
}
pub fn heard<K: AsRef<[Message]>>(shells: &[Attended<K>]) -> Vec<Said<'_>> {
let mut said: Vec<Said<'_>> = shells
.iter()
.flat_map(|at| {
at.kept.as_ref().iter().map(|message| Said {
shell: &at.shell,
message,
})
})
.collect();
said.sort_by_key(|said| said.message.stamp.sent_at);
said
}