Skip to content

Commit

Permalink
Merge pull request #25 from CosmWasm/reorg-tests
Browse files Browse the repository at this point in the history
Pull out mocks into a separate crate
  • Loading branch information
uint committed Apr 15, 2024
2 parents 38ed04e + b01b3f4 commit 662f149
Show file tree
Hide file tree
Showing 19 changed files with 248 additions and 184 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ keywords = ["CosmWasm"]

[workspace.dependencies]
storey = { path = "crates/storey", version = "0.1" }
storey-encoding = { path = "crates/storey-encoding", version = "0.1" }
storey-storage = { path = "crates/storey-storage", version = "0.1" }
15 changes: 15 additions & 0 deletions crates/mocks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "mocks"
version = "0.1.0"
edition = "2021"
publish = false
authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
categories.workspace = true
keywords.workspace = true

[dependencies]
storey-encoding.workspace = true
storey-storage.workspace = true
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{cell::UnsafeCell, collections::BTreeMap};

use storey::storage::IterableStorage as _;
use storey_storage::IterableStorage as _;

// `UnsafeCell` is needed here to implement interior mutability.
// https://doc.rust-lang.org/book/ch15-05-interior-mutability.html
Expand All @@ -18,21 +18,27 @@ impl TestStorage {
}
}

impl Default for TestStorage {
fn default() -> Self {
Self::new()
}
}

// Safety: in each of the unsafe blocks in this file, we drop the reference to
// the BTreeMap before the function returns, so we can guarantee that no two references
// to it exist at the same time.
//
// Moreover, we can further guarantee that the dereference is valid because the data
// is always initialized during construction.

impl storey::storage::StorageBackend for TestStorage {
impl storey_storage::StorageBackend for TestStorage {
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
// Safety: see above
unsafe { (*self.0.get()).get(key).cloned() }
}
}

impl storey::storage::StorageBackendMut for TestStorage {
impl storey_storage::StorageBackendMut for TestStorage {
fn set(&mut self, key: &[u8], value: &[u8]) {
// Safety: see above
unsafe {
Expand All @@ -48,7 +54,7 @@ impl storey::storage::StorageBackendMut for TestStorage {
}
}

impl storey::storage::IterableStorage for TestStorage {
impl storey_storage::IterableStorage for TestStorage {
type KeysIterator<'a> = Box<dyn DoubleEndedIterator<Item = Vec<u8>> + 'a>;
type ValuesIterator<'a> = Box<dyn DoubleEndedIterator<Item = Vec<u8>> + 'a>;
type PairsIterator<'a> = Box<dyn DoubleEndedIterator<Item = (Vec<u8>, Vec<u8>)> + 'a>;
Expand Down Expand Up @@ -92,7 +98,7 @@ impl storey::storage::IterableStorage for TestStorage {
}
}

impl storey::storage::RevIterableStorage for TestStorage {
impl storey_storage::RevIterableStorage for TestStorage {
type RevKeysIterator<'a> = Box<dyn Iterator<Item = Vec<u8>> + 'a>;
type RevValuesIterator<'a> = Box<dyn Iterator<Item = Vec<u8>> + 'a>;
type RevPairsIterator<'a> = Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use storey::encoding::{Cover, DecodableWithImpl, EncodableWithImpl, Encoding};
use storey_encoding::{Cover, DecodableWithImpl, EncodableWithImpl, Encoding};

// An implementation of an encoding used for tests.
//
Expand Down
2 changes: 0 additions & 2 deletions crates/storey/tests/common/mod.rs → crates/mocks/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
#![allow(dead_code)]

pub mod backend;
pub mod encoding;
13 changes: 13 additions & 0 deletions crates/storey-encoding/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "storey-encoding"
description = "Interfaces for storey encodings"
version = "0.1.0"
edition = "2021"
authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
categories.workspace = true
keywords.workspace = true

[dependencies]
59 changes: 59 additions & 0 deletions crates/storey-encoding/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
pub trait Encoding {
type EncodeError;
type DecodeError;
}

pub trait EncodableWith<E: Encoding>: sealed::SealedE<E> {
fn encode(&self) -> Result<Vec<u8>, E::EncodeError>;
}

pub trait EncodableWithImpl<E: Encoding> {
fn encode_impl(self) -> Result<Vec<u8>, E::EncodeError>;
}

impl<E: Encoding, T> EncodableWith<E> for T
where
for<'a> Cover<&'a T>: EncodableWithImpl<E>,
{
fn encode(&self) -> Result<Vec<u8>, <E as Encoding>::EncodeError> {
Cover(self).encode_impl()
}
}

pub trait DecodableWith<E: Encoding>: Sized + sealed::SealedD<E> {
fn decode(data: &[u8]) -> Result<Self, E::DecodeError>;
}

pub trait DecodableWithImpl<E: Encoding>: Sized {
fn decode_impl(data: &[u8]) -> Result<Self, E::DecodeError>;
}

impl<E: Encoding, T> DecodableWith<E> for T
where
Cover<T>: DecodableWithImpl<E>,
{
fn decode(data: &[u8]) -> Result<Self, <E as Encoding>::DecodeError> {
let wrapper = <Cover<Self>>::decode_impl(data)?;
Ok(wrapper.0)
}
}

mod sealed {
// This module is private to the crate. It's used to seal the `EncodableWith` and
// `DecodableWith` traits, so that the only way they can be implemented outside
// this crate is through the blanket implementations provided by `EncodableWithImpl`
// and `DecodableWithImpl`.
//
// More information on sealed traits:
// https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed

use super::*;

pub trait SealedE<E> {}
pub trait SealedD<E> {}

impl<E: Encoding, T> SealedE<E> for T where for<'a> Cover<&'a T>: EncodableWithImpl<E> {}
impl<E: Encoding, T> SealedD<E> for T where Cover<T>: DecodableWithImpl<E> {}
}

pub struct Cover<T>(pub T);
13 changes: 13 additions & 0 deletions crates/storey-storage/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "storey-storage"
description = "Interfaces for storey storage backends"
version = "0.1.0"
edition = "2021"
authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
categories.workspace = true
keywords.workspace = true

[dependencies]
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Storage, StorageMut};
use super::storage::{Storage, StorageMut};

pub trait StorageBackend {
fn get(&self, key: &[u8]) -> Option<Vec<u8>>;
Expand Down
5 changes: 5 additions & 0 deletions crates/storey-storage/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod backend;
mod storage;

pub use backend::{StorageBackend, StorageBackendMut};
pub use storage::{IterableStorage, RevIterableStorage, Storage, StorageMut};
100 changes: 100 additions & 0 deletions crates/storey-storage/src/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
pub trait Storage {
fn get(&self, key: &[u8]) -> Option<Vec<u8>>;

fn has(&self, key: &[u8]) -> bool {
self.get(key).is_some()
}

fn get_meta(&self, _key: &[u8]) -> Option<Vec<u8>>;
fn has_meta(&self, key: &[u8]) -> bool {
self.get_meta(key).is_some()
}
}

pub trait StorageMut {
fn set(&mut self, key: &[u8], value: &[u8]);
fn remove(&mut self, key: &[u8]);

fn set_meta(&mut self, _key: &[u8], _value: &[u8]);
fn remove_meta(&mut self, _key: &[u8]);
}

pub trait IterableStorage {
type KeysIterator<'a>: Iterator<Item = Vec<u8>>
where
Self: 'a;
type ValuesIterator<'a>: Iterator<Item = Vec<u8>>
where
Self: 'a;
type PairsIterator<'a>: Iterator<Item = (Vec<u8>, Vec<u8>)>
where
Self: 'a;

fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a>;
fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a>;
fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a>;
}

impl<T: IterableStorage> IterableStorage for &T {
type KeysIterator<'a> = T::KeysIterator<'a> where Self: 'a;
type ValuesIterator<'a> = T::ValuesIterator<'a> where Self: 'a;
type PairsIterator<'a> = T::PairsIterator<'a> where Self: 'a;

fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> {
(**self).keys(start, end)
}

fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> {
(**self).values(start, end)
}

fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> {
(**self).pairs(start, end)
}
}

impl<T: IterableStorage> IterableStorage for &mut T {
type KeysIterator<'a> = T::KeysIterator<'a> where Self: 'a;
type ValuesIterator<'a> = T::ValuesIterator<'a> where Self: 'a;
type PairsIterator<'a> = T::PairsIterator<'a> where Self: 'a;

fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> {
(**self).keys(start, end)
}

fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> {
(**self).values(start, end)
}

fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> {
(**self).pairs(start, end)
}
}

pub trait RevIterableStorage {
type RevKeysIterator<'a>: Iterator<Item = Vec<u8>>
where
Self: 'a;
type RevValuesIterator<'a>: Iterator<Item = Vec<u8>>
where
Self: 'a;
type RevPairsIterator<'a>: Iterator<Item = (Vec<u8>, Vec<u8>)>
where
Self: 'a;

fn rev_keys<'a>(
&'a self,
start: Option<&[u8]>,
end: Option<&[u8]>,
) -> Self::RevKeysIterator<'a>;
fn rev_values<'a>(
&'a self,
start: Option<&[u8]>,
end: Option<&[u8]>,
) -> Self::RevValuesIterator<'a>;
fn rev_pairs<'a>(
&'a self,
start: Option<&[u8]>,
end: Option<&[u8]>,
) -> Self::RevPairsIterator<'a>;
}
18 changes: 12 additions & 6 deletions crates/storey/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@
name = "storey"
description = "Storage abstractions for blockchains"
readme = "../../README.md"
authors = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
categories = { workspace = true }
keywords = { workspace = true }
version = "0.1.0"
edition = "2021"
rust-version = "1.65" # https://caniuse.rs/features/generic_associated_types
license = { workspace = true }
authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
categories.workspace = true
keywords.workspace = true

[dependencies]
thiserror = "1"

storey-encoding.workspace = true
storey-storage.workspace = true

[dev-dependencies]
mocks = { path = "../mocks" }
1 change: 1 addition & 0 deletions crates/storey/src/containers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod column;
mod item;
mod map;
mod test_mocks;

use std::marker::PhantomData;

Expand Down
1 change: 1 addition & 0 deletions crates/storey/src/containers/test_mocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading

0 comments on commit 662f149

Please sign in to comment.