use alloc::vec::Vec;
use crate::{
semantics::{Enumeration, Structure, Target},
syntax::{Import, Inline},
};
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
mod conversion;
mod display;
mod error;
use conversion::ConversionImpl;
use display::{DisplayImpl, EnumDisplayImpl};
use error::{EnumErrorImpl, ErrorImpl};
pub trait Expand {
type Context;
fn expand_with(self, context: Self::Context) -> syn::Result<TokenStream>;
#[inline]
fn expand(self) -> syn::Result<TokenStream>
where
Self::Context: Default,
Self: Sized,
{
self.expand_with(Self::Context::default())
}
}
pub fn target(target: Target) -> syn::Result<TokenStream> {
match target {
Target::Struct(structure) => StructureExpansion(*structure).expand(),
Target::Enum(enumeration) => EnumerationExpansion(enumeration).expand(),
}
}
struct StructureExpansion(Structure);
impl Expand for StructureExpansion {
type Context = ();
fn expand_with(self, (): Self::Context) -> syn::Result<TokenStream> {
let Self(structure) = self;
let (header, fields, display, source, conversion) = structure.parts();
let (inline, import, name, generics) = header.parts();
let context = Context::new(inline, import);
let display = DisplayImpl::new(&name, &generics, &fields, &display).expand_with(context.clone())?;
let error = ErrorImpl::new(&name, &generics, &fields, &source).expand_with(context.clone())?;
let conversion = conversion
.as_ref()
.map(|conversion| ConversionImpl::structure(&name, &generics, &fields, conversion).expand_with(context.clone()))
.transpose()?;
Ok(quote! {
#conversion
#display
#error
})
}
}
struct EnumerationExpansion(Enumeration);
impl Expand for EnumerationExpansion {
type Context = ();
fn expand_with(self, (): Self::Context) -> syn::Result<TokenStream> {
let Self(enumeration) = self;
let (header, variants) = enumeration.parts();
let (inline, import, name, generics) = header.parts();
let context = Context::new(inline, import);
let display = EnumDisplayImpl::new(&name, &generics, &variants).expand_with(context.clone())?;
let error = EnumErrorImpl::new(&name, &generics, &variants).expand_with(context.clone())?;
let conversions = variants
.iter()
.filter_map(|variant| {
variant
.conversion()
.map(|conversion| ConversionImpl::variant(&name, &generics, variant, conversion).expand_with(context.clone()))
})
.collect::<syn::Result<Vec<_>>>()?;
Ok(quote! {
#(#conversions)*
#display
#error
})
}
}
#[derive(Clone, Debug)]
pub struct Context {
inline: TokenStream,
root: TokenStream,
}
impl Context {
#[must_use]
#[inline]
fn new(inline: Option<Inline>, import: Option<Import>) -> Self {
let inline = match inline {
Some(Inline::Neutral) => quote! { #[inline] },
Some(Inline::Always) => quote! { #[inline(always)] },
Some(Inline::Never) => quote! { #[inline(never)] },
None => TokenStream::new(),
};
let root = match import {
Some(Import(path)) => path.to_token_stream(),
None => quote! { ::core },
};
Self { inline, root }
}
#[inline]
#[must_use]
const fn inline(&self) -> &TokenStream {
let Self { inline, .. } = self;
inline
}
#[inline]
#[must_use]
const fn root(&self) -> &TokenStream {
let Self { root, .. } = self;
root
}
}