use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use bevy_ecs::prelude::*;
use bevy_tasks::ConditionalSendFuture;
use rig_core::effect::Answered;
use rig_core::effect::{EffectId, EffectKind};
use rig_core::error::ErrorReport;
use rig_core::serve::{
Dispatch, DispatchContext, ErasedHandler, RecordBuffer, Recorder, Reply, catch_panics,
};
use rig_core::tool::{PublishedContext, ToolContext, ToolResultContext};
pub(crate) type Ran = (Result<Answered, ErrorReport>, Option<ToolResultContext>);
#[derive(Resource)]
pub struct Effects {
last: AtomicU64,
buffer: RecordBuffer,
}
impl Default for Effects {
fn default() -> Self {
Self {
last: AtomicU64::new(0),
buffer: RecordBuffer::new().timed(),
}
}
}
impl Effects {
pub fn continue_after(last: Option<EffectId>) -> Self {
Self {
last: AtomicU64::new(last.map_or(0, EffectId::as_u64)),
..Self::default()
}
}
pub(crate) fn run<F>(
&self,
id: EffectId,
origin: DispatchContext,
handler: ErasedHandler,
kind: EffectKind,
consume: impl FnOnce(Reply) -> F + Send + 'static,
) -> impl ConditionalSendFuture<Output = Ran> + 'static
where
F: ConditionalSendFuture<Output = Result<Answered, ErrorReport>> + 'static,
{
let recorder: Arc<dyn Recorder + Send + Sync> = Arc::new(self.buffer.clone());
recorder.begin(id, handler.key().clone(), kind.clone(), origin);
let published = PublishedContext::new();
let dispatch = Dispatch::new(id, kind.streams())
.with_scope(published.clone())
.recorded_by(recorder.clone());
let work = async move { consume(handler.handle(kind, dispatch).await).await };
let work = catch_panics(Some(recorder), id, work);
async move {
let outcome = work.await;
let facts = published.take().map(ToolContext::into_result_context);
(
outcome,
facts.filter(|facts| *facts != ToolResultContext::default()),
)
}
}
pub(crate) fn buffer(&self) -> &RecordBuffer {
&self.buffer
}
pub(crate) fn next_id(&self) -> EffectId {
EffectId::from_raw(self.last.fetch_add(1, Ordering::Relaxed) + 1)
}
}
#[derive(Clone)]
pub struct Handler(pub ErasedHandler);
#[cfg(all(
target_arch = "wasm32",
target_os = "unknown",
not(target_feature = "atomics")
))]
unsafe impl Send for Handler {}
#[cfg(all(
target_arch = "wasm32",
target_os = "unknown",
not(target_feature = "atomics")
))]
unsafe impl Sync for Handler {}