parent
1a7acb4e39
commit
ef6313f579
@ -1,2 +1,3 @@
|
||||
/target
|
||||
*~
|
||||
test*.txt
|
||||
|
@ -0,0 +1,74 @@
|
||||
use std::{
|
||||
io::{self, Read, Write,},
|
||||
};
|
||||
|
||||
pub struct MulticastStream<T>
|
||||
where T: Write
|
||||
{
|
||||
outputs: Vec<T>,
|
||||
continue_on_fail: bool,
|
||||
}
|
||||
|
||||
impl<T> MulticastStream<T>
|
||||
where T: Write
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
outputs: Vec::new(),
|
||||
continue_on_fail: false,
|
||||
}
|
||||
}
|
||||
pub fn continue_on_fail(self, set: bool) -> Self {
|
||||
Self {
|
||||
continue_on_fail: set,
|
||||
..self
|
||||
}
|
||||
}
|
||||
pub fn cast(&mut self, output: T) {
|
||||
self.outputs.push(output);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Write for MulticastStream<T>
|
||||
where T: Write
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
|
||||
{
|
||||
let mut sz =0;
|
||||
let mut one_ok = self.outputs.len() < 1;
|
||||
for res in self.outputs.iter_mut().map(|output| output.write(&buf)) {
|
||||
match res {
|
||||
Ok(res) if res > sz => sz = res,
|
||||
Err(err) if !self.continue_on_fail => return Err(err),
|
||||
Err(_) => continue,
|
||||
_ => (),
|
||||
};
|
||||
one_ok = true;
|
||||
}
|
||||
|
||||
if !one_ok {
|
||||
Err(io::Error::new(io::ErrorKind::UnexpectedEof, "All write streams failed"))
|
||||
} else {
|
||||
Ok(sz)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()>
|
||||
{
|
||||
let mut errors = 0;
|
||||
for res in self.outputs.iter_mut().map(|output| output.flush()) {
|
||||
if let Err(err) = res {
|
||||
if !self.continue_on_fail {
|
||||
return Err(err);
|
||||
}
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if errors > 0 && errors == self.outputs.len() {
|
||||
Err(io::Error::new(io::ErrorKind::UnexpectedEof, "All write streams failed"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
use super::*;
|
||||
use std::{
|
||||
io::{self,},
|
||||
fmt,
|
||||
error,
|
||||
num::NonZeroUsize,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorLocation {
|
||||
Oneesan,
|
||||
Imouto,
|
||||
Internal,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
explination: String,
|
||||
bytes_done: Option<NonZeroUsize>,
|
||||
location: ErrorLocation,
|
||||
inner: Option<io::Error>,
|
||||
}
|
||||
|
||||
fn clamp(value: i64, min: i64, max: i64) -> i64
|
||||
{
|
||||
if value < min {
|
||||
min
|
||||
} else if value > max {
|
||||
max
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fn human_bytes(bytes: usize) -> String
|
||||
{
|
||||
const POSTFIXES: [&'static str; 7] = ["b", "kb", "mb", "gb", "tb", "pb", "eb"];
|
||||
|
||||
if bytes == 0 {
|
||||
return format!("0 {}", POSTFIXES[0]);
|
||||
}
|
||||
|
||||
let idx = (bytes as f64).log(1024f64).floor() as i64;
|
||||
let suf = &POSTFIXES[clamp(idx, 0, 6) as usize];
|
||||
let num = (1024_i64).pow(idx as u32);
|
||||
|
||||
format!("{:.2} {}", num, suf)
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn new(expl: String, from: ErrorLocation, bytes_done: usize) -> Self {
|
||||
Self {
|
||||
explination: expl,
|
||||
location: from,
|
||||
inner: None,
|
||||
bytes_done: NonZeroUsize::new(bytes_done),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<io::Error> for Error
|
||||
{
|
||||
fn from(f: io::Error) -> Self {
|
||||
Self {
|
||||
explination: format!("i/o error: "),
|
||||
location: ErrorLocation::Internal,
|
||||
bytes_done: None,
|
||||
inner: Some(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorLocation
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ErrorLocation::Oneesan => write!(f, "<onee>"),
|
||||
ErrorLocation::Imouto => write!(f, "<imou>"),
|
||||
_ => write!(f, "<internal>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for Error
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}+{}: {}{}", self.location, match &self.bytes_done {
|
||||
Some(value) => human_bytes(value.get()),
|
||||
None => "0 b".to_owned(),
|
||||
}, self.explination, match &self.inner {
|
||||
Some(inner) => format!(" {}", inner),
|
||||
None => "".to_owned(),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl error::Error for Error
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)>
|
||||
{
|
||||
match &self.inner {
|
||||
Some(x) => Some(x),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) trait WeebExt
|
||||
{
|
||||
fn into_imouto(self) -> Error;
|
||||
fn into_oneesan(self) -> Error;
|
||||
}
|
||||
pub(super) trait TryWeebExt<T>
|
||||
{
|
||||
fn try_imouto(self, bytes_done: usize) -> Result<T, Error>;
|
||||
fn try_oneesan(self, bytes_done: usize) -> Result<T, Error>;
|
||||
}
|
||||
|
||||
impl WeebExt for io::Error
|
||||
{
|
||||
fn into_imouto(self) -> Error
|
||||
{
|
||||
Error {
|
||||
location: ErrorLocation::Imouto,
|
||||
..Error::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn into_oneesan(self) -> Error
|
||||
{
|
||||
Error {
|
||||
location: ErrorLocation::Oneesan,
|
||||
..Error::from(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryWeebExt<T> for io::Result<T>
|
||||
{
|
||||
fn try_imouto(self, bytes_done: usize) -> Result<T, Error>
|
||||
{
|
||||
match self {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => Err(Error {
|
||||
bytes_done: NonZeroUsize::new(bytes_done),
|
||||
..e.into_imouto()
|
||||
}),
|
||||
}
|
||||
}
|
||||
fn try_oneesan(self, bytes_done: usize) -> Result<T, Error>
|
||||
{
|
||||
match self {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => Err(Error {
|
||||
bytes_done: NonZeroUsize::new(bytes_done),
|
||||
..e.into_oneesan()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
use std::{
|
||||
io::{self, Read, Write,},
|
||||
};
|
||||
use crate::{
|
||||
multicast,
|
||||
};
|
||||
|
||||
mod error;
|
||||
pub use error::*;
|
||||
|
||||
pub struct LeakyPipe<'a, T, U>
|
||||
where T: Read,
|
||||
U: Write
|
||||
{
|
||||
oneesan: &'a mut T,
|
||||
imouto: multicast::MulticastStream<&'a mut U>
|
||||
}
|
||||
|
||||
impl<'a, T, U> LeakyPipe<'a, T, U>
|
||||
where T: Read,
|
||||
U: Write
|
||||
{
|
||||
pub fn new(oneesan: &'a mut T, imouto: multicast::MulticastStream<&'a mut U>) -> Self
|
||||
{
|
||||
Self {
|
||||
oneesan,
|
||||
imouto,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pipe(mut self, buf_size: usize, output_to_tty: bool) -> Result<(), Error>
|
||||
{
|
||||
let mut buffer = vec![0u8; buf_size];
|
||||
let mut bytes_done=0;
|
||||
let stdout = io::stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
loop {
|
||||
let sz = self.oneesan.read(&mut buffer[..]).try_oneesan(bytes_done)?;
|
||||
if sz == 0 {return Ok(());}
|
||||
|
||||
self.imouto.write(&buffer[0..sz]).try_imouto(bytes_done)?;
|
||||
if output_to_tty {
|
||||
stdout.write(&buffer[0..sz])?;
|
||||
}
|
||||
bytes_done+=sz;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue