use std::{
convert::TryFrom,
fmt::{self, Debug, Formatter},
};
use indexmap::IndexMap;
use inflector::cases::pascalcase::to_pascal_case;
use syn::{
braced, bracketed,
export::quote::quote,
parenthesized,
parse::{Parse, ParseStream, Result},
punctuated::Punctuated,
Expr, Ident, ItemType, Path, Token,
};
use crate::{rust_type::RustType, util::to_ident};
use proc_macro2::{Span, TokenStream};
#[derive(Debug)]
pub(crate) struct ReactorDefinition {
reactor_type_ident: Ident,
config_type: RustType,
components: IndexMap<Ident, ComponentDefinition>,
events: IndexMap<Ident, EventDefinition>,
requests: Vec<RequestDefinition>,
announcements: Vec<AnnouncementDefinition>,
}
impl ReactorDefinition {
pub fn reactor_ident(&self) -> Ident {
self.reactor_type_ident.clone()
}
pub fn event_ident(&self) -> Ident {
let mut event_str = self.reactor_ident().to_string();
event_str.push_str("Event");
to_ident(&event_str)
}
pub fn error_ident(&self) -> Ident {
let mut event_str = self.reactor_ident().to_string();
event_str.push_str("Error");
to_ident(&event_str)
}
pub fn announcements(&self) -> impl Iterator<Item = &AnnouncementDefinition> {
self.announcements.iter()
}
pub fn components(&self) -> impl Iterator<Item = &ComponentDefinition> {
self.components.values()
}
pub fn config_type(&self) -> &RustType {
&self.config_type
}
pub fn requests(&self) -> impl Iterator<Item = &RequestDefinition> {
self.requests.iter()
}
pub fn component(&self, ident: &Ident) -> &ComponentDefinition {
&self.components[ident]
}
pub fn component_event(&self, component: &ComponentDefinition) -> TokenStream {
let component_type = component.component_type();
let module_ident = component_type.module_ident();
let event_ident = if let Some(event_def) = self.events.get(component.field_ident()) {
let path = event_def.event_type.as_given();
quote!(#path)
} else {
let ident = to_ident("Event");
quote!(#ident)
};
quote!(crate::components::#module_ident::#event_ident)
}
}
impl Parse for ReactorDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let reactor_type_ident: Ident = input.parse()?;
braced!(content in input);
let config: ItemType = content.parse()?;
let component_content;
let _: kw::components = content.parse()?;
let _: Token!(:) = content.parse()?;
braced!(component_content in content);
let mut components = IndexMap::new();
for cdef in component_content
.parse_terminated::<ComponentDefinition, Token!(;)>(ComponentDefinition::parse)?
{
components.insert(cdef.name.clone(), cdef);
}
let event_content;
let _: kw::events = content.parse()?;
let _: Token!(:) = content.parse()?;
braced!(event_content in content);
let mut events = IndexMap::new();
for edef in
event_content.parse_terminated::<EventDefinition, Token!(;)>(EventDefinition::parse)?
{
events.insert(edef.name.clone(), edef);
}
let requests_content;
let _: kw::requests = content.parse()?;
let _: Token!(:) = content.parse()?;
braced!(requests_content in content);
let requests = requests_content
.parse_terminated::<RequestDefinition, Token!(;)>(RequestDefinition::parse)?
.into_iter()
.collect();
let announcements_content;
let _: kw::announcements = content.parse()?;
let _: Token!(:) = content.parse()?;
braced!(announcements_content in content);
let announcements = announcements_content
.parse_terminated::<AnnouncementDefinition, Token!(;)>(AnnouncementDefinition::parse)?
.into_iter()
.collect();
Ok(ReactorDefinition {
reactor_type_ident,
config_type: RustType::try_from(config.ty.as_ref().clone()).map_err(|err| {
syn::parse::Error::new(
Span::call_site(), err,
)
})?,
components,
events,
requests,
announcements,
})
}
}
pub(crate) struct ComponentDefinition {
name: Ident,
component_type: RustType,
component_arguments: Vec<Expr>,
has_effects: bool,
}
impl ComponentDefinition {
pub(crate) fn component_arguments(&self) -> &[Expr] {
self.component_arguments.as_slice()
}
pub(crate) fn field_ident(&self) -> &Ident {
&self.name
}
pub fn variant_ident(&self) -> Ident {
to_ident(&to_pascal_case(&self.field_ident().to_string()))
}
pub(crate) fn component_type(&self) -> &RustType {
&self.component_type
}
pub fn full_component_type(&self) -> TokenStream {
let component_type = self.component_type();
let module_ident = component_type.module_ident();
let ty = component_type.ty();
quote!(crate::components::#module_ident::#ty)
}
pub fn full_error_type(&self, reactor_event_type: TokenStream) -> TokenStream {
let comp_type = self.full_component_type();
quote!(<#comp_type as crate::components::Component<#reactor_event_type>>::ConstructionError)
}
pub fn has_effects(&self) -> bool {
self.has_effects
}
}
impl Debug for ComponentDefinition {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("ComponentDefinition")
.field("name", &self.name.to_string())
.field("component_type", &self.component_type)
.field("component_arguments", &"TODO: fmtargs")
.finish()
}
}
impl Parse for ComponentDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let name: Ident = input.parse()?;
let _: Token!(=) = input.parse()?;
let has_effects = if input.peek(Token!(@)) {
let _: Token!(@) = input.parse()?;
true
} else {
false
};
let ty: Path = input.parse()?;
let content;
parenthesized!(content in input);
let args: Punctuated<Expr, Token!(,)> = content.parse_terminated(Expr::parse)?;
Ok(ComponentDefinition {
name,
component_type: RustType::new(ty),
component_arguments: args.into_iter().collect(),
has_effects,
})
}
}
#[derive(Debug)]
pub(crate) struct EventDefinition {
pub name: Ident,
pub event_type: RustType,
}
impl Parse for EventDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let name: Ident = input.parse()?;
let _: Token!(=) = input.parse()?;
let ty: Path = input.parse()?;
Ok(EventDefinition {
name,
event_type: RustType::new(ty),
})
}
}
#[derive(Debug)]
pub(crate) struct RequestDefinition {
pub request_type: RustType,
pub target: Target,
}
impl RequestDefinition {
pub fn variant_ident(&self) -> Ident {
self.request_type.ident()
}
pub(crate) fn request_type(&self) -> &RustType {
&self.request_type
}
pub(crate) fn target(&self) -> &Target {
&self.target
}
pub fn full_request_type(&self) -> TokenStream {
let request_type = self.request_type();
let ty = request_type.ty();
quote!(crate::effect::requests::#ty)
}
}
impl Parse for RequestDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let request_type = RustType::new(input.parse()?);
let _: Token!(->) = input.parse()?;
let target = input.parse()?;
Ok(RequestDefinition {
request_type,
target,
})
}
}
#[derive(Debug)]
pub(crate) struct AnnouncementDefinition {
pub announcement_type: RustType,
pub targets: Vec<Target>,
}
impl AnnouncementDefinition {
pub(crate) fn announcement_type(&self) -> &RustType {
&self.announcement_type
}
pub(crate) fn full_announcement_type(&self) -> TokenStream {
let announcement_type = self.announcement_type();
let ty = announcement_type.ty();
quote!(crate::effect::announcements::#ty)
}
pub(crate) fn targets(&self) -> impl Iterator<Item = &Target> {
self.targets.iter()
}
pub fn variant_ident(&self) -> Ident {
self.announcement_type.ident()
}
}
impl Parse for AnnouncementDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let announcement_type = RustType::new(input.parse()?);
let _: Token!(->) = input.parse()?;
let content;
bracketed!(content in input);
let targets = content
.parse_terminated::<Target, Token!(,)>(Target::parse)?
.into_iter()
.collect();
Ok(AnnouncementDefinition {
announcement_type,
targets,
})
}
}
pub(crate) enum Target {
Discard,
Dest(Ident),
}
impl Debug for Target {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Target::Discard => write!(f, "!"),
Target::Dest(id) => write!(f, "{}", id.to_string()),
}
}
}
impl Parse for Target {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(Token!(!)) {
let _: Token!(!) = input.parse()?;
Ok(Target::Discard)
} else {
input.parse().map(Target::Dest)
}
}
}
mod kw {
syn::custom_keyword!(components);
syn::custom_keyword!(events);
syn::custom_keyword!(requests);
syn::custom_keyword!(announcements);
}