use std::{any::TypeId, collections::HashMap, sync::LazyLock};
use cairo_lang_defs::plugin::{
InlineMacroExprPlugin, MacroPlugin, MacroPluginMetadata, PluginResult,
};
use cairo_lang_executable_plugin::executable_plugin_suite;
use cairo_lang_filesystem::ids::SmolStrId;
use cairo_lang_semantic::plugin::{AnalyzerPlugin, PluginSuite};
use cairo_lang_starknet::starknet_plugin_suite;
use cairo_lang_syntax::node::ast::ModuleItem;
use cairo_lang_test_plugin::{test_assert_suite, test_plugin_suite};
use itertools::chain;
use salsa::Database;
use scarb_metadata::{CompilationUnitCairoPluginMetadata, Metadata};
use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
pub enum BuiltinPlugin {
AssertMacros,
Executable,
CairoRun,
CairoTest,
Starknet,
}
impl BuiltinPlugin {
pub fn from_plugin_metadata(
metadata: &Metadata,
plugin_metadata: &CompilationUnitCairoPluginMetadata,
) -> Option<Self> {
if !metadata.is_builtin_plugin(plugin_metadata).unwrap_or_default() {
return None;
}
let package_id_repr = &plugin_metadata.package.repr;
let package_metadata = metadata
.packages
.iter()
.find(|package_metadata| &package_metadata.id.repr == package_id_repr)?;
match package_metadata.name.as_str() {
"assert_macros" => Some(Self::AssertMacros),
"cairo_execute" => Some(Self::Executable),
"cairo_run" => Some(Self::CairoRun),
"cairo_test" => Some(Self::CairoTest),
"starknet" => Some(Self::Starknet),
_ => None,
}
}
pub fn suite(&self) -> PluginSuite {
match self {
BuiltinPlugin::AssertMacros => test_assert_suite(),
BuiltinPlugin::CairoTest => test_plugin_suite(),
BuiltinPlugin::CairoRun => cairo_run_plugin_suite(),
BuiltinPlugin::Executable => executable_plugin_suite(),
BuiltinPlugin::Starknet => starknet_plugin_suite(),
}
}
pub fn try_from_compiler_macro_plugin(plugin: &dyn MacroPlugin) -> Option<Self> {
PLUGIN_TYPE_IDS.get(&plugin.plugin_type_id()).cloned()
}
pub fn try_from_compiler_inline_macro_plugin(
plugin: &dyn InlineMacroExprPlugin,
) -> Option<Self> {
PLUGIN_TYPE_IDS.get(&plugin.plugin_type_id()).cloned()
}
pub fn try_from_compiler_analyzer_plugin(plugin: &dyn AnalyzerPlugin) -> Option<Self> {
PLUGIN_TYPE_IDS.get(&plugin.plugin_type_id()).cloned()
}
}
fn cairo_run_plugin_suite() -> PluginSuite {
let mut suite = PluginSuite::default();
suite.add_plugin::<CairoRunPlugin>();
suite
}
const CAIRO_RUN_EXECUTABLE: &str = "main";
#[derive(Debug, Default)]
struct CairoRunPlugin {}
impl MacroPlugin for CairoRunPlugin {
fn generate_code<'db>(
&self,
_db: &'db dyn Database,
_item_ast: ModuleItem<'db>,
_metadata: &MacroPluginMetadata<'_>,
) -> PluginResult<'db> {
PluginResult::default()
}
fn declared_attributes<'db>(&self, db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
vec![SmolStrId::from(db, CAIRO_RUN_EXECUTABLE)]
}
fn executable_attributes<'db>(&self, db: &'db dyn Database) -> Vec<SmolStrId<'db>> {
self.declared_attributes(db)
}
}
static PLUGIN_TYPE_IDS: LazyLock<HashMap<TypeId, BuiltinPlugin>> = LazyLock::new(|| {
chain!(
plugin_type_ids(test_plugin_suite()).map(|id| (id, BuiltinPlugin::CairoTest)),
plugin_type_ids(test_assert_suite()).map(|id| (id, BuiltinPlugin::AssertMacros)),
plugin_type_ids(cairo_run_plugin_suite()).map(|id| (id, BuiltinPlugin::CairoRun)),
plugin_type_ids(executable_plugin_suite()).map(|id| (id, BuiltinPlugin::Executable)),
plugin_type_ids(starknet_plugin_suite()).map(|id| (id, BuiltinPlugin::Starknet)),
)
.collect()
});
fn plugin_type_ids(suite: PluginSuite) -> impl Iterator<Item = TypeId> {
let PluginSuite { plugins, inline_macro_plugins, analyzer_plugins } = suite;
chain!(
plugins.into_iter().map(|plugin| plugin.plugin_type_id()),
inline_macro_plugins.into_iter().map(|(_, plugin)| plugin.plugin_type_id()),
analyzer_plugins.into_iter().map(|plugin| plugin.plugin_type_id()),
)
}