use std::collections::HashSet;
use async_trait::async_trait;
use super::{
error::ComputationError,
manager::{ChangedComponents, SharedDerivedDataRef},
};
use crate::feed::market_data::SharedMarketDataRef;
pub type ComputationId = &'static str;
#[derive(Debug, Clone, thiserror::Error)]
#[error("conflicting requirement: '{id}' cannot be both fresh and stale")]
pub struct RequirementConflict {
pub(crate) id: ComputationId,
}
impl RequirementConflict {
pub fn id(&self) -> ComputationId {
self.id
}
}
#[derive(Debug, Clone, Default)]
pub struct ComputationRequirements {
pub(crate) require_fresh: HashSet<ComputationId>,
pub(crate) allow_stale: HashSet<ComputationId>,
}
impl ComputationRequirements {
pub fn fresh_requirements(&self) -> &HashSet<ComputationId> {
&self.require_fresh
}
pub fn stale_requirements(&self) -> &HashSet<ComputationId> {
&self.allow_stale
}
pub fn none() -> Self {
Self::default()
}
pub fn require_fresh(mut self, id: ComputationId) -> Result<Self, RequirementConflict> {
if self.allow_stale.contains(&id) {
return Err(RequirementConflict { id });
}
self.require_fresh.insert(id);
Ok(self)
}
pub fn allow_stale(mut self, id: ComputationId) -> Result<Self, RequirementConflict> {
if self.require_fresh.contains(&id) {
return Err(RequirementConflict { id });
}
self.allow_stale.insert(id);
Ok(self)
}
pub fn has_requirements(&self) -> bool {
!self.require_fresh.is_empty() || !self.allow_stale.is_empty()
}
pub fn is_required(&self, id: ComputationId) -> bool {
self.require_fresh.contains(&id) || self.allow_stale.contains(&id)
}
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum FailedItemError {
#[error("missing simulation state")]
MissingSimulationState,
#[error("missing token metadata")]
MissingTokenMetadata,
#[error("missing spot price")]
MissingSpotPrice,
#[error("extreme decimal mismatch ({from}\u{2192}{to})")]
ExtremeDecimalMismatch { from: u32, to: u32 },
#[error("spot price too small: {0}")]
SpotPriceTooSmall(f64),
#[error("simulation failed: {0}")]
SimulationFailed(String),
#[error("all simulation paths failed")]
AllSimulationPathsFailed,
}
#[derive(Debug, Clone)]
pub struct FailedItem {
pub key: String,
pub error: FailedItemError,
}
#[derive(Debug, Clone)]
pub struct ComputationOutput<T> {
pub data: T,
pub failed_items: Vec<FailedItem>,
}
impl<T> ComputationOutput<T> {
pub fn success(data: T) -> Self {
Self { data, failed_items: vec![] }
}
pub fn with_failures(data: T, failed_items: Vec<FailedItem>) -> Self {
Self { data, failed_items }
}
pub fn has_failures(&self) -> bool {
!self.failed_items.is_empty()
}
}
#[async_trait]
pub trait DerivedComputation: Send + Sync + 'static {
type Output: Clone + Send + Sync + 'static;
const ID: ComputationId;
async fn compute(
&self,
market: &SharedMarketDataRef,
store: &SharedDerivedDataRef,
changed: &ChangedComponents,
) -> Result<ComputationOutput<Self::Output>, ComputationError>;
}