parent
1a7acb4e39
commit
ef6313f579
@ -1,2 +1,3 @@
|
|||||||
/target
|
/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