use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;
pub use wickra_backtest_core::{BacktestReport, Candle, StrategySpec};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum DatasetRef {
Inline {
data: BTreeMap<String, Vec<Candle>>,
},
Files {
symbols: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
hash: Option<String>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Claim {
pub strategy: Value,
pub dataset_ref: DatasetRef,
pub claimed_report: Value,
}
impl Claim {
pub fn from_json(s: &str) -> Result<Self> {
let claim: Self = serde_json::from_str(s).map_err(|e| Error::BadClaim(e.to_string()))?;
claim.validate()?;
Ok(claim)
}
pub fn from_toml(s: &str) -> Result<Self> {
let claim: Self = toml::from_str(s).map_err(|e| Error::Parse(e.to_string()))?;
claim.validate()?;
Ok(claim)
}
#[must_use]
pub fn inline_data(&self) -> Option<&BTreeMap<String, Vec<Candle>>> {
match &self.dataset_ref {
DatasetRef::Inline { data } => Some(data),
DatasetRef::Files { .. } => None,
}
}
pub(crate) fn validate(&self) -> Result<()> {
if !self.strategy.is_object() {
return Err(Error::BadClaim(
"strategy must be a JSON object (a StrategySpec)".to_string(),
));
}
if !self.claimed_report.is_object() {
return Err(Error::BadReport(
"claimed_report must be a JSON object (a BacktestReport)".to_string(),
));
}
Ok(())
}
}