use std::hash::Hash;
use proc_macro2::Span;
use quote::ToTokens;
use syn::{
punctuated::Punctuated,
token::{Brace, Bracket, Comma, Paren},
Attribute, Field, FieldsNamed, Generics, Ident, ImplItem, ImplItemFn, ItemImpl, ItemStruct,
MetaList, Path, PathSegment, Token, Type, TypePath, Visibility,
};
use syn_path::path;
use crate::wir::{WBlock, WSignature, WSpan, WSpanned};
use super::{IntoSyn, WIdent, WImplItemType, WPath, YStage};
#[derive(Clone, Debug, Hash)]
pub struct WItemFn<Y: YStage> {
pub visibility: WVisibility,
pub signature: WSignature<Y>,
pub locals: Vec<Y::Local>,
pub block: WBlock<Y::AssignTypes>,
pub result: Y::FnResult,
}
#[derive(Clone, Debug, Hash)]
pub struct WItemStruct<FT: IntoSyn<Type>> {
pub visibility: WVisibility,
pub derives: Vec<WPath>,
pub ident: WIdent,
pub fields: Vec<WField<FT>>,
}
#[derive(Clone, Debug)]
pub enum WVisibility {
Public(WSpan),
Inherited,
}
impl Hash for WVisibility {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
}
}
#[derive(Clone, Debug, Hash)]
pub struct WField<FT: IntoSyn<Type>> {
pub visibility: WVisibility,
pub ident: WIdent,
pub ty: FT,
}
#[derive(Clone, Debug, Hash)]
pub struct WItemImpl<Y: YStage> {
pub self_ty: WPath,
pub trait_: Option<Y::ItemImplTrait>,
pub impl_item_fns: Vec<WItemFn<Y>>,
pub impl_item_types: Vec<WImplItemType>,
}
#[derive(Clone, Debug)]
pub enum WItemImplTrait {
Machine(WSpan),
}
impl Hash for WItemImplTrait {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
}
}
impl IntoSyn<Path> for WItemImplTrait {
fn into_syn(self) -> Path {
match self {
WItemImplTrait::Machine(_span) => {
path!(::mck::forward::Machine)
}
}
}
}
impl WSpanned for WItemImplTrait {
fn wir_span(&self) -> WSpan {
match self {
WItemImplTrait::Machine(span) => *span,
}
}
}
impl<FT: IntoSyn<Type>> IntoSyn<ItemStruct> for WItemStruct<FT> {
fn into_syn(self) -> ItemStruct {
let span = Span::call_site();
let named = Punctuated::from_iter(self.fields.into_iter().map(|field| Field {
attrs: Vec::new(),
vis: field.visibility.into_syn(),
mutability: syn::FieldMutability::None,
ident: Some(field.ident.into()),
colon_token: Some(Token),
ty: field.ty.into_syn(),
}));
let fields = FieldsNamed {
brace_token: Brace::default(),
named,
};
let mut attrs = Vec::new();
if !self.derives.is_empty() {
let derive_tokens =
Punctuated::<Path, Comma>::from_iter(self.derives.into_iter().map(Path::from))
.into_token_stream();
let derive_attribute = Attribute {
pound_token: Token,
style: syn::AttrStyle::Outer,
bracket_token: Bracket::default(),
meta: syn::Meta::List(MetaList {
path: Path {
leading_colon: None,
segments: Punctuated::from_iter([PathSegment {
ident: Ident::new("derive", span),
arguments: syn::PathArguments::None,
}]),
},
delimiter: syn::MacroDelimiter::Paren(Paren::default()),
tokens: derive_tokens,
}),
};
attrs.push(derive_attribute);
}
ItemStruct {
attrs,
vis: self.visibility.into_syn(),
struct_token: Token,
ident: self.ident.into(),
generics: Generics::default(),
fields: syn::Fields::Named(fields),
semi_token: None,
}
}
}
impl<FT: IntoSyn<Type>> WSpanned for WItemStruct<FT> {
fn wir_span(&self) -> WSpan {
self.ident.wir_span()
}
}
impl<Y: YStage> IntoSyn<ItemImpl> for WItemImpl<Y>
where
WItemFn<Y>: IntoSyn<ImplItemFn>,
{
fn into_syn(self) -> ItemImpl {
let span = Span::call_site();
let items = self
.impl_item_types
.into_iter()
.map(|type_item| ImplItem::Type(type_item.into_syn()))
.chain(
self.impl_item_fns
.into_iter()
.map(|fn_item| ImplItem::Fn(fn_item.into_syn())),
)
.collect();
let trait_path = self.trait_.map(|trait_| trait_.into_syn());
ItemImpl {
attrs: Vec::new(),
defaultness: None,
unsafety: None,
impl_token: Token,
generics: Generics::default(),
trait_: trait_path.map(|path| (None, path, Token)),
self_ty: Box::new(Type::Path(TypePath {
qself: None,
path: self.self_ty.into(),
})),
brace_token: Brace::default(),
items,
}
}
}
impl<Y: YStage> WSpanned for WItemImpl<Y>
where
WItemFn<Y>: IntoSyn<ImplItemFn>,
{
fn wir_span(&self) -> WSpan {
self.self_ty.wir_span()
}
}
impl IntoSyn<Visibility> for WVisibility {
fn into_syn(self) -> Visibility {
match self {
WVisibility::Public(span) => Visibility::Public(Token)),
WVisibility::Inherited => Visibility::Inherited,
}
}
}
impl WSpanned for WVisibility {
fn wir_span(&self) -> WSpan {
match self {
WVisibility::Public(span) => *span,
WVisibility::Inherited => WSpan::call_site(),
}
}
}