use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ScriptErrorKind {
Syntax,
Runtime,
Timeout,
MemoryLimit,
SandboxViolation,
Internal,
}
impl fmt::Display for ScriptErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Syntax => write!(f, "syntax_error"),
Self::Runtime => write!(f, "runtime_error"),
Self::Timeout => write!(f, "timeout"),
Self::MemoryLimit => write!(f, "memory_limit"),
Self::SandboxViolation => write!(f, "sandbox_violation"),
Self::Internal => write!(f, "internal_error"),
}
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct ScriptError {
pub kind: ScriptErrorKind,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub stack: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_snippet: Option<String>,
}
impl ScriptError {
#[must_use]
pub fn internal(message: impl Into<String>) -> Self {
Self {
kind: ScriptErrorKind::Internal,
message: message.into(),
stack: None,
line: None,
column: None,
source_snippet: None,
}
}
#[must_use]
pub fn timeout(elapsed_ms: u64, limit_ms: u64) -> Self {
Self {
kind: ScriptErrorKind::Timeout,
message: format!("script exceeded timeout: {elapsed_ms}ms > {limit_ms}ms"),
stack: None,
line: None,
column: None,
source_snippet: None,
}
}
#[must_use]
pub fn memory_limit(limit_bytes: usize) -> Self {
Self {
kind: ScriptErrorKind::MemoryLimit,
message: format!("script exceeded memory limit of {limit_bytes} bytes"),
stack: None,
line: None,
column: None,
source_snippet: None,
}
}
#[must_use]
pub fn sandbox(message: impl Into<String>) -> Self {
Self {
kind: ScriptErrorKind::SandboxViolation,
message: message.into(),
stack: None,
line: None,
column: None,
source_snippet: None,
}
}
}
impl fmt::Display for ScriptError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.kind, self.message)?;
if let (Some(l), Some(c)) = (self.line, self.column) {
write!(f, " (at {l}:{c})")?;
}
Ok(())
}
}
impl std::error::Error for ScriptError {}