use crate::cli_outbound::Binary;
use crate::config_edit::FileIo;
use crate::ui_state::Clock;
use crate::world::toolgate::ToolProbe;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, PoisonError};
use std::time::{Duration, Instant};
pub(crate) static SPAWN_LOCK: Mutex<()> = Mutex::new(());
pub(crate) fn spawn_guard() -> std::sync::MutexGuard<'static, ()> {
SPAWN_LOCK.lock().unwrap_or_else(PoisonError::into_inner)
}
#[derive(Clone)]
pub(crate) struct FakeClock {
at: Arc<Mutex<Instant>>,
}
impl FakeClock {
pub(crate) fn new() -> Self {
Self {
at: Arc::new(Mutex::new(Instant::now())),
}
}
pub(crate) fn handle(&self) -> Self {
Self {
at: Arc::clone(&self.at),
}
}
pub(crate) fn arc(&self) -> Arc<dyn Clock> {
Arc::new(self.handle())
}
pub(crate) fn advance(&self, delta: Duration) {
*self
.at
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) += delta;
}
}
impl Clock for FakeClock {
fn now(&self) -> Instant {
*self
.at
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
pub(crate) struct OkProbe;
impl ToolProbe for OkProbe {
fn status(&self, _binary: Binary, _args: &[&str]) -> std::io::Result<i32> {
Ok(0)
}
}
#[derive(Default)]
pub(crate) struct FakeFs {
pub(crate) files: Mutex<HashMap<PathBuf, Vec<u8>>>,
pub(crate) fail_write: bool,
}
impl FakeFs {
pub(crate) fn map(&self) -> std::sync::MutexGuard<'_, HashMap<PathBuf, Vec<u8>>> {
self.files
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn seed(path: &Path, bytes: &[u8]) -> Self {
let me = Self::default();
me.map().insert(path.to_path_buf(), bytes.to_vec());
me
}
pub(crate) fn get(&self, path: &Path) -> Option<Vec<u8>> {
self.map().get(path).cloned()
}
}
impl FileIo for FakeFs {
fn read(&self, path: &Path) -> std::io::Result<Option<Vec<u8>>> {
Ok(self.get(path))
}
fn write(&self, path: &Path, bytes: &[u8]) -> std::io::Result<()> {
if self.fail_write {
return Err(std::io::Error::other("boom"));
}
self.map().insert(path.to_path_buf(), bytes.to_vec());
Ok(())
}
fn rename(&self, from: &Path, to: &Path) -> std::io::Result<()> {
let bytes = self.map().remove(from).unwrap_or_default();
self.map().insert(to.to_path_buf(), bytes);
Ok(())
}
fn remove(&self, path: &Path) -> std::io::Result<()> {
self.map().remove(path);
Ok(())
}
fn exists(&self, path: &Path) -> bool {
self.map().contains_key(path)
}
fn list_dir(&self, dir: &Path) -> std::io::Result<Vec<PathBuf>> {
Ok(self
.map()
.keys()
.filter(|p| p.parent() == Some(dir))
.cloned()
.collect())
}
}
pub(crate) fn spawn_locked(
cmd: &mut std::process::Command,
) -> std::io::Result<std::process::Child> {
let _g = spawn_guard();
cmd.spawn()
}