use crate::config_edit::FileIo;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Mutex, PoisonError};
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(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) const TEMPLATE_PROVIDERS: &str = "roles:\n worker:\n provider: anthropic\n \
model: claude-sonnet-5\n tools: [apply_patch, bash, cd, dispatch, load_skill, message, \
multi_tool, read_file]\n compactor:\n provider: anthropic\n model: claude-haiku-4-5\n";
pub(crate) fn authoring_new_arm() -> String {
format!(
"new)\n mkdir -p \"$2/repo.git\"\n \
git init -q --bare -b config/default \"$2/repo.git\"\n \
git -C \"$2/repo.git\" worktree add -q --orphan -b config/default \"$2/.author\"\n \
printf '%s' '{TEMPLATE_PROVIDERS}' > \"$2/.author/providers.yaml\"\n \
git -C \"$2/.author\" add -A\n \
git -C \"$2/.author\" -c user.email=t@t.local -c user.name=T \
-c commit.gpgsign=false commit -q -m 'config: init [config/default]'\n \
git -C \"$2/repo.git\" worktree remove \"$2/.author\"\n ;;\n"
)
}
pub(crate) fn replace_directory(path: &std::path::Path) {
let fresh = prepared_replacement(path);
std::fs::remove_dir_all(path).expect("the original is unlinked");
std::fs::rename(&fresh, path).expect("the replacement takes the path");
}
pub(crate) fn prepared_replacement(path: &std::path::Path) -> std::path::PathBuf {
let mut fresh = path.as_os_str().to_owned();
fresh.push(".replacement");
let fresh = std::path::PathBuf::from(fresh);
std::fs::create_dir_all(&fresh).expect("the replacement is created beside the original");
fresh
}
pub(crate) mod world;
pub(crate) use world::{fixture_workspace, no_wall, no_world, wall_paths, world_under};
pub(crate) mod workspace;
pub(crate) mod engine;
pub(crate) mod seat;
pub(crate) mod wire;
pub(crate) mod chrome;
pub(crate) mod clock;
pub(crate) use clock::FakeClock;