use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct FrameId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FrameKind {
Function,
Native,
GC,
Eval,
Wasm,
Builtin,
RegExp,
Idle,
Program,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FrameCategory {
App,
Deps,
NodeInternal,
V8Internal,
Native,
}
impl FrameCategory {
pub fn is_internal(&self) -> bool {
matches!(self, Self::NodeInternal | Self::V8Internal | Self::Native)
}
}
#[derive(Debug, Clone, Serialize)]
pub struct Frame {
pub id: FrameId,
pub name: String,
pub file: Option<String>,
pub line: Option<u32>,
pub col: Option<u32>,
pub kind: FrameKind,
pub category: FrameCategory,
pub minified_name: Option<String>,
pub minified_location: Option<String>,
}
impl Frame {
pub fn new(
id: FrameId,
name: String,
file: Option<String>,
line: Option<u32>,
col: Option<u32>,
kind: FrameKind,
category: FrameCategory,
) -> Self {
Self {
id,
name,
file,
line,
col,
kind,
category,
minified_name: None,
minified_location: None,
}
}
pub fn location(&self) -> String {
match (&self.file, self.line, self.col) {
(Some(file), Some(line), Some(col)) => {
format!("{}:{line}:{col}", Self::clean_file_path(file))
}
(Some(file), Some(line), None) => {
format!("{}:{line}", Self::clean_file_path(file))
}
(Some(file), None, None) => Self::clean_file_path(file),
_ => "(unknown)".to_string(),
}
}
fn clean_file_path(path: &str) -> String {
path.strip_prefix("file://").unwrap_or(path).to_string()
}
pub fn clean_file(&self) -> Option<String> {
self.file.as_ref().map(|f| Self::clean_file_path(f))
}
pub fn display_name(&self) -> String {
if !self.name.is_empty() && self.name != "(anonymous)" {
return self.name.clone();
}
if let Some(file) = &self.file {
let clean_path = Self::clean_file_path(file);
let filename = std::path::Path::new(&clean_path)
.file_name()
.map(|s| s.to_string_lossy())
.unwrap_or_else(|| clean_path.as_str().into());
if let Some(line) = self.line {
return format!("(anonymous @ {filename}:{line})");
}
return format!("(anonymous @ {filename})");
}
"(anonymous)".to_string()
}
}
impl std::fmt::Display for FrameKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Function => write!(f, "function"),
Self::Native => write!(f, "native"),
Self::GC => write!(f, "gc"),
Self::Eval => write!(f, "eval"),
Self::Wasm => write!(f, "wasm"),
Self::Builtin => write!(f, "builtin"),
Self::RegExp => write!(f, "regexp"),
Self::Idle => write!(f, "idle"),
Self::Program => write!(f, "program"),
Self::Unknown => write!(f, "unknown"),
}
}
}
impl std::fmt::Display for FrameCategory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::App => write!(f, "App"),
Self::Deps => write!(f, "Dependencies"),
Self::NodeInternal => write!(f, "Node.js Internal"),
Self::V8Internal => write!(f, "V8 Internal"),
Self::Native => write!(f, "Native"),
}
}
}