Skip to content

Commit

Permalink
feat: Added missing BorshSchema for IterableSet and IterableMap and…
Browse files Browse the repository at this point in the history
… refactored and added ABI defiintions tests (#1212)
  • Loading branch information
dj8yfo authored Jul 2, 2024
1 parent d5903b7 commit 23f0545
Show file tree
Hide file tree
Showing 12 changed files with 487 additions and 130 deletions.
1 change: 1 addition & 0 deletions near-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ anyhow = "1.0"
tokio = { version = "1", features = ["full"] }
strum = "0.25.0"
strum_macros = "0.25.3"
insta = "1.39.0"

[features]
default = ["wee_alloc"]
Expand Down
20 changes: 20 additions & 0 deletions near-sdk/compilation_tests/schema_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ pub fn json_borsh_schema_spec() {
const_assert_impls!(StructNoSchemaSpec: near_sdk::borsh::BorshSchema);
}

// original comment by @miraclx
// fixme! this should fail, since A__NEAR_SCHEMA_PROXY does not derive NearSchema
// fixme! hygeinic macro expansion is required to make this work
// fixme! or just explicit checks, making sure that no ident is suffixed with
Expand All @@ -255,6 +256,25 @@ pub fn json_borsh_schema_spec() {
#[allow(non_camel_case_types)]
struct A__NEAR_SCHEMA_PROXY {}

/// additional comment by @dj8yfo
/// FIXME: VERY LOW PRIORITY, as such a camel case type if unlikely to be used in practice
/// derive should fail, since the real A__NEAR_SCHEMA_PROXY type does not derive NearSchema
/// but it compiles and results in recursive definition
///
/// ```
/// definitions: {
/// "A": Object(
/// SchemaObject {
/// ...
/// reference: Some(
/// "#/definitions/A",
/// ),
/// ...
/// },
/// ),
/// ```
/// It compiles due to mutually recursive implementations of `NearSchema` for outer `A`,
/// present in source code, and *hidden* inner `A`, present in derive macro expansion.
#[derive(NearSchema)]
struct A(A__NEAR_SCHEMA_PROXY);

Expand Down
23 changes: 19 additions & 4 deletions near-sdk/src/store/free_list/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub use self::iter::{Drain, Iter, IterMut};

use super::{Vector, ERR_INCONSISTENT_STATE};
use crate::{env, IntoStorageKey};
use near_sdk_macros::{near, NearSchema};
use near_sdk_macros::near;

use borsh::{BorshDeserialize, BorshSerialize};

Expand All @@ -17,9 +17,7 @@ pub struct FreeListIndex(pub(crate) u32);
/// Unordered container of values. This is similar to [`Vector`] except that values are not
/// re-arranged on removal, keeping the indices consistent. When an element is removed, it will
/// be replaced with an empty cell which will be populated on the next insertion.
#[derive(NearSchema, BorshSerialize, BorshDeserialize)]
#[inside_nearsdk]
#[abi(borsh)]
#[near(inside_nearsdk)]
pub(crate) struct FreeList<T>
where
T: BorshSerialize,
Expand Down Expand Up @@ -523,4 +521,21 @@ mod tests {
}
}
}

#[cfg(feature = "abi")]
#[test]
fn test_borsh_schema() {
#[derive(
borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, PartialOrd, Ord,
)]
struct NoSchemaStruct;

assert_eq!(
"FreeList".to_string(),
<FreeList<NoSchemaStruct> as borsh::BorshSchema>::declaration()
);
let mut defs = Default::default();
<FreeList<NoSchemaStruct> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);
insta::assert_snapshot!(format!("{:#?}", defs));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
source: near-sdk/src/store/free_list/mod.rs
expression: "format!(\"{:#?}\", defs)"
---
{
"()": Primitive(
0,
),
"FreeList": Struct {
fields: NamedFields(
[
(
"first_free",
"Option<FreeListIndex>",
),
(
"occupied_count",
"u32",
),
(
"elements",
"Vector",
),
],
),
},
"FreeListIndex": Struct {
fields: UnnamedFields(
[
"u32",
],
),
},
"IndexMap": Struct {
fields: NamedFields(
[
(
"prefix",
"Vec<u8>",
),
],
),
},
"Option<FreeListIndex>": Enum {
tag_width: 1,
variants: [
(
0,
"None",
"()",
),
(
1,
"Some",
"FreeListIndex",
),
],
},
"Vec<u8>": Sequence {
length_width: 4,
length_range: 0..=4294967295,
elements: "u8",
},
"Vector": Struct {
fields: NamedFields(
[
(
"len",
"u32",
),
(
"values",
"IndexMap",
),
],
),
},
"u32": Primitive(
4,
),
"u8": Primitive(
1,
),
}
36 changes: 31 additions & 5 deletions near-sdk/src/store/iterable_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ use super::{LookupMap, ERR_INCONSISTENT_STATE, ERR_NOT_EXIST};
/// ```
///
/// [`with_hasher`]: Self::with_hasher
#[derive(BorshDeserialize, BorshSerialize)]
#[near(inside_nearsdk)]
pub struct IterableMap<K, V, H = Sha256>
where
K: BorshSerialize + Ord,
Expand All @@ -93,11 +93,19 @@ where
// See https://github.com/near/near-sdk-rs/issues/1134 to understand the difference between
// `store::UnorderedMap` and `store::IterableMap`.

// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
keys: Vector<K>,
// ser/de is independent of `K`, `V`, `H` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
// ser/de is independent of `K`, `V`, `H` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
values: LookupMap<K, ValueAndIndex<V>, H>,
}

Expand Down Expand Up @@ -1378,4 +1386,22 @@ mod test_map {
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
}

#[cfg(feature = "abi")]
#[test]
fn test_borsh_schema() {
#[derive(
borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, PartialOrd, Ord,
)]
struct NoSchemaStruct;

assert_eq!(
"IterableMap".to_string(),
<IterableMap<NoSchemaStruct, NoSchemaStruct> as borsh::BorshSchema>::declaration()
);
let mut defs = Default::default();
<IterableMap<NoSchemaStruct, NoSchemaStruct> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);

insta::assert_snapshot!(format!("{:#?}", defs));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
source: near-sdk/src/store/iterable_map/mod.rs
expression: "format!(\"{:#?}\", defs)"
---
{
"IndexMap": Struct {
fields: NamedFields(
[
(
"prefix",
"Vec<u8>",
),
],
),
},
"IterableMap": Struct {
fields: NamedFields(
[
(
"keys",
"Vector",
),
(
"values",
"LookupMap",
),
],
),
},
"LookupMap": Struct {
fields: NamedFields(
[
(
"prefix",
"Vec<u8>",
),
],
),
},
"Vec<u8>": Sequence {
length_width: 4,
length_range: 0..=4294967295,
elements: "u8",
},
"Vector": Struct {
fields: NamedFields(
[
(
"len",
"u32",
),
(
"values",
"IndexMap",
),
],
),
},
"u32": Primitive(
4,
),
"u8": Primitive(
1,
),
}
35 changes: 32 additions & 3 deletions near-sdk/src/store/iterable_set/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::store::key::{Sha256, ToKey};
use crate::store::Vector;
use crate::{env, IntoStorageKey};
use borsh::{BorshDeserialize, BorshSerialize};
use near_sdk_macros::near;
use std::borrow::Borrow;
use std::fmt;

Expand Down Expand Up @@ -83,15 +84,25 @@ type VecIndex = u32;
///
/// [`with_hasher`]: Self::with_hasher
/// [`LookupSet`]: crate::store::LookupSet
#[derive(BorshDeserialize, BorshSerialize)]
#[near(inside_nearsdk)]
pub struct IterableSet<T, H = Sha256>
where
T: BorshSerialize + Ord,
H: ToKey,
{
#[borsh(bound(serialize = "", deserialize = ""))]
// ser/de is independent of `T` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
elements: Vector<T>,
#[borsh(bound(serialize = "", deserialize = ""))]
// ser/de is independent of `T`,`H` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
index: LookupMap<T, VecIndex, H>,
}

Expand Down Expand Up @@ -874,4 +885,22 @@ mod tests {
}
}
}

#[cfg(feature = "abi")]
#[test]
fn test_borsh_schema() {
#[derive(
borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, PartialOrd, Ord,
)]
struct NoSchemaStruct;

assert_eq!(
"IterableSet".to_string(),
<IterableSet<NoSchemaStruct> as borsh::BorshSchema>::declaration()
);
let mut defs = Default::default();
<IterableSet<NoSchemaStruct> as borsh::BorshSchema>::add_definitions_recursively(&mut defs);

insta::assert_snapshot!(format!("{:#?}", defs));
}
}
Loading

0 comments on commit 23f0545

Please sign in to comment.