#![allow(clippy::new_ret_no_self)]
use crate::{
serde_hex,
utils::{
deserialize_from_byte_str,
serialize_as_byte_str,
trim_extra_whitespace,
},
};
#[cfg(not(feature = "std"))]
use alloc::{
collections::BTreeMap,
format,
string::String,
vec,
vec::Vec,
};
use core::{
fmt::Display,
marker::PhantomData,
};
use scale_info::{
form::{
Form,
MetaForm,
PortableForm,
},
meta_type,
IntoPortable,
Registry,
TypeInfo,
};
use schemars::JsonSchema;
use serde::{
de::DeserializeOwned,
Deserialize,
Serialize,
};
#[cfg(feature = "std")]
use std::{
collections::BTreeMap,
hash::Hash,
};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct ContractSpec<F: Form = MetaForm>
where
TypeSpec<F>: Default,
{
constructors: Vec<ConstructorSpec<F>>,
messages: Vec<MessageSpec<F>>,
events: Vec<EventSpec<F>>,
docs: Vec<F::String>,
lang_error: TypeSpec<F>,
environment: EnvironmentSpec<F>,
}
impl IntoPortable for ContractSpec {
type Output = ContractSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
ContractSpec {
constructors: self
.constructors
.into_iter()
.map(|constructor| constructor.into_portable(registry))
.collect::<Vec<_>>(),
messages: self
.messages
.into_iter()
.map(|msg| msg.into_portable(registry))
.collect::<Vec<_>>(),
events: self
.events
.into_iter()
.map(|event| event.into_portable(registry))
.collect::<Vec<_>>(),
docs: registry.map_into_portable(self.docs),
lang_error: self.lang_error.into_portable(registry),
environment: self.environment.into_portable(registry),
}
}
}
impl<F> ContractSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn constructors(&self) -> &[ConstructorSpec<F>] {
&self.constructors
}
pub fn messages(&self) -> &[MessageSpec<F>] {
&self.messages
}
pub fn events(&self) -> &[EventSpec<F>] {
&self.events
}
pub fn docs(&self) -> &[F::String] {
&self.docs
}
pub fn lang_error(&self) -> &TypeSpec<F> {
&self.lang_error
}
pub fn environment(&self) -> &EnvironmentSpec<F> {
&self.environment
}
}
pub enum Valid {}
pub enum Invalid {}
#[must_use]
pub struct ContractSpecBuilder<F, S = Invalid>
where
F: Form,
TypeSpec<F>: Default,
{
spec: ContractSpec<F>,
marker: PhantomData<fn() -> S>,
}
impl<F> ContractSpecBuilder<F, Invalid>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn constructors<C>(self, constructors: C) -> ContractSpecBuilder<F, Valid>
where
C: IntoIterator<Item = ConstructorSpec<F>>,
{
debug_assert!(self.spec.constructors.is_empty());
ContractSpecBuilder {
spec: ContractSpec {
constructors: constructors.into_iter().collect::<Vec<_>>(),
..self.spec
},
marker: Default::default(),
}
}
}
impl<F, S> ContractSpecBuilder<F, S>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn messages<M>(self, messages: M) -> Self
where
M: IntoIterator<Item = MessageSpec<F>>,
{
debug_assert!(self.spec.messages.is_empty());
Self {
spec: ContractSpec {
messages: messages.into_iter().collect::<Vec<_>>(),
..self.spec
},
..self
}
}
pub fn events<E>(self, events: E) -> Self
where
E: IntoIterator<Item = EventSpec<F>>,
{
debug_assert!(self.spec.events.is_empty());
Self {
spec: ContractSpec {
events: events.into_iter().collect::<Vec<_>>(),
..self.spec
},
..self
}
}
pub fn docs<D>(self, docs: D) -> Self
where
D: IntoIterator<Item = <F as Form>::String>,
{
debug_assert!(self.spec.docs.is_empty());
Self {
spec: ContractSpec {
docs: docs.into_iter().collect::<Vec<_>>(),
..self.spec
},
..self
}
}
pub fn lang_error(self, lang_error: TypeSpec<F>) -> Self {
Self {
spec: ContractSpec {
lang_error,
..self.spec
},
..self
}
}
pub fn environment(self, environment: EnvironmentSpec<F>) -> Self {
Self {
spec: ContractSpec {
environment,
..self.spec
},
..self
}
}
}
impl<S> ContractSpecBuilder<MetaForm, S> {
pub fn collect_events(self) -> Self {
self.events(crate::collect_events())
}
}
impl<F> ContractSpecBuilder<F, Valid>
where
F: Form,
F::String: Display,
TypeSpec<F>: Default,
{
pub fn done(self) -> ContractSpec<F> {
assert!(
!self.spec.constructors.is_empty(),
"must have at least one constructor"
);
assert!(
!self.spec.messages.is_empty(),
"must have at least one message"
);
assert!(
self.spec.constructors.iter().filter(|c| c.default).count() < 2,
"only one default constructor is allowed"
);
assert!(
self.spec.messages.iter().filter(|m| m.default).count() < 2,
"only one default message is allowed"
);
let max_topics = self.spec.environment.max_event_topics;
let events_exceeding_max_topics_limit = self
.spec
.events
.iter()
.filter_map(|e| {
let signature_topic = if e.signature_topic.is_some() { 1 } else { 0 };
let topics_count =
signature_topic + e.args.iter().filter(|a| a.indexed).count();
if topics_count > max_topics {
Some(format!(
"`{}::{}` ({} topics)",
e.module_path, e.label, topics_count
))
} else {
None
}
})
.collect::<Vec<_>>();
assert!(
events_exceeding_max_topics_limit.is_empty(),
"maximum of {max_topics} event topics exceeded: {}",
events_exceeding_max_topics_limit.join(", ")
);
let mut signature_topics: BTreeMap<Vec<u8>, Vec<String>> = BTreeMap::new();
for e in self.spec.events.iter() {
if let Some(signature_topic) = &e.signature_topic {
signature_topics
.entry(signature_topic.bytes.clone())
.or_default()
.push(format!("`{}::{}`", e.module_path, e.label));
}
}
let signature_topic_collisions = signature_topics
.iter()
.filter_map(|(_, topics)| {
if topics.len() > 1 {
Some(format!(
"event signature topic collision: {}",
topics.join(", ")
))
} else {
None
}
})
.collect::<Vec<_>>();
assert!(
signature_topic_collisions.is_empty(),
"{}",
signature_topic_collisions.join("\n")
);
self.spec
}
}
impl<F> ContractSpec<F>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn new() -> ContractSpecBuilder<F, Invalid> {
ContractSpecBuilder {
spec: Self {
constructors: Vec::new(),
messages: Vec::new(),
events: Vec::new(),
docs: Vec::new(),
lang_error: Default::default(),
environment: Default::default(),
},
marker: PhantomData,
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned",
))]
#[serde(rename_all = "camelCase")]
pub struct ConstructorSpec<F: Form = MetaForm> {
pub label: F::String,
pub selector: Selector,
pub payable: bool,
pub args: Vec<MessageParamSpec<F>>,
pub return_type: ReturnTypeSpec<F>,
pub docs: Vec<F::String>,
default: bool,
}
impl IntoPortable for ConstructorSpec {
type Output = ConstructorSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
ConstructorSpec {
label: self.label.to_string(),
selector: self.selector,
payable: self.payable,
args: self
.args
.into_iter()
.map(|arg| arg.into_portable(registry))
.collect::<Vec<_>>(),
return_type: self.return_type.into_portable(registry),
docs: self.docs.into_iter().map(|s| s.into()).collect(),
default: self.default,
}
}
}
impl<F> ConstructorSpec<F>
where
F: Form,
{
pub fn label(&self) -> &F::String {
&self.label
}
pub fn selector(&self) -> &Selector {
&self.selector
}
pub fn payable(&self) -> &bool {
&self.payable
}
pub fn args(&self) -> &[MessageParamSpec<F>] {
&self.args
}
pub fn return_type(&self) -> &ReturnTypeSpec<F> {
&self.return_type
}
pub fn docs(&self) -> &[F::String] {
&self.docs
}
pub fn default(&self) -> &bool {
&self.default
}
}
#[allow(clippy::type_complexity)]
#[must_use]
pub struct ConstructorSpecBuilder<F: Form, Selector, IsPayable, Returns> {
spec: ConstructorSpec<F>,
marker: PhantomData<fn() -> (Selector, IsPayable, Returns)>,
}
impl<F> ConstructorSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn from_label(
label: <F as Form>::String,
) -> ConstructorSpecBuilder<
F,
Missing<state::Selector>,
Missing<state::IsPayable>,
Missing<state::Returns>,
> {
ConstructorSpecBuilder {
spec: Self {
label,
selector: Selector::default(),
payable: Default::default(),
args: Vec::new(),
return_type: ReturnTypeSpec::new(TypeSpec::default()),
docs: Vec::new(),
default: false,
},
marker: PhantomData,
}
}
}
impl<F, P, R> ConstructorSpecBuilder<F, Missing<state::Selector>, P, R>
where
F: Form,
{
pub fn selector(
self,
selector: [u8; 4],
) -> ConstructorSpecBuilder<F, state::Selector, P, R> {
ConstructorSpecBuilder {
spec: ConstructorSpec {
selector: selector.into(),
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, S, R> ConstructorSpecBuilder<F, S, Missing<state::IsPayable>, R>
where
F: Form,
{
pub fn payable(
self,
is_payable: bool,
) -> ConstructorSpecBuilder<F, S, state::IsPayable, R> {
ConstructorSpecBuilder {
spec: ConstructorSpec {
payable: is_payable,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, S, P> ConstructorSpecBuilder<F, S, P, Missing<state::Returns>>
where
F: Form,
{
pub fn returns(
self,
return_type: ReturnTypeSpec<F>,
) -> ConstructorSpecBuilder<F, S, P, state::Returns> {
ConstructorSpecBuilder {
spec: ConstructorSpec {
return_type,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, S, P, R> ConstructorSpecBuilder<F, S, P, R>
where
F: Form,
{
pub fn args<A>(self, args: A) -> Self
where
A: IntoIterator<Item = MessageParamSpec<F>>,
{
let mut this = self;
debug_assert!(this.spec.args.is_empty());
this.spec.args = args.into_iter().collect::<Vec<_>>();
this
}
pub fn docs<'a, D>(self, docs: D) -> Self
where
D: IntoIterator<Item = &'a str>,
F::String: From<&'a str>,
{
let mut this = self;
debug_assert!(this.spec.docs.is_empty());
this.spec.docs = docs
.into_iter()
.map(|s| trim_extra_whitespace(s).into())
.collect::<Vec<_>>();
this
}
pub fn default(self, default: bool) -> Self {
ConstructorSpecBuilder {
spec: ConstructorSpec {
default,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F> ConstructorSpecBuilder<F, state::Selector, state::IsPayable, state::Returns>
where
F: Form,
{
pub fn done(self) -> ConstructorSpec<F> {
self.spec
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub struct MessageSpec<F: Form = MetaForm> {
label: F::String,
selector: Selector,
mutates: bool,
payable: bool,
args: Vec<MessageParamSpec<F>>,
return_type: ReturnTypeSpec<F>,
docs: Vec<F::String>,
default: bool,
}
pub struct Missing<S>(PhantomData<fn() -> S>);
mod state {
pub struct Selector;
pub struct Mutates;
pub struct IsPayable;
pub struct Returns;
pub struct AccountId;
pub struct Balance;
pub struct Hash;
pub struct Timestamp;
pub struct BlockNumber;
pub struct ChainExtension;
pub struct MaxEventTopics;
pub struct BufferSize;
}
impl<F> MessageSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn from_label(
label: <F as Form>::String,
) -> MessageSpecBuilder<
F,
Missing<state::Selector>,
Missing<state::Mutates>,
Missing<state::IsPayable>,
Missing<state::Returns>,
> {
MessageSpecBuilder {
spec: Self {
label,
selector: Selector::default(),
mutates: false,
payable: false,
args: Vec::new(),
return_type: ReturnTypeSpec::new(TypeSpec::default()),
docs: Vec::new(),
default: false,
},
marker: PhantomData,
}
}
}
impl<F> MessageSpec<F>
where
F: Form,
{
pub fn label(&self) -> &F::String {
&self.label
}
pub fn selector(&self) -> &Selector {
&self.selector
}
pub fn mutates(&self) -> bool {
self.mutates
}
pub fn payable(&self) -> bool {
self.payable
}
pub fn args(&self) -> &[MessageParamSpec<F>] {
&self.args
}
pub fn return_type(&self) -> &ReturnTypeSpec<F> {
&self.return_type
}
pub fn docs(&self) -> &[F::String] {
&self.docs
}
pub fn default(&self) -> &bool {
&self.default
}
}
#[allow(clippy::type_complexity)]
#[must_use]
pub struct MessageSpecBuilder<F, Selector, Mutates, IsPayable, Returns>
where
F: Form,
{
spec: MessageSpec<F>,
marker: PhantomData<fn() -> (Selector, Mutates, IsPayable, Returns)>,
}
impl<F, M, P, R> MessageSpecBuilder<F, Missing<state::Selector>, M, P, R>
where
F: Form,
{
pub fn selector(
self,
selector: [u8; 4],
) -> MessageSpecBuilder<F, state::Selector, M, P, R> {
MessageSpecBuilder {
spec: MessageSpec {
selector: selector.into(),
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, S, P, R> MessageSpecBuilder<F, S, Missing<state::Mutates>, P, R>
where
F: Form,
{
pub fn mutates(
self,
mutates: bool,
) -> MessageSpecBuilder<F, S, state::Mutates, P, R> {
MessageSpecBuilder {
spec: MessageSpec {
mutates,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, S, M, R> MessageSpecBuilder<F, S, M, Missing<state::IsPayable>, R>
where
F: Form,
{
pub fn payable(
self,
is_payable: bool,
) -> MessageSpecBuilder<F, S, M, state::IsPayable, R> {
MessageSpecBuilder {
spec: MessageSpec {
payable: is_payable,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, M, S, P> MessageSpecBuilder<F, S, M, P, Missing<state::Returns>>
where
F: Form,
{
pub fn returns(
self,
return_type: ReturnTypeSpec<F>,
) -> MessageSpecBuilder<F, S, M, P, state::Returns> {
MessageSpecBuilder {
spec: MessageSpec {
return_type,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, S, M, P, R> MessageSpecBuilder<F, S, M, P, R>
where
F: Form,
{
pub fn args<A>(self, args: A) -> Self
where
A: IntoIterator<Item = MessageParamSpec<F>>,
{
let mut this = self;
debug_assert!(this.spec.args.is_empty());
this.spec.args = args.into_iter().collect::<Vec<_>>();
this
}
pub fn docs<D>(self, docs: D) -> Self
where
D: IntoIterator<Item = <F as Form>::String>,
{
let mut this = self;
debug_assert!(this.spec.docs.is_empty());
this.spec.docs = docs.into_iter().collect::<Vec<_>>();
this
}
pub fn default(self, default: bool) -> Self {
MessageSpecBuilder {
spec: MessageSpec {
default,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F>
MessageSpecBuilder<
F,
state::Selector,
state::Mutates,
state::IsPayable,
state::Returns,
>
where
F: Form,
{
pub fn done(self) -> MessageSpec<F> {
self.spec
}
}
impl IntoPortable for MessageSpec {
type Output = MessageSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
MessageSpec {
label: self.label.to_string(),
selector: self.selector,
mutates: self.mutates,
payable: self.payable,
default: self.default,
args: self
.args
.into_iter()
.map(|arg| arg.into_portable(registry))
.collect::<Vec<_>>(),
return_type: self.return_type.into_portable(registry),
docs: self.docs.into_iter().map(|s| s.into()).collect(),
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct EventSpec<F: Form = MetaForm> {
label: F::String,
module_path: F::String,
signature_topic: Option<SignatureTopic>,
args: Vec<EventParamSpec<F>>,
docs: Vec<F::String>,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)]
pub struct SignatureTopic {
#[serde(
serialize_with = "serialize_as_byte_str",
deserialize_with = "deserialize_from_byte_str"
)]
bytes: Vec<u8>,
}
impl<T> From<T> for SignatureTopic
where
T: AsRef<[u8]>,
{
fn from(bytes: T) -> Self {
SignatureTopic {
bytes: bytes.as_ref().to_vec(),
}
}
}
impl SignatureTopic {
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
#[must_use]
pub struct EventSpecBuilder<F>
where
F: Form,
{
spec: EventSpec<F>,
}
impl<F> EventSpecBuilder<F>
where
F: Form,
{
pub fn module_path<'a>(self, path: &'a str) -> Self
where
F::String: From<&'a str>,
{
let mut this = self;
this.spec.module_path = path.into();
this
}
pub fn args<A>(self, args: A) -> Self
where
A: IntoIterator<Item = EventParamSpec<F>>,
{
let mut this = self;
debug_assert!(this.spec.args.is_empty());
this.spec.args = args.into_iter().collect::<Vec<_>>();
this
}
pub fn signature_topic<T>(self, topic: Option<T>) -> Self
where
T: AsRef<[u8]>,
{
let mut this = self;
debug_assert!(this.spec.signature_topic.is_none());
this.spec.signature_topic = topic.as_ref().map(SignatureTopic::from);
this
}
pub fn docs<'a, D>(self, docs: D) -> Self
where
D: IntoIterator<Item = &'a str>,
F::String: From<&'a str>,
{
let mut this = self;
debug_assert!(this.spec.docs.is_empty());
this.spec.docs = docs
.into_iter()
.map(|s| trim_extra_whitespace(s).into())
.collect::<Vec<_>>();
this
}
pub fn done(self) -> EventSpec<F> {
self.spec
}
}
impl IntoPortable for EventSpec {
type Output = EventSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
EventSpec {
label: self.label.to_string(),
module_path: self.module_path.to_string(),
signature_topic: self.signature_topic,
args: self
.args
.into_iter()
.map(|arg| arg.into_portable(registry))
.collect::<Vec<_>>(),
docs: self.docs.into_iter().map(|s| s.into()).collect(),
}
}
}
impl<F> EventSpec<F>
where
F: Form,
F::String: Default,
{
pub fn new(label: <F as Form>::String) -> EventSpecBuilder<F> {
EventSpecBuilder {
spec: Self {
label,
module_path: Default::default(),
signature_topic: None,
args: Vec::new(),
docs: Vec::new(),
},
}
}
}
impl<F> EventSpec<F>
where
F: Form,
{
pub fn label(&self) -> &F::String {
&self.label
}
pub fn args(&self) -> &[EventParamSpec<F>] {
&self.args
}
pub fn signature_topic(&self) -> Option<&SignatureTopic> {
self.signature_topic.as_ref()
}
pub fn docs(&self) -> &[F::String] {
&self.docs
}
}
#[cfg_attr(feature = "std", derive(Hash))]
#[derive(Debug, Default, PartialEq, Eq, derive_more::From, JsonSchema)]
pub struct Selector(#[schemars(with = "String")] [u8; 4]);
impl serde::Serialize for Selector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_hex::serialize(&self.0, serializer)
}
}
impl<'de> serde::Deserialize<'de> for Selector {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut arr = [0; 4];
serde_hex::deserialize_check_len(d, serde_hex::ExpectedLen::Exact(&mut arr[..]))?;
Ok(arr.into())
}
}
impl Selector {
pub fn new<T>(bytes: T) -> Self
where
T: Into<[u8; 4]>,
{
Self(bytes.into())
}
pub fn to_bytes(&self) -> &[u8] {
&self.0
}
}
pub type DisplayName<F> = scale_info::Path<F>;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub struct TypeSpec<F: Form = MetaForm> {
#[serde(rename = "type")]
ty: F::Type,
display_name: DisplayName<F>,
}
impl Default for TypeSpec<MetaForm> {
fn default() -> Self {
TypeSpec::of_type::<()>()
}
}
impl Default for TypeSpec<PortableForm> {
fn default() -> Self {
Self {
ty: u32::default().into(),
display_name: Default::default(),
}
}
}
impl IntoPortable for TypeSpec {
type Output = TypeSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
TypeSpec {
ty: registry.register_type(&self.ty),
display_name: self.display_name.into_portable(registry),
}
}
}
impl TypeSpec {
pub fn with_name_str<T>(display_name: &'static str) -> Self
where
T: TypeInfo + 'static,
{
Self::with_name_segs::<T, _>(display_name.split("::"))
}
pub fn with_name_segs<T, S>(segments: S) -> Self
where
T: TypeInfo + 'static,
S: IntoIterator<Item = &'static str>,
{
Self {
ty: meta_type::<T>(),
display_name: DisplayName::from_segments(segments)
.unwrap_or_else(|err| panic!("display name is invalid: {err:?}")),
}
}
pub fn of_type<T>() -> Self
where
T: TypeInfo + 'static,
{
Self {
ty: meta_type::<T>(),
display_name: DisplayName::default(),
}
}
}
impl<F> TypeSpec<F>
where
F: Form,
{
pub fn ty(&self) -> &F::Type {
&self.ty
}
pub fn display_name(&self) -> &DisplayName<F> {
&self.display_name
}
pub fn new(ty: <F as Form>::Type, display_name: DisplayName<F>) -> Self {
Self { ty, display_name }
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct EventParamSpec<F: Form = MetaForm> {
label: F::String,
indexed: bool,
#[serde(rename = "type")]
ty: TypeSpec<F>,
docs: Vec<F::String>,
}
impl IntoPortable for EventParamSpec {
type Output = EventParamSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
EventParamSpec {
label: self.label.to_string(),
indexed: self.indexed,
ty: self.ty.into_portable(registry),
docs: self.docs.into_iter().map(|s| s.into()).collect(),
}
}
}
impl<F> EventParamSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn new(label: F::String) -> EventParamSpecBuilder<F> {
EventParamSpecBuilder {
spec: Self {
label,
indexed: false,
ty: Default::default(),
docs: vec![],
},
}
}
pub fn label(&self) -> &F::String {
&self.label
}
pub fn indexed(&self) -> bool {
self.indexed
}
pub fn ty(&self) -> &TypeSpec<F> {
&self.ty
}
pub fn docs(&self) -> &[F::String] {
&self.docs
}
}
#[must_use]
pub struct EventParamSpecBuilder<F>
where
F: Form,
{
spec: EventParamSpec<F>,
}
impl<F> EventParamSpecBuilder<F>
where
F: Form,
{
pub fn of_type(self, spec: TypeSpec<F>) -> Self {
let mut this = self;
this.spec.ty = spec;
this
}
pub fn indexed(self, is_indexed: bool) -> Self {
let mut this = self;
this.spec.indexed = is_indexed;
this
}
pub fn docs<'a, D>(self, docs: D) -> Self
where
D: IntoIterator<Item = &'a str>,
F::String: From<&'a str>,
{
debug_assert!(self.spec.docs.is_empty());
Self {
spec: EventParamSpec {
docs: docs
.into_iter()
.map(|s| trim_extra_whitespace(s).into())
.collect::<Vec<_>>(),
..self.spec
},
}
}
pub fn done(self) -> EventParamSpec<F> {
self.spec
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[must_use]
pub struct ReturnTypeSpec<F: Form = MetaForm> {
#[serde(rename = "type")]
ret_type: TypeSpec<F>,
}
impl IntoPortable for ReturnTypeSpec {
type Output = ReturnTypeSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
ReturnTypeSpec {
ret_type: self.ret_type.into_portable(registry),
}
}
}
impl<F> ReturnTypeSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn new<T>(ty: T) -> Self
where
T: Into<TypeSpec<F>>,
{
Self {
ret_type: ty.into(),
}
}
pub fn ret_type(&self) -> &TypeSpec<F> {
&self.ret_type
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct MessageParamSpec<F: Form = MetaForm> {
label: F::String,
#[serde(rename = "type")]
ty: TypeSpec<F>,
}
impl IntoPortable for MessageParamSpec {
type Output = MessageParamSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
MessageParamSpec {
label: self.label.to_string(),
ty: self.ty.into_portable(registry),
}
}
}
impl<F> MessageParamSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn new(label: F::String) -> MessageParamSpecBuilder<F> {
MessageParamSpecBuilder {
spec: Self {
label,
ty: TypeSpec::default(),
},
}
}
pub fn label(&self) -> &F::String {
&self.label
}
pub fn ty(&self) -> &TypeSpec<F> {
&self.ty
}
}
#[must_use]
pub struct MessageParamSpecBuilder<F: Form> {
spec: MessageParamSpec<F>,
}
impl<F> MessageParamSpecBuilder<F>
where
F: Form,
{
pub fn of_type(self, ty: TypeSpec<F>) -> Self {
let mut this = self;
this.spec.ty = ty;
this
}
pub fn done(self) -> MessageParamSpec<F> {
self.spec
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub struct EnvironmentSpec<F: Form = MetaForm>
where
TypeSpec<F>: Default,
{
account_id: TypeSpec<F>,
balance: TypeSpec<F>,
hash: TypeSpec<F>,
timestamp: TypeSpec<F>,
block_number: TypeSpec<F>,
chain_extension: TypeSpec<F>,
max_event_topics: usize,
static_buffer_size: usize,
}
impl<F> Default for EnvironmentSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
fn default() -> Self {
Self {
account_id: Default::default(),
balance: Default::default(),
hash: Default::default(),
timestamp: Default::default(),
block_number: Default::default(),
chain_extension: Default::default(),
max_event_topics: Default::default(),
static_buffer_size: Default::default(),
}
}
}
impl IntoPortable for EnvironmentSpec {
type Output = EnvironmentSpec<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
EnvironmentSpec {
account_id: self.account_id.into_portable(registry),
balance: self.balance.into_portable(registry),
hash: self.hash.into_portable(registry),
timestamp: self.timestamp.into_portable(registry),
block_number: self.block_number.into_portable(registry),
chain_extension: self.chain_extension.into_portable(registry),
max_event_topics: self.max_event_topics,
static_buffer_size: self.static_buffer_size,
}
}
}
impl<F> EnvironmentSpec<F>
where
F: Form,
TypeSpec<F>: Default,
{
pub fn account_id(&self) -> &TypeSpec<F> {
&self.account_id
}
pub fn balance(&self) -> &TypeSpec<F> {
&self.balance
}
pub fn hash(&self) -> &TypeSpec<F> {
&self.hash
}
pub fn timestamp(&self) -> &TypeSpec<F> {
&self.timestamp
}
pub fn block_number(&self) -> &TypeSpec<F> {
&self.block_number
}
pub fn chain_extension(&self) -> &TypeSpec<F> {
&self.chain_extension
}
pub fn max_event_topics(&self) -> usize {
self.max_event_topics
}
}
#[allow(clippy::type_complexity)]
impl<F> EnvironmentSpec<F>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn new() -> EnvironmentSpecBuilder<
F,
Missing<state::AccountId>,
Missing<state::Balance>,
Missing<state::Hash>,
Missing<state::Timestamp>,
Missing<state::BlockNumber>,
Missing<state::ChainExtension>,
Missing<state::MaxEventTopics>,
Missing<state::BufferSize>,
> {
EnvironmentSpecBuilder {
spec: Default::default(),
marker: PhantomData,
}
}
}
#[allow(clippy::type_complexity)]
#[must_use]
pub struct EnvironmentSpecBuilder<F, A, B, H, T, BN, C, M, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
spec: EnvironmentSpec<F>,
marker: PhantomData<fn() -> (A, B, H, T, BN, C, M, BS)>,
}
impl<F, B, H, T, BN, C, M, BS>
EnvironmentSpecBuilder<F, Missing<state::AccountId>, B, H, T, BN, C, M, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn account_id(
self,
account_id: TypeSpec<F>,
) -> EnvironmentSpecBuilder<F, state::AccountId, B, H, T, BN, C, M, BS> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec {
account_id,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, A, H, T, BN, C, M, BS>
EnvironmentSpecBuilder<F, A, Missing<state::Balance>, H, T, BN, C, M, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn balance(
self,
balance: TypeSpec<F>,
) -> EnvironmentSpecBuilder<F, A, state::Balance, H, T, BN, C, M, BS> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec {
balance,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, A, B, T, BN, C, M, BS>
EnvironmentSpecBuilder<F, A, B, Missing<state::Hash>, T, BN, C, M, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn hash(
self,
hash: TypeSpec<F>,
) -> EnvironmentSpecBuilder<F, A, B, state::Hash, T, BN, C, M, BS> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec { hash, ..self.spec },
marker: PhantomData,
}
}
}
impl<F, A, B, H, BN, C, M, BS>
EnvironmentSpecBuilder<F, A, B, H, Missing<state::Timestamp>, BN, C, M, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn timestamp(
self,
timestamp: TypeSpec<F>,
) -> EnvironmentSpecBuilder<F, A, B, H, state::Timestamp, BN, C, M, BS> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec {
timestamp,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, A, B, H, T, C, M, BS>
EnvironmentSpecBuilder<F, A, B, H, T, Missing<state::BlockNumber>, C, M, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn block_number(
self,
block_number: TypeSpec<F>,
) -> EnvironmentSpecBuilder<F, A, B, H, T, state::BlockNumber, C, M, BS> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec {
block_number,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, A, B, H, T, BN, M, BS>
EnvironmentSpecBuilder<F, A, B, H, T, BN, Missing<state::ChainExtension>, M, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn chain_extension(
self,
chain_extension: TypeSpec<F>,
) -> EnvironmentSpecBuilder<F, A, B, H, T, BN, state::ChainExtension, M, BS> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec {
chain_extension,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, A, B, H, T, BN, C, BS>
EnvironmentSpecBuilder<F, A, B, H, T, BN, C, Missing<state::MaxEventTopics>, BS>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn max_event_topics(
self,
max_event_topics: usize,
) -> EnvironmentSpecBuilder<F, A, B, H, T, BN, C, state::MaxEventTopics, BS> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec {
max_event_topics,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F, A, B, H, T, BN, C, M>
EnvironmentSpecBuilder<F, A, B, H, T, BN, C, M, Missing<state::BufferSize>>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn static_buffer_size(
self,
static_buffer_size: usize,
) -> EnvironmentSpecBuilder<F, A, B, H, T, BN, C, M, state::BufferSize> {
EnvironmentSpecBuilder {
spec: EnvironmentSpec {
static_buffer_size,
..self.spec
},
marker: PhantomData,
}
}
}
impl<F>
EnvironmentSpecBuilder<
F,
state::AccountId,
state::Balance,
state::Hash,
state::Timestamp,
state::BlockNumber,
state::ChainExtension,
state::MaxEventTopics,
state::BufferSize,
>
where
F: Form,
TypeSpec<F>: Default,
EnvironmentSpec<F>: Default,
{
pub fn done(self) -> EnvironmentSpec<F> {
self.spec
}
}