use crate::{ChannelError, ChildMessage, ShepherdMessage, endpoint, session};
#[derive(Debug)]
pub struct Channel {
pub(crate) reader: std::io::BufReader<endpoint::ReadHalf>,
pub(crate) writer: endpoint::Transport,
pub(crate) version: Option<String>,
pub(crate) read_line: Scratch,
pub(crate) write_line: Scratch,
}
impl Channel {
pub fn open() -> Result<Option<Self>, ChannelError> {
let found = endpoint::discover()?;
if found == endpoint::Endpoint::Absent {
return Ok(None);
}
let (reader, writer) = endpoint::connect(&found)?;
Ok(Some(Self {
reader: std::io::BufReader::new(reader),
writer,
version: std::env::var(endpoint::VERSION_VAR).ok(),
read_line: Scratch::default(),
write_line: Scratch::default(),
}))
}
pub fn recv(&mut self) -> Result<Option<ShepherdMessage>, ChannelError> {
session::read_message(&mut self.reader, &mut self.read_line.0)
}
pub fn send(&mut self, message: &ChildMessage) -> Result<(), ChannelError> {
session::write_messages(
&mut self.writer,
std::slice::from_ref(message),
&mut self.write_line.0,
)
}
#[must_use]
pub fn version(&self) -> Option<&str> {
self.version.as_deref()
}
pub(crate) fn into_halves(
self,
) -> (
std::io::BufReader<endpoint::ReadHalf>,
endpoint::Transport,
Option<String>,
) {
(self.reader, self.writer, self.version)
}
}
#[derive(Default)]
pub(crate) struct Scratch(pub(crate) Vec<u8>);
impl core::fmt::Debug for Scratch {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Scratch")
.field("capacity", &self.0.capacity())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_scratch_debug_names_its_capacity_and_never_its_bytes() {
let scratch = Scratch(b"SECRET-REPLY-BODY".to_vec());
let rendered = format!("{scratch:?}");
assert_eq!(
rendered,
format!("Scratch {{ capacity: {} }}", scratch.0.capacity())
);
assert!(
!rendered.contains("SECRET-REPLY-BODY"),
"the buffered bytes reached the Debug output: {rendered}"
);
}
}