use crate::{
ir,
ir::utils,
};
use proc_macro2::Ident;
use syn::spanned::Spanned as _;
#[derive(Debug, PartialEq, Eq)]
pub struct Storage {
ast: syn::ItemStruct,
}
impl quote::ToTokens for Storage {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.ast.to_tokens(tokens)
}
}
impl Storage {
pub(super) fn is_ink_storage(
item_struct: &syn::ItemStruct,
) -> Result<bool, syn::Error> {
if !ir::contains_ink_attributes(&item_struct.attrs) {
return Ok(false)
}
let attr = ir::first_ink_attribute(&item_struct.attrs)?
.expect("missing expected ink! attribute for struct");
Ok(matches!(attr.first().kind(), ir::AttributeArg::Storage))
}
}
impl TryFrom<syn::ItemStruct> for Storage {
type Error = syn::Error;
fn try_from(item_struct: syn::ItemStruct) -> Result<Self, Self::Error> {
let struct_span = item_struct.span();
let (_ink_attrs, other_attrs) = ir::sanitize_attributes(
struct_span,
item_struct.attrs,
&ir::AttributeArgKind::Storage,
|arg| {
match arg.kind() {
ir::AttributeArg::Storage => Ok(()),
_ => Err(None),
}
},
)?;
utils::ensure_pub_visibility("storage structs", struct_span, &item_struct.vis)?;
Ok(Self {
ast: syn::ItemStruct {
attrs: other_attrs,
..item_struct
},
})
}
}
impl Storage {
pub fn attrs(&self) -> &[syn::Attribute] {
&self.ast.attrs
}
pub fn ident(&self) -> &Ident {
&self.ast.ident
}
pub fn generics(&self) -> &syn::Generics {
&self.ast.generics
}
pub fn fields(&self) -> syn::punctuated::Iter<syn::Field> {
self.ast.fields.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple_try_from_works() {
let item_struct: syn::ItemStruct = syn::parse_quote! {
#[ink(storage)]
pub struct MyStorage {
field_1: i32,
field_2: bool,
}
};
assert!(Storage::try_from(item_struct).is_ok())
}
fn assert_try_from_fails(item_struct: syn::ItemStruct, expected: &str) {
assert_eq!(
Storage::try_from(item_struct).map_err(|err| err.to_string()),
Err(expected.to_string())
)
}
#[test]
fn conflicting_attributes_fails() {
assert_try_from_fails(
syn::parse_quote! {
#[ink(storage)]
#[ink(event)]
pub struct MyStorage {
field_1: i32,
field_2: bool,
}
},
"encountered conflicting ink! attribute argument",
)
}
#[test]
fn duplicate_attributes_fails() {
assert_try_from_fails(
syn::parse_quote! {
#[ink(storage)]
#[ink(storage)]
pub struct MyStorage {
field_1: i32,
field_2: bool,
}
},
"encountered duplicate ink! attribute",
)
}
#[test]
fn wrong_first_attribute_fails() {
assert_try_from_fails(
syn::parse_quote! {
#[ink(event)]
#[ink(storage)]
pub struct MyStorage {
field_1: i32,
field_2: bool,
}
},
"unexpected first ink! attribute argument",
)
}
#[test]
fn missing_storage_attribute_fails() {
assert_try_from_fails(
syn::parse_quote! {
pub struct MyStorage {
field_1: i32,
field_2: bool,
}
},
"encountered unexpected empty expanded ink! attribute arguments",
)
}
#[test]
fn non_pub_storage_struct() {
assert_try_from_fails(
syn::parse_quote! {
#[ink(storage)]
struct PrivateStorage {
field_1: i32,
field_2: bool,
}
},
"non `pub` ink! storage structs are not supported",
)
}
}