use async_trait::async_trait;
use tracing::{debug, warn};
use super::{
computation::{ComputationId, ComputationRequirements, DerivedComputation, FailedItem},
error::ComputationError,
manager::{ChangedComponents, SharedDerivedDataRef},
store::DerivedData,
};
use crate::feed::market_data::MarketData;
pub(crate) struct ComputedWrite {
pub(crate) failed_items: Vec<FailedItem>,
pub(crate) persist: Box<dyn FnOnce(&mut DerivedData) + Send>,
}
#[async_trait]
pub(crate) trait ErasedComputation: Send + Sync {
fn id(&self) -> ComputationId;
fn requirements(&self) -> ComputationRequirements;
async fn compute_erased(
&self,
market: &MarketData,
store: &SharedDerivedDataRef,
changed: &ChangedComponents,
block: u64,
) -> Result<ComputedWrite, ComputationError>;
}
#[async_trait]
impl<C> ErasedComputation for C
where
C: DerivedComputation,
{
fn id(&self) -> ComputationId {
C::ID
}
fn requirements(&self) -> ComputationRequirements {
DerivedComputation::requirements(self)
}
async fn compute_erased(
&self,
market: &MarketData,
store: &SharedDerivedDataRef,
changed: &ChangedComponents,
block: u64,
) -> Result<ComputedWrite, ComputationError> {
let output = C::compute(self, market, store, changed).await?;
if output.has_failures() {
warn!(
computation = C::ID,
failed = output.failed_items.len(),
"computation completed with partial failures"
);
for item in &output.failed_items {
debug!(computation = C::ID, key = %item.key, error = %item.error, "failed item");
}
}
let failed_items = output.failed_items.clone();
let is_full_recompute = changed.is_full_recompute;
Ok(ComputedWrite {
failed_items,
persist: Box::new(move |store: &mut DerivedData| {
C::persist(store, output, block, is_full_recompute);
}),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::derived::computation::ComputationOutput;
#[derive(Clone, Debug, PartialEq)]
struct DummyData(u32);
struct DummyComputation;
#[async_trait]
impl DerivedComputation for DummyComputation {
type Output = DummyData;
const ID: ComputationId = "dummy";
async fn compute(
&self,
_market: &MarketData,
_store: &SharedDerivedDataRef,
_changed: &ChangedComponents,
) -> Result<ComputationOutput<Self::Output>, ComputationError> {
Ok(ComputationOutput::success(DummyData(42)))
}
}
#[test]
fn typed_computation_coerces_into_erased_registry() {
let registry: Vec<Box<dyn ErasedComputation>> = vec![Box::new(DummyComputation)];
assert_eq!(registry[0].id(), "dummy");
assert!(!registry[0]
.requirements()
.has_requirements());
}
#[test]
fn default_persist_writes_to_generic_slot() {
let mut store = DerivedData::new();
<DummyComputation as DerivedComputation>::persist(
&mut store,
ComputationOutput::success(DummyData(7)),
100,
true,
);
assert_eq!(store.output::<DummyData>(DummyComputation::ID), Some(&DummyData(7)));
assert_eq!(store.output_block(DummyComputation::ID), Some(100));
}
}