use std::collections::HashSet;
use std::path::{Path, PathBuf};
use crate::ctry;
use crate::errors::Result;
use crate::io::format_cache::FormatCache;
use crate::io::stdstreams::BufferedPrimaryIo;
use crate::io::{
Bundle, FilesystemIo, FilesystemPrimaryInputIo, GenuineStdoutIo, IoProvider, IoStack, MemoryIo,
};
use crate::status::StatusBackend;
pub struct IoSetup {
primary_input: Box<dyn IoProvider>,
pub bundle: Option<Box<dyn Bundle>>,
pub mem: MemoryIo,
filesystem: FilesystemIo,
pub format_cache: Option<FormatCache>,
genuine_stdout: Option<GenuineStdoutIo>,
format_primary: Option<BufferedPrimaryIo>,
}
impl IoSetup {
pub fn as_stack(&mut self) -> IoStack {
let mut providers: Vec<&mut dyn IoProvider> = Vec::new();
if let Some(ref mut p) = self.genuine_stdout {
providers.push(p);
}
providers.push(&mut *self.primary_input);
providers.push(&mut self.mem);
providers.push(&mut self.filesystem);
if let Some(ref mut b) = self.bundle {
providers.push(b.as_ioprovider_mut());
}
if let Some(ref mut c) = self.format_cache {
providers.push(&mut *c);
}
IoStack::new(providers)
}
pub fn as_stack_for_format<'a>(&'a mut self, format_file_name: &str) -> IoStack<'a> {
let mut providers: Vec<&mut dyn IoProvider> = Vec::new();
if let Some(ref mut p) = self.genuine_stdout {
providers.push(p);
}
self.format_primary = Some(BufferedPrimaryIo::from_text(&format!(
"\\input {}",
format_file_name
)));
providers.push(self.format_primary.as_mut().unwrap());
providers.push(&mut self.mem);
if let Some(ref mut b) = self.bundle {
providers.push(b.as_ioprovider_mut());
}
if let Some(ref mut c) = self.format_cache {
providers.push(&mut *c);
}
IoStack::new(providers)
}
}
enum PrimaryInputMode {
Undefined,
Stdin,
Path(PathBuf),
Buffer(Vec<u8>),
}
pub struct IoSetupBuilder {
primary_input: PrimaryInputMode,
filesystem_root: PathBuf,
format_cache_path: Option<PathBuf>,
bundle: Option<Box<dyn Bundle>>,
use_genuine_stdout: bool,
hidden_input_paths: HashSet<PathBuf>,
}
impl Default for IoSetupBuilder {
fn default() -> Self {
IoSetupBuilder {
primary_input: PrimaryInputMode::Undefined,
filesystem_root: PathBuf::new(),
format_cache_path: None,
bundle: None,
use_genuine_stdout: false,
hidden_input_paths: HashSet::new(),
}
}
}
impl IoSetupBuilder {
pub fn primary_input_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.primary_input = PrimaryInputMode::Path(path.as_ref().to_owned());
self
}
pub fn primary_input_stdin(&mut self) -> &mut Self {
self.primary_input = PrimaryInputMode::Stdin;
self
}
pub fn primary_input_buffer(&mut self, buf: Vec<u8>) -> &mut Self {
self.primary_input = PrimaryInputMode::Buffer(buf);
self
}
pub fn filesystem_root<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.filesystem_root = path.as_ref().to_owned();
self
}
pub fn format_cache_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.format_cache_path = Some(path.as_ref().to_owned());
self
}
pub fn bundle<T: 'static + Bundle>(&mut self, bundle: T) -> &mut Self {
self.bundle = Some(Box::new(bundle));
self
}
pub fn use_genuine_stdout(&mut self, setting: bool) -> &mut Self {
self.use_genuine_stdout = setting;
self
}
pub fn hide_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.hidden_input_paths.insert(path.as_ref().to_owned());
self
}
pub fn create(mut self, status: &mut dyn StatusBackend) -> Result<IoSetup> {
let format_cache = if let Some(ref mut b) = self.bundle {
let default_path = self.filesystem_root.clone(); let format_cache_path = self.format_cache_path.unwrap_or(default_path);
Some(FormatCache::new(b.get_digest(status)?, format_cache_path))
} else {
None
};
let pio: Box<dyn IoProvider> = match self.primary_input {
PrimaryInputMode::Stdin => {
Box::new(ctry!(BufferedPrimaryIo::from_stdin(); "error reading standard input"))
}
PrimaryInputMode::Path(pip) => Box::new(FilesystemPrimaryInputIo::new(&pip)),
PrimaryInputMode::Buffer(buf) => Box::new(BufferedPrimaryIo::from_buffer(buf)),
PrimaryInputMode::Undefined => {
panic!("no primary input mechanism specified");
}
};
Ok(IoSetup {
primary_input: pio,
mem: MemoryIo::new(true),
filesystem: FilesystemIo::new(
&self.filesystem_root,
false,
true,
self.hidden_input_paths,
),
format_cache,
bundle: self.bundle,
genuine_stdout: if self.use_genuine_stdout {
Some(GenuineStdoutIo::new())
} else {
None
},
format_primary: None,
})
}
}