#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IoFail {
pub path: String,
pub detail: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReadFail {
File(IoFail),
Budget(IoFail),
}
pub fn missing(label: &str) -> IoFail {
IoFail {
path: label.into(),
detail: "no such file".into(),
}
}
pub trait Host {
fn read_classified(&self, label: &str, max: usize) -> Result<Option<Vec<u8>>, ReadFail>;
fn ensure_absent(&self, label: &str) -> Result<(), IoFail>;
fn create(&self, label: &str, bytes: &[u8]) -> Result<(), IoFail>;
fn write_all(&self, files: &[(String, Vec<u8>)]) -> Result<(), IoFail>;
fn join(&self, dir: &str, name: &str) -> String;
fn read(&self, label: &str, limit: usize) -> Result<Vec<u8>, IoFail> {
self.read_bounded(label, limit.saturating_add(1))
.and_then(|r| r.ok_or_else(|| missing(label)))
}
fn read_bounded(&self, label: &str, max: usize) -> Result<Option<Vec<u8>>, IoFail> {
self.read_classified(label, max).map_err(|e| match e {
ReadFail::File(f) | ReadFail::Budget(f) => f,
})
}
}
#[derive(Debug, Default)]
pub struct Memory {
inputs: std::collections::BTreeMap<String, Vec<u8>>,
outputs: std::cell::RefCell<std::collections::BTreeMap<String, Vec<u8>>>,
}
impl Memory {
pub fn new(inputs: std::collections::BTreeMap<String, Vec<u8>>) -> Self {
Memory {
inputs,
outputs: Default::default(),
}
}
pub fn inputs(&self) -> &std::collections::BTreeMap<String, Vec<u8>> {
&self.inputs
}
pub fn output(&self, label: &str) -> Option<Vec<u8>> {
self.outputs.borrow().get(label).cloned()
}
pub fn into_outputs(self) -> std::collections::BTreeMap<String, Vec<u8>> {
self.outputs.into_inner()
}
}
impl Host for Memory {
fn read_classified(&self, label: &str, max: usize) -> Result<Option<Vec<u8>>, ReadFail> {
Ok(self
.inputs
.get(label)
.map(|b| b[..b.len().min(max)].to_vec()))
}
fn ensure_absent(&self, label: &str) -> Result<(), IoFail> {
let detail = if self.outputs.borrow().contains_key(label) {
"already exists; outputs are never overwritten"
} else if self.inputs.contains_key(label) {
"names an input; an output never replaces one"
} else {
return Ok(());
};
Err(IoFail {
path: label.into(),
detail: detail.into(),
})
}
fn create(&self, label: &str, bytes: &[u8]) -> Result<(), IoFail> {
self.ensure_absent(label)?;
self.outputs
.borrow_mut()
.insert(label.into(), bytes.to_vec());
Ok(())
}
fn write_all(&self, files: &[(String, Vec<u8>)]) -> Result<(), IoFail> {
for (i, (l, b)) in files.iter().enumerate() {
if let Err(e) = self.create(l, b) {
let mut out = self.outputs.borrow_mut();
for (written, _) in &files[..i] {
out.remove(written);
}
return Err(e);
}
}
Ok(())
}
fn join(&self, dir: &str, name: &str) -> String {
if dir.is_empty() {
name.to_string()
} else {
format!("{dir}/{name}")
}
}
}