use crate::open_input::{open_input, Input};
use crate::{MediaType, Pseudonym};
use clap::{AmbientAuthority, TryFromOsArg};
use io_streams::StreamReader;
use layered_io::{Bufferable, LayeredReader, ReadLayered, Status};
use std::ffi::OsStr;
use std::fmt::{self, Debug, Formatter};
use std::io::{self, IoSliceMut, Read};
use terminal_io::NeverTerminalReader;
pub struct InputByteStream {
name: String,
reader: LayeredReader<NeverTerminalReader<StreamReader>>,
media_type: MediaType,
initial_size: Option<u64>,
}
impl InputByteStream {
#[inline]
pub fn media_type(&self) -> &MediaType {
&self.media_type
}
#[inline]
pub fn initial_size(&self) -> Option<u64> {
self.initial_size
}
#[inline]
pub fn pseudonym(&self) -> Pseudonym {
Pseudonym::new(self.name.clone())
}
fn from_input(input: Input) -> Self {
let reader = NeverTerminalReader::new(input.reader);
let reader = LayeredReader::new(reader);
Self {
name: input.name,
reader,
media_type: input.media_type,
initial_size: input.initial_size,
}
}
}
#[doc(hidden)]
impl TryFromOsArg for InputByteStream {
type Error = anyhow::Error;
#[inline]
fn try_from_os_str_arg(
os: &OsStr,
ambient_authority: AmbientAuthority,
) -> anyhow::Result<Self> {
open_input(os, ambient_authority).map(Self::from_input)
}
}
impl ReadLayered for InputByteStream {
#[inline]
fn read_with_status(&mut self, buf: &mut [u8]) -> io::Result<(usize, Status)> {
self.reader.read_with_status(buf)
}
#[inline]
fn read_vectored_with_status(
&mut self,
bufs: &mut [IoSliceMut<'_>],
) -> io::Result<(usize, Status)> {
self.reader.read_vectored_with_status(bufs)
}
}
impl Read for InputByteStream {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.reader.read(buf)
}
#[inline]
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.reader.read_vectored(bufs)
}
#[cfg(can_vector)]
#[inline]
fn is_read_vectored(&self) -> bool {
self.reader.is_read_vectored()
}
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.reader.read_to_end(buf)
}
#[inline]
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
self.reader.read_to_string(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.reader.read_exact(buf)
}
}
impl Bufferable for InputByteStream {
#[inline]
fn abandon(&mut self) {
self.reader.abandon()
}
}
impl Debug for InputByteStream {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut b = f.debug_struct("InputByteStream");
b.field("media_type", &self.media_type);
b.field("initial_size", &self.initial_size);
b.finish()
}
}
#[test]
fn data_url_plain() {
let mut s = String::new();
InputByteStream::try_from_os_str_arg(
"data:,Hello%2C%20World!".as_ref(),
clap::ambient_authority(),
)
.unwrap()
.read_to_string(&mut s)
.unwrap();
assert_eq!(s, "Hello, World!");
}
#[test]
fn data_url_base64() {
let mut s = String::new();
InputByteStream::try_from_os_str_arg(
"data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==".as_ref(),
clap::ambient_authority(),
)
.unwrap()
.read_to_string(&mut s)
.unwrap();
assert_eq!(s, "Hello, World!");
}