use serde::{Deserialize, Serialize};
use crate::abi::{SpanIR, swc_ast};
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum InsertPos {
Top,
Above,
Within,
#[default]
Below,
Bottom,
}
#[cfg(feature = "swc")]
use swc_core::common::{DUMMY_SP, SyntaxContext};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum Patch {
Insert {
at: SpanIR,
code: PatchCode,
#[serde(default)]
source_macro: Option<String>,
},
Replace {
span: SpanIR,
code: PatchCode,
#[serde(default)]
source_macro: Option<String>,
},
Delete {
span: SpanIR,
},
InsertRaw {
at: SpanIR,
code: String,
context: Option<String>,
#[serde(default)]
source_macro: Option<String>,
},
ReplaceRaw {
span: SpanIR,
code: String,
context: Option<String>,
#[serde(default)]
source_macro: Option<String>,
},
}
impl Patch {
pub fn source_macro(&self) -> Option<&str> {
match self {
Patch::Insert { source_macro, .. } => source_macro.as_deref(),
Patch::Replace { source_macro, .. } => source_macro.as_deref(),
Patch::Delete { .. } => None,
Patch::InsertRaw { source_macro, .. } => source_macro.as_deref(),
Patch::ReplaceRaw { source_macro, .. } => source_macro.as_deref(),
}
}
pub fn with_source_macro(self, macro_name: &str) -> Self {
match self {
Patch::Insert { at, code, .. } => Patch::Insert {
at,
code,
source_macro: Some(macro_name.to_string()),
},
Patch::Replace { span, code, .. } => Patch::Replace {
span,
code,
source_macro: Some(macro_name.to_string()),
},
Patch::Delete { span } => Patch::Delete { span },
Patch::InsertRaw {
at, code, context, ..
} => Patch::InsertRaw {
at,
code,
context,
source_macro: Some(macro_name.to_string()),
},
Patch::ReplaceRaw {
span,
code,
context,
..
} => Patch::ReplaceRaw {
span,
code,
context,
source_macro: Some(macro_name.to_string()),
},
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum PatchCode {
Text(String),
ClassMember(swc_ast::ClassMember),
Stmt(swc_ast::Stmt),
ModuleItem(swc_ast::ModuleItem),
}
impl serde::Serialize for PatchCode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
PatchCode::Text(s) => serializer.serialize_str(s),
_ => serializer.serialize_str("/* AST node - cannot serialize */"),
}
}
}
impl<'de> serde::Deserialize<'de> for PatchCode {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(PatchCode::Text(s))
}
}
impl From<String> for PatchCode {
fn from(value: String) -> Self {
PatchCode::Text(value)
}
}
impl From<&str> for PatchCode {
fn from(value: &str) -> Self {
PatchCode::Text(value.to_string())
}
}
impl From<swc_ast::ClassMember> for PatchCode {
fn from(member: swc_ast::ClassMember) -> Self {
PatchCode::ClassMember(member)
}
}
impl From<swc_ast::Stmt> for PatchCode {
fn from(stmt: swc_ast::Stmt) -> Self {
PatchCode::Stmt(stmt)
}
}
impl From<swc_ast::ModuleItem> for PatchCode {
fn from(item: swc_ast::ModuleItem) -> Self {
PatchCode::ModuleItem(item)
}
}
impl From<Vec<swc_ast::Stmt>> for PatchCode {
fn from(stmts: Vec<swc_ast::Stmt>) -> Self {
if stmts.len() == 1 {
PatchCode::Stmt(stmts.into_iter().next().unwrap())
} else {
PatchCode::Stmt(swc_ast::Stmt::Block(swc_ast::BlockStmt {
span: DUMMY_SP,
ctxt: SyntaxContext::empty(),
stmts,
}))
}
}
}
impl From<Vec<swc_ast::ModuleItem>> for PatchCode {
fn from(items: Vec<swc_ast::ModuleItem>) -> Self {
if items.len() == 1 {
PatchCode::ModuleItem(items.into_iter().next().unwrap())
} else {
let code = items
.iter()
.map(|_| "/* generated code */")
.collect::<Vec<_>>()
.join("\n");
PatchCode::Text(code)
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct MacroResult {
pub runtime_patches: Vec<Patch>,
pub type_patches: Vec<Patch>,
pub diagnostics: Vec<Diagnostic>,
pub tokens: Option<String>,
#[serde(default)]
pub insert_pos: InsertPos,
pub debug: Option<String>,
#[serde(default)]
pub cross_module_suffixes: Vec<String>,
#[serde(default)]
pub cross_module_type_suffixes: Vec<String>,
#[serde(default)]
pub imports: Vec<crate::import_registry::GeneratedImport>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Diagnostic {
pub level: DiagnosticLevel,
pub message: String,
pub span: Option<SpanIR>,
pub notes: Vec<String>,
pub help: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum DiagnosticLevel {
Error,
Warning,
Info,
}
#[derive(Default, Clone, Debug)]
pub struct DiagnosticCollector {
diagnostics: Vec<Diagnostic>,
}
impl DiagnosticCollector {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, diagnostic: Diagnostic) {
self.diagnostics.push(diagnostic);
}
pub fn error(&mut self, span: SpanIR, message: impl Into<String>) {
self.push(Diagnostic {
level: DiagnosticLevel::Error,
message: message.into(),
span: Some(span),
notes: vec![],
help: None,
});
}
pub fn error_with_help(
&mut self,
span: SpanIR,
message: impl Into<String>,
help: impl Into<String>,
) {
self.push(Diagnostic {
level: DiagnosticLevel::Error,
message: message.into(),
span: Some(span),
notes: vec![],
help: Some(help.into()),
});
}
pub fn warning(&mut self, span: SpanIR, message: impl Into<String>) {
self.push(Diagnostic {
level: DiagnosticLevel::Warning,
message: message.into(),
span: Some(span),
notes: vec![],
help: None,
});
}
pub fn extend(&mut self, other: DiagnosticCollector) {
self.diagnostics.extend(other.diagnostics);
}
pub fn has_errors(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.level == DiagnosticLevel::Error)
}
pub fn is_empty(&self) -> bool {
self.diagnostics.is_empty()
}
pub fn len(&self) -> usize {
self.diagnostics.len()
}
pub fn into_vec(self) -> Vec<Diagnostic> {
self.diagnostics
}
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
}
}