use core::fmt;
use std::io;
use crate::vfs::{BdFile, ReadSeek};
pub struct TsInterleavedFile {
name: String,
file: Box<dyn BdFile>,
}
impl TsInterleavedFile {
#[must_use]
pub fn new(file: Box<dyn BdFile>) -> Self {
Self { name: file.name().to_ascii_uppercase(), file }
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
pub fn open_read(&self) -> io::Result<Box<dyn ReadSeek>> {
self.file.open_read()
}
}
impl fmt::Debug for TsInterleavedFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TsInterleavedFile").field("name", &self.name).finish_non_exhaustive()
}
}
#[cfg(test)]
pub(crate) struct MemBdFile {
name: String,
extension: String,
bytes: Vec<u8>,
fail: bool,
}
#[cfg(test)]
impl MemBdFile {
pub(crate) fn new(name: &str, bytes: Vec<u8>, fail: bool) -> Self {
let extension =
name.rsplit_once('.').map_or_else(String::new, |(_, ext)| format!(".{ext}"));
Self { name: name.to_owned(), extension, bytes, fail }
}
}
#[cfg(test)]
impl BdFile for MemBdFile {
fn name(&self) -> &str {
&self.name
}
fn full_name(&self) -> &str {
&self.name
}
fn extension(&self) -> &str {
&self.extension
}
fn length(&self) -> u64 {
u64::try_from(self.bytes.len()).unwrap_or(u64::MAX)
}
fn is_dir(&self) -> bool {
false
}
fn open_read(&self) -> io::Result<Box<dyn ReadSeek>> {
if self.fail {
Err(io::Error::other("injected ssif open failure"))
} else {
Ok(Box::new(io::Cursor::new(self.bytes.clone())))
}
}
fn open_text(&self) -> io::Result<Box<dyn io::BufRead>> {
Ok(Box::new(io::BufReader::new(io::Cursor::new(self.bytes.clone()))))
}
}
#[cfg(test)]
mod tests {
use std::io::Read;
use super::{BdFile, MemBdFile, TsInterleavedFile};
#[test]
fn new_uppercases_the_name_and_streams_the_bytes() {
let file = MemBdFile::new("00000.ssif", vec![1, 2, 3, 4], false);
let interleaved = TsInterleavedFile::new(Box::new(file));
assert_eq!(interleaved.name(), "00000.SSIF");
let mut reader = interleaved.open_read().expect("open the interleaved file");
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).expect("read the interleaved file");
assert_eq!(bytes, vec![1, 2, 3, 4]);
}
#[test]
fn open_read_propagates_the_io_error() {
let file = MemBdFile::new("00000.ssif", vec![1], true);
let interleaved = TsInterleavedFile::new(Box::new(file));
assert!(interleaved.open_read().is_err());
}
#[test]
fn debug_shows_the_name_without_the_handle() {
let file = MemBdFile::new("00000.ssif", vec![0; 8], false);
let interleaved = TsInterleavedFile::new(Box::new(file));
let dump = format!("{interleaved:?}");
assert!(dump.contains("TsInterleavedFile"));
assert!(dump.contains("00000.SSIF"));
}
#[test]
fn mem_bd_file_trait_surface_is_exercised() {
let file = MemBdFile::new("00000.ssif", vec![9, 8, 7], false);
assert_eq!(file.name(), "00000.ssif");
assert_eq!(file.full_name(), "00000.ssif");
assert_eq!(file.extension(), ".ssif");
assert_eq!(file.length(), 3);
assert!(!file.is_dir());
let extensionless = MemBdFile::new("ssifnodot", Vec::new(), false);
assert_eq!(extensionless.extension(), "");
let mut text = String::new();
file.open_text().expect("open_text").read_to_string(&mut text).expect("read text");
assert_eq!(text, "\u{9}\u{8}\u{7}");
}
}