use super::{MacroRegistry, Macroforge, Result};
use crate::ts_syn::abi::MacroKind;
use serde::Serialize;
use std::{collections::BTreeSet, sync::Arc};
pub const DYNAMIC_MODULE_MARKER: &str = "__DYNAMIC_MODULE__";
pub struct DerivedMacroDescriptor {
pub package: &'static str,
pub module: &'static str,
pub runtime: &'static [&'static str],
pub name: &'static str,
pub kind: MacroKind,
pub description: &'static str,
pub constructor: fn() -> Arc<dyn Macroforge>,
pub decorators: &'static [DecoratorDescriptor],
}
pub struct DecoratorDescriptor {
pub module: &'static str,
pub export: &'static str,
pub kind: DecoratorKind,
pub docs: &'static str,
}
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum DecoratorKind {
Class,
Property,
Method,
Accessor,
Parameter,
}
impl DecoratorKind {
pub fn ts_type(&self) -> &'static str {
match self {
DecoratorKind::Class => "ClassDecorator",
DecoratorKind::Property => "PropertyDecorator",
DecoratorKind::Method => "MethodDecorator",
DecoratorKind::Accessor => "MethodDecorator",
DecoratorKind::Parameter => "ParameterDecorator",
}
}
}
pub struct DerivedMacroRegistration {
pub descriptor: &'static DerivedMacroDescriptor,
}
inventory::collect!(DerivedMacroRegistration);
pub fn modules() -> BTreeSet<&'static str> {
inventory::iter::<DerivedMacroRegistration>
.into_iter()
.map(|entry| entry.descriptor.module)
.collect()
}
pub fn register_module(module: &str, registry: &MacroRegistry) -> Result<bool> {
let descriptors: Vec<&DerivedMacroDescriptor> = inventory::iter::<DerivedMacroRegistration>
.into_iter()
.filter(|entry| entry.descriptor.module == module)
.map(|entry| entry.descriptor)
.collect();
if descriptors.is_empty() {
return Ok(false);
}
let mut runtime: BTreeSet<String> = BTreeSet::new();
for descriptor in &descriptors {
for entry in descriptor.runtime {
runtime.insert(entry.to_string());
}
}
for descriptor in descriptors {
registry.register(module, descriptor.name, (descriptor.constructor)())?;
}
Ok(true)
}
#[derive(Debug, Clone, Serialize)]
pub struct DecoratorMetadata {
pub module: &'static str,
pub export: &'static str,
pub kind: DecoratorKind,
pub docs: &'static str,
}
pub fn decorator_metadata() -> Vec<DecoratorMetadata> {
inventory::iter::<DerivedMacroRegistration>
.into_iter()
.flat_map(|entry| entry.descriptor.decorators)
.map(|decorator| DecoratorMetadata {
module: decorator.module,
export: decorator.export,
kind: decorator.kind,
docs: decorator.docs,
})
.collect()
}
pub fn decorator_modules() -> BTreeSet<&'static str> {
inventory::iter::<DerivedMacroRegistration>
.into_iter()
.flat_map(|entry| entry.descriptor.decorators)
.map(|decorator| decorator.module)
.collect()
}
pub fn decorator_annotation_names() -> BTreeSet<&'static str> {
inventory::iter::<DerivedMacroRegistration>
.into_iter()
.flat_map(|entry| entry.descriptor.decorators)
.map(|decorator| decorator.export)
.collect()
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MacroManifestEntry {
pub name: &'static str,
pub kind: MacroKind,
pub description: &'static str,
pub package: &'static str,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MacroManifest {
pub version: u32,
pub macros: Vec<MacroManifestEntry>,
pub decorators: Vec<DecoratorMetadata>,
}
pub fn get_manifest() -> MacroManifest {
let macros: Vec<MacroManifestEntry> = inventory::iter::<DerivedMacroRegistration>
.into_iter()
.map(|entry| MacroManifestEntry {
name: entry.descriptor.name,
kind: entry.descriptor.kind,
description: entry.descriptor.description,
package: entry.descriptor.package,
})
.collect();
let decorators = decorator_metadata();
MacroManifest {
version: 1,
macros,
decorators,
}
}
pub fn macro_names() -> Vec<&'static str> {
inventory::iter::<DerivedMacroRegistration>
.into_iter()
.map(|entry| entry.descriptor.name)
.collect()
}
pub fn lookup_by_name(name: &str) -> Option<&'static DerivedMacroDescriptor> {
inventory::iter::<DerivedMacroRegistration>
.into_iter()
.find(|entry| entry.descriptor.name == name)
.map(|entry| entry.descriptor)
}
pub fn register_all_with_module(actual_module: &str, registry: &MacroRegistry) -> Result<usize> {
let mut count = 0;
for entry in inventory::iter::<DerivedMacroRegistration> {
let descriptor = entry.descriptor;
let module = if descriptor.module == DYNAMIC_MODULE_MARKER {
actual_module
} else {
descriptor.module
};
registry.register(module, descriptor.name, (descriptor.constructor)())?;
count += 1;
}
Ok(count)
}