use crate::{
ast::utils::Named,
ir::{EnumeratedTypedField, ModuleStructIR, TypeIR},
utils
};
use quote::ToTokens;
pub struct SchemaErrorsItem {
module_ident: syn::Ident,
errors: Option<syn::Type>,
fields: Vec<EnumeratedTypedField>
}
impl ToTokens for SchemaErrorsItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let module_ident = &self.module_ident;
let errors = self
.errors
.iter()
.chain(self.fields.iter().map(|f| &f.ty))
.map(|ty| quote::quote!(.chain(<#ty as odra::schema::SchemaErrors>::schema_errors())))
.collect::<Vec<_>>();
let item = quote::quote! {
#[automatically_derived]
#[cfg(not(target_arch = "wasm32"))]
impl odra::schema::SchemaErrors for #module_ident {
fn schema_errors() -> odra::prelude::Vec<odra::schema::casper_contract_schema::UserError> {
odra::prelude::BTreeSet::<odra::schema::casper_contract_schema::UserError>::new()
.into_iter()
#(#errors)*
.collect::<odra::prelude::BTreeSet<odra::schema::casper_contract_schema::UserError>>()
.into_iter()
.collect::<odra::prelude::Vec<_>>()
}
}
};
item.to_tokens(tokens);
}
}
impl TryFrom<&ModuleStructIR> for SchemaErrorsItem {
type Error = syn::Error;
fn try_from(ir: &ModuleStructIR) -> Result<Self, Self::Error> {
Ok(Self {
module_ident: ir.module_ident(),
errors: ir.errors(),
fields: ir.typed_fields()?
})
}
}
pub struct SchemaErrorItem {
ty_ident: syn::Ident,
errors: Vec<syn::Variant>
}
impl ToTokens for SchemaErrorItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let ident = &self.ty_ident;
let errors = enum_variants(&self.errors);
let item = quote::quote! {
#[automatically_derived]
#[cfg(not(target_arch = "wasm32"))]
impl odra::schema::SchemaErrors for #ident {
fn schema_errors() -> odra::prelude::Vec<odra::schema::casper_contract_schema::UserError> {
#errors
}
}
};
item.to_tokens(tokens);
}
}
impl TryFrom<&TypeIR> for SchemaErrorItem {
type Error = syn::Error;
fn try_from(ir: &TypeIR) -> Result<Self, Self::Error> {
if let syn::Item::Enum(e) = ir.self_code() {
let variants = utils::syn::extract_unit_variants(e)?;
Ok(Self {
ty_ident: ir.name()?,
errors: variants
})
} else {
Err(syn::Error::new_spanned(ir.self_code(), "An enum expected."))
}
}
}
fn enum_variants(variants: &[syn::Variant]) -> proc_macro2::TokenStream {
utils::syn::transform_variants(variants, |name, _, discriminant, docs| {
let description = docs.first().cloned().unwrap_or_default().trim().to_string();
quote::quote!(odra::schema::error(#name, #description, #discriminant),)
})
}
pub struct FactorySchemaErrorsItem {
module_ident: syn::Ident,
}
impl TryFrom<&ModuleStructIR> for FactorySchemaErrorsItem {
type Error = syn::Error;
fn try_from(ir: &ModuleStructIR) -> Result<Self, Self::Error> {
Ok(Self {
module_ident: ir.module_ident(),
})
}
}
impl ToTokens for FactorySchemaErrorsItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let module_ident = &self.module_ident;
let item = quote::quote! {
#[automatically_derived]
#[cfg(not(target_arch = "wasm32"))]
impl odra::schema::SchemaErrors for #module_ident {
fn schema_errors() -> odra::prelude::Vec<odra::schema::casper_contract_schema::UserError> {
odra::prelude::vec![]
}
}
};
item.to_tokens(tokens);
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_utils;
#[test]
fn custom_types_works() {
let ir = test_utils::mock::module_definition();
let item = SchemaErrorsItem::try_from(&ir).unwrap();
let expected = quote::quote!(
#[automatically_derived]
#[cfg(not(target_arch = "wasm32"))]
impl odra::schema::SchemaErrors for CounterPack {
fn schema_errors(
) -> odra::prelude::Vec<odra::schema::casper_contract_schema::UserError>
{
odra::prelude::BTreeSet::<odra::schema::casper_contract_schema::UserError>::new()
.into_iter()
.chain(<Erc20Errors as odra::schema::SchemaErrors>::schema_errors())
.chain(<SubModule<Counter> as odra::schema::SchemaErrors>::schema_errors())
.chain(<SubModule<Counter> as odra::schema::SchemaErrors>::schema_errors())
.chain(<SubModule<Counter> as odra::schema::SchemaErrors>::schema_errors())
.chain(<Var<u32> as odra::schema::SchemaErrors>::schema_errors())
.chain(<Mapping<u8, Counter> as odra::schema::SchemaErrors>::schema_errors())
.collect::<odra::prelude::BTreeSet<odra::schema::casper_contract_schema::UserError>>()
.into_iter()
.collect::<odra::prelude::Vec<_>>()
}
}
);
test_utils::assert_eq(item, expected);
}
#[test]
fn test_odra_error_item() {
let ty = test_utils::mock::custom_enum();
let item = SchemaErrorItem::try_from(&ty).unwrap();
let expected = quote::quote! {
#[automatically_derived]
#[cfg(not(target_arch = "wasm32"))]
impl odra::schema::SchemaErrors for MyType {
fn schema_errors() -> odra::prelude::Vec<odra::schema::casper_contract_schema::UserError> {
odra::prelude::vec![
odra::schema::error("A", "Description of A", 10u16),
odra::schema::error("B", "Description of B", 11u16),
]
}
}
};
test_utils::assert_eq(item, expected);
}
}