Skip to main content

yuzu_core/
error.rs

1//! [`EngineError`]: shape violations from panel construction and evaluation failures.
2//!
3//! Surface via [`std::fmt::Display`] / [`ToString`] in CLI and WASM bindings, or
4//! match on variants when you need structured handling.
5
6use thiserror::Error;
7
8/// Errors from panel construction, strategy evaluation, and backtest setup.
9///
10/// Callers (CLI / server / WASM) typically surface these via
11/// [`std::fmt::Display`] / [`ToString`]; match on variants when you need
12/// structured handling.
13#[derive(Debug, Error)]
14pub enum EngineError {
15    /// `Panel` dimensions do not match `dates` / `symbols`.
16    #[error("shape mismatch: {rows}x{cols} != data len {data_len}")]
17    ShapeMismatch {
18        rows: usize,
19        cols: usize,
20        data_len: usize,
21    },
22
23    #[error("unknown series '{name}'")]
24    UnknownSeries { name: String },
25
26    #[error("unknown price series '{key}'")]
27    UnknownPriceKey { key: String },
28
29    #[error("unknown benchmark series '{key}'")]
30    UnknownBenchmark { key: String },
31
32    #[error("benchmark series '{key}' has no symbols")]
33    EmptyBenchmark { key: String },
34
35    #[error("bare Const {value} not allowed at top level")]
36    BareConst { value: f64 },
37
38    #[error("bad freq '{freq}'")]
39    BadFreq { freq: String },
40
41    #[error("Rebalance takes either freq or on, not both")]
42    RebalanceBoth,
43
44    #[error("Rebalance needs freq or on")]
45    RebalanceNeither,
46
47    #[error("bad groupby agg '{agg}'")]
48    BadGroupbyAgg { agg: String },
49
50    #[error("both operands of a binary op are Const")]
51    BothOperandsConst,
52
53    #[error("spec parse error: {0}")]
54    SpecParse(String),
55
56    /// Catch-all for residual evaluation failures.
57    #[error("eval error: {0}")]
58    Eval(String),
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn display_messages() {
67        let e = EngineError::ShapeMismatch {
68            rows: 1,
69            cols: 2,
70            data_len: 3,
71        };
72        assert!(e.to_string().contains("shape mismatch"));
73        assert_eq!(
74            EngineError::UnknownSeries { name: "pe".into() }.to_string(),
75            "unknown series 'pe'"
76        );
77        assert_eq!(
78            EngineError::BadFreq { freq: "X".into() }.to_string(),
79            "bad freq 'X'"
80        );
81        assert!(EngineError::Eval("boom".into())
82            .to_string()
83            .contains("boom"));
84        assert!(EngineError::SpecParse("eof".into())
85            .to_string()
86            .contains("spec parse"));
87    }
88
89    #[test]
90    fn is_std_error() {
91        let e: Box<dyn std::error::Error> = Box::new(EngineError::RebalanceNeither);
92        assert!(e.to_string().contains("Rebalance"));
93    }
94}