openpine_vm/
error.rs

1use std::{fmt, sync::Arc};
2
3use openpine_compiler::{CompileErrors, loader::ModuleKind};
4use openpine_error::{ErrorWithSourceFile, SourceFile};
5use openpine_visitor::ast::{Position, Span};
6
7use crate::{CreateSymbolInfoError, TimeFrame, TradeSession, context::ModuleFrame};
8
9/// A single frame in a runtime call stack.
10#[derive(Debug, Clone)]
11pub struct BacktraceFrame {
12    /// Qualified callee function name (e.g. `"request.security"`, `"ta.sma"`).
13    /// `None` for root-level frames.
14    pub func_name: Option<String>,
15    /// Source span of the call site (the ExprMarker span captured when the
16    /// ModuleMarker executed).  Points to the statement in the *caller's* file
17    /// that triggered this call.
18    pub span: Span,
19    /// Module kind of the call site (derived from `span.start.file_id`).
20    pub module_kind: ModuleKind,
21}
22
23/// A complete runtime call stack captured when an exception occurs.
24#[derive(Debug, Clone, Default)]
25pub struct Backtrace {
26    /// Stack frames, outermost first.
27    pub frames: Vec<BacktraceFrame>,
28    /// Source files referenced by the frame spans.
29    pub source_files: Arc<[SourceFile<String, String>]>,
30}
31
32impl fmt::Display for Backtrace {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        writeln!(f, "call stack (most recent call last):")?;
35        for (i, frame) in self.frames.iter().enumerate() {
36            let file_id = frame.span.start.file_id as usize;
37            let path = self
38                .source_files
39                .get(file_id)
40                .map(|sf| sf.path.as_str())
41                .unwrap_or("<unknown>");
42            let name = frame.func_name.as_deref().unwrap_or("<root>");
43            writeln!(
44                f,
45                "  #{i}: {name} at {path}:{line}:{col}",
46                line = frame.span.start.line,
47                col = frame.span.start.column,
48            )?;
49        }
50        Ok(())
51    }
52}
53
54/// A runtime exception.
55#[derive(Debug, thiserror::Error)]
56#[error("{}", error.error.message)]
57#[non_exhaustive]
58pub struct Exception {
59    error: ErrorWithSourceFile<Span, Arc<[SourceFile<String, String>]>>,
60    backtrace_frames: Vec<ModuleFrame>,
61}
62
63impl Exception {
64    #[inline]
65    pub(crate) fn new(
66        error: ErrorWithSourceFile<Span, Arc<[SourceFile<String, String>]>>,
67        backtrace_frames: Vec<ModuleFrame>,
68    ) -> Self {
69        Self {
70            error,
71            backtrace_frames,
72        }
73    }
74
75    /// Returns a displayable representation of the error.
76    #[inline]
77    pub fn display(&self) -> openpine_error::ErrorDisplay<'_, Span, String, String> {
78        self.error.error.display(&self.error.source_files)
79    }
80
81    /// Returns the error message of this exception.
82    #[inline]
83    pub fn message(&self) -> &str {
84        &self.error.error.message
85    }
86
87    /// Returns the first source span of this exception, if any.
88    #[inline]
89    pub fn first_span(&self) -> Option<&Span> {
90        self.error.error.spans.first()
91    }
92
93    /// Returns the full call stack backtrace captured at the point of the
94    /// exception.
95    pub fn backtrace(&self) -> Backtrace {
96        Backtrace {
97            frames: self
98                .backtrace_frames
99                .iter()
100                .map(|f| {
101                    let span = f.entry_span.unwrap_or_else(|| {
102                        Span::new(
103                            Position::new(f.file_id, 1, 1, 0),
104                            Position::new(f.file_id, 1, 1, 0),
105                        )
106                    });
107                    BacktraceFrame {
108                        func_name: f.func_name.clone(),
109                        span,
110                        module_kind: f.module_kind,
111                    }
112                })
113                .collect(),
114            source_files: self.error.source_files.clone(),
115        }
116    }
117}
118
119/// Errors that can occur while building or executing an
120/// [`Instance`](crate::Instance).
121#[derive(Debug, thiserror::Error)]
122#[non_exhaustive]
123pub enum Error {
124    /// Compilation errors (may contain multiple errors).
125    #[error(transparent)]
126    Compile(#[from] CompileErrors),
127    /// Script type unspecified.
128    #[error("missing script type")]
129    MissingScriptType,
130    /// Input value not found.
131    #[error("input value not found for id `{0}`")]
132    InputValueNotFound(usize),
133    /// Failed to serialize input value.
134    #[error("failed to serialize input value for id `{0}`: {1}")]
135    SetInputValue(usize, String),
136    /// Unsupported time frame.
137    #[error("unsupported time frame: {0}")]
138    UnsupportedTimeFrame(TimeFrame),
139    /// Trade session not allowed.
140    #[error("trade session not allowed: {0:?}")]
141    SessionNotAllowed(TradeSession),
142    /// Failed to create symbol info.
143    #[error(transparent)]
144    CreateSymbolInfo(#[from] CreateSymbolInfoError),
145    /// A runtime exception.
146    #[error(transparent)]
147    Exception(#[from] Exception),
148    /// Library scripts cannot be executed.
149    #[error("library scripts cannot be executed")]
150    LibraryScriptNotExecutable,
151    /// Data provider returned an error.
152    #[error("data provider error: {0}")]
153    DataProvider(String),
154}