Unverified Commit e8094452 authored by Andrew Jones's avatar Andrew Jones Committed by GitHub
Browse files

Move storage derives to `ink` crate (#1400)



* Move storage derives to ink_macro crate

* Reexport storage traits and derives from ink crate

* Fix macro storage tests

* Fmt

* Remove storage/traits/codegen crate

* Fix ui test

* Attempt to fix UI tests

* Attempt to fix docs job

* Try to fox codecov job

* Update traits crate description

* Attempt to fix docs job

* Update crates/ink/ir/src/ir/utils.rs
Co-authored-by: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* Add comment
Co-authored-by: Michael Müller's avatarMichael Müller <mich@elmueller.net>
parent 74b2248f
Pipeline #216143 passed with stages
in 22 minutes and 18 seconds
......@@ -29,8 +29,8 @@ variables:
# CI_IMAGE is changed to "-:staging" when the CI image gets rebuilt
# read more https://github.com/paritytech/scripts/pull/244
CI_IMAGE: "paritytech/ink-ci-linux:production"
PURELY_STD_CRATES: "ink/codegen storage/traits/codegen metadata engine"
ALSO_WASM_CRATES: "env storage storage/traits storage/traits/derive allocator prelude primitives ink ink/macro ink/ir"
PURELY_STD_CRATES: "ink/codegen metadata engine"
ALSO_WASM_CRATES: "env storage storage/traits allocator prelude primitives ink ink/macro ink/ir"
ALL_CRATES: "${PURELY_STD_CRATES} ${ALSO_WASM_CRATES}"
DELEGATOR_SUBCONTRACTS: "accumulator adder subber"
UPGRADEABLE_CONTRACTS: "forward-calls set-code-hash"
......@@ -282,7 +282,7 @@ docs:
script:
- cargo doc --no-deps --all-features
-p scale-info -p ink_metadata -p ink_env
-p ink_storage -p ink_storage_traits -p ink_storage_codegen -p ink_storage_derive
-p ink_storage -p ink_storage_traits
-p ink_primitives -p ink_prelude
-p ink -p ink_macro -p ink_ir -p ink_codegen
- mv ${CARGO_TARGET_DIR}/doc ./crate-docs
......@@ -290,6 +290,53 @@ docs:
- chown -R nonroot:nonroot ./crate-docs
codecov:
stage: workspace
<<: *docker-env
<<: *test-refs
needs:
- job: check-std
artifacts: false
variables:
# For codecov it's sufficient to run the fuzz tests only once.
QUICKCHECK_TESTS: 1
INK_COVERAGE_REPORTING: "true"
CARGO_INCREMENTAL: 0
# Needed because `codecov` requires nightly features to work
# (see `-Z` in the `RUSTFLAGS` below).
RUSTC_BOOTSTRAP: "1"
# Variables partly came from https://github.com/mozilla/grcov/blob/master/README.md
RUSTFLAGS: "-Zprofile -Zmir-opt-level=0 -Ccodegen-units=1
-Clink-dead-code -Copt-level=0 -Coverflow-checks=off"
# The `cargo-taurpalin` coverage reporting tool seems to have better code instrumentation and thus
# produces better results for Rust codebases in general. However, unlike `grcov` it requires
# running docker with `--security-opt seccomp=unconfined` which is why we use `grcov` instead.
before_script:
- *rust-info-script
# RUSTFLAGS are the cause target cache can't be used here
# FIXME: cust-covfix doesn't support the external target dir
# https://github.com/Kogia-sima/rust-covfix/issues/7
- unset "CARGO_TARGET_DIR"
- cargo clean
# make sure there's no stale coverage artifacts
- find . -name "*.profraw" -type f -delete
- find . -name "*.gcda" -type f -delete
script:
# RUSTFLAGS are the cause target cache can't be used here
- cargo build --verbose --all-features --workspace
- cargo test --verbose --all-features --no-fail-fast --workspace
# coverage with branches
- grcov . --binary-path ./target/debug/ --source-dir . --output-type lcov --llvm --branch
--ignore-not-existing --ignore "/*" --ignore "tests/*" --output-path lcov-w-branch.info
- rust-covfix lcov-w-branch.info --output lcov-w-branch-fixed.info
- codecov --token "$CODECOV_P_TOKEN" --file lcov-w-branch-fixed.info --nonZero
# lines coverage
- grcov . --binary-path ./target/debug/ --source-dir . --output-type lcov --llvm
--ignore-not-existing --ignore "/*" --ignore "tests/*" --output-path lcov-lines.info
- rust-covfix lcov-lines.info --output lcov-lines-fixed.info
- codecov --token "$CODECOV_TOKEN" --file lcov-lines-fixed.info --nonZero
#### stage: examples
examples-test:
......
......@@ -15,7 +15,6 @@ members = [
"crates/env",
"crates/storage",
"crates/storage/traits",
"crates/storage/traits/derive",
]
exclude = [
"examples/",
......
......@@ -18,7 +18,6 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
name = "ink_ir"
[dependencies]
ink_storage_codegen = { version = "4.0.0-alpha.1", path = "../../storage/traits/codegen" }
quote = "1"
syn = { version = "1.0", features = ["parsing", "full", "visit", "extra-traits"] }
proc-macro2 = "1.0"
......
......@@ -14,13 +14,14 @@
mod config;
use crate::utils::find_storage_key_salt;
use config::StorageItemConfig;
use ink_storage_codegen::DeriveUtils;
use proc_macro2::TokenStream as TokenStream2;
use quote::{
quote,
ToTokens,
};
use std::collections::HashSet;
/// A checked ink! storage item with its configuration.
pub struct StorageItem {
......@@ -59,7 +60,36 @@ impl StorageItem {
/// Returns all types that were used in the storage declaration.
pub fn all_used_types(&self) -> Vec<syn::Type> {
self.ast.all_types()
let res: Vec<_> = match self.data().clone() {
syn::Data::Struct(st) => {
st.fields.iter().map(|field| field.ty.clone()).collect()
}
syn::Data::Enum(en) => {
en.variants
.iter()
.flat_map(|variant| variant.fields.iter())
.map(|field| field.ty.clone())
.collect()
}
syn::Data::Union(un) => {
un.fields
.named
.iter()
.map(|field| field.ty.clone())
.collect()
}
};
let mut set = HashSet::new();
res.into_iter()
.filter(|ty| {
if !set.contains(ty) {
set.insert(ty.clone());
true
} else {
false
}
})
.collect()
}
/// Returns the config of the storage.
......@@ -94,7 +124,7 @@ impl StorageItem {
/// Returns salt for storage key.
pub fn salt(&self) -> TokenStream2 {
if let Some(param) = self.ast.find_salt() {
if let Some(param) = find_storage_key_salt(&self.ast) {
param.ident.to_token_stream()
} else {
quote! { () }
......
......@@ -144,3 +144,24 @@ where
name
))
}
/// Finds the salt of a struct, enum or union.
/// The salt is any generic that has bound `StorageKey`.
/// In most cases it is the parent storage key or the auto-generated storage key.
pub fn find_storage_key_salt(input: &syn::DeriveInput) -> Option<syn::TypeParam> {
input.generics.params.iter().find_map(|param| {
if let syn::GenericParam::Type(type_param) = param {
if let Some(syn::TypeParamBound::Trait(trait_bound)) =
type_param.bounds.first()
{
let segments = &trait_bound.path.segments;
if let Some(last) = segments.last() {
if last.ident == "StorageKey" {
return Some(type_param.clone())
}
}
}
}
None
})
}
......@@ -21,7 +21,9 @@ ink_primitives = { version = "4.0.0-alpha.1", path = "../../primitives/", defaul
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
syn = "1"
synstructure = "0.12.6"
proc-macro2 = "1"
quote = "1"
[dev-dependencies]
ink_env = { path = "../../env" }
......
......@@ -19,6 +19,7 @@ mod chain_extension;
mod contract;
mod ink_test;
mod selector;
mod storage;
mod storage_item;
mod trait_def;
......@@ -213,7 +214,7 @@ pub fn selector_bytes(input: TokenStream) -> TokenStream {
/// The user is able to use a variety of built-in facilities, combine them in various ways
/// or even provide their own implementations of storage data structures.
///
/// For more information visit the `ink_storage` crate documentation.
/// For more information visit the `ink::storage` crate documentation.
///
/// **Example:**
///
......@@ -674,22 +675,22 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
///
/// ```
/// use ink_prelude::vec::Vec;
/// use ink_storage::{
/// use ink::storage::{
/// Lazy,
/// Mapping,
/// };
/// use ink_storage::traits::{
/// use ink::storage::traits::{
/// StorageKey,
/// StorableHint,
/// };
/// use ink_storage::traits::Storable;
/// use ink::storage::traits::Storable;
///
/// // Deriving `scale::Decode` and `scale::Encode` also derives blanket implementation of all
/// // required traits to be storable.
/// #[derive(scale::Decode, scale::Encode)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct Packed {
/// s1: u128,
......@@ -702,9 +703,9 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// #[derive(scale::Decode, scale::Encode)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct PackedGeneric<T: ink_storage::traits::Packed> {
/// struct PackedGeneric<T: ink::storage::traits::Packed> {
/// s1: (u128, bool),
/// s2: Vec<T>,
/// s3: String,
......@@ -722,9 +723,9 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// #[derive(Storable, StorableHint, StorageKey)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct NonPackedGeneric<T: ink_storage::traits::Packed> {
/// struct NonPackedGeneric<T: ink::storage::traits::Packed> {
/// s1: u32,
/// s2: T,
/// s3: Mapping<u128, T>,
......@@ -734,7 +735,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// #[derive(scale::Decode, scale::Encode)]
/// #[cfg_attr(
/// feature = "std",
/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout)
/// derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
/// )]
/// struct PackedComplex {
/// s1: u128,
......@@ -752,7 +753,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
/// s5: Lazy<NonPacked>,
/// s6: PackedGeneric<Packed>,
/// s7: NonPackedGeneric<Packed>,
/// // Fails because: the trait `ink_storage::traits::Packed` is not implemented for `NonPacked`
/// // Fails because: the trait `ink::storage::traits::Packed` is not implemented for `NonPacked`
/// // s8: Mapping<u128, NonPacked>,
/// }
/// ```
......@@ -769,16 +770,16 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream {
///
/// **Usage Example:**
/// ```
/// use ink_storage::Mapping;
/// use ink_storage::traits::{
/// use ink::storage::Mapping;
/// use ink::storage::traits::{
/// StorableHint,
/// StorageKey,
/// };
/// use ink_storage::traits::Storable;
/// use ink::storage::traits::Storable;
///
/// #[ink::storage_item(derive = false)]
/// #[derive(StorableHint, Storable, StorageKey)]
/// struct NonPackedGeneric<T: ink_storage::traits::Packed> {
/// struct NonPackedGeneric<T: ink::storage::traits::Packed> {
/// s1: u32,
/// s2: Mapping<u128, T>,
/// }
......@@ -1261,5 +1262,118 @@ pub fn chain_extension(attr: TokenStream, item: TokenStream) -> TokenStream {
chain_extension::generate(attr.into(), item.into()).into()
}
synstructure::decl_derive!(
[Storable] =>
/// Derives `ink::storage`'s `Storable` trait for the given `struct`, `enum` or `union`.
///
/// # Examples
///
/// ```
/// use ink::storage::traits::Storable;
///
/// #[derive(Storable)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 1],
/// }
///
/// let value = <NamedFields as Storable>::decode(&mut &[123, 123][..]);
/// ```
storage::storable_derive
);
synstructure::decl_derive!(
[StorableHint] =>
/// Derives `ink::storage`'s `StorableHint` trait for the given `struct` or `enum`.
///
/// If the type declaration contains generic `StorageKey`,
/// it will use it as salt to generate a combined storage key.
///
/// # Examples
///
/// ```
/// use ink::storage::traits::{
/// Storable,
/// StorableHint,
/// StorageKey,
/// AutoStorableHint,
/// AutoKey,
/// ManualKey,
/// };
///
/// #[derive(Default, StorableHint, Storable)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 32],
/// }
///
/// let _: NamedFields = <NamedFields as StorableHint<AutoKey>>::Type::default();
/// let _: NamedFields = <NamedFields as StorableHint<ManualKey<123>>>::Type::default();
/// ```
storage::storable_hint_derive
);
synstructure::decl_derive!(
[StorageKey] =>
/// Derives `ink::storage`'s `StorageKey` trait for the given `struct` or `enum`.
///
/// # Examples
///
/// ```
/// use ink::storage::traits::{
/// AutoStorableHint,
/// StorageKey,
/// ManualKey,
/// AutoKey,
/// };
///
/// #[derive(StorageKey)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 32],
/// }
///
/// assert_eq!(<NamedFields as StorageKey>::KEY, 0);
///
/// #[derive(StorageKey)]
/// struct NamedFieldsManualKey<KEY: StorageKey> {
/// a: <u32 as AutoStorableHint<ManualKey<0, KEY>>>::Type,
/// b: <[u32; 32] as AutoStorableHint<ManualKey<1, KEY>>>::Type,
/// }
///
/// assert_eq!(<NamedFieldsManualKey<()> as StorageKey>::KEY, 0);
/// assert_eq!(<NamedFieldsManualKey<AutoKey> as StorageKey>::KEY, 0);
/// assert_eq!(<NamedFieldsManualKey<ManualKey<123>> as StorageKey>::KEY, 123);
/// ```
storage::storage_key_derive
);
synstructure::decl_derive!(
[StorageLayout] =>
/// Derives `ink::storage`'s `StorageLayout` trait for the given `struct` or `enum`.
///
/// # Examples
///
/// ```
/// use ink_metadata::layout::Layout::Struct;
/// use ink::storage::traits::StorageLayout;
///
/// #[derive(StorageLayout)]
/// struct NamedFields {
/// a: u32,
/// b: [u32; 32],
/// }
///
/// let key = 0x123;
/// let mut value = NamedFields {
/// a: 123,
/// b: [22; 32],
/// };
///
/// if let Struct(layout) = <NamedFields as StorageLayout>::layout(&key) {
/// assert_eq!(*layout.fields()[0].name(), "a");
/// assert_eq!(*layout.fields()[1].name(), "b");
/// }
/// ```
storage::storage_layout_derive
);
#[cfg(test)]
pub use contract::generate_or_err;
// Copyright 2018-2022 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Custom derive for `ink_storage` traits.
//!
//! This crate provides helpers to define your very own custom storage data
//! structures that work along the `ink_storage` data structures.
mod storable;
mod storable_hint;
mod storage_key;
mod storage_layout;
pub use self::{
storable::storable_derive,
storable_hint::storable_hint_derive,
storage_key::storage_key_derive,
storage_layout::storage_layout_derive,
};
#[cfg(test)]
mod tests;
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use ink_storage_codegen::DeriveUtils;
use ink_ir::utils::find_storage_key_salt;
use proc_macro2::TokenStream as TokenStream2;
use quote::{
format_ident,
......@@ -36,7 +36,7 @@ fn storable_hint_inner(s: synstructure::Structure) -> TokenStream2 {
let (impl_generics, _, where_clause) = generics.split_for_impl();
let (_, ty_generics_original, _) = s.ast().generics.split_for_impl();
if let Some(inner_salt_ident) = s.ast().find_salt() {
if let Some(inner_salt_ident) = find_storage_key_salt(s.ast()) {
let inner_salt_ident = inner_salt_ident.ident.to_token_stream();
let ty_generics: Vec<_> = s
.ast()
......
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use ink_storage_codegen::DeriveUtils;
use ink_ir::utils::find_storage_key_salt;
use proc_macro2::TokenStream as TokenStream2;
use quote::{
quote,
......@@ -23,7 +23,7 @@ pub fn storage_key_derive(mut s: synstructure::Structure) -> TokenStream2 {
s.add_bounds(synstructure::AddBounds::None)
.underscore_const(true);
let salt = if let Some(param) = s.ast().find_salt() {
let salt = if let Some(param) = find_storage_key_salt(s.ast()) {
param.ident.to_token_stream()
} else {
quote! { () }
......
......@@ -17,6 +17,13 @@ mod storable_hint;
mod storage_key;
mod storage_layout;
use crate::storage::{
storable::storable_derive,
storable_hint::storable_hint_derive,
storage_key::storage_key_derive,
storage_layout::storage_layout_derive,
};
#[macro_export]
macro_rules! test_derive {
($name:path { $($i:tt)* } expands to { $($o:tt)* }) => {
......
......@@ -20,7 +20,7 @@
#![allow(clippy::eq_op)]
#![allow(clippy::match_single_binding)]
use crate::storable_derive;
use super::storable_derive;
#[test]
fn unit_struct_works() {
......
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::storable_hint_derive;
use super::storable_hint_derive;
#[test]
fn unit_struct_works() {
......
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::storage_key_derive;
use super::storage_key_derive;
#[test]
fn unit_struct_works() {
......
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::storage_layout_derive;
use super::storage_layout_derive;
#[test]
fn unit_struct_works() {
......
......@@ -32,7 +32,22 @@ pub use ink_env as env;
pub use ink_metadata as metadata;
pub use ink_prelude as prelude;
pub use ink_primitives as primitives;
pub use ink_storage as storage;
pub mod storage {
pub mod traits {
pub use ink_macro::{
Storable,
StorableHint,
StorageKey,
StorageLayout,
};
pub use ink_storage::traits::*;
}
pub use ink_storage::{
Lazy,
Mapping,
};
}
pub use self::{
chain_extension::{
......
#[ink::contract]
mod contract {
use ink_storage::traits::StorageLayout;
use ink::storage::traits::StorageLayout;
#[ink(storage)]
pub struct Contract {
......
use ink_storage::traits::Storable;
use ink_storage::traits::{
use ink::storage::traits::Storable;
use ink::storage::traits::{
ManualKey,
StorageKey,
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment