use std::collections::HashMap;
use std::path::Path;
use std::time::Duration;
pub struct ScriptContext<'a> {
pub project_root: &'a Path,
pub php_bin: &'a Path,
pub bin_dir: &'a Path,
pub base_env: Vec<(String, String)>,
pub dev_mode: bool,
pub timeout: Option<Duration>,
pub callbacks: &'a CallbackRegistry,
}
impl std::fmt::Debug for ScriptContext<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScriptContext")
.field("project_root", &self.project_root)
.field("php_bin", &self.php_bin)
.field("bin_dir", &self.bin_dir)
.field("dev_mode", &self.dev_mode)
.field("timeout", &self.timeout)
.field("callbacks", &self.callbacks)
.finish_non_exhaustive()
}
}
pub type CallbackHandler = Box<dyn Fn(&ScriptContext) -> eyre::Result<()> + Send + Sync>;
#[derive(Default)]
pub struct CallbackRegistry(HashMap<String, CallbackHandler>);
impl CallbackRegistry {
#[must_use]
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn register(&mut self, key: &str, handler: CallbackHandler) {
self.0.insert(normalize_key(key), handler);
}
#[must_use]
pub fn get(&self, class: &str, method: &str) -> Option<&CallbackHandler> {
self.0.get(&normalize_key(&format!("{class}::{method}")))
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl std::fmt::Debug for CallbackRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CallbackRegistry").field("keys", &self.0.keys()).finish()
}
}
fn normalize_key(key: &str) -> String {
key.strip_prefix('\\').unwrap_or(key).to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_lookup_is_leading_slash_insensitive() {
let mut reg = CallbackRegistry::new();
reg.register("\\Foo\\Bar::baz", Box::new(|_| Ok(())));
assert!(reg.get("Foo\\Bar", "baz").is_some());
assert!(reg.get("\\Foo\\Bar", "baz").is_some());
assert!(reg.get("Foo\\Bar", "other").is_none());
}
}