use oharness_core::{
ApprovalChannel, BudgetHandle, Cancellation, EventSink, MetadataMap, NullApprovalChannel,
NullBudget, NullSink,
};
use std::path::{Path, PathBuf};
use std::sync::Arc;
pub struct ToolContext {
pub events: Arc<dyn EventSink>,
pub budget: Arc<dyn BudgetHandle>,
pub cancellation: Cancellation,
pub approval: Arc<dyn ApprovalChannel>,
pub workspace: Option<Arc<Workspace>>,
pub extensions: MetadataMap,
}
impl ToolContext {
pub fn null() -> Self {
Self {
events: Arc::new(NullSink),
budget: Arc::new(NullBudget),
cancellation: Cancellation::new(),
approval: Arc::new(NullApprovalChannel),
workspace: None,
extensions: MetadataMap::new(),
}
}
pub fn workspace_path(&self) -> Option<&Path> {
self.workspace.as_ref().map(|w| w.path.as_path())
}
}
pub struct Workspace {
pub path: PathBuf,
cleanup: std::sync::Mutex<Option<WorkspaceCleanup>>,
}
impl Workspace {
pub fn new(path: PathBuf) -> Self {
Self {
path,
cleanup: std::sync::Mutex::new(None),
}
}
pub fn with_sync_cleanup(mut self, f: impl FnOnce() + Send + 'static) -> Self {
self.cleanup = std::sync::Mutex::new(Some(WorkspaceCleanup::Sync(Box::new(f))));
self
}
pub async fn teardown(self) {
let cleanup = {
let mut guard = self.cleanup.lock().unwrap();
guard.take()
};
if let Some(cleanup) = cleanup {
match cleanup {
WorkspaceCleanup::Sync(f) => f(),
WorkspaceCleanup::Async(fut) => fut.await,
}
}
}
}
impl std::fmt::Debug for Workspace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Workspace")
.field("path", &self.path)
.finish()
}
}
impl Drop for Workspace {
fn drop(&mut self) {
if let Ok(mut guard) = self.cleanup.lock() {
if let Some(cleanup) = guard.take() {
match cleanup {
WorkspaceCleanup::Sync(f) => f(),
WorkspaceCleanup::Async(fut) => {
if let Ok(handle) = tokio::runtime::Handle::try_current() {
handle.spawn(fut);
}
}
}
}
}
}
}
pub enum WorkspaceCleanup {
Sync(Box<dyn FnOnce() + Send>),
Async(futures::future::BoxFuture<'static, ()>),
}