diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 682f8124..e0e8a77c 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -2,9 +2,38 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; use crate::config::AudioFormat; use crate::convert::Converter; use crate::decoder::AudioPacket; + use std::fs::OpenOptions; use std::io::{self, Write}; use std::process::exit; +use thiserror::Error; + +#[derive(Debug, Error)] +enum StdoutError { + #[error(" {0}")] + OnWrite(std::io::Error), + + #[error(" File Path {file} Can Not be Opened and/or Created, {e}")] + OpenFailure { file: String, e: std::io::Error }, + + #[error(" Failed to Flush the Output Stream, {0}")] + FlushFailure(std::io::Error), + + #[error(" The Output Stream is None")] + NoOutput, +} + +impl From for SinkError { + fn from(e: StdoutError) -> SinkError { + use StdoutError::*; + let es = e.to_string(); + match e { + FlushFailure(_) | OnWrite(_) => SinkError::OnWrite(es), + OpenFailure { .. } => SinkError::ConnectionRefused(es), + NoOutput => SinkError::NotConnected(es), + } + } +} pub struct StdoutSink { output: Option>, @@ -15,13 +44,12 @@ pub struct StdoutSink { impl Open for StdoutSink { fn open(file: Option, format: AudioFormat) -> Self { if let Some("?") = file.as_deref() { - info!("Usage:"); - println!(" Output to stdout: --backend pipe"); - println!(" Output to file: --backend pipe --device {{filename}}"); + println!("\nUsage:\n\nOutput to stdout:\n\n\t--backend pipe\n\nOutput to file:\n\n\t--backend pipe --device {{filename}}\n"); exit(0); } - info!("Using pipe sink with format: {:?}", format); + info!("Using StdoutSink (pipe) with format: {:?}", format); + Self { output: None, file, @@ -32,21 +60,31 @@ impl Open for StdoutSink { impl Sink for StdoutSink { fn start(&mut self) -> SinkResult<()> { - if self.output.is_none() { - let output: Box = match self.file.as_deref() { - Some(file) => { - let open_op = OpenOptions::new() + self.output.get_or_insert({ + match self.file.as_deref() { + Some(file) => Box::new( + OpenOptions::new() .write(true) .create(true) .open(file) - .map_err(|e| SinkError::ConnectionRefused(e.to_string()))?; - Box::new(open_op) - } + .map_err(|e| StdoutError::OpenFailure { + file: file.to_string(), + e, + })?, + ), None => Box::new(io::stdout()), - }; + } + }); - self.output = Some(output); - } + Ok(()) + } + + fn stop(&mut self) -> SinkResult<()> { + self.output + .take() + .ok_or(StdoutError::NoOutput)? + .flush() + .map_err(StdoutError::FlushFailure)?; Ok(()) } @@ -56,19 +94,11 @@ impl Sink for StdoutSink { impl SinkAsBytes for StdoutSink { fn write_bytes(&mut self, data: &[u8]) -> SinkResult<()> { - match self.output.as_deref_mut() { - Some(output) => { - output - .write_all(data) - .map_err(|e| SinkError::OnWrite(e.to_string()))?; - output - .flush() - .map_err(|e| SinkError::OnWrite(e.to_string()))?; - } - None => { - return Err(SinkError::NotConnected("Output is None".to_string())); - } - } + self.output + .as_deref_mut() + .ok_or(StdoutError::NoOutput)? + .write_all(data) + .map_err(StdoutError::OnWrite)?; Ok(()) }