use crate::{
config::cfg::EnumRepresentation, context::Context, error::E, nature::{Nature, TypeAsString, TypeTokenStream, VariableTokenStream}
};
use proc_macro2::TokenStream;
use quote::{quote, format_ident};
#[derive(Clone, Debug)]
pub enum Referred {
TupleStruct(String, Context, Option<Box<Nature>>),
Struct(String, Context, Vec<Nature>),
Enum(String, Context, Vec<Nature>, EnumRepresentation),
EnumVariant(String, Context, Vec<Nature>, bool, EnumRepresentation),
Func(String, Context, Box<Nature>),
Field(String, Context, Box<Nature>, Option<String>),
FuncArg(String, Context, Box<Nature>, Option<String>),
Ref(String, Option<Context>),
Generic(String, Box<Nature>),
Constant(String, Context, Box<Nature>, String)
}
impl Referred {
pub fn is_flat_varians(variants: &[Nature]) -> Result<bool, E> {
for variant in variants {
if let Nature::Referred(Referred::EnumVariant(_, _, values, ..)) = variant {
if !values.is_empty() {
return Ok(false);
}
} else {
return Err(E::Parsing(String::from("Given Nature isn't enum varian")));
}
}
Ok(true)
}
pub fn is_enum_flat(&self) -> Result<bool, E> {
if let Referred::Enum(_, _, variants,_) = self {
Referred::is_flat_varians(variants)
} else {
Err(E::Parsing(String::from("Given Nature isn't enum")))
}
}
}
impl TypeTokenStream for Referred {
fn type_token_stream(&self) -> Result<TokenStream, E> {
let ident = match self {
Self::TupleStruct(name, ..) => Ok(format_ident!("{}", name)),
Self::Struct(name, ..) => Ok(format_ident!("{}", name)),
Self::Enum(name, ..) => Ok(format_ident!("{}", name)),
Self::Ref(name, ..) => Ok(format_ident!("{}", name)),
Self::Constant(name, ..) => Ok(format_ident!("{}", name)),
Self::EnumVariant(..) |
Self::Func(..) |
Self::Field(..) |
Self::FuncArg(..) |
Self::Generic(..) => Err(E::Other("EnumVariant, Func, Field, FuncArg, Generic of Referred doesn't support TypeTokenStream".to_string()))
}?;
Ok(quote! { #ident })
}
}
impl TypeAsString for Referred {
fn type_as_string(&self) -> Result<String, E> {
match self {
Self::TupleStruct(name, ..) => Ok(name.clone()),
Self::Struct(name, ..) => Ok(name.clone()),
Self::Enum(name, ..) => Ok(name.clone()),
Self::Ref(name, ..) => Ok(name.clone()),
Self::Constant(name, ..) => Ok(name.clone()),
Self::EnumVariant(..) |
Self::Func(..) |
Self::Field(..) |
Self::FuncArg(..) |
Self::Generic(..) => Err(E::Other("EnumVariant, Func, Field, FuncArg, Generic of Referred doesn't support TypeAsString".to_string()))
}
}
}
impl VariableTokenStream for Referred {
fn variable_token_stream(&self, var_name: &str, err: Option<&Nature>) -> Result<TokenStream, E> {
let var_name = format_ident!("{}", var_name);
match self {
Self::Ref(_, _) => {
Ok(if let Some(nature) = err {
let err_type_ref = nature.type_token_stream()?;
quote! {
serde_json::to_string(&#var_name).map_err(|e| Into::<#err_type_ref>::into(e))?
}
} else {
quote! {
serde_json::to_string(&#var_name).expect("Converting to JSON string")
}
})
},
_ => {
Err(E::Parsing(format!("Only reference to entity (struct / enum) can be convert into JSON string (var: {var_name})")))
}
}
}
}