mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 10:31:04 +00:00
Second pass over Address type and start impl in Subxt
This commit is contained in:
@@ -27,6 +27,8 @@ std = [
|
||||
"tracing/std",
|
||||
"impl-serde/std",
|
||||
"primitive-types/std",
|
||||
"sp-core/std",
|
||||
"sp-keyring/std",
|
||||
"sp-crypto-hashing/std",
|
||||
]
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ impl<T: Config> Extrinsics<T> {
|
||||
// Try to decode the extrinsic.
|
||||
let decoded_info = frame_decode::extrinsics::decode_extrinsic(
|
||||
cursor,
|
||||
metadata.deref(),
|
||||
&metadata,
|
||||
metadata.types(),
|
||||
)
|
||||
.map_err(|error| BlockError::ExtrinsicDecodeError {
|
||||
|
||||
@@ -6,6 +6,19 @@ use crate::error::MetadataError;
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::sync::Arc;
|
||||
use frame_decode::extrinsics::{
|
||||
ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicInfoError,
|
||||
ExtrinsicSignatureInfo,
|
||||
};
|
||||
use frame_decode::storage::{
|
||||
StorageEntry, StorageInfo, StorageInfoError
|
||||
};
|
||||
use frame_decode::runtime_apis::{
|
||||
RuntimeApi, RuntimeApiInfo, RuntimeApiInfoError
|
||||
};
|
||||
use frame_decode::view_functions::{
|
||||
ViewFunction, ViewFunctionInfo, ViewFunctionInfoError
|
||||
};
|
||||
|
||||
/// A cheaply clone-able representation of the runtime metadata received from a node.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -20,6 +33,79 @@ impl core::ops::Deref for Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_decode::storage::StorageTypeInfo for Metadata {
|
||||
type TypeId = u32;
|
||||
|
||||
fn storage_info(
|
||||
&self,
|
||||
pallet_name: &str,
|
||||
storage_entry: &str,
|
||||
) -> Result<StorageInfo<'_, Self::TypeId>, StorageInfoError<'_>> {
|
||||
self.inner.storage_info(pallet_name, storage_entry)
|
||||
}
|
||||
|
||||
fn storage_entries(&self) -> impl Iterator<Item = StorageEntry<'_>> {
|
||||
self.inner.storage_entries()
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_decode::runtime_apis::RuntimeApiTypeInfo for Metadata {
|
||||
type TypeId = u32;
|
||||
|
||||
fn runtime_api_info(
|
||||
&self,
|
||||
trait_name: &str,
|
||||
method_name: &str,
|
||||
) -> Result<RuntimeApiInfo<'_, Self::TypeId>, RuntimeApiInfoError<'_>> {
|
||||
self.inner.runtime_api_info(trait_name, method_name)
|
||||
}
|
||||
|
||||
fn runtime_apis(&self) -> impl Iterator<Item = RuntimeApi<'_>> {
|
||||
self.inner.runtime_apis()
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata {
|
||||
type TypeId = u32;
|
||||
|
||||
fn extrinsic_call_info(
|
||||
&self,
|
||||
pallet_index: u8,
|
||||
call_index: u8,
|
||||
) -> Result<ExtrinsicCallInfo<'_, Self::TypeId>, ExtrinsicInfoError<'_>> {
|
||||
self.inner.extrinsic_call_info(pallet_index, call_index)
|
||||
}
|
||||
|
||||
fn extrinsic_signature_info(
|
||||
&self,
|
||||
) -> Result<ExtrinsicSignatureInfo<Self::TypeId>, ExtrinsicInfoError<'_>> {
|
||||
self.inner.extrinsic_signature_info()
|
||||
}
|
||||
|
||||
fn extrinsic_extension_info(
|
||||
&self,
|
||||
extension_version: Option<u8>,
|
||||
) -> Result<ExtrinsicExtensionInfo<'_, Self::TypeId>, ExtrinsicInfoError<'_>> {
|
||||
self.inner.extrinsic_extension_info(extension_version)
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_decode::view_functions::ViewFunctionTypeInfo for Metadata {
|
||||
type TypeId = u32;
|
||||
|
||||
fn view_function_info(
|
||||
&self,
|
||||
pallet_name: &str,
|
||||
function_name: &str,
|
||||
) -> Result<ViewFunctionInfo<'_, Self::TypeId>, ViewFunctionInfoError<'_>> {
|
||||
self.inner.view_function_info(pallet_name, function_name)
|
||||
}
|
||||
|
||||
fn view_functions(&self) -> impl Iterator<Item = ViewFunction<'_>> {
|
||||
self.inner.view_functions()
|
||||
}
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// Identical to `metadata.pallet_by_name()`, but returns an error if the pallet is not found.
|
||||
pub fn pallet_by_name_err(
|
||||
|
||||
+38
-166
@@ -5,17 +5,28 @@
|
||||
//! Construct addresses to access storage entries with.
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::vec::Vec;
|
||||
use frame_decode::storage::{IntoDecodableValues, IntoEncodableValues};
|
||||
use frame_decode::storage::IntoEncodableValues;
|
||||
use scale_decode::DecodeAsType;
|
||||
use crate::utils::Maybe;
|
||||
|
||||
/// A storage address. Concrete addresses are expected to implement either [`FetchableAddress`]
|
||||
/// or [`IterableAddress`], which extends this to define fetchable and iterable storage keys.
|
||||
/// A storage address. This allows access to a given storage entry, which can then
|
||||
/// be iterated over or fetched from by providing the relevant set of keys, or
|
||||
/// otherwise inspected.
|
||||
pub trait Address {
|
||||
/// A set of types we'll hash and append to the prefix to build the storage key.
|
||||
/// All of the keys required to get to an individual value at this address.
|
||||
/// Keys must always impl [`IntoEncodableValues`], and for iteration must
|
||||
/// also impl [`frame_decode::storage::IntoDecodableValues`].
|
||||
type KeyParts: IntoEncodableValues;
|
||||
/// Type of the storage value at this location.
|
||||
type Value: DecodeAsType;
|
||||
/// Does the address have a default value defined for it.
|
||||
/// Set to [`crate::utils::Yes`] to enable APIs which require one,
|
||||
/// or [`crate::utils::Maybe`] to enable APIs which allow one
|
||||
type HasDefaultValue;
|
||||
/// Does the address point to a map (as opposed to a plain value)?
|
||||
/// Set to [`crate::utils::Yes`] to enable APIs which require a map,
|
||||
/// or [`crate::utils::Maybe`] to enable APIs which allow a map.
|
||||
type IsMap;
|
||||
|
||||
/// The pallet containing this storage entry.
|
||||
fn pallet_name(&self) -> &str;
|
||||
@@ -23,69 +34,35 @@ pub trait Address {
|
||||
/// The name of the storage entry.
|
||||
fn entry_name(&self) -> &str;
|
||||
|
||||
/// Return the input key parts needed to point to this storage entry / entries.
|
||||
fn key_parts(&self) -> impl IntoEncodableValues;
|
||||
|
||||
/// Return a unique hash for this address which can be used to validate it against metadata.
|
||||
fn validation_hash(&self) -> Option<[u8; 32]>;
|
||||
}
|
||||
|
||||
/// This trait represents any storage address which points to a single value we can fetch.
|
||||
pub trait FetchableAddress: Address {
|
||||
/// Does the address have a default value defined for it.
|
||||
/// Set to [`Yes`] to enable APIs which require one.
|
||||
type HasDefaultValue;
|
||||
}
|
||||
|
||||
/// This trait represents any storage address which points to multiple 0 or more values to iterate over.
|
||||
pub trait IterableAddress: Address {
|
||||
/// The storage key values that we'll decode for each value
|
||||
type OutputKeys: IntoDecodableValues;
|
||||
}
|
||||
|
||||
/// An address which points to an individual storage value.
|
||||
pub struct StaticFetchableAddress<KeyParts, Value, HasDefaultValue> {
|
||||
/// An address which is generated by the static APIs.
|
||||
pub struct StaticAddress<KeyParts, Value, HasDefaultValue, IsMap> {
|
||||
pallet_name: Cow<'static, str>,
|
||||
entry_name: Cow<'static, str>,
|
||||
key_parts: KeyParts,
|
||||
validation_hash: Option<[u8; 32]>,
|
||||
marker: core::marker::PhantomData<(Value, HasDefaultValue)>,
|
||||
marker: core::marker::PhantomData<(KeyParts, Value, HasDefaultValue, IsMap)>,
|
||||
}
|
||||
|
||||
impl<KeyParts, Value, HasDefaultValue> StaticFetchableAddress<KeyParts, Value, HasDefaultValue> {
|
||||
/// Create a new [`StaticFetchableAddress`] using static strings for the pallet and call name.
|
||||
impl<KeyParts, Value, HasDefaultValue, IsMap> StaticAddress<KeyParts, Value, HasDefaultValue, IsMap> {
|
||||
/// Create a new [`StaticAddress`] using static strings for the pallet and call name.
|
||||
/// This is only expected to be used from codegen.
|
||||
#[doc(hidden)]
|
||||
pub fn new_static(
|
||||
pallet_name: &'static str,
|
||||
entry_name: &'static str,
|
||||
key_parts: KeyParts,
|
||||
hash: [u8; 32],
|
||||
) -> Self {
|
||||
Self {
|
||||
pallet_name: Cow::Borrowed(pallet_name),
|
||||
entry_name: Cow::Borrowed(entry_name),
|
||||
key_parts,
|
||||
validation_hash: Some(hash),
|
||||
marker: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`StaticFetchableAddress`].
|
||||
pub fn new(
|
||||
pallet_name: impl Into<Cow<'static, str>>,
|
||||
entry_name: impl Into<Cow<'static, str>>,
|
||||
key_parts: KeyParts,
|
||||
) -> Self {
|
||||
Self {
|
||||
pallet_name: pallet_name.into(),
|
||||
entry_name: entry_name.into(),
|
||||
key_parts,
|
||||
validation_hash: None,
|
||||
marker: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Do not validate this storage entry prior to accessing it.
|
||||
pub fn unvalidated(mut self) -> Self {
|
||||
self.validation_hash = None;
|
||||
@@ -93,105 +70,16 @@ impl<KeyParts, Value, HasDefaultValue> StaticFetchableAddress<KeyParts, Value, H
|
||||
}
|
||||
}
|
||||
|
||||
impl<KeyParts, Value, HasDefaultValue> Address
|
||||
for StaticFetchableAddress<KeyParts, Value, HasDefaultValue>
|
||||
impl<KeyParts, Value, HasDefaultValue, IsMap> Address
|
||||
for StaticAddress<KeyParts, Value, HasDefaultValue, IsMap>
|
||||
where
|
||||
KeyParts: IntoEncodableValues,
|
||||
Value: DecodeAsType,
|
||||
{
|
||||
type KeyParts = KeyParts;
|
||||
type Value = Value;
|
||||
|
||||
fn key_parts(&self) -> impl IntoEncodableValues {
|
||||
&self.key_parts
|
||||
}
|
||||
|
||||
fn pallet_name(&self) -> &str {
|
||||
&self.pallet_name
|
||||
}
|
||||
|
||||
fn entry_name(&self) -> &str {
|
||||
&self.entry_name
|
||||
}
|
||||
|
||||
fn validation_hash(&self) -> Option<[u8; 32]> {
|
||||
self.validation_hash
|
||||
}
|
||||
}
|
||||
|
||||
impl<KeyParts, Value, HasDefaultValue> FetchableAddress
|
||||
for StaticFetchableAddress<KeyParts, Value, HasDefaultValue>
|
||||
where
|
||||
KeyParts: IntoEncodableValues,
|
||||
Value: DecodeAsType,
|
||||
{
|
||||
type HasDefaultValue = HasDefaultValue;
|
||||
}
|
||||
|
||||
/// An address which points to a set of storage values.
|
||||
pub struct StaticIterableAddress<InputKeyParts, OutputKeyParts, Value> {
|
||||
pallet_name: Cow<'static, str>,
|
||||
entry_name: Cow<'static, str>,
|
||||
input_key_parts: InputKeyParts,
|
||||
validation_hash: Option<[u8; 32]>,
|
||||
marker: core::marker::PhantomData<(OutputKeyParts, Value)>,
|
||||
}
|
||||
|
||||
impl<InputKeyParts, OutputKeyParts, Value>
|
||||
StaticIterableAddress<InputKeyParts, OutputKeyParts, Value>
|
||||
{
|
||||
/// Create a new [`StaticIterableAddress`] using static strings for the pallet and call name.
|
||||
/// This is only expected to be used from codegen.
|
||||
#[doc(hidden)]
|
||||
pub fn new_static(
|
||||
pallet_name: &'static str,
|
||||
entry_name: &'static str,
|
||||
input_key_parts: InputKeyParts,
|
||||
hash: [u8; 32],
|
||||
) -> Self {
|
||||
Self {
|
||||
pallet_name: Cow::Borrowed(pallet_name),
|
||||
entry_name: Cow::Borrowed(entry_name),
|
||||
input_key_parts,
|
||||
validation_hash: Some(hash),
|
||||
marker: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`StaticIterableAddress`].
|
||||
pub fn new(
|
||||
pallet_name: impl Into<Cow<'static, str>>,
|
||||
entry_name: impl Into<Cow<'static, str>>,
|
||||
input_key_parts: InputKeyParts,
|
||||
) -> Self {
|
||||
Self {
|
||||
pallet_name: pallet_name.into(),
|
||||
entry_name: entry_name.into(),
|
||||
input_key_parts,
|
||||
validation_hash: None,
|
||||
marker: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Do not validate this storage entry prior to accessing it.
|
||||
pub fn unvalidated(mut self) -> Self {
|
||||
self.validation_hash = None;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<InputKeyParts, OutputKeyParts, Value> Address
|
||||
for StaticIterableAddress<InputKeyParts, OutputKeyParts, Value>
|
||||
where
|
||||
InputKeyParts: IntoEncodableValues,
|
||||
Value: DecodeAsType,
|
||||
{
|
||||
type KeyParts = InputKeyParts;
|
||||
type Value = Value;
|
||||
|
||||
fn key_parts(&self) -> impl IntoEncodableValues {
|
||||
&self.input_key_parts
|
||||
}
|
||||
type IsMap = IsMap;
|
||||
|
||||
fn pallet_name(&self) -> &str {
|
||||
&self.pallet_name
|
||||
@@ -206,38 +94,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<InputKeyParts, OutputKeyParts, Value> IterableAddress
|
||||
for StaticIterableAddress<InputKeyParts, OutputKeyParts, Value>
|
||||
where
|
||||
InputKeyParts: IntoEncodableValues,
|
||||
OutputKeyParts: IntoDecodableValues,
|
||||
Value: DecodeAsType,
|
||||
{
|
||||
type OutputKeys = OutputKeyParts;
|
||||
}
|
||||
/// A dynamic address is simply a [`StaticAddress`] which asserts that the
|
||||
/// entry could be a map and could have a default value.
|
||||
pub type DynamicAddress<KeyParts, Value> = StaticAddress<KeyParts, Value, Maybe, Maybe>;
|
||||
|
||||
/// Construct a new dynamic storage fetch address.
|
||||
pub fn dynamic_fetch<Keys: IntoEncodableValues>(
|
||||
/// Construct a new dynamic storage address. You can define the type of the
|
||||
/// storage keys and value yourself here, but have no guarantee that they will
|
||||
/// be correct.
|
||||
pub fn dynamic<KeyParts: IntoEncodableValues, Value: DecodeAsType>(
|
||||
pallet_name: impl Into<Cow<'static, str>>,
|
||||
entry_name: impl Into<Cow<'static, str>>,
|
||||
storage_entry_keys: Keys,
|
||||
) -> impl FetchableAddress {
|
||||
StaticFetchableAddress::<Keys, scale_value::Value, ()>::new(
|
||||
pallet_name,
|
||||
entry_name,
|
||||
storage_entry_keys,
|
||||
)
|
||||
) -> DynamicAddress<KeyParts, Value> {
|
||||
DynamicAddress::<KeyParts, Value> {
|
||||
pallet_name: pallet_name.into(),
|
||||
entry_name: entry_name.into(),
|
||||
validation_hash: None,
|
||||
marker: core::marker::PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new dynamic storage iter address.
|
||||
pub fn dynamic_iter<Keys: IntoEncodableValues>(
|
||||
pallet_name: impl Into<Cow<'static, str>>,
|
||||
entry_name: impl Into<Cow<'static, str>>,
|
||||
storage_entry_keys: Keys,
|
||||
) -> impl IterableAddress {
|
||||
StaticIterableAddress::<Keys, Vec<scale_value::Value>, scale_value::Value>::new(
|
||||
pallet_name,
|
||||
entry_name,
|
||||
storage_entry_keys,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
//! println!("Alice's account info: {value:?}");
|
||||
//! ```
|
||||
|
||||
mod prefix_of;
|
||||
|
||||
pub mod address;
|
||||
|
||||
use crate::{
|
||||
@@ -52,6 +54,8 @@ use alloc::vec::Vec;
|
||||
use frame_decode::storage::StorageTypeInfo;
|
||||
use scale_decode::IntoVisitor;
|
||||
|
||||
pub use prefix_of::{ EqualOrPrefixOf, PrefixOf };
|
||||
|
||||
/// When the provided `address` is statically generated via the `#[subxt]` macro, this validates
|
||||
/// that the shape of the storage value is the same as the shape expected by the static address.
|
||||
///
|
||||
@@ -78,14 +82,15 @@ pub fn validate<Addr: Address>(address: &Addr, metadata: &Metadata) -> Result<()
|
||||
|
||||
/// Given a storage address and some metadata, this encodes the address into bytes which can be
|
||||
/// handed to a node to retrieve the corresponding value.
|
||||
pub fn get_address_bytes<Addr: Address>(
|
||||
pub fn get_address_bytes<Addr: Address, Keys: EqualOrPrefixOf<Addr::KeyParts>>(
|
||||
address: &Addr,
|
||||
metadata: &Metadata,
|
||||
keys: Keys,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
frame_decode::storage::encode_storage_key(
|
||||
address.pallet_name(),
|
||||
address.entry_name(),
|
||||
&address.key_parts(),
|
||||
&keys,
|
||||
&**metadata,
|
||||
metadata.types(),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use frame_decode::helpers::IntoEncodableValues;
|
||||
use scale_encode::EncodeAsType;
|
||||
|
||||
/// For a given set of values that can be used as keys for a storage entry,
|
||||
/// this is implemented for any prefixes of that set. ie if the keys `(A,B,C)`
|
||||
/// would access a storage value, then `PrefixOf<(A,B,C)>` is implemented for
|
||||
/// `(A,B)`, `(A,)` and `()`.
|
||||
pub trait PrefixOf<Keys>: IntoEncodableValues {}
|
||||
|
||||
// Any reference which impls PrefixOf<K> also impls PrefixOf<K>
|
||||
impl <'a, K, T: PrefixOf<K>> PrefixOf<K> for &'a T {}
|
||||
|
||||
// Impls for tuples up to length 6 (storage maps rarely require more than 2 entries
|
||||
// so it's very unlikely we'll ever need to go this deep).
|
||||
impl <A> PrefixOf<(A,)> for () {}
|
||||
|
||||
impl <A, B> PrefixOf<(A,B)> for () {}
|
||||
impl <A, B> PrefixOf<(A, B)> for (A,)
|
||||
where (A,): IntoEncodableValues {}
|
||||
|
||||
impl <A, B, C> PrefixOf<(A, B, C)> for () {}
|
||||
impl <A, B, C> PrefixOf<(A, B, C)> for (A,)
|
||||
where (A,): IntoEncodableValues {}
|
||||
impl <A, B, C> PrefixOf<(A, B, C)> for (A, B)
|
||||
where (A, B): IntoEncodableValues {}
|
||||
|
||||
impl <A, B, C, D> PrefixOf<(A, B, C, D)> for () {}
|
||||
impl <A, B, C, D> PrefixOf<(A, B, C, D)> for (A,)
|
||||
where (A,): IntoEncodableValues {}
|
||||
impl <A, B, C, D> PrefixOf<(A, B, C, D)> for (A, B)
|
||||
where (A, B): IntoEncodableValues {}
|
||||
impl <A, B, C, D> PrefixOf<(A, B, C, D)> for (A, B, C)
|
||||
where (A, B, C): IntoEncodableValues {}
|
||||
|
||||
impl <A, B, C, D, E> PrefixOf<(A, B, C, D, E)> for () {}
|
||||
impl <A, B, C, D, E> PrefixOf<(A, B, C, D, E)> for (A,)
|
||||
where (A,): IntoEncodableValues {}
|
||||
impl <A, B, C, D, E> PrefixOf<(A, B, C, D, E)> for (A, B)
|
||||
where (A, B): IntoEncodableValues {}
|
||||
impl <A, B, C, D, E> PrefixOf<(A, B, C, D, E)> for (A, B, C)
|
||||
where (A, B, C): IntoEncodableValues {}
|
||||
impl <A, B, C, D, E> PrefixOf<(A, B, C, D, E)> for (A, B, C, D)
|
||||
where (A, B, C, D): IntoEncodableValues {}
|
||||
|
||||
impl <A, B, C, D, E, F> PrefixOf<(A, B, C, D, E, F)> for () {}
|
||||
impl <A, B, C, D, E, F> PrefixOf<(A, B, C, D, E, F)> for (A,)
|
||||
where (A,): IntoEncodableValues {}
|
||||
impl <A, B, C, D, E, F> PrefixOf<(A, B, C, D, E, F)> for (A, B)
|
||||
where (A, B): IntoEncodableValues {}
|
||||
impl <A, B, C, D, E, F> PrefixOf<(A, B, C, D, E, F)> for (A, B, C)
|
||||
where (A, B, C): IntoEncodableValues {}
|
||||
impl <A, B, C, D, E, F> PrefixOf<(A, B, C, D, E, F)> for (A, B, C, D)
|
||||
where (A, B, C, D): IntoEncodableValues {}
|
||||
impl <A, B, C, D, E, F> PrefixOf<(A, B, C, D, E, F)> for (A, B, C, D, E)
|
||||
where (A, B, C, D, E): IntoEncodableValues {}
|
||||
|
||||
// Vecs are prefixes of vecs. The length is not statically known and so
|
||||
// these would be given dynamically only, leaving the correct length to the user.
|
||||
impl <T: EncodeAsType> PrefixOf<Vec<T>> for Vec<T> {}
|
||||
|
||||
// We don't use arrays in Subxt for storage entry access, but `IntoEncodableValues`
|
||||
// supports them so let's allow impls which do use them to benefit too.
|
||||
macro_rules! array_impl {
|
||||
($n:literal: $($p:literal)+) => {
|
||||
$(
|
||||
impl <T: EncodeAsType> PrefixOf<[T; $n]> for [T; $p] {}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
array_impl!(1: 0);
|
||||
array_impl!(2: 1 0);
|
||||
array_impl!(3: 2 1 0);
|
||||
array_impl!(4: 3 2 1 0);
|
||||
array_impl!(5: 4 3 2 1 0);
|
||||
array_impl!(6: 5 4 3 2 1 0);
|
||||
|
||||
/// This is much like [`PrefixOf`] except that it also includes `Self` as an allowed type,
|
||||
/// where `Self` must impl [`IntoEncodableValues`] just as every [`PrefixOf<Self>`] does.
|
||||
pub trait EqualOrPrefixOf<K>: IntoEncodableValues {}
|
||||
|
||||
// Tuples
|
||||
macro_rules! tuple_impl_eq {
|
||||
($($t:ident)+) => {
|
||||
// Any T that is a PrefixOf<Keys> impls EqualOrPrefixOf<keys> too
|
||||
impl <$($t,)+ T: PrefixOf<($($t,)+)>> EqualOrPrefixOf<($($t,)+)> for T {}
|
||||
// Keys impls EqualOrPrefixOf<Keys>
|
||||
impl <$($t),+> EqualOrPrefixOf<($($t,)+)> for ($($t,)+) where ($($t,)+): IntoEncodableValues {}
|
||||
// &'a Keys impls EqualOrPrefixOf<Keys>
|
||||
impl <'a, $($t),+> EqualOrPrefixOf<($($t,)+)> for &'a ($($t,)+) where ($($t,)+): IntoEncodableValues {}
|
||||
}
|
||||
}
|
||||
|
||||
tuple_impl_eq!(A);
|
||||
tuple_impl_eq!(A B);
|
||||
tuple_impl_eq!(A B C);
|
||||
tuple_impl_eq!(A B C D);
|
||||
tuple_impl_eq!(A B C D E);
|
||||
tuple_impl_eq!(A B C D E F);
|
||||
|
||||
// Vec
|
||||
impl <T: EncodeAsType> EqualOrPrefixOf<Vec<T>> for Vec<T> {}
|
||||
impl <'a, T: EncodeAsType> EqualOrPrefixOf<Vec<T>> for &'a Vec<T> {}
|
||||
|
||||
// Arrays
|
||||
macro_rules! array_impl_eq {
|
||||
($($n:literal)+) => {
|
||||
$(
|
||||
impl <A: EncodeAsType> EqualOrPrefixOf<[A; $n]> for [A; $n] {}
|
||||
impl <'a, A: EncodeAsType> EqualOrPrefixOf<[A; $n]> for &'a [A; $n] {}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
impl <const N: usize, A, T> EqualOrPrefixOf<[A; N]> for T where T: PrefixOf<[A; N]> {}
|
||||
array_impl_eq!(1 2 3 4 5 6);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
struct Test<Keys: IntoEncodableValues>(core::marker::PhantomData<Keys>);
|
||||
|
||||
impl <Keys: IntoEncodableValues> Test<Keys> {
|
||||
fn new() -> Self {
|
||||
Test(core::marker::PhantomData)
|
||||
}
|
||||
fn accepts_prefix_of<P: PrefixOf<Keys>>(&self, keys: P) {
|
||||
let _encoder = keys.into_encodable_values();
|
||||
}
|
||||
fn accepts_eq_or_prefix_of<P: EqualOrPrefixOf<Keys>>(&self, keys: P) {
|
||||
let _encoder = keys.into_encodable_values();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prefix_of() {
|
||||
// In real life we'd have a struct a bit like this:
|
||||
let t = Test::<(bool, String, u64)>::new();
|
||||
|
||||
// And we'd want to be able to call some method like this:
|
||||
//// This shouldn't work:
|
||||
// t.accepts_prefix_of((true, String::from("hi"), 0));
|
||||
t.accepts_prefix_of(&(true, String::from("hi")));
|
||||
t.accepts_prefix_of((true, String::from("hi")));
|
||||
t.accepts_prefix_of((true,));
|
||||
t.accepts_prefix_of(());
|
||||
|
||||
let t = Test::<[u64; 5]>::new();
|
||||
|
||||
//// This shouldn't work:
|
||||
// t.accepts_prefix_of([0,1,2,3,4]);
|
||||
t.accepts_prefix_of(&[0,1,2,3]);
|
||||
t.accepts_prefix_of([0,1,2,3]);
|
||||
t.accepts_prefix_of([0,1,2]);
|
||||
t.accepts_prefix_of([0,1]);
|
||||
t.accepts_prefix_of([0]);
|
||||
t.accepts_prefix_of([]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq_or_prefix_of() {
|
||||
// In real life we'd have a struct a bit like this:
|
||||
let t = Test::<(bool, String, u64)>::new();
|
||||
|
||||
// And we'd want to be able to call some method like this:
|
||||
t.accepts_eq_or_prefix_of(&(true, String::from("hi"), 0));
|
||||
t.accepts_eq_or_prefix_of(&(true, String::from("hi")));
|
||||
t.accepts_eq_or_prefix_of(&(true,));
|
||||
t.accepts_eq_or_prefix_of(&());
|
||||
|
||||
t.accepts_eq_or_prefix_of((true, String::from("hi"), 0));
|
||||
t.accepts_eq_or_prefix_of((true, String::from("hi")));
|
||||
t.accepts_eq_or_prefix_of((true,));
|
||||
t.accepts_eq_or_prefix_of(());
|
||||
|
||||
let t = Test::<[u64; 5]>::new();
|
||||
|
||||
t.accepts_eq_or_prefix_of(&[0,1,2,3,4]);
|
||||
t.accepts_eq_or_prefix_of(&[0,1,2,3]);
|
||||
t.accepts_eq_or_prefix_of(&[0,1,2]);
|
||||
t.accepts_eq_or_prefix_of(&[0,1]);
|
||||
t.accepts_eq_or_prefix_of(&[0,]);
|
||||
t.accepts_eq_or_prefix_of(&[]);
|
||||
|
||||
t.accepts_eq_or_prefix_of([0,1,2,3,4]);
|
||||
t.accepts_eq_or_prefix_of([0,1,2,3]);
|
||||
t.accepts_eq_or_prefix_of([0,1,2]);
|
||||
t.accepts_eq_or_prefix_of([0,1]);
|
||||
t.accepts_eq_or_prefix_of([0]);
|
||||
t.accepts_eq_or_prefix_of([]);
|
||||
}
|
||||
}
|
||||
@@ -73,8 +73,12 @@ unsafe impl<T> Sync for PhantomDataSendSync<T> {}
|
||||
/// as `BTreeMap` which allows us to easily swap the two during codegen.
|
||||
pub type KeyedVec<K, V> = Vec<(K, V)>;
|
||||
|
||||
/// A unit marker struct.
|
||||
pub struct Yes;
|
||||
/// A unit marker enum.
|
||||
pub enum Yes {}
|
||||
/// A unit marker enum.
|
||||
pub enum Maybe {}
|
||||
/// A unit marker enum.
|
||||
pub enum No {}
|
||||
|
||||
/// A quick helper to encode some bytes to hex.
|
||||
pub fn to_hex(bytes: impl AsRef<[u8]>) -> String {
|
||||
|
||||
Reference in New Issue
Block a user