use std::any::{self, Any};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::sync::Arc;
use cairo_lang_diagnostics::{ErrorCode, Severity};
use cairo_lang_filesystem::cfg::CfgSet;
use cairo_lang_filesystem::db::Edition;
use cairo_lang_filesystem::ids::{CodeMapping, SmolStrId};
use cairo_lang_filesystem::span::TextWidth;
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
use cairo_lang_syntax::node::{SyntaxNode, ast};
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
use salsa::Database;
use serde::{Deserialize, Serialize};
#[typetag::serde]
pub trait GeneratedFileAuxData: std::fmt::Debug + Sync + Send {
fn as_any(&self) -> &dyn Any;
fn eq(&self, other: &dyn GeneratedFileAuxData) -> bool;
fn hash_value(&self) -> u64;
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DynGeneratedFileAuxData(pub Arc<dyn GeneratedFileAuxData>);
unsafe impl salsa::Update for DynGeneratedFileAuxData {
unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
let old_aux_data: &mut Self = unsafe { &mut *old_pointer };
if Arc::ptr_eq(&old_aux_data.0, &new_value.0) {
return false;
}
if GeneratedFileAuxData::eq(&*old_aux_data.0, &*new_value.0) {
return false;
}
*old_aux_data = new_value;
true
}
}
impl DynGeneratedFileAuxData {
pub fn new<T: GeneratedFileAuxData + 'static>(aux_data: T) -> Self {
DynGeneratedFileAuxData(Arc::new(aux_data))
}
}
impl Deref for DynGeneratedFileAuxData {
type Target = Arc<dyn GeneratedFileAuxData>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl PartialEq for DynGeneratedFileAuxData {
fn eq(&self, that: &DynGeneratedFileAuxData) -> bool {
self.0.eq(&*that.0)
}
}
impl Eq for DynGeneratedFileAuxData {}
impl Hash for DynGeneratedFileAuxData {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash_value().hash(state);
}
}
pub struct PluginGeneratedFile {
pub name: String,
pub content: String,
pub code_mappings: Vec<CodeMapping>,
pub aux_data: Option<DynGeneratedFileAuxData>,
pub diagnostics_note: Option<String>,
pub is_unhygienic: bool,
}
#[derive(Default)]
pub struct PluginResult<'db> {
pub code: Option<PluginGeneratedFile>,
pub diagnostics: Vec<PluginDiagnostic<'db>>,
pub remove_original_item: bool,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
pub struct PluginDiagnostic<'db> {
pub stable_ptr: SyntaxStablePtrId<'db>,
pub message: String,
pub severity: Severity,
pub inner_span: Option<(TextWidth, TextWidth)>,
pub error_code: Option<ErrorCode>,
}
impl<'db> PluginDiagnostic<'db> {
pub fn error(
stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
message: String,
) -> PluginDiagnostic<'db> {
PluginDiagnostic {
stable_ptr: stable_ptr.into(),
message,
severity: Severity::Error,
inner_span: None,
error_code: None,
}
}
pub fn error_with_inner_span(
db: &dyn Database,
stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
inner_span: SyntaxNode<'_>,
message: String,
) -> PluginDiagnostic<'db> {
let stable_ptr = stable_ptr.into();
let offset = inner_span.offset(db) - stable_ptr.lookup(db).offset(db);
let width = inner_span.width(db);
PluginDiagnostic {
stable_ptr,
message,
severity: Severity::Error,
inner_span: Some((offset, width)),
error_code: None,
}
}
pub fn warning(
stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
message: String,
) -> PluginDiagnostic<'db> {
PluginDiagnostic {
stable_ptr: stable_ptr.into(),
message,
severity: Severity::Warning,
inner_span: None,
error_code: None,
}
}
pub fn with_error_code(self, error_code: ErrorCode) -> PluginDiagnostic<'db> {
PluginDiagnostic { error_code: Some(error_code), ..self }
}
}
pub struct MacroPluginMetadata<'a> {
pub cfg_set: &'a CfgSet,
pub declared_derives: &'a OrderedHashSet<SmolStrId<'a>>,
pub allowed_features: &'a OrderedHashSet<SmolStrId<'a>>,
pub edition: Edition,
}
pub trait MacroPlugin: std::fmt::Debug + Sync + Send + Any {
fn generate_code<'db>(
&self,
db: &'db dyn Database,
item_ast: ast::ModuleItem<'db>,
metadata: &MacroPluginMetadata<'_>,
) -> PluginResult<'db>;
fn declared_attributes<'db>(&self, db: &'db dyn Database) -> Vec<SmolStrId<'db>>;
fn declared_derives<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
Vec::new()
}
fn executable_attributes<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
Vec::new()
}
fn phantom_type_attributes<'db>(&self, _db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
Vec::new()
}
fn plugin_type_id(&self) -> any::TypeId {
self.type_id()
}
}
#[derive(Default)]
pub struct InlinePluginResult<'db> {
pub code: Option<PluginGeneratedFile>,
pub diagnostics: Vec<PluginDiagnostic<'db>>,
}
pub trait InlineMacroExprPlugin: std::fmt::Debug + Sync + Send + Any {
fn generate_code<'db>(
&self,
db: &'db dyn Database,
item_ast: &ast::ExprInlineMacro<'db>,
metadata: &MacroPluginMetadata<'_>,
) -> InlinePluginResult<'db>;
fn documentation(&self) -> Option<String> {
None
}
fn plugin_type_id(&self) -> any::TypeId {
self.type_id()
}
}
pub trait NamedPlugin: Default + 'static {
const NAME: &'static str;
}