pezkuwi-subxt-core 0.44.0

A no-std compatible subset of Subxt's functionality
Documentation
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

//! Construct addresses to access storage entries with.

use crate::utils::{Maybe, YesMaybe};
use alloc::{borrow::Cow, string::String, vec::Vec};
use frame_decode::storage::{IntoDecodableValues, IntoEncodableValues};
use scale_decode::DecodeAsType;

/// 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 {
	/// 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 + IntoDecodableValues;
	/// Type of the storage value at this location.
	type Value: DecodeAsType;
	/// Does the address point to a plain value (as opposed to a map)?
	/// Set to [`crate::utils::Yes`] to enable APIs which require a map,
	/// or [`crate::utils::Maybe`] to enable APIs which allow a map.
	type IsPlain: YesMaybe;

	/// The pallet containing this storage entry.
	fn pallet_name(&self) -> &str;

	/// The name of the storage entry.
	fn entry_name(&self) -> &str;

	/// Return a unique hash for this address which can be used to validate it against metadata.
	fn validation_hash(&self) -> Option<[u8; 32]>;
}

// Any reference to an address is a valid address.
impl<A: Address + ?Sized> Address for &'_ A {
	type KeyParts = A::KeyParts;
	type Value = A::Value;
	type IsPlain = A::IsPlain;

	fn pallet_name(&self) -> &str {
		A::pallet_name(*self)
	}

	fn entry_name(&self) -> &str {
		A::entry_name(*self)
	}

	fn validation_hash(&self) -> Option<[u8; 32]> {
		A::validation_hash(*self)
	}
}

/// An address which is generated by the static APIs.
pub struct StaticAddress<KeyParts, Value, IsPlain> {
	pallet_name: Cow<'static, str>,
	entry_name: Cow<'static, str>,
	validation_hash: Option<[u8; 32]>,
	marker: core::marker::PhantomData<(KeyParts, Value, IsPlain)>,
}

impl<KeyParts, Value, IsPlain> Clone for StaticAddress<KeyParts, Value, IsPlain> {
	fn clone(&self) -> Self {
		Self {
			pallet_name: self.pallet_name.clone(),
			entry_name: self.entry_name.clone(),
			validation_hash: self.validation_hash,
			marker: self.marker,
		}
	}
}

impl<KeyParts, Value, IsPlain> core::fmt::Debug for StaticAddress<KeyParts, Value, IsPlain> {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		f.debug_struct("StaticAddress")
			.field("pallet_name", &self.pallet_name)
			.field("entry_name", &self.entry_name)
			.field("validation_hash", &self.validation_hash)
			.finish()
	}
}

impl<KeyParts, Value, IsPlain> StaticAddress<KeyParts, Value, IsPlain> {
	/// 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, hash: [u8; 32]) -> Self {
		Self {
			pallet_name: Cow::Borrowed(pallet_name),
			entry_name: Cow::Borrowed(entry_name),
			validation_hash: Some(hash),
			marker: core::marker::PhantomData,
		}
	}

	/// Create a new address.
	pub fn new(pallet_name: impl Into<String>, entry_name: impl Into<String>) -> Self {
		Self {
			pallet_name: pallet_name.into().into(),
			entry_name: entry_name.into().into(),
			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<KeyParts, Value, IsPlain> Address for StaticAddress<KeyParts, Value, IsPlain>
where
	KeyParts: IntoEncodableValues + IntoDecodableValues,
	Value: DecodeAsType,
	IsPlain: YesMaybe,
{
	type KeyParts = KeyParts;
	type Value = Value;
	type IsPlain = IsPlain;

	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<A: AsRef<str>, B: AsRef<str>> Address for (A, B) {
	type KeyParts = Vec<scale_value::Value>;
	type Value = scale_value::Value;
	type IsPlain = Maybe;

	fn pallet_name(&self) -> &str {
		self.0.as_ref()
	}

	fn entry_name(&self) -> &str {
		self.1.as_ref()
	}

	fn validation_hash(&self) -> Option<[u8; 32]> {
		None
	}
}

/// A dynamic address is simply a [`StaticAddress`] which asserts that the
/// entry *might* be a map and *might* have a default value.
pub type DynamicAddress<KeyParts = Vec<scale_value::Value>, Value = scale_value::Value> =
	StaticAddress<KeyParts, Value, Maybe>;

/// 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<String>,
	entry_name: impl Into<String>,
) -> DynamicAddress<KeyParts, Value> {
	DynamicAddress::<KeyParts, Value>::new(pallet_name.into(), entry_name.into())
}