use std::collections::HashMap;
use std::env::VarError;
use std::ffi::OsString;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::os_shim_internal::fs::Fake;
#[derive(Clone, Debug)]
pub struct Fs(fs::Inner);
impl Default for Fs {
fn default() -> Self {
Fs::real()
}
}
impl Fs {
pub fn real() -> Self {
Fs(fs::Inner::Real)
}
pub fn from_raw_map(fs: HashMap<OsString, Vec<u8>>) -> Self {
Fs(fs::Inner::Fake(Arc::new(Fake::MapFs(fs))))
}
pub fn from_map(data: HashMap<String, impl Into<Vec<u8>>>) -> Self {
let fs = data
.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect();
Self::from_raw_map(fs)
}
pub fn from_test_dir(
test_directory: impl Into<PathBuf>,
namespaced_to: impl Into<PathBuf>,
) -> Self {
Self(fs::Inner::Fake(Arc::new(Fake::NamespacedFs {
real_path: test_directory.into(),
namespaced_to: namespaced_to.into(),
})))
}
pub fn from_slice<'a>(files: &[(&'a str, &'a str)]) -> Self {
let fs: HashMap<String, Vec<u8>> = files
.iter()
.map(|(k, v)| {
let k = (*k).to_owned();
let v = v.as_bytes().to_vec();
(k, v)
})
.collect();
Self::from_map(fs)
}
pub async fn read_to_end(&self, path: impl AsRef<Path>) -> std::io::Result<Vec<u8>> {
use fs::Inner;
let path = path.as_ref();
match &self.0 {
Inner::Real => std::fs::read(path),
Inner::Fake(fake) => match fake.as_ref() {
Fake::MapFs(fs) => fs
.get(path.as_os_str())
.cloned()
.ok_or_else(|| std::io::ErrorKind::NotFound.into()),
Fake::NamespacedFs {
real_path,
namespaced_to,
} => {
let actual_path = path
.strip_prefix(namespaced_to)
.map_err(|_| std::io::Error::from(std::io::ErrorKind::NotFound))?;
std::fs::read(real_path.join(actual_path))
}
},
}
}
}
mod fs {
use std::collections::HashMap;
use std::ffi::OsString;
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub(super) enum Inner {
Real,
Fake(Arc<Fake>),
}
#[derive(Debug)]
pub(super) enum Fake {
MapFs(HashMap<OsString, Vec<u8>>),
NamespacedFs {
real_path: PathBuf,
namespaced_to: PathBuf,
},
}
}
#[derive(Clone, Debug)]
pub struct Env(env::Inner);
impl Default for Env {
fn default() -> Self {
Self::real()
}
}
impl Env {
pub fn get(&self, k: &str) -> Result<String, VarError> {
use env::Inner;
match &self.0 {
Inner::Real => std::env::var(k),
Inner::Fake(map) => map.get(k).cloned().ok_or(VarError::NotPresent),
}
}
pub fn from_slice<'a>(vars: &[(&'a str, &'a str)]) -> Self {
let map: HashMap<_, _> = vars
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
Self::from(map)
}
pub fn real() -> Self {
Self(env::Inner::Real)
}
}
impl From<HashMap<String, String>> for Env {
fn from(hash_map: HashMap<String, String>) -> Self {
Self(env::Inner::Fake(Arc::new(hash_map)))
}
}
mod env {
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub(super) enum Inner {
Real,
Fake(Arc<HashMap<String, String>>),
}
}
#[cfg(test)]
mod test {
use std::env::VarError;
use futures_util::FutureExt;
use crate::os_shim_internal::{Env, Fs};
#[test]
fn env_works() {
let env = Env::from_slice(&[("FOO", "BAR")]);
assert_eq!(env.get("FOO").unwrap(), "BAR");
assert_eq!(
env.get("OTHER").expect_err("no present"),
VarError::NotPresent
)
}
#[test]
fn fs_works() {
let fs = Fs::from_test_dir(".", "/users/test-data");
let _ = fs
.read_to_end("/users/test-data/Cargo.toml")
.now_or_never()
.expect("future should not poll")
.expect("file exists");
let _ = fs
.read_to_end("doesntexist")
.now_or_never()
.expect("future should not poll")
.expect_err("file doesnt exists");
}
}