use crate::{extract_fields_with_types, extract_variants_with_types, RESERVED_FIELDS};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use std::collections::HashSet;
use syn::{Data, DataEnum, DataStruct, DeriveInput, Ident, Type, TypePath};
pub fn dispatch_struct_enum(input: DeriveInput) -> TokenStream {
let ident = input.ident;
match input.data {
Data::Struct(ds) => operate_qubits_struct(ds, ident),
Data::Enum(de) => operate_qubits_enum(de, ident),
_ => panic!("InvolveQubits can only be derived on structs and enums"),
}
}
fn operate_qubits_enum(de: DataEnum, ident: Ident) -> TokenStream {
let variants_with_type = extract_variants_with_types(de).into_iter();
let tags_quotes = variants_with_type.clone().map(|(vident, _, _)| {
quote! {
&#ident::#vident(ref inner) => {Operate::tags(&(*inner))},
}
});
let hqslang_quotes = variants_with_type.clone().map(|(vident, _, _)| {
quote! {
&#ident::#vident(ref inner) => {Operate::hqslang(&(*inner))},
}
});
let from_quotes = variants_with_type.clone().map(|(vident, _, ty)| {
quote! {
#[automatically_derived]
impl From<#ty> for #ident{
fn from(v: #ty) -> Self{
#ident::#vident(v)
}
}
}
});
let try_from_quotes = variants_with_type.clone().map(|(vident, _, ty)| {
quote! {
#[automatically_derived]
impl core::convert::TryFrom<#ident> for #ty{
type Error = &'static str;
fn try_from(e: #ident) -> Result<Self, Self::Error>{
match e{
#ident::#vident(v) => Ok(v),
_ => Err("Type can not be created from variant")
}
}
}
}
});
let is_parametrized_quotes = variants_with_type.map(|(vident, _, _)| {
quote! {
&#ident::#vident(ref inner) => {Operate::is_parametrized(&(*inner))},
}
});
let qtags = quote! {
fn tags(&self) -> &'static [&'static str] {
match self{
#(#tags_quotes)*
_ => panic!("Unexpectedly cannot match variant")
}
}
};
let qhqslang = quote! {
fn hqslang(&self) -> &'static str {
match self{
#(#hqslang_quotes)*
_ => panic!("Unexpectedly cannot match variant")
}
}
};
let qisparametrized = quote! {
fn is_parametrized(&self) -> bool {
match self{
#(#is_parametrized_quotes)*
_ => panic!("Unexpectedly cannot match variant")
}
}
};
quote! {
#[automatically_derived]
#[cfg_attr(feature = "dynamic", typetag::serde)]
impl Operate for #ident{
#qtags
#qhqslang
#qisparametrized
}
#(#from_quotes)*
#(#try_from_quotes)*
}
}
fn operate_qubits_struct(ds: DataStruct, ident: Ident) -> TokenStream {
let reserved_fields: HashSet<&str> = RESERVED_FIELDS.iter().cloned().collect();
let fields_with_type = extract_fields_with_types(ds).into_iter();
let input_arguments = fields_with_type.clone().map(|(id, _, ty)| {
quote! {#id: #ty}
});
let arguments = fields_with_type.clone().map(|(id, _, _)| {
quote! {#id}
});
let calculator_float_fields = fields_with_type
.clone()
.filter(|(_, _, ty)| match &ty {
Type::Path(TypePath { path: p, .. }) => match p.get_ident() {
None => false,
Some(id) => *id == "CalculatorFloat",
},
_ => false,
})
.map(|(id, _, _)| {
quote! {
!self.#id.is_float()
}
});
let circuit_fields = fields_with_type
.clone()
.filter(|(_, type_string, _)| type_string == &Some("Circuit".to_string()))
.map(|(id, _, _)| {
quote! {
self.#id.is_parametrized()
}
});
let circuit_fields2 = fields_with_type
.clone()
.filter(|(_, type_string, _)| type_string == &Some("Option<Circuit>".to_string()))
.map(|(id, _, _)| {
quote! {
match self.#id.as_ref(){
Some(x) => x.is_parametrized(),
None => false
}
}
});
let struqture_fields = fields_with_type
.clone()
.filter(|(_, type_string, _)| type_string == &Some("SpinHamiltonian".to_string()))
.map(|(id, _, _)| {
quote! {
self.#id.values().any(|x| !x.is_float())
}
});
let is_parametrized_fields = if calculator_float_fields.clone().last().is_none()
&& circuit_fields.clone().last().is_none()
&& circuit_fields2.clone().last().is_none()
&& struqture_fields.clone().last().is_none()
{
vec![quote!(false)]
} else {
calculator_float_fields
.chain(circuit_fields)
.chain(circuit_fields2)
.chain(struqture_fields)
.collect()
};
let getter_fields = fields_with_type
.filter(|(id, _, _)| {
!reserved_fields.contains(id.to_string().as_str())
})
.map(|(id, _, ty)| {
let msg = format!("Returns the value of the field `{}`.", id);
quote! {
#[doc = #msg]
#[inline]
pub fn #id(&self) -> &#ty{
&self.#id
}
}
});
let formated_tags = format_ident!("TAGS_{}", ident);
let formated_hqslang = format!("{}", ident);
let msg = format!("Creates a new instance of `{}`.\n\n", ident);
quote! {
#[automatically_derived]
impl #ident{
#(#getter_fields)*
#[doc = #msg]
#[inline]
pub fn new(#(#input_arguments),*) -> Self{
Self{#(#arguments),*}
}
}
#[automatically_derived]
#[cfg_attr(feature = "dynamic", typetag::serde)]
impl Operate for #ident{
#[inline]
fn is_parametrized(&self) -> bool {
(#(#is_parametrized_fields)||*)
}
#[inline]
fn tags(&self) -> &'static [&'static str]{
#formated_tags
}
#[inline]
fn hqslang(&self) -> &'static str{
#formated_hqslang
}
}
}
}
pub fn dispatch_try_from_enum(input: DeriveInput) -> TokenStream {
let ident = input.ident;
match input.data {
Data::Enum(de) => try_from_operations_enum(de, ident),
_ => panic!("TryFrom implementations for Operation can only be derived on enums"),
}
}
fn try_from_operations_enum(de: DataEnum, ident: Ident) -> TokenStream {
let id_name = ident.to_string();
let variants_with_type = extract_variants_with_types(de).into_iter();
let operation_from_quotes = variants_with_type.clone().map(|(vident, _, _)| {
quote! {
#ident::#vident(inner) => {Operation::#vident(inner)}
}
});
let from_quotes = variants_with_type.clone().map(|(vident, _, _)| {
quote! {
Operation::#vident(inner) => {Ok(#ident::#vident(inner))}
}
});
let from_clone_quotes = variants_with_type.map(|(vident, _, _)| {
quote! {
Operation::#vident(inner) => {Ok(#ident::#vident(inner.clone()))}
}
});
quote! {impl std::convert::From<#ident> for Operation{
fn from(op: #ident) -> Self {
match op{
#(#operation_from_quotes),*
}
}
}
impl std::convert::TryFrom<Operation> for #ident{
type Error = RoqoqoError;
fn try_from(op: Operation) -> Result<Self, Self::Error> {
match op{
#(#from_quotes),*
_ => Err(RoqoqoError::ConversionError{start_type:"Operation", end_type:#id_name}),
}
}
}
impl std::convert::TryFrom<&Operation> for #ident{
type Error = RoqoqoError;
fn try_from(op: &Operation) -> Result<Self, Self::Error> {
match op{
#(#from_clone_quotes),*
_ => Err(RoqoqoError::ConversionError{start_type:"Operation", end_type:#id_name}),
}
}
}}
}