use std::fs;
use std::path::PathBuf;
use tokio::io::AsyncWriteExt;
use tokio::net::unix::pipe;
use super::lines::{Lines, Raw};
use super::message::{Answer, Line};
use crate::failure::{Doing, Failure};
pub(crate) struct Pipe {
lines: Lines,
up: PathBuf,
rep: PathBuf,
}
impl Pipe {
pub(crate) fn open(up: PathBuf, rep: PathBuf) -> Result<Self, Failure> {
Ok(Self {
lines: Lines::open(&up)?,
up,
rep,
})
}
pub(crate) async fn next(&mut self) -> Result<Option<Line>, Failure> {
self.lines
.next()
.await?
.map(|raw| self.line(raw))
.transpose()
}
pub(crate) fn drain(&mut self) -> Result<Vec<Line>, Failure> {
self.lines
.drain()?
.into_iter()
.map(|raw| self.line(raw))
.collect()
}
fn line(&self, raw: Raw) -> Result<Line, Failure> {
let text = String::from_utf8(raw.bytes).doing(|| format!("reading {} as text", self.lines.what()))?;
Ok(Line {
text,
heard_at: raw.heard_at,
})
}
pub(crate) async fn answer(&self, answer: Answer) -> Result<(), Failure> {
let answering = || format!("answering on {}", self.rep.display());
let mut sender = pipe::OpenOptions::new()
.open_sender(&self.rep)
.doing(answering)?;
sender
.write_all(format!("{answer}\n").as_bytes())
.await
.doing(answering)
}
pub(crate) fn close(self) -> Result<(), Failure> {
for path in [&self.up, &self.rep] {
fs::remove_file(path).doing(|| format!("removing {}", path.display()))?;
}
self.lines.finish()
}
}