diff --git a/near-sdk/Cargo.toml b/near-sdk/Cargo.toml index 203449124..7a9c60eb1 100644 --- a/near-sdk/Cargo.toml +++ b/near-sdk/Cargo.toml @@ -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"] diff --git a/near-sdk/compilation_tests/schema_derive.rs b/near-sdk/compilation_tests/schema_derive.rs index 1bddac5d4..1293d9aba 100644 --- a/near-sdk/compilation_tests/schema_derive.rs +++ b/near-sdk/compilation_tests/schema_derive.rs @@ -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 @@ -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); diff --git a/near-sdk/src/store/free_list/mod.rs b/near-sdk/src/store/free_list/mod.rs index 10cf36442..3c5513d04 100644 --- a/near-sdk/src/store/free_list/mod.rs +++ b/near-sdk/src/store/free_list/mod.rs @@ -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}; @@ -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 where T: BorshSerialize, @@ -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(), + as borsh::BorshSchema>::declaration() + ); + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + insta::assert_snapshot!(format!("{:#?}", defs)); + } } diff --git a/near-sdk/src/store/free_list/snapshots/near_sdk__store__free_list__tests__borsh_schema.snap b/near-sdk/src/store/free_list/snapshots/near_sdk__store__free_list__tests__borsh_schema.snap new file mode 100644 index 000000000..20e719630 --- /dev/null +++ b/near-sdk/src/store/free_list/snapshots/near_sdk__store__free_list__tests__borsh_schema.snap @@ -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", + ), + ( + "occupied_count", + "u32", + ), + ( + "elements", + "Vector", + ), + ], + ), + }, + "FreeListIndex": Struct { + fields: UnnamedFields( + [ + "u32", + ], + ), + }, + "IndexMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "Option": Enum { + tag_width: 1, + variants: [ + ( + 0, + "None", + "()", + ), + ( + 1, + "Some", + "FreeListIndex", + ), + ], + }, + "Vec": Sequence { + length_width: 4, + length_range: 0..=4294967295, + elements: "u8", + }, + "Vector": Struct { + fields: NamedFields( + [ + ( + "len", + "u32", + ), + ( + "values", + "IndexMap", + ), + ], + ), + }, + "u32": Primitive( + 4, + ), + "u8": Primitive( + 1, + ), +} diff --git a/near-sdk/src/store/iterable_map/mod.rs b/near-sdk/src/store/iterable_map/mod.rs index 37f884a54..19234a3ed 100644 --- a/near-sdk/src/store/iterable_map/mod.rs +++ b/near-sdk/src/store/iterable_map/mod.rs @@ -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 where K: BorshSerialize + Ord, @@ -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, - // 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, H>, } @@ -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(), + as borsh::BorshSchema>::declaration() + ); + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + + insta::assert_snapshot!(format!("{:#?}", defs)); + } } diff --git a/near-sdk/src/store/iterable_map/snapshots/near_sdk__store__iterable_map__test_map__borsh_schema.snap b/near-sdk/src/store/iterable_map/snapshots/near_sdk__store__iterable_map__test_map__borsh_schema.snap new file mode 100644 index 000000000..a79c7d1e3 --- /dev/null +++ b/near-sdk/src/store/iterable_map/snapshots/near_sdk__store__iterable_map__test_map__borsh_schema.snap @@ -0,0 +1,65 @@ +--- +source: near-sdk/src/store/iterable_map/mod.rs +expression: "format!(\"{:#?}\", defs)" +--- +{ + "IndexMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "IterableMap": Struct { + fields: NamedFields( + [ + ( + "keys", + "Vector", + ), + ( + "values", + "LookupMap", + ), + ], + ), + }, + "LookupMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "Vec": Sequence { + length_width: 4, + length_range: 0..=4294967295, + elements: "u8", + }, + "Vector": Struct { + fields: NamedFields( + [ + ( + "len", + "u32", + ), + ( + "values", + "IndexMap", + ), + ], + ), + }, + "u32": Primitive( + 4, + ), + "u8": Primitive( + 1, + ), +} diff --git a/near-sdk/src/store/iterable_set/mod.rs b/near-sdk/src/store/iterable_set/mod.rs index e5612f7e3..ce368a24c 100644 --- a/near-sdk/src/store/iterable_set/mod.rs +++ b/near-sdk/src/store/iterable_set/mod.rs @@ -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; @@ -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 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, - #[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, } @@ -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(), + as borsh::BorshSchema>::declaration() + ); + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + + insta::assert_snapshot!(format!("{:#?}", defs)); + } } diff --git a/near-sdk/src/store/iterable_set/snapshots/near_sdk__store__iterable_set__tests__borsh_schema.snap b/near-sdk/src/store/iterable_set/snapshots/near_sdk__store__iterable_set__tests__borsh_schema.snap new file mode 100644 index 000000000..bfdab6e7a --- /dev/null +++ b/near-sdk/src/store/iterable_set/snapshots/near_sdk__store__iterable_set__tests__borsh_schema.snap @@ -0,0 +1,65 @@ +--- +source: near-sdk/src/store/iterable_set/mod.rs +expression: "format!(\"{:#?}\", defs)" +--- +{ + "IndexMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "IterableSet": Struct { + fields: NamedFields( + [ + ( + "elements", + "Vector", + ), + ( + "index", + "LookupMap", + ), + ], + ), + }, + "LookupMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "Vec": Sequence { + length_width: 4, + length_range: 0..=4294967295, + elements: "u8", + }, + "Vector": Struct { + fields: NamedFields( + [ + ( + "len", + "u32", + ), + ( + "values", + "IndexMap", + ), + ], + ), + }, + "u32": Primitive( + 4, + ), + "u8": Primitive( + 1, + ), +} diff --git a/near-sdk/src/store/unordered_map/mod.rs b/near-sdk/src/store/unordered_map/mod.rs index 4c289629b..059c4f157 100644 --- a/near-sdk/src/store/unordered_map/mod.rs +++ b/near-sdk/src/store/unordered_map/mod.rs @@ -850,26 +850,9 @@ mod tests { assert_eq!(map.remove_entry(&3).unwrap(), (3, 3)); } - #[allow(unused)] - macro_rules! schema_map( - () => { BTreeMap::new() }; - { $($key:expr => $value:expr),+ } => { - { - let mut m = BTreeMap::new(); - $( - m.insert($key.to_string(), $value); - )+ - m - } - }; - ); - #[cfg(feature = "abi")] #[test] fn test_borsh_schema() { - use borsh::schema::{Definition, Fields}; - use std::collections::BTreeMap; - #[derive( borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, PartialOrd, Ord, )] @@ -881,102 +864,7 @@ mod tests { ); let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); - println!("{:#?}", defs); - assert_eq!( - schema_map! { - "UnorderedMap" => Definition::Struct { - fields: Fields::NamedFields(vec![ - ( - "keys".to_string(), - "FreeList".to_string(), - ), - ( - "values".to_string(), - "LookupMap".to_string(), - ), - ] - )}, - "FreeList" => Definition::Struct { - fields: Fields::NamedFields(vec![ - ( - "first_free".to_string(), - "Option".to_string(), - ), - ( - "occupied_count".to_string(), - "u32".to_string(), - ), - ( - "elements".to_string(), - "Vector".to_string(), - ), - ] - )}, - "Option" => Definition::Enum { - tag_width: 1, - variants: vec![ - ( - 0, - "None".to_string(), - "()".to_string(), - ), - ( - 1, - "Some".to_string(), - "FreeListIndex".to_string(), - ), - ]}, - "()" => Definition::Primitive(0), - "FreeListIndex" => Definition::Struct { - fields: Fields::UnnamedFields( - vec![ - "u32".to_string(), - ], - ), - }, - "u32" => Definition::Primitive(4 ), - "Vector" => Definition::Struct { - fields: Fields::NamedFields( - vec![ - ( - "len".to_string(), - "u32".to_string(), - ), - ( - "values".to_string(), - "IndexMap".to_string(), - ), - ], - ), - }, - "IndexMap" => Definition::Struct { - fields: Fields::NamedFields( - vec![ - ( - "prefix".to_string(), - "Vec".to_string(), - ), - ], - ), - }, - "Vec" => Definition::Sequence { - length_width: 4, - length_range: 0..=4294967295, - elements: "u8".to_string(), - }, - "u8" => Definition::Primitive(1), - "LookupMap" => Definition::Struct { - fields: Fields::NamedFields( - vec![ - ( - "prefix".to_string(), - "Vec".to_string(), - ), - ], - ), - } - }, - defs - ); + + insta::assert_snapshot!(format!("{:#?}", defs)); } } diff --git a/near-sdk/src/store/unordered_map/snapshots/near_sdk__store__unordered_map__tests__borsh_schema.snap b/near-sdk/src/store/unordered_map/snapshots/near_sdk__store__unordered_map__tests__borsh_schema.snap new file mode 100644 index 000000000..b520b045c --- /dev/null +++ b/near-sdk/src/store/unordered_map/snapshots/near_sdk__store__unordered_map__tests__borsh_schema.snap @@ -0,0 +1,108 @@ +--- +source: near-sdk/src/store/unordered_map/mod.rs +expression: "format!(\"{:#?}\", defs)" +--- +{ + "()": Primitive( + 0, + ), + "FreeList": Struct { + fields: NamedFields( + [ + ( + "first_free", + "Option", + ), + ( + "occupied_count", + "u32", + ), + ( + "elements", + "Vector", + ), + ], + ), + }, + "FreeListIndex": Struct { + fields: UnnamedFields( + [ + "u32", + ], + ), + }, + "IndexMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "LookupMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "Option": Enum { + tag_width: 1, + variants: [ + ( + 0, + "None", + "()", + ), + ( + 1, + "Some", + "FreeListIndex", + ), + ], + }, + "UnorderedMap": Struct { + fields: NamedFields( + [ + ( + "keys", + "FreeList", + ), + ( + "values", + "LookupMap", + ), + ], + ), + }, + "Vec": Sequence { + length_width: 4, + length_range: 0..=4294967295, + elements: "u8", + }, + "Vector": Struct { + fields: NamedFields( + [ + ( + "len", + "u32", + ), + ( + "values", + "IndexMap", + ), + ], + ), + }, + "u32": Primitive( + 4, + ), + "u8": Primitive( + 1, + ), +} diff --git a/near-sdk/src/store/vec/mod.rs b/near-sdk/src/store/vec/mod.rs index 8703d7ee4..6b6742bcc 100644 --- a/near-sdk/src/store/vec/mod.rs +++ b/near-sdk/src/store/vec/mod.rs @@ -61,7 +61,7 @@ use std::{ }; use borsh::{BorshDeserialize, BorshSerialize}; -use near_sdk_macros::NearSchema; +use near_sdk_macros::near; pub use self::iter::{Drain, Iter, IterMut}; use super::ERR_INCONSISTENT_STATE; @@ -112,9 +112,7 @@ fn expect_consistent_state(val: Option) -> T { /// vec.extend([1, 2, 3].iter().copied()); /// assert!(Iterator::eq(vec.into_iter(), [7, 1, 2, 3].iter())); /// ``` -#[derive(NearSchema, BorshSerialize, BorshDeserialize)] -#[inside_nearsdk] -#[abi(borsh)] +#[near(inside_nearsdk)] pub struct Vector where T: BorshSerialize, @@ -1003,4 +1001,21 @@ mod tests { let vec = Vector::::deserialize(&mut serialized.as_slice()).unwrap(); assert_eq!(vec[0], "Some data"); } + + #[cfg(feature = "abi")] + #[test] + fn test_borsh_schema() { + #[derive( + borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, PartialOrd, Ord, + )] + struct NoSchemaStruct; + + assert_eq!( + "Vector".to_string(), + as borsh::BorshSchema>::declaration() + ); + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + insta::assert_snapshot!(format!("{:#?}", defs)); + } } diff --git a/near-sdk/src/store/vec/snapshots/near_sdk__store__vec__tests__borsh_schema.snap b/near-sdk/src/store/vec/snapshots/near_sdk__store__vec__tests__borsh_schema.snap new file mode 100644 index 000000000..0ef01c0db --- /dev/null +++ b/near-sdk/src/store/vec/snapshots/near_sdk__store__vec__tests__borsh_schema.snap @@ -0,0 +1,41 @@ +--- +source: near-sdk/src/store/vec/mod.rs +expression: "format!(\"{:#?}\", defs)" +--- +{ + "IndexMap": Struct { + fields: NamedFields( + [ + ( + "prefix", + "Vec", + ), + ], + ), + }, + "Vec": Sequence { + length_width: 4, + length_range: 0..=4294967295, + elements: "u8", + }, + "Vector": Struct { + fields: NamedFields( + [ + ( + "len", + "u32", + ), + ( + "values", + "IndexMap", + ), + ], + ), + }, + "u32": Primitive( + 4, + ), + "u8": Primitive( + 1, + ), +}