Improve pipe backend

* Implement stop
* Better error handling
This commit is contained in:
JasonLG1979 2022-06-30 21:57:23 -05:00
parent 179cedaebe
commit 2532687cc6

View file

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