Skip to content

Commit

Permalink
Use transparent attribute for deriving SchemaType (#297)
Browse files Browse the repository at this point in the history
* Add more test cases for smart contract related macros

* Derive schema type when possible in concordium-cis2

* Bumb common

* Address review comments

* Use new transparent attribute when deriving schema type

* Add derive schematype test with size_length field attribute
  • Loading branch information
limemloh authored Jun 13, 2023
1 parent 3818284 commit d912966
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 93 deletions.
116 changes: 24 additions & 92 deletions concordium-cis2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,14 +1065,10 @@ impl From<AccountAddress> for Receiver {
}

/// Additional information to include with a transfer.
#[derive(Debug, Serialize, Clone)]
#[derive(Debug, Serialize, Clone, SchemaType)]
#[concordium(transparent)]
pub struct AdditionalData(#[concordium(size_length = 2)] Vec<u8>);

// Implemented manually to hide the newtype wrapper.
impl schema::SchemaType for AdditionalData {
fn get_type() -> schema::Type { schema::Type::ByteList(schema::SizeLength::U16) }
}

impl AdditionalData {
/// Construct an AdditionalData containing no data.
pub fn empty() -> Self { AdditionalData(Vec::new()) }
Expand Down Expand Up @@ -1105,18 +1101,12 @@ pub struct Transfer<T: IsTokenId, A: IsTokenAmount> {
}

/// The parameter type for the contract function `transfer`.
#[derive(Debug, Serialize, Clone)]
#[derive(Debug, Serialize, Clone, SchemaType)]
#[concordium(transparent)]
pub struct TransferParams<T: IsTokenId, A: IsTokenAmount>(
#[concordium(size_length = 2)] pub Vec<Transfer<T, A>>,
);

// Implemented manually to hide the newtype wrapper.
impl<T: IsTokenId, A: IsTokenAmount> schema::SchemaType for TransferParams<T, A> {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(Transfer::<T, A>::get_type()))
}
}

impl<T: IsTokenId, A: IsTokenAmount> From<Vec<Transfer<T, A>>> for TransferParams<T, A> {
fn from(transfers: Vec<Transfer<T, A>>) -> Self { TransferParams(transfers) }
}
Expand Down Expand Up @@ -1150,16 +1140,10 @@ pub struct UpdateOperator {
}

/// The parameter type for the contract function `updateOperator`.
#[derive(Debug, Serialize, Clone)]
#[derive(Debug, Serialize, Clone, SchemaType)]
#[concordium(transparent)]
pub struct UpdateOperatorParams(#[concordium(size_length = 2)] pub Vec<UpdateOperator>);

// Implemented manually to hide the newtype wrapper.
impl schema::SchemaType for UpdateOperatorParams {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(UpdateOperator::get_type()))
}
}

/// A query for the balance of a given address for a given token.
// Note: For the serialization to be derived according to the CIS2
// specification, the order of the fields cannot be changed.
Expand All @@ -1174,33 +1158,21 @@ pub struct BalanceOfQuery<T: IsTokenId> {
/// The parameter type for the contract function `balanceOf`.
// Note: For the serialization to be derived according to the CIS2
// specification, the order of the fields cannot be changed.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct BalanceOfQueryParams<T: IsTokenId> {
/// List of balance queries.
#[concordium(size_length = 2)]
pub queries: Vec<BalanceOfQuery<T>>,
}

// Implemented manually to hide the newtype wrapper.
impl<T: IsTokenId> schema::SchemaType for BalanceOfQueryParams<T> {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(BalanceOfQuery::<T>::get_type()))
}
}

/// The response which is sent back when calling the contract function
/// `balanceOf`.
/// It consists of the list of results corresponding to the list of queries.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct BalanceOfQueryResponse<A: IsTokenAmount>(#[concordium(size_length = 2)] pub Vec<A>);

// Implemented manually to hide the newtype wrapper.
impl<A: IsTokenAmount> schema::SchemaType for BalanceOfQueryResponse<A> {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(A::get_type()))
}
}

impl<A: IsTokenAmount> From<Vec<A>> for BalanceOfQueryResponse<A> {
fn from(results: Vec<A>) -> Self { BalanceOfQueryResponse(results) }
}
Expand All @@ -1221,34 +1193,22 @@ pub struct OperatorOfQuery {
}

/// The parameter type for the contract function `operatorOf`.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct OperatorOfQueryParams {
/// List of operatorOf queries.
#[concordium(size_length = 2)]
pub queries: Vec<OperatorOfQuery>,
}

// Implemented manually to hide the newtype wrapper.
impl schema::SchemaType for OperatorOfQueryParams {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(OperatorOfQuery::get_type()))
}
}

/// The response which is sent back when calling the contract function
/// `operatorOf`.
/// It consists of the list of result in the same order and length as the
/// queries in the parameter.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct OperatorOfQueryResponse(#[concordium(size_length = 2)] pub Vec<bool>);

// Implemented manually to hide the newtype wrapper.
impl schema::SchemaType for OperatorOfQueryResponse {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(bool::get_type()))
}
}

impl From<Vec<bool>> for OperatorOfQueryResponse {
fn from(results: Vec<bool>) -> Self { OperatorOfQueryResponse(results) }
}
Expand All @@ -1260,33 +1220,21 @@ impl AsRef<[bool]> for OperatorOfQueryResponse {
/// The parameter type for the contract function `tokenMetadata`.
// Note: For the serialization to be derived according to the CIS2
// specification, the order of the fields cannot be changed.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct TokenMetadataQueryParams<T: IsTokenId> {
/// List of balance queries.
#[concordium(size_length = 2)]
pub queries: Vec<T>,
}

// Implemented manually to hide the newtype wrapper.
impl<T: IsTokenId> schema::SchemaType for TokenMetadataQueryParams<T> {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(T::get_type()))
}
}

/// The response which is sent back when calling the contract function
/// `tokenMetadata`.
/// It consists of the list of results corresponding to the list of queries.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct TokenMetadataQueryResponse(#[concordium(size_length = 2)] pub Vec<MetadataUrl>);

// Implemented manually to hide the newtype wrapper.
impl schema::SchemaType for TokenMetadataQueryResponse {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(MetadataUrl::get_type()))
}
}

impl From<Vec<MetadataUrl>> for TokenMetadataQueryResponse {
fn from(results: Vec<MetadataUrl>) -> Self { TokenMetadataQueryResponse(results) }
}
Expand Down Expand Up @@ -1354,17 +1302,13 @@ impl<'a> StandardIdentifier<'a> {
/// Consists of a string of ASCII characters up to a length of 255.
///
/// See [StandardIdentifier] for the borrowed version.
#[derive(Debug, Serialize, PartialEq, Eq)]
#[derive(Debug, Serialize, PartialEq, Eq, SchemaType)]
#[concordium(transparent)]
pub struct StandardIdentifierOwned {
#[concordium(size_length = 1)]
id: String,
}

// This is implemented manually to flatten and simplify the schema.
impl schema::SchemaType for StandardIdentifierOwned {
fn get_type() -> schema::Type { schema::Type::String(schema::SizeLength::U8) }
}

impl StandardIdentifierOwned {
/// Validate and construct a standard identifier.
pub fn new(id: String) -> Result<Self, InvalidStandardIdentifierError> {
Expand All @@ -1391,20 +1335,14 @@ impl StandardIdentifierOwned {
}

/// The parameter type for the contract function `supports`.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct SupportsQueryParams {
/// The list of support queries.
#[concordium(size_length = 2)]
pub queries: Vec<StandardIdentifierOwned>,
}

// This is implemented manually to flatten and simplify the schema.
impl schema::SchemaType for SupportsQueryParams {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(StandardIdentifierOwned::get_type()))
}
}

/// The query result type for whether a smart contract supports a standard.
// Note: For the serialization to be derived according to the CIS0
// specification, the order of the variants and their fields cannot be changed.
Expand All @@ -1421,20 +1359,14 @@ pub enum SupportResult {
/// The response which is sent back when calling the contract function
/// `supports`. It consists of a list of results corresponding to the list of
/// queries.
#[derive(Debug, Serialize)]
#[derive(Debug, Serialize, SchemaType)]
#[concordium(transparent)]
pub struct SupportsQueryResponse {
/// List of support results corresponding to the list of queries.
#[concordium(size_length = 2)]
pub results: Vec<SupportResult>,
}

// This is implemented manually to flatten and simplify the schema.
impl schema::SchemaType for SupportsQueryResponse {
fn get_type() -> schema::Type {
schema::Type::List(schema::SizeLength::U16, Box::new(SupportResult::get_type()))
}
}

impl From<Vec<SupportResult>> for SupportsQueryResponse {
fn from(results: Vec<SupportResult>) -> Self {
SupportsQueryResponse {
Expand Down
44 changes: 44 additions & 0 deletions concordium-std/tests/derive-schema-type/success-transparent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/// Test ensuring derivation works for newtype wrappers with transparent
use concordium_std::*;

/// Simple struct
#[derive(SchemaType)]
#[concordium(transparent)]
struct MyStruct {
field: u32,
}

/// Nested struct
#[derive(SchemaType)]
#[concordium(transparent)]
struct MyOtherStruct {
field: MyStruct,
}

/// With field attributes
#[derive(SchemaType)]
#[concordium(transparent)]
struct MyCollection {
#[concordium(size_length = 1)]
field: Vec<u32>,
}

fn main() {
{
let schema_type = <MyStruct as schema::SchemaType>::get_type();
assert_eq!(schema_type, schema::Type::U32);
}

{
let schema_type = <MyOtherStruct as schema::SchemaType>::get_type();
assert_eq!(schema_type, schema::Type::U32);
}

{
let schema_type = <MyCollection as schema::SchemaType>::get_type();
assert_eq!(
schema_type,
schema::Type::List(schema::SizeLength::U8, Box::new(schema::Type::U32))
);
}
}

0 comments on commit d912966

Please sign in to comment.