Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1,418 @@
# `0.10.2` -> `1.0.0` `nearcore` upgrade *migration guide*
The link to `nearcore` pr is [chore: update borsh dependency](https://github.com/near/nearcore/pull/9432)
Steps:
## 1. update dependencies in `nearcore` workspace `Cargo.toml`
First we update to `1.0.0-alpha.5` version, which contains [deprecation](https://github.com/near/borsh-rs/pull/206) of `BorshSerialize::try_to_vec` method.
```diff
diff --git a/Cargo.toml b/Cargo.toml
index f38e88411..1587f4131 100644
--- a/Cargo.toml
+++ b/Cargo.toml
-borsh = { version = "0.10.2", features = ["rc"] }
+borsh = { version = "=1.0.0-alpha.5", features = ["derive", "rc"] }
```
## 2. We receive a great number of deprecation warnings of `borsh::BorshSerialize::try_to_vec` method (`near-primitives-core` and other packages):
```bash
warning: use of deprecated method `borsh::BorshSerialize::try_to_vec`: use `borsh::to_vec(&object)` instead
--> core/primitives-core/src/account.rs:246:25
|
246 | let bytes = acc.try_to_vec().unwrap();
| ^^^^^^^^^^
|
= note: `#[warn(deprecated)]` on by default
```
We choose to fix it at once, as this method is [removed](https://github.com/near/borsh-rs/pull/221) in `1.0.0-alpha.5` -> `1.0.0` transition completely
with following diff:
```diff
diff --git a/core/primitives-core/src/account.rs b/core/primitives-core/src/account.rs
index 50a3a340d..380bf4494 100644
--- a/core/primitives-core/src/account.rs
+++ b/core/primitives-core/src/account.rs
@@ -236,3 +236,2 @@ pub struct FunctionCallPermission {
mod tests {
- use borsh::BorshSerialize;
@@ -245,3 +244,3 @@ mod tests {
let acc = Account::new(1_000_000, 1_000_000, CryptoHash::default(), 100);
- let bytes = acc.try_to_vec().unwrap();
+ let bytes = borsh::to_vec(&acc).unwrap();
assert_eq!(hash(&bytes).to_string(), "EVk5UaxBe8LQ8r8iD5EAxVBs6TJcMDKqyH7PBuho6bBJ");
@@ -257,3 +256,3 @@ mod tests {
};
- let mut old_bytes = &old_account.try_to_vec().unwrap()[..];
+ let mut old_bytes = &borsh::to_vec(&old_account).unwrap()[..];
let new_account = <Account as BorshDeserialize>::deserialize(&mut old_bytes).unwrap();
@@ -264,3 +263,3 @@ mod tests {
assert_eq!(new_account.version, AccountVersion::V1);
- let mut new_bytes = &new_account.try_to_vec().unwrap()[..];
+ let mut new_bytes = &borsh::to_vec(&new_account).unwrap()[..];
let deserialized_account =
...
...
...
```
As `nearcore` has a considerable number of occurencies of usage of `borsh::BorshSerialize::try_to_vec` method,
which are tedious to replace manually, [ast-grep](https://github.com/ast-grep/ast-grep) tool may help to
do the refactoring automatically:
```bash
sg -p '$A.try_to_vec()' -l rs -r 'borsh::to_vec(&$A)' # preview changes
sg -p '$A.try_to_vec()' -l rs -r 'borsh::to_vec(&$A)' -A # apply changes without preview
```
## 3. next we encounter error in `near-primitives` package:
```bash
1 error[E0433]: failed to resolve: could not find `maybestd` in `borsh` ▐
--> core/primitives/src/receipt.rs:1:19 ▐
| ▐
1 | use crate::borsh::maybestd::collections::HashMap; ▐
| ^^^^^^^^ could not find `maybestd` in `borsh` ▐
```
`maybestd` [has moved](https://github.com/near/borsh-rs/pull/171) to a `__private` package in `borsh`, and is not supposed to be
accessed directly now besides from within code, derived in `borsh` traits implementations.
As `near-primitives` crate is not supposed to be used in `no_std` context, we can
replace import with standard collections `HashMap`:
```diff
diff --git a/core/primitives/src/receipt.rs b/core/primitives/src/receipt.rs
index 30af36fb9..d5a6632ed 100644
--- a/core/primitives/src/receipt.rs
+++ b/core/primitives/src/receipt.rs
@@ -1,11 +1,11 @@
-use crate::borsh::maybestd::collections::HashMap;
+use std::collections::HashMap;
```
Otherwise, we would've imported from `hashbrown`:
```diff
-use crate::borsh::maybestd::collections::HashMap;
+#[cfg(feature = "std")]
+use std::collections::HashMap;
+#[cfg(not(feature = "std"))]
+use hashbrown::HashMap;
```
## 4. next we encounter a bunch of similar errors in `near-primitives` with `#[borsh_init(...)]`:
```bash
1 error: cannot find attribute `borsh_init` in this scope ▐
--> core/primitives/src/block_header.rs:267:3 ▐
| ▐
267 | #[borsh_init(init)] ▐
| ^^^^^^^^^^ help: a derive helper attribute with a similar name exists: `borsh_skip`▐
```
The syntax of this attribute [has changed](https://github.com/near/borsh-rs/pull/187). We change all of these occurencies according to
`#[borsh(init=<initialization_method>)]` syntax. The following diff is shortened to first and last
occurencies:
```diff
diff --git a/core/primitives/src/block_header.rs b/core/primitives/src/block_header.rs
index 38491b52c..84ab48238 100644
--- a/core/primitives/src/block_header.rs
+++ b/core/primitives/src/block_header.rs
@@ -266,3 +266,3 @@ impl ApprovalMessage {
#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)]
-#[borsh_init(init)]
+#[borsh(init=init)]
pub struct BlockHeaderV1 {
...
diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs
index 912120b56..2de7a1d52 100644
--- a/core/primitives/src/transaction.rs
+++ b/core/primitives/src/transaction.rs
@@ -58,3 +58,3 @@ impl Transaction {
)]
-#[borsh_init(init)]
+#[borsh(init=init)]
pub struct SignedTransaction {
```
## 5. next we encounter a large number of similar syntax errors in `near-primitives` package
```bash
1 error: cannot find attribute `borsh_skip` in this scope ▐
--> core/primitives/src/transaction.rs:196:7 ▐
| ▐
196 | #[borsh_skip] ▐
| ^^^^^^^^^^ ▐
▐
```
We change all of these occurencies according to [new](https://github.com/near/borsh-rs/pull/192)
`#[borsh(skip)]` syntax. The following diff is shortened to first and last
occurencies:
```diff
diff --git a/core/primitives/src/block_header.rs b/core/primitives/src/block_header.rs
index 84ab48238..6514f8222 100644
--- a/core/primitives/src/block_header.rs
+++ b/core/primitives/src/block_header.rs
@@ -279,3 +279,3 @@ pub struct BlockHeaderV1 {
/// Cached value of hash for this block.
- #[borsh_skip]
+ #[borsh(skip)]
pub hash: CryptoHash,
...
diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs
index 2de7a1d52..f3ac54ba8 100644
--- a/core/primitives/src/transaction.rs
+++ b/core/primitives/src/transaction.rs
@@ -62,5 +62,5 @@ pub struct SignedTransaction {
pub signature: Signature,
- #[borsh_skip]
+ #[borsh(skip)]
hash: CryptoHash,
- #[borsh_skip]
+ #[borsh(skip)]
size: u64,
...
```
## 6. next we encounter 2 errors in `near-primitives` package similar to those in point 3.:
```bash
1 error[E0433]: failed to resolve: could not find `maybestd` in `borsh`
--> core/primitives/src/action/delegate.rs:119:41
|
119 | fn deserialize_reader<R: borsh::maybestd::io::Read>(
| ^^^^^^^^ could not find `maybestd` in `borsh`
2 error[E0433]: failed to resolve: could not find `maybestd` in `borsh`
--> core/primitives/src/action/delegate.rs:121:50
|
121 | ) -> ::core::result::Result<Self, borsh::maybestd::io::Error> {
| ^^^^^^^^ could not find `maybestd` i
n `borsh`
```
As `near-primitives` crate is not supposed to be used in `no_std` context, we can
replace import with `std::io`:
```diff
diff --git a/core/primitives/src/action/delegate.rs b/core/primitives/src/action/delegate.rs
index 25db73022..ebd009a44 100644
--- a/core/primitives/src/action/delegate.rs
+++ b/core/primitives/src/action/delegate.rs
@@ -14,3 +14,3 @@ use near_primitives_core::types::{AccountId, Nonce};
use serde::{Deserialize, Serialize};
-use std::io::{Error, ErrorKind};
+use std::io::{Error, ErrorKind, Read};
@@ -118,5 +118,5 @@ mod private_non_delegate_action {
impl borsh::de::BorshDeserialize for NonDelegateAction {
- fn deserialize_reader<R: borsh::maybestd::io::Read>(
+ fn deserialize_reader<R: Read>(
rd: &mut R,
- ) -> ::core::result::Result<Self, borsh::maybestd::io::Error> {
+ ) -> ::core::result::Result<Self, Error> {
match u8::deserialize_reader(rd)? {
```
Otherwise, if we intended to support [both `std` and `no_std`](https://github.com/near/borsh-rs/pull/212), we would've imported from `borsh::io`:
```diff
+use borsh::io::{Error, ErrorKind, Read};
```
## 7. next we encounter an error with `BorshDeserialize` trait derivation:
```bash
1 error[E0277]: the trait bound `&T: borsh::BorshDeserialize` is not satisfied
--> core/primitives/src/signable_message.rs:58:26
|
58 | #[derive(BorshSerialize, BorshDeserialize)]
| ^^^^^^^^^^^^^^^^ the trait `borsh::BorshDeserialize` is not implemented for `&T`
```
on
```rust
/// A wrapper around a message that should be signed using this scheme.
///
/// Only used for constructing a signature, not used to transmit messages. The
/// discriminant prefix is implicit and should be known by the receiver based on
/// the context in which the message is received.
#[derive(BorshSerialize, BorshDeserialize)]
pub struct SignableMessage<'a, T> {
pub discriminant: MessageDiscriminant,
pub msg: &'a T,
}
```
On version change `0.10.3` -> `1.0.0-alpha.5` bounds derivation in `borsh` [has changed](https://github.com/near/borsh-rs/pull/178):
From bounds on the types of the fields:
```rust
impl<'a, T> borsh::de::BorshDeserialize for SignableMessage<'a, T>
where
MessageDiscriminant: borsh::de::BorshDeserialize,
&'a T: borsh::de::BorshDeserialize,
```
to bounds on type parameters, encountered in fields:
```rust
impl<'a, T> borsh::de::BorshDeserialize for SignableMessage<'a, T>
where
T: borsh::de::BorshDeserialize,
```
We could potentially [patch the bounds](https://github.com/near/borsh-rs/pull/180) on struct to make it compile:
```rust
#[derive(BorshSerialize, BorshDeserialize)]
pub struct SignableMessage<'a, T> {
pub discriminant: MessageDiscriminant,
#[borsh(bound(deserialize="&'a T: borsh::de::BorshDeserialize"))]
pub msg: &'a T,
}
```
which would transform into following bound on trait's implementation:
```rust
where
&'a T: borsh::de::BorshDeserialize,
```
But the real issue here is that `borsh` doesn't have a generic implementation
of `BorshDeserialize` for `&'a T`, where `T: borsh::de::BorshDeserialize` (nor did it have it in 0.10.2 version),
and that the derived `BorshDeserialize` wasn't used (and it couldn't be for such a field's type).
So the right change is to remove `BorshDeserialize` derive from the struct:
```diff
diff --git a/core/primitives/src/signable_message.rs b/core/primitives/src/signable_message.rs
index efdd489ac..db97eb1fd 100644
--- a/core/primitives/src/signable_message.rs
+++ b/core/primitives/src/signable_message.rs
@@ -57,3 +57,3 @@ pub struct MessageDiscriminant {
/// the context in which the message is received.
-#[derive(BorshSerialize, BorshDeserialize)]
+#[derive(BorshSerialize)]
pub struct SignableMessage<'a, T> {
```
## 8. next we encounter an error in `near-network` package:
```rust
1 error: You have to specify `#[borsh(use_discriminant=true)]` or `#[borsh(use_discriminant=false)]` for all enums with explicit discriminant ▐
--> chain/network/src/types.rs:56:10 ▐
| ▐
56 | pub enum ReasonForBan { ▐
| ^^^^^^^^^^^^ ▐
```
```rust
#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq, Copy)]
pub enum ReasonForBan {
None = 0,
BadBlock = 1,
BadBlockHeader = 2,
HeightFraud = 3,
BadHandshake = 4,
BadBlockApproval = 5,
Abusive = 6,
InvalidSignature = 7,
InvalidPeerId = 8,
InvalidHash = 9,
InvalidEdge = 10,
InvalidDistanceVector = 11,
Blacklisted = 14,
}
```
We fix it with `#[borsh(use_discriminant=false)]` to preserve the behaviour of borsh before
1.0 release which serialized `ReasonForBan::Blacklisted` as 12 instead of 14
(borsh 0.10 and older [ignored explicit discriminant values in enum definitions](https://github.com/near/borsh-rs/issues/137)):
```diff
diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs
index b2dd97c32..ea2d67f2d 100644
--- a/chain/network/src/types.rs
+++ b/chain/network/src/types.rs
@@ -55,2 +55,3 @@ pub struct KnownProducer {
#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq, Copy)]
+#[borsh(use_discriminant=false)]
pub enum ReasonForBan {
```
## 9. change in behaviour, unit test error in ci in `near-primitives` package
Assertion fails:
```rust
#[test]
fn test_delegate_action_deserialization() {
// Expected an error. Buffer is empty
assert_eq!(
NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()),
Err(ErrorKind::InvalidInput)
);
```
```bash
--- STDERR: near-primitives action::delegate::tests::test_delegate_action_deserialization ---
thread 'action::delegate::tests::test_delegate_action_deserialization' panicked at 'assertion failed: `(left == right)`
left: `Err(InvalidData)`,
right: `Err(InvalidInput)`', core/primitives/src/action/delegate.rs:172:9
```
The `ErrorKind` in error in `borsh` [has changed](https://github.com/near/borsh-rs/pull/170), so we apply the following diff:
```diff
diff --git a/core/primitives/src/action/delegate.rs b/core/primitives/src/action/delegate.rs
index ebd009a44..80a0475b6 100644
--- a/core/primitives/src/action/delegate.rs
+++ b/core/primitives/src/action/delegate.rs
@@ -173,3 +173,3 @@ mod tests {
NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()),
- Err(ErrorKind::InvalidInput)
+ Err(ErrorKind::InvalidData)
);
```
And there's also a similar error in
```bash
--- STDERR: near-store tests::test_save_to_file ---
thread 'tests::test_save_to_file' panicked at 'assertion failed: `(left == right)`
left: `InvalidInput`,
right: `InvalidData`', core/store/src/lib.rs:1096:9
```
with similar fix.
## 10. errors similar to previous ones, in `near-store`, `near-network` and `near-state-viewer` packages
There was a bunch of `borsh::maybestd` imports, which got replaced by their direct from-`std` counterparts.
## 11. finally, we update `borsh` version to `1.0.0`:
```diff
diff --git a/Cargo.toml b/Cargo.toml
index f38e88411..1587f4131 100644
--- a/Cargo.toml
+++ b/Cargo.toml
-borsh = { version = "=1.0.0-alpha.5", features = ["derive", "rc"] }
+borsh = { version = "1.0.0", features = ["derive", "rc"] }
```
@@ -0,0 +1,587 @@
# `v0.9` -> `v1.0.0` `near-sdk-rs` upgrade *migration guide*
The link to `near-sdk-rs` pr is [chore: borsh version update](https://github.com/near/near-sdk-rs/pull/1075)
Steps:
## 1. update dependencies in `near-sdk/Cargo.toml`.
First we update to `1.0.0-alpha.5` version, which contains [deprecation](https://github.com/near/borsh-rs/pull/206) of `BorshSerialize::try_to_vec` method.
We enable `derive` feature by default, and make `unstable__schema` feature optional, enabled
depending on whether `abi` feature of `near-sdk` package is enabled or not.
```diff
diff --git a/near-sdk/Cargo.toml b/near-sdk/Cargo.toml
index a015a64..e6099d4 100644
--- a/near-sdk/Cargo.toml
+++ b/near-sdk/Cargo.toml
@@ -26,3 +26,3 @@ near-sys = { path = "../near-sys", version = "0.2" }
base64 = "0.13"
-borsh = { version = "0.9", features = ["const-generics"] }
+borsh = { version = "=1.0.0-alpha.5", features = ["derive"] }
bs58 = "0.4"
@@ -35,3 +35,4 @@ once_cell = { version = "1.17", default-features = false }
@@ -58,3 +59,3 @@ unstable = []
legacy = []
-abi = ["near-abi", "schemars", "near-sdk-macros/abi"]
+abi = ["borsh/unstable__schema", "near-abi", "schemars", "near-sdk-macros/abi"]
unit-testing = ["near-vm-logic", "near-primitives-core", "near-primitives", "near-crypto"]
```
## 2. We receive a great number of deprecation warnings of `borsh::BorshSerialize::try_to_vec` method (`near-sdk` package):
```bash
2 warning: use of deprecated method `borsh::BorshSerialize::try_to_vec`: use `borsh::to_vec(&object)` instead
--> near-sdk/src/store/lazy/mod.rs:43:28
|
43 | let serialized = value.try_to_vec().unwrap_or_else(|_| env::panic_str(ERR_VALUE_SERIALIZATION));
| ^^^^^^^^^^
|
= note: `#[warn(deprecated)]` on by default
```
We choose to fix it at once, as this method is [removed](https://github.com/near/borsh-rs/pull/221) in `1.0.0-alpha.5` -> `1.0.0` transition completely
with following diff:
```diff
diff --git a/near-sdk/src/store/lazy/mod.rs b/near-sdk/src/store/lazy/mod.rs
index 7df7ee4..42112ea 100644
--- a/near-sdk/src/store/lazy/mod.rs
+++ b/near-sdk/src/store/lazy/mod.rs
@@ -8,3 +8,3 @@ mod impls;
-use borsh::{BorshDeserialize, BorshSerialize};
+use borsh::{BorshDeserialize, BorshSerialize, to_vec};
use once_cell::unsync::OnceCell;
@@ -42,3 +42,3 @@ where
{
- let serialized = value.try_to_vec().unwrap_or_else(|_| env::panic_str(ERR_VALUE_SERIALIZATION));
+ let serialized = to_vec(value).unwrap_or_else(|_| env::panic_str(ERR_VALUE_SERIALIZATION));
env::storage_write(key, &serialized);
...
...
```
where `value` is `&T`, where `T: BorshSerialize`.
## 3. We replace the usage of `BorshSchema::schema_container` method (`near-sdk-macros` package)
To prevent compilation errors in the future, we grep for `schema_container` string.
`schema_container` was changed from being a `BorshSchema` trait method to being a function, external
to the trait in [chore!: make BorshSchema::{add_definition,schema_container} free-standing funcs](https://github.com/near/borsh-rs/pull/204)
We fix code, generated with `near_bindgen` procedural macro, with following diff:
```diff
diff --git a/near-sdk-macros/src/core_impl/abi/abi_generator.rs b/near-sdk-macros/src/core_impl/abi/abi_generator.rs
index cbe659a..994e63c 100644
--- a/near-sdk-macros/src/core_impl/abi/abi_generator.rs
+++ b/near-sdk-macros/src/core_impl/abi/abi_generator.rs
@@ -239,21 +239,21 @@ impl ImplItemMethodInfo {
}
}
}
fn generate_schema(ty: &Type, serializer_type: &SerializerType) -> TokenStream2 {
match serializer_type {
SerializerType::JSON => quote! {
gen.subschema_for::<#ty>()
},
SerializerType::Borsh => quote! {
- <#ty as ::near_sdk::borsh::BorshSchema>::schema_container()
+ ::near_sdk::borsh::schema_container_of::<#ty>()
},
}
}
fn generate_abi_type(ty: &Type, serializer_type: &SerializerType) -> TokenStream2 {
let schema = generate_schema(ty, serializer_type);
match serializer_type {
SerializerType::JSON => quote! {
::near_sdk::__private::AbiType::Json {
type_schema: #schema,
```
## 4. next we encounter error with `#[borsh(use_discriminant=<bool>)]` (`near-sdk` package):
```bash
1 error: You have to specify `#[borsh(use_discriminant=true)]` or `#[borsh(use_discriminant=false)]` for all enums with explicit discriminant
--> near-sdk/src/types/public_key.rs:8:10
|
8 | pub enum CurveType {
| ^^^^^^^^^
```
on
```rust
/// PublicKey curve
#[derive(Debug, Clone, Copy, PartialOrd, Ord, Eq, PartialEq, BorshDeserialize, BorshSerialize)]
#[repr(u8)]
pub enum CurveType {
ED25519 = 0,
SECP256K1 = 1,
}
```
We fix it with `#[borsh(use_discriminant=true)]`, which will behave the same as `#[borsh(use_discriminant=false)]`
in this particular case, where `false` preserves the behaviour of borsh before 1.0 release
(borsh 0.10 and older [ignored explicit discriminant values in enum definitions](https://github.com/near/borsh-rs/issues/137)):
```diff
diff --git a/near-sdk/src/types/public_key.rs b/near-sdk/src/types/public_key.rs
index 30ebd43..b539ddd 100644
--- a/near-sdk/src/types/public_key.rs
+++ b/near-sdk/src/types/public_key.rs
@@ -7,2 +7,3 @@ use std::convert::TryFrom;
#[repr(u8)]
+#[borsh(use_discriminant=true)]
pub enum CurveType {
@@ -144,4 +145,4 @@ impl serde::Serialize for PublicKey {
```
## 5. next we encounter errors with `borsh::maybestd` imports (`near-sdk` package):
```bash
1 error[E0432]: unresolved import `borsh::maybestd`
--> near-sdk/src/types/public_key.rs:1:13
|
1 | use borsh::{maybestd::io, BorshDeserialize, BorshSerialize};
| ^^^^^^^^ could not find `maybestd` in `borsh`
2 error[E0432]: unresolved import `borsh::maybestd`
--> near-sdk/src/types/account_id.rs:1:13
|
1 | use borsh::{maybestd::io, BorshDeserialize, BorshSchema, BorshSerialize};
| ^^^^^^^^ could not find `maybestd` in `borsh`
```
```rust
// near-sdk/src/types/public_key.rs
impl BorshDeserialize for PublicKey {
fn deserialize(buf: &mut &[u8]) -> io::Result<Self> {
<Vec<u8> as BorshDeserialize>::deserialize(buf).and_then(|s| {
Self::try_from(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
})
}
}
```
`maybestd` [has moved](https://github.com/near/borsh-rs/pull/171) to a `__private` package in `borsh`, and is not supposed to be
accessed directly now besides from within code, derived in `borsh` traits implementations.
As `near-sdk` crate is not supposed to be used in `no_std` context, we can
replace imports with `std::io`:
```diff
diff --git a/near-sdk/src/types/account_id.rs b/near-sdk/src/types/account_id.rs
index a338b5c..7876d77 100644
--- a/near-sdk/src/types/account_id.rs
+++ b/near-sdk/src/types/account_id.rs
@@ -1,5 +1,5 @@
-use borsh::{maybestd::io, BorshDeserialize, BorshSchema, BorshSerialize};
+use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use serde::{de, Deserialize, Serialize};
use std::convert::TryFrom;
-use std::fmt;
+use std::{fmt, io};
diff --git a/near-sdk/src/types/public_key.rs b/near-sdk/src/types/public_key.rs
index 10175a0..4280f70 100644
--- a/near-sdk/src/types/public_key.rs
+++ b/near-sdk/src/types/public_key.rs
@@ -1,4 +1,4 @@
-use borsh::{maybestd::io, BorshDeserialize, BorshSerialize};
+use borsh::{BorshDeserialize, BorshSerialize};
use bs58::decode::Error as B58Error;
-use std::convert::TryFrom;
+use std::{convert::TryFrom, io};
```
Otherwise, if we intended to support [both `std` and `no_std`](https://github.com/near/borsh-rs/pull/212), we would've imported from `borsh::io`:
```diff
-use borsh::{maybestd::io, BorshDeserialize, BorshSerialize};
+use borsh::{BorshDeserialize, BorshSerialize};
+use borsh::io;
```
## 6. next we encounter a large number of similar syntax errors with `borsh_skip` (`near-sdk` package):
```bash
1 error: cannot find attribute `borsh_skip` in this scope
--> near-sdk/src/store/lookup_map/mod.rs:89:7
|
89 | #[borsh_skip]
| ^^^^^^^^^^
```
We change all of these occurencies according to [new](https://github.com/near/borsh-rs/pull/192)
`#[borsh(skip)]` syntax. The following diff is shortened to first and last
occurencies:
```diff
diff --git a/near-sdk/src/collections/lazy_option.rs b/near-sdk/src/collections/lazy_option.rs
index 04e79fb..f4ea0dc 100644
--- a/near-sdk/src/collections/lazy_option.rs
+++ b/near-sdk/src/collections/lazy_option.rs
@@ -19,3 +19,3 @@ pub struct LazyOption<T> {
storage_key: Vec<u8>,
- #[borsh_skip]
+ #[borsh(skip)]
el: PhantomData<T>,
...
diff --git a/near-sdk/src/store/lookup_set/mod.rs b/near-sdk/src/store/lookup_set/mod.rs
index 762956a..b2d1ac0 100644
--- a/near-sdk/src/store/lookup_set/mod.rs
+++ b/near-sdk/src/store/lookup_set/mod.rs
@@ -54,3 +54,3 @@ where
- #[borsh_skip]
+ #[borsh(skip)]
hasher: PhantomData<fn() -> (T, H)>,
```
## 7. next there's a bunch of similar errors with `borsh::maybestd::io` imports (`near-sdk` package):
They're fixed in a similar way as in 5.
## 8. next there's a bunch of similar errors due to `BorshDeserialize` trait signature change (`near-sdk` package):
```bash
1 error[E0046]: not all trait items implemented, missing: `deserialize_reader`
--> near-sdk/src/store/vec/mod.rs:138:1
|
138 | impl<T> BorshDeserialize for Vector<T>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `deserialize_reader` in implementation
= help: implement the missing item: `fn deserialize_reader<R>(_: &mut R) -> std::result::Result<Self, std::io::Error> where R: std::io::Read { todo!() }
```
The signature of trait has changed on 0.9.3 -> 0.10.0 transition in [implement deserialize_reader](https://github.com/near/borsh-rs/pull/116).
We fix it the following way:
```diff
diff --git a/near-sdk/src/store/free_list/mod.rs b/near-sdk/src/store/free_list/mod.rs
index 43d8908..20a1cc7 100644
--- a/near-sdk/src/store/free_list/mod.rs
+++ b/near-sdk/src/store/free_list/mod.rs
@@ -47,7 +47,7 @@ where
{
- fn deserialize(buf: &mut &[u8]) -> Result<Self, std::io::Error> {
+ fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
Ok(Self {
- first_free: BorshDeserialize::deserialize(buf)?,
- occupied_count: BorshDeserialize::deserialize(buf)?,
- elements: BorshDeserialize::deserialize(buf)?,
+ first_free: BorshDeserialize::deserialize_reader(reader)?,
+ occupied_count: BorshDeserialize::deserialize_reader(reader)?,
+ elements: BorshDeserialize::deserialize_reader(reader)?,
})
diff --git a/near-sdk/src/store/unordered_map/mod.rs b/near-sdk/src/store/unordered_map/mod.rs
index 5decc60..d82a8aa 100644
--- a/near-sdk/src/store/unordered_map/mod.rs
+++ b/near-sdk/src/store/unordered_map/mod.rs
@@ -117,6 +117,6 @@ where
{
- fn deserialize(buf: &mut &[u8]) -> Result<Self, std::io::Error> {
+ fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
Ok(Self {
- keys: BorshDeserialize::deserialize(buf)?,
- values: BorshDeserialize::deserialize(buf)?,
+ keys: BorshDeserialize::deserialize_reader(reader)?,
+ values: BorshDeserialize::deserialize_reader(reader)?,
})
diff --git a/near-sdk/src/store/vec/mod.rs b/near-sdk/src/store/vec/mod.rs
index 9d19614..94127ba 100644
--- a/near-sdk/src/store/vec/mod.rs
+++ b/near-sdk/src/store/vec/mod.rs
@@ -141,6 +141,6 @@ where
{
- fn deserialize(buf: &mut &[u8]) -> Result<Self, std::io::Error> {
+ fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
Ok(Self {
- len: BorshDeserialize::deserialize(buf)?,
- values: BorshDeserialize::deserialize(buf)?,
+ len: BorshDeserialize::deserialize_reader(reader)?,
+ values: BorshDeserialize::deserialize_reader(reader)?,
})
diff --git a/near-sdk/src/types/account_id.rs b/near-sdk/src/types/account_id.rs
index 7876d77..3da417a 100644
--- a/near-sdk/src/types/account_id.rs
+++ b/near-sdk/src/types/account_id.rs
@@ -88,4 +88,4 @@ impl<'de> Deserialize<'de> for AccountId {
impl BorshDeserialize for AccountId {
- fn deserialize(buf: &mut &[u8]) -> io::Result<Self> {
- <String as BorshDeserialize>::deserialize(buf).and_then(|s| {
+ fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
+ <String as BorshDeserialize>::deserialize_reader(reader).and_then(|s| {
Self::try_from(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
diff --git a/near-sdk/src/types/public_key.rs b/near-sdk/src/types/public_key.rs
index 4280f70..b539ddd 100644
--- a/near-sdk/src/types/public_key.rs
+++ b/near-sdk/src/types/public_key.rs
@@ -145,4 +145,4 @@ impl serde::Serialize for PublicKey {
impl BorshDeserialize for PublicKey {
- fn deserialize(buf: &mut &[u8]) -> io::Result<Self> {
- <Vec<u8> as BorshDeserialize>::deserialize(buf).and_then(|s| {
+ fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
+ <Vec<u8> as BorshDeserialize>::deserialize_reader(reader).and_then(|s| {
Self::try_from(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
diff --git a/near-sdk/src/collections/unordered_map/mod.rs b/near-sdk/src/collections/unordered_map/mod.rs
index d3ba8d5..aab31a4 100644
--- a/near-sdk/src/collections/unordered_map/mod.rs
+++ b/near-sdk/src/collections/unordered_map/mod.rs
@@ -512,5 +512,5 @@ mod tests {
impl BorshDeserialize for DeserializeCounter {
- fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
+ fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
DES_COUNT.fetch_add(1, Ordering::SeqCst);
- u64::deserialize(buf).map(DeserializeCounter)
+ u64::deserialize_reader(reader).map(DeserializeCounter)
}
```
## 9. next we encounter an error with `BorshDeserialize` trait derivation (`near-sdk` package):
```bash
6 error[E0277]: the trait bound `T: Default` is not satisfied
--> near-sdk/src/store/vec/mod.rs:145:21
|
145 | values: BorshDeserialize::deserialize_reader(reader)?,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `T`
|
note: required for `IndexMap<T>` to implement `BorshDeserialize`
--> near-sdk/src/store/index_map.rs:12:26
|
12 | #[derive(BorshSerialize, BorshDeserialize)]
| ^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
13 | pub(crate) struct IndexMap<T>
| ^^^^^^^^^^^
= note: this error originates in the derive macro `BorshDeserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting this bound
|
140 | T: BorshSerialize + std::default::Default,
| +++++++++++++++++++++++
```
where `IndexMap<T>` looks like the following:
```rust
#[derive(BorshSerialize, BorshDeserialize)]
pub(crate) struct IndexMap<T>
where
T: BorshSerialize,
{
pub(crate) prefix: Box<[u8]>,
/// Cache for loads and intermediate changes to the underlying index map.
/// The cached entries are wrapped in a [`Box`] to avoid existing pointers from being
/// invalidated.
///
/// Note: u32 indices are used over usize to have consistent functionality across architectures.
/// Some functionality would be different from tests to Wasm if exceeding 32-bit length.
#[borsh(skip)]
pub(crate) cache: StableMap<u32, OnceCell<CacheEntry<T>>>,
}
```
On version change `v0.9` -> `v1.0.0-alpha.5` bounds derivation in `borsh` [has changed](https://github.com/near/borsh-rs/pull/178):
From bounds on the types of the fields:
```rust
// cd near-sdk; cargo expand ::store::index_map
impl<T> borsh::de::BorshDeserialize for IndexMap<T>
where
T: BorshSerialize,
Box<[u8]>: borsh::BorshDeserialize,
{
fn deserialize(
buf: &mut &[u8],
) -> ::core::result::Result<Self, borsh::maybestd::io::Error> {
Ok(Self {
prefix: borsh::BorshDeserialize::deserialize(buf)?,
cache: Default::default(),
})
}
}
```
to bounds on type parameters, encountered in fields. `borsh::de::BorshDeserialize` bound
for parameters in non-skipped fields, `core::default::Default` bound - otherwise:
```rust
impl<T> borsh::de::BorshDeserialize for IndexMap<T>
where
T: BorshSerialize,
T: core::default::Default,
{
fn deserialize_reader<R: borsh::__private::maybestd::io::Read>(
reader: &mut R,
) -> ::core::result::Result<Self, borsh::__private::maybestd::io::Error> {
Ok(Self {
prefix: borsh::BorshDeserialize::deserialize_reader(reader)?,
cache: core::default::Default::default(),
})
}
}
```
We can instruct `borsh` to [replace automatically derived bound](https://github.com/near/borsh-rs/pull/180) with nothing, as `StableMap` has a `impl<K: Ord, V> Default for StableMap<K, V>`
implementation of its own, as it will be used when deserializing skipped field, irrelevant of bounds on `V`:
```diff
diff --git a/near-sdk/src/store/index_map.rs b/near-sdk/src/store/index_map.rs
index 834fc98..7d1df75 100644
--- a/near-sdk/src/store/index_map.rs
+++ b/near-sdk/src/store/index_map.rs
@@ -23,3 +23,3 @@ where
/// Some functionality would be different from tests to Wasm if exceeding 32-bit length.
- #[borsh(skip)]
+ #[borsh(skip, bound(deserialize = ""))]
pub(crate) cache: StableMap<u32, OnceCell<CacheEntry<T>>>,
```
which would transform into following bound on trait's implementation:
```rust
// line with `T: core::default::Default,` disappeared
impl<T> borsh::de::BorshDeserialize for IndexMap<T>
where
T: BorshSerialize,
{
...
```
Similar diffs were also applied here:
```diff
diff --git a/near-sdk/src/store/lookup_map/mod.rs b/near-sdk/src/store/lookup_map/mod.rs
index 0b20345..927b2d6 100644
--- a/near-sdk/src/store/lookup_map/mod.rs
+++ b/near-sdk/src/store/lookup_map/mod.rs
@@ -88,3 +88,3 @@ where
/// invalidated.
- #[borsh(skip)]
+ #[borsh(skip, bound(deserialize = ""))]
cache: StableMap<K, EntryAndHash<V, H::KeyType>>,
```
```diff
diff --git a/near-sdk/src/store/unordered_set/mod.rs b/near-sdk/src/store/unordered_set/mod.rs
index 4504580..77621b9 100644
--- a/near-sdk/src/store/unordered_set/mod.rs
+++ b/near-sdk/src/store/unordered_set/mod.rs
@@ -83,9 +83,11 @@ pub struct UnorderedSet<T, H = Sha256>
where
T: BorshSerialize + Ord,
H: ToKey,
{
+ #[borsh(bound(serialize = "", deserialize = ""))]
elements: FreeList<T>,
+ #[borsh(bound(serialize = "", deserialize = ""))]
index: LookupMap<T, FreeListIndex, H>,
}
```
## 10. next we encounter an error with `BorshSchema` trait derivation (`near-sdk` package):
```bash
4 error[E0053]: method `add_definitions_recursively` has an incompatible type for trait
--> near-sdk/src/promise.rs:232:22
|
232 | definitions: &mut HashMap<borsh::schema::Declaration, borsh::schema::Definition>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected `BTreeMap<String, Definition>`, found `HashMap<String, Definition>`
| help: change the parameter type to match the trait: `&mut BTreeMap<std::string::String, Definition>`
|
= note: expected signature `fn(&mut BTreeMap<std::string::String, Definition>)`
found signature `fn(&mut HashMap<std::string::String, Definition>)`
```
Signature in trait's method [has changed](https://github.com/near/borsh-rs/pull/165/):
```diff
diff --git a/near-sdk/src/promise.rs b/near-sdk/src/promise.rs
index f8afe56..a430568 100644
--- a/near-sdk/src/promise.rs
+++ b/near-sdk/src/promise.rs
@@ -2,3 +2,3 @@ use borsh::BorshSchema;
use std::cell::RefCell;
-use std::collections::HashMap;
+use std::collections::BTreeMap;
use std::io::{Error, Write};
@@ -231,3 +231,3 @@ impl BorshSchema for Promise {
fn add_definitions_recursively(
- definitions: &mut HashMap<borsh::schema::Declaration, borsh::schema::Definition>,
+ definitions: &mut BTreeMap<borsh::schema::Declaration, borsh::schema::Definition>,
) {
@@ -576,3 +576,3 @@ where
fn add_definitions_recursively(
- definitions: &mut HashMap<borsh::schema::Declaration, borsh::schema::Definition>,
+ definitions: &mut BTreeMap<borsh::schema::Declaration, borsh::schema::Definition>,
) {
```
## 11. next we encounter an error with both `BorshSerialize` and `BorshDeserialize` traits' derivation (`near-contract-standards` package):
```bash
1 error: proc-macro derive panicked
--> near-contract-standards/src/fungible_token/core_impl.rs:27:10
|
27 | #[derive(BorshDeserialize, BorshSerialize)]
| ^^^^^^^^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/home/user/Documents/code/near-sdk-rs/near-contract-standards/Cargo.toml" }
2 error: proc-macro derive panicked
--> near-contract-standards/src/fungible_token/core_impl.rs:27:28
|
27 | #[derive(BorshDeserialize, BorshSerialize)]
| ^^^^^^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/home/user/Documents/code/near-sdk-rs/near-contract-standards/Cargo.toml" }
```
Thing is, `borsh` [has started getting into a `panic`](https://github.com/near/borsh-rs/pull/149) when using [proc-macro-crate](https://crates.io/crates/proc-macro-crate) dependency for derives,
in the cases when `borsh` is not imported as direct dependency in the crate, which attempts to use its derive macros.
`near-contract-standards` wasn't importing `borsh` directly, just using `near-sdk`'s reexports.
We may instruct `BorshSerialize` and `BorshDeserialize` derives to skip this check of direct import
and to [use a reexported version](https://github.com/near/borsh-rs/pull/210) of `borsh` via following diff:
```diff
diff --git a/near-contract-standards/src/fungible_token/core_impl.rs b/near-contract-standards/src/fungible_token/core_impl.rs
index d61ee8e..cae776c 100644
--- a/near-contract-standards/src/fungible_token/core_impl.rs
+++ b/near-contract-standards/src/fungible_token/core_impl.rs
@@ -27,2 +27,3 @@ const ERR_TOTAL_SUPPLY_OVERFLOW: &str = "Total supply overflow";
#[derive(BorshDeserialize, BorshSerialize)]
+#[borsh(crate = "::near_sdk::borsh")]
pub struct FungibleToken {
```
## 12. finally, we update `borsh` version to `1.0.0`:
```diff
diff --git a/near-sdk/Cargo.toml b/near-sdk/Cargo.toml
index a015a64..e6099d4 100644
--- a/near-sdk/Cargo.toml
+++ b/near-sdk/Cargo.toml
@@ -26,3 +26,3 @@ near-sys = { path = "../near-sys", version = "0.2" }
base64 = "0.13"
-borsh = { version = "=1.0.0-alpha.5", features = ["derive"] }
+borsh = { version = "1.0.0", features = ["derive"] }
bs58 = "0.4"
```
@@ -0,0 +1,79 @@
# Crate features
### Ecosystem features
* **std** -
When enabled, `borsh` uses the standard library. Disabling this feature will
result in building the crate in `no_std` environment.
To carter such builds, Borsh offers [`io`] module which includes a items which
are used in [`BorshSerialize`] and [`BorshDeserialize`] traits. Most notably
`io::Read`, `io::Write` and `io::Result`.
When **std** feature is enabled, those items are re-exports of corresponding
`std::io` items. Otherwise they are borsh-specific types which mimic
behaviour of corresponding standard types.
### Default features
* **std** - enabled by default.
### Other features
* **derive** -
Gates derive macros of [BorshSerialize] and
[BorshDeserialize] traits.
* **unstable__schema** -
Gates [BorshSchema] trait and its derive macro.
Gates [schema] module.
This feature requires **derive** to be enabled too.
* **rc** -
Gates implementation of [BorshSerialize] and [BorshDeserialize]
for [`Rc<T>`](std::rc::Rc)/[`Arc<T>`](std::sync::Arc) respectively.
In `no_std` setting `Rc`/`Arc` are pulled from `alloc` crate.
Serializing and deserializing these types
does not preserve identity and may result in multiple copies of the same data.
Be sure that this is what you want before enabling this feature.
* **hashbrown** -
Pulls in [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet) when no `std` is available.
This feature is set to be mutually exclusive with **std** feature.
* **bytes** -
Gates implementation of [BorshSerialize] and [BorshDeserialize]
for [Bytes](https://docs.rs/bytes/1.5.0/bytes/struct.Bytes.html) and [BytesMut](https://docs.rs/bytes/1.5.0/bytes/struct.BytesMut.html).
In `no_std` builds this keeps `bytes` in `no_std` mode; when **std** is enabled,
`bytes/std` is enabled as well.
* **bson** -
Gates implementation of [BorshSerialize] and [BorshDeserialize]
for [ObjectId](https://docs.rs/bson/2.9.0/bson/oid/struct.ObjectId.html).
* **indexmap** -
Gates implementation of [BorshSerialize] and [BorshDeserialize]
for [indexmap::IndexMap](https://docs.rs/indexmap/2.8.0/indexmap/map/struct.IndexMap.html) and [IndexSet](https://docs.rs/indexmap/2.8.0/indexmap/set/struct.IndexSet.html)
* **ascii** -
Gates implementation of [BorshSerialize], [BorshDeserialize], [BorshSchema] for
types from [ascii](https://docs.rs/ascii/1.1.0/ascii/) crate.
* **de_strict_order** -
Enables check that keys, parsed during deserialization of
[HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet) and
[BTreeSet](std::collections::BTreeSet)/[BTreeMap](std::collections::BTreeMap)
are encountered in ascending order with respect to [PartialOrd] for hash collections,
and [Ord] for btree ones. Deserialization emits error otherwise.
If this feature is not enabled, it is possible that two different byte slices could deserialize into the same `HashMap`/`HashSet` object.
### Config aliases
* **hash_collections** -
This is a feature alias, set up in `build.rs` to be equivalent to (**std** OR **hashbrown**).
Gates implementation of [BorshSerialize], [BorshDeserialize]
and [BorshSchema]
for [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet).
# Shortcuts
Following pages are highlighted here just to give reader a chance at learning that
they exist.
- [Derive Macro `BorshSerialize`](macro@crate::BorshSerialize)
- [Derive Macro `BorshDeserialize`](macro@crate::BorshDeserialize)
- [Derive Macro `BorshSchema`](macro@crate::BorshSchema)
@@ -0,0 +1,376 @@
Derive macro available if borsh is built with `features = ["derive"]`.
# derive proc-macro for [`BorshDeserialize`] trait
## Bounds
Generally, `BorshDeserialize` adds `borsh::de::BorshDeserialize` bound to any type parameter
found in item's fields and `core::default::Default` bound to any type parameter found
in item's skipped fields.
```rust
use borsh::BorshDeserialize;
/// impl<U, V> borsh::de::BorshDeserialize for A<U, V>
/// where
/// U: borsh::de::BorshDeserialize,
/// V: borsh::de::BorshDeserialize,
#[derive(BorshDeserialize)]
struct A<U, V> {
x: U,
y: V,
}
```
```rust
use borsh::BorshDeserialize;
/// impl<U, V> borsh::de::BorshDeserialize for A<U, V>
/// where
/// U: borsh::de::BorshDeserialize,
/// V: core::default::Default,
#[derive(BorshDeserialize)]
struct A<U, V> {
x: U,
#[borsh(skip)]
y: V,
}
```
## Attributes
Multiple `#[borsh(...)]` attributes on the same item or field are merged: the
top-level keys of every attribute are combined into a single set. This makes it
possible to split disjoint keys across separate (e.g. `#[cfg_attr(...)]`-gated)
attributes. Supplying the same top-level key more than once is an error.
### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute)
###### syntax
Attribute takes literal string value, which is the syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html) to `borsh` crate used.
###### usage
Attribute is optional.
1. If the attribute is not provided, [crate_name](https://docs.rs/proc-macro-crate/3.2.0/proc_macro_crate/fn.crate_name.html) is used to find a version of `borsh`
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
```bash
1 error: proc-macro derive panicked
--> path/to/file.rs:27:10
|
27 | #[derive(BorshDeserialize, BorshSerialize)]
| ^^^^^^^^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/path/to/Cargo.toml" }
```
2. If the attribute is provided, the check for `borsh` in `[dependencies]` of the relevant `Cargo.toml` is skipped.
Examples of usage:
(example is not tested, as there's usually no `reexporter` crate during doc build)
```rust,ignore
use reexporter::borsh::BorshDeserialize;
// specifying the attribute removes need for a direct import of `borsh` into `[dependencies]`
#[derive(BorshDeserialize)]
#[borsh(crate = "reexporter::borsh")]
struct B {
x: u64,
y: i32,
c: String,
}
```
```rust,ignore
use reexporter::borsh::{self, BorshDeserialize};
// specifying the attribute removes need for a direct import of `borsh` into `[dependencies]`
#[derive(BorshDeserialize)]
#[borsh(crate = "borsh")]
struct B {
x: u64,
y: i32,
c: String,
}
```
### 2. `#[borsh(init=...)]` (item level attribute)
###### syntax
Attribute's value is syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html)-s, passed to borsh top level meta attribute as value of `init` argument.
###### usage
`#[borsh(init=...)]` allows to automatically run an initialization function right after deserialization.
This adds a lot of convenience for objects that are architectured to be used as strictly immutable.
```rust
type CryptoHash = String;
use borsh::BorshDeserialize;
#[derive(BorshDeserialize)]
#[borsh(init=init)]
struct Message {
message: String,
timestamp: u64,
hash: CryptoHash,
}
impl Message {
pub fn init(&mut self) {
self.hash = {
let mut hash = CryptoHash::new();
hash.push_str(&self.message);
hash.push_str(&format!("{}", self.timestamp));
hash
};
}
}
```
### 3. `borsh(use_discriminant=<bool>)` (item level attribute)
This attribute is only applicable to enums.
`use_discriminant` allows to override the default behavior of serialization of enums with explicit discriminant.
`use_discriminant` is `false` behaves like version of borsh of 0.10.3.
It's useful for backward compatibility and you can set this value to `false` to deserialise data serialised by older version of `borsh`.
You must specify `use_discriminant` for all enums with explicit discriminants in your project.
This is equivalent of borsh version 0.10.3 (explicit discriminant is ignored and this enum is equivalent to `A` without explicit discriminant):
```rust
use borsh::BorshDeserialize;
#[derive(BorshDeserialize)]
#[borsh(use_discriminant = false)]
enum A {
A,
B = 10,
}
```
To have explicit discriminant value serialized as is, you must specify `borsh(use_discriminant=true)` for enum.
```rust
use borsh::BorshDeserialize;
#[derive(BorshDeserialize)]
#[borsh(use_discriminant = true)]
enum B {
A,
B = 10,
}
```
###### borsh, expressions, evaluating to `isize`, as discriminant
This case is not supported:
```rust,compile_fail
use borsh::BorshDeserialize;
const fn discrim() -> isize {
0x14
}
#[derive(BorshDeserialize)]
#[borsh(use_discriminant = true)]
enum X {
A,
B = discrim(), // expressions, evaluating to `isize`, which are allowed outside of `borsh` context
C,
D,
E = 10,
F,
}
```
###### borsh explicit discriminant does not support literal values outside of u8 range.
This is not supported:
```rust,compile_fail
#[derive(BorshDeserialize)]
#[borsh(use_discriminant = true)]
enum X {
A,
B = 0x100, // literal values outside of `u8` range
C,
D,
E = 10,
F,
}
```
### 4. `#[borsh(skip)]` (field level attribute)
`#[borsh(skip)]` makes derive skip deserializing annotated field.
`#[borsh(skip)]` makes derive skip adding any type parameters, present in the field, to parameters bound by `borsh::de::BorshDeserialize`.
It adds `core::default::Default` bound to any
parameters encountered in annotated field.
```rust
use borsh::BorshDeserialize;
#[derive(BorshDeserialize)]
struct A {
x: u64,
#[borsh(skip)]
y: f32,
}
```
### 5. `#[borsh(bound(deserialize = ...))]` (field level attribute)
###### syntax
Attribute takes literal string value, which is a comma-separated list of syn's [WherePredicate](https://docs.rs/syn/latest/syn/enum.WherePredicate.html)-s, which may be empty.
###### usage
Attribute adds possibility to override bounds for `BorshDeserialize` in order to enable:
1. removal of bounds on type parameters from struct/enum definition itself and moving them to the trait's implementation block.
2. fixing complex cases, when derive hasn't figured out the right bounds on type parameters automatically.
```rust
use borsh::BorshDeserialize;
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
use core::hash::Hash;
/// additional bounds `T: Ord + Hash + Eq` (required by `HashMap`) are injected into
/// derived trait implementation via attribute to avoid adding the bounds on the struct itself
#[cfg(any(feature = "hashbrown", feature = "std"))]
#[derive(BorshDeserialize)]
struct A<T, U> {
a: String,
#[borsh(bound(
deserialize =
"T: Ord + Hash + Eq + borsh::de::BorshDeserialize,
U: borsh::de::BorshDeserialize"
))]
b: HashMap<T, U>,
}
```
```rust
use borsh::BorshDeserialize;
trait TraitName {
type Associated;
fn method(&self);
}
// derive here figures the bound erroneously as `T: borsh::de::BorshDeserialize,`
#[derive(BorshDeserialize)]
struct A<T, V>
where
T: TraitName,
{
#[borsh(bound(deserialize = "<T as TraitName>::Associated: borsh::de::BorshDeserialize"))]
field: <T as TraitName>::Associated,
another: V,
}
```
###### interaction with `#[borsh(skip)]`
`#[borsh(bound(deserialize = ...))]` replaces bounds, which are derived automatically,
irrelevant of whether `#[borsh(skip)]` attribute is present.
```rust
use borsh::BorshDeserialize;
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
/// implicit derived `core::default::Default` bounds on `K` and `V` type parameters are removed by
/// empty bound specified, as `HashMap` has its own `Default` implementation
#[cfg(any(feature = "hashbrown", feature = "std"))]
#[derive(BorshDeserialize)]
struct A<K, V, U>(
#[borsh(skip, bound(deserialize = ""))]
HashMap<K, V>,
U,
);
```
### 6. `#[borsh(deserialize_with = ...)]` (field level attribute)
###### syntax
Attribute takes literal string value, which is a syn's [ExprPath](https://docs.rs/syn/latest/syn/struct.ExprPath.html).
###### usage
Attribute adds possibility to specify full path of function, optionally qualified with generics,
with which to deserialize the annotated field.
It may be used when `BorshDeserialize` cannot be implemented for field's type, if it's from foreign crate.
It may be used to override the implementation of deserialization for some other reason.
```rust
use borsh::BorshDeserialize;
use indexmap::IndexMap;
use core::hash::Hash;
/// this a stub module, representing a 3rd party crate `indexmap`
mod indexmap {
/// this a stub struct, representing a 3rd party `indexmap::IndexMap`
/// or some local type we want to override trait implementation for
pub struct IndexMap<K, V> {
pub(crate) tuples: Vec<(K, V)>,
}
}
mod index_map_impl {
use super::IndexMap;
use core::hash::Hash;
pub fn deserialize_index_map<
R: borsh::io::Read,
K: borsh::de::BorshDeserialize + Hash + Eq,
V: borsh::de::BorshDeserialize,
>(
reader: &mut R,
) -> ::core::result::Result<IndexMap<K, V>, borsh::io::Error> {
let vec: Vec<(K, V)> = borsh::BorshDeserialize::deserialize_reader(reader)?;
// the line of implementation for type from real `indexmap` crate
// let result: IndexMap<K, V> = vec.into_iter().collect();
let result = IndexMap {
tuples: vec,
};
Ok(result)
}
}
#[derive(BorshDeserialize)]
struct B<K: Hash + Eq, V> {
#[borsh(
deserialize_with = "index_map_impl::deserialize_index_map",
)]
x: IndexMap<K, V>,
y: String,
}
# fn main() {
# }
```
###### usage (comprehensive example)
[borsh/examples/serde_json_value.rs](https://github.com/near/borsh-rs/blob/master/borsh/examples/serde_json_value.rs) is
a more complex example of how the attribute may be used.
###### interaction with `#[borsh(skip)]`
`#[borsh(deserialize_with = ...)]` is not allowed to be used simultaneously with `#[borsh(skip)]`.
@@ -0,0 +1,339 @@
Derive macro available if borsh is built with `features = ["unstable__schema"]`.
# derive proc-macro for [`BorshSchema`] trait
## Bounds
Generally, `BorshSchema` adds `borsh::BorshSchema` bound to any type parameter
found in item's fields.
```rust
use borsh::BorshSchema;
/// impl<U, V> borsh::BorshSchema for A<U, V>
/// where
/// U: borsh::BorshSchema,
/// V: borsh::BorshSchema,
#[derive(BorshSchema)]
struct A<U, V> {
x: U,
y: V,
}
```
```rust
use borsh::BorshSchema;
/// impl<U, V> borsh::BorshSchema for A<U, V>
/// where
/// U: borsh::BorshSchema,
#[derive(BorshSchema)]
struct A<U, V> {
x: U,
#[borsh(skip)]
y: V,
}
```
## Attributes
Multiple `#[borsh(...)]` attributes on the same item or field are merged: the
top-level keys of every attribute are combined into a single set. This makes it
possible to split disjoint keys across separate (e.g. `#[cfg_attr(...)]`-gated)
attributes. Supplying the same top-level key more than once is an error.
### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute)
###### syntax
Attribute takes literal string value, which is the syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html) to `borsh` crate used.
###### usage
Attribute is optional.
1. If the attribute is not provided, [crate_name](https://docs.rs/proc-macro-crate/3.2.0/proc_macro_crate/fn.crate_name.html) is used to find a version of `borsh`
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
```bash
1 error: proc-macro derive panicked
--> path/to/file.rs:27:10
|
27 | #[derive(BorshSchema, BorshSerialize)]
| ^^^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/path/to/Cargo.toml" }
```
2. If the attribute is provided, the check for `borsh` in `[dependencies]` of the relevant `Cargo.toml` is skipped.
Examples of usage:
(example is not tested, as there's usually no `reexporter` crate during doc build)
```rust,ignore
use reexporter::borsh::BorshSchema;
// specifying the attribute removes need for a direct import of `borsh` into `[dependencies]`
#[derive(BorshSchema)]
#[borsh(crate = "reexporter::borsh")]
struct B {
x: u64,
y: i32,
c: String,
}
```
```rust,ignore
use reexporter::borsh::{self, BorshSchema};
// specifying the attribute removes need for a direct import of `borsh` into `[dependencies]`
#[derive(BorshSchema)]
#[borsh(crate = "borsh")]
struct B {
x: u64,
y: i32,
c: String,
}
```
### 2. `borsh(use_discriminant=<bool>)` (item level attribute)
This attribute is only applicable to enums.
`use_discriminant` allows to override the default behavior of serialization of enums with explicit discriminant.
`use_discriminant` is `false` behaves like version of borsh of 0.10.3.
You must specify `use_discriminant` for all enums with explicit discriminants in your project.
This is equivalent of borsh version 0.10.3 (explicit discriminant is ignored and this enum is equivalent to `A` without explicit discriminant):
```rust
use borsh::BorshSchema;
#[derive(BorshSchema)]
#[borsh(use_discriminant = false)]
enum A {
A,
B = 10,
}
```
To have explicit discriminant value serialized as is, you must specify `borsh(use_discriminant=true)` for enum.
```rust
use borsh::BorshSchema;
#[derive(BorshSchema)]
#[borsh(use_discriminant = true)]
enum B {
A,
B = 10,
}
```
###### borsh, expressions, evaluating to `isize`, as discriminant
This case is not supported:
```rust,compile_fail
const fn discrim() -> isize {
0x14
}
#[derive(BorshSchema)]
#[borsh(use_discriminant = true)]
enum X {
A,
B = discrim(), // expressions, evaluating to `isize`, which are allowed outside of `borsh` context
C,
D,
E = 10,
F,
}
```
###### borsh explicit discriminant does not support literal values outside of u8 range
This is not supported:
```rust,compile_fail
#[derive(BorshSchema)]
#[borsh(use_discriminant = true)]
enum X {
A,
B = 0x100, // literal values outside of `u8` range
C,
D,
E = 10,
F,
}
```
### 3. `#[borsh(skip)]` (field level attribute)
`#[borsh(skip)]` makes derive skip including schema from annotated field into schema's implementation.
`#[borsh(skip)]` makes derive skip adding any type parameters, present in the field, to parameters bound by `borsh::BorshSchema`.
```rust
use borsh::BorshSchema;
#[derive(BorshSchema)]
struct A {
x: u64,
#[borsh(skip)]
y: f32,
}
```
### 4. `#[borsh(schema(params = ...))]` (field level attribute)
###### syntax
Attribute takes literal string value, which is a comma-separated list of `ParameterOverride`-s, which may be empty.
###### usage
It may be used in order to:
1. fix complex cases, when derive hasn't figured out the right bounds on type parameters and
declaration parameters automatically.
2. remove parameters, which do not take part in serialization/deserialization, from bounded ones and from declaration parameters.
`ParameterOverride` describes an entry like `order_param => override_type`,
e.g. `K => <K as TraitName>::Associated`.
Such an entry instructs `BorshSchema` derive to:
1. add `override_type` to types, bounded by `borsh::BorshSchema` in implementation block.
2. add `<override_type>::declaration()` to parameters vector in `fn declaration()` method of `BorshSchema` trait that is being derived.
3. the `order_param` is required to establish the same order in parameters vector (2.) as that of type parameters in generics of type, that `BorshSchema` is derived for.
4. entries, specified for a field, together replace whatever would've been derived automatically for 1. and 2. .
```rust
use borsh::BorshSchema;
trait TraitName {
type Associated;
fn method(&self);
}
// derive here figures the bound erroneously as `T: borsh::BorshSchema` .
// attribute replaces it with <T as TraitName>::Associated: borsh::BorshSchema`
#[derive(BorshSchema)]
struct A<V, T>
where
T: TraitName,
{
#[borsh(schema(params = "T => <T as TraitName>::Associated"))]
field: <T as TraitName>::Associated,
another: V,
}
```
```rust
use borsh::BorshSchema;
use core::marker::PhantomData;
trait EntityRef {
fn key_property(&self) -> u64;
}
// K in PrimaryMap isn't stored during serialization / read during deserialization.
// thus, it's not a parameter, relevant for `BorshSchema`
// ...
// impl<K: EntityRef, V> borsh::BorshSchema for A<K, V>
// where
// V: borsh::BorshSchema,
#[derive(BorshSchema)]
struct A<K: EntityRef, V> {
#[borsh(
schema(
params = "V => V"
)
)]
x: PrimaryMap<K, V>,
y: String,
}
#[derive(BorshSchema)]
pub struct PrimaryMap<K, V>
where
K: EntityRef,
{
elems: Vec<V>,
unused: PhantomData<K>,
}
```
###### interaction with `#[borsh(skip)]`
`#[borsh(schema(params = ...))]` is not allowed to be used simultaneously with `#[borsh(skip)]`.
### 5. `#[borsh(schema(with_funcs(declaration = ..., definitions = ...)))]` (field level attribute)
###### syntax
Each of `declaration` and `definitions` nested sub-attributes takes literal string value, which is a syn's [ExprPath](https://docs.rs/syn/latest/syn/struct.ExprPath.html).
Currently both `declaration` and `definitions` are required to be specified at the same time.
###### usage
Attribute adds possibility to specify full path of 2 functions, optionally qualified with generics,
with which to generate borsh schema for annotated field.
It may be used when `BorshSchema` cannot be implemented for field's type, if it's from foreign crate.
It may be used to override the implementation of schema for some other reason.
```rust
use borsh::BorshSchema;
use indexmap::IndexMap;
/// this a stub module, representing a 3rd party crate `indexmap`
mod indexmap {
/// this a stub struct, representing a 3rd party `indexmap::IndexMap`
/// or some local type we want to override trait implementation for
pub struct IndexMap<K, V> {
pub(crate) tuples: Vec<(K, V)>,
}
}
mod index_map_impl {
pub mod schema {
use std::collections::BTreeMap;
use borsh::{
schema::{Declaration, Definition, self},
BorshSchema,
};
pub fn declaration<K: BorshSchema, V: BorshSchema>() -> Declaration {
let params = vec![<K>::declaration(), <V>::declaration()];
format!(r#"{}<{}>"#, "IndexMap", params.join(", "))
}
pub fn add_definitions_recursively<K: BorshSchema, V: BorshSchema>(
definitions: &mut BTreeMap<Declaration, Definition>,
) {
let definition = Definition::Sequence {
elements: <(K, V)>::declaration(),
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
};
let no_recursion_flag = definitions.get(&declaration::<K, V>()).is_none();
schema::add_definition(declaration::<K, V>(), definition, definitions);
if no_recursion_flag {
<(K, V)>::add_definitions_recursively(definitions);
}
}
}
}
#[derive(BorshSchema)]
struct B<K, V> {
#[borsh(
schema(
with_funcs(
declaration = "index_map_impl::schema::declaration::<K, V>",
definitions = "index_map_impl::schema::add_definitions_recursively::<K, V>"
),
)
)]
x: IndexMap<K, V>,
y: String,
}
# fn main() {
# }
```
###### interaction with `#[borsh(skip)]`
`#[borsh(schema(with_funcs(declaration = ..., definitions = ...)))]` is not allowed to be used simultaneously with `#[borsh(skip)]`.
@@ -0,0 +1,306 @@
Derive macro available if borsh is built with `features = ["derive"]`.
# derive proc-macro for [`BorshSerialize`] trait
## Bounds
Generally, `BorshSerialize` adds `borsh::ser::BorshSerialize` bound to any type parameter
found in item's fields.
```rust
use borsh::BorshSerialize;
/// impl<U, V> borsh::ser::BorshSerialize for A<U, V>
/// where
/// U: borsh::ser::BorshSerialize,
/// V: borsh::ser::BorshSerialize,
#[derive(BorshSerialize)]
struct A<U, V> {
x: U,
y: V,
}
```
```rust
use borsh::BorshSerialize;
/// impl<U, V> borsh::ser::BorshSerialize for A<U, V>
/// where
/// U: borsh::ser::BorshSerialize,
#[derive(BorshSerialize)]
struct A<U, V> {
x: U,
#[borsh(skip)]
y: V,
}
```
## Attributes
Multiple `#[borsh(...)]` attributes on the same item or field are merged: the
top-level keys of every attribute are combined into a single set. This makes it
possible to split disjoint keys across separate (e.g. `#[cfg_attr(...)]`-gated)
attributes. Supplying the same top-level key more than once is an error.
### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute)
###### syntax
Attribute takes literal string value, which is the syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html) to `borsh` crate used.
###### usage
Attribute is optional.
1. If the attribute is not provided, [crate_name](https://docs.rs/proc-macro-crate/3.2.0/proc_macro_crate/fn.crate_name.html) is used to find a version of `borsh`
in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised:
```bash
1 error: proc-macro derive panicked
--> path/to/file.rs:27:10
|
27 | #[derive(BorshSerialize, BorshDeserialize)]
| ^^^^^^^^^^^^^^
|
= help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/path/to/Cargo.toml" }
```
2. If the attribute is provided, the check for `borsh` in `[dependencies]` of the relevant `Cargo.toml` is skipped.
Examples of usage:
(example is not tested, as there's usually no `reexporter` crate during doc build)
```rust,ignore
use reexporter::borsh::BorshSerialize;
// specifying the attribute removes need for a direct import of `borsh` into `[dependencies]`
#[derive(BorshSerialize)]
#[borsh(crate = "reexporter::borsh")]
struct B {
x: u64,
y: i32,
c: String,
}
```
```rust,ignore
use reexporter::borsh::{self, BorshSerialize};
// specifying the attribute removes need for a direct import of `borsh` into `[dependencies]`
#[derive(BorshSerialize)]
#[borsh(crate = "borsh")]
struct B {
x: u64,
y: i32,
c: String,
}
```
### 2. `borsh(use_discriminant=<bool>)` (item level attribute)
This attribute is only applicable to enums.
`use_discriminant` allows to override the default behavior of serialization of enums with explicit discriminant.
`use_discriminant` is `false` behaves like version of borsh of 0.10.3.
You must specify `use_discriminant` for all enums with explicit discriminants in your project.
This is equivalent of borsh version 0.10.3 (explicit discriminant is ignored and this enum is equivalent to `A` without explicit discriminant):
```rust
use borsh::BorshSerialize;
#[derive(BorshSerialize)]
#[borsh(use_discriminant = false)]
enum A {
A,
B = 10,
}
```
To have explicit discriminant value serialized as is, you must specify `borsh(use_discriminant=true)` for enum.
```rust
use borsh::BorshSerialize;
#[derive(BorshSerialize)]
#[borsh(use_discriminant = true)]
enum B {
A,
B = 10,
}
```
###### borsh, expressions, evaluating to `isize`, as discriminant
This case is not supported:
```rust,compile_fail
const fn discrim() -> isize {
0x14
}
#[derive(BorshSerialize)]
#[borsh(use_discriminant = true)]
enum X {
A,
B = discrim(), // expressions, evaluating to `isize`, which are allowed outside of `borsh` context
C,
D,
E = 10,
F,
}
```
###### borsh explicit discriminant does not support literal values outside of u8 range
This is not supported:
```rust,compile_fail
#[derive(BorshSerialize)]
#[borsh(use_discriminant = true)]
enum X {
A,
B = 0x100, // literal values outside of `u8` range
C,
D,
E = 10,
F,
}
```
### 3. `#[borsh(skip)]` (field level attribute)
`#[borsh(skip)]` makes derive skip serializing annotated field.
`#[borsh(skip)]` makes derive skip adding any type parameters, present in the field, to parameters bound by `borsh::ser::BorshSerialize`.
```rust
use borsh::BorshSerialize;
#[derive(BorshSerialize)]
struct A {
x: u64,
#[borsh(skip)]
y: f32,
}
```
### 4. `#[borsh(bound(serialize = ...))]` (field level attribute)
###### syntax
Attribute takes literal string value, which is a comma-separated list of syn's [WherePredicate](https://docs.rs/syn/latest/syn/enum.WherePredicate.html)-s, which may be empty.
###### usage
Attribute adds possibility to override bounds for `BorshSerialize` in order to enable:
1. removal of bounds on type parameters from struct/enum definition itself and moving them to the trait's implementation block.
2. fixing complex cases, when derive hasn't figured out the right bounds on type parameters automatically.
```rust
use borsh::BorshSerialize;
#[cfg(feature = "hashbrown")]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;
/// additional bound `T: Ord` (required by `HashMap`) is injected into
/// derived trait implementation via attribute to avoid adding the bounds on the struct itself
#[cfg(any(feature = "hashbrown", feature = "std"))]
#[derive(BorshSerialize)]
struct A<T, U> {
a: String,
#[borsh(bound(serialize =
"T: borsh::ser::BorshSerialize + Ord,
U: borsh::ser::BorshSerialize"))]
b: HashMap<T, U>,
}
```
```rust
use borsh::BorshSerialize;
trait TraitName {
type Associated;
fn method(&self);
}
/// derive here figures the bound erroneously as `T: borsh::ser::BorshSerialize`
#[derive(BorshSerialize)]
struct A<T, V>
where
T: TraitName,
{
#[borsh(bound(serialize = "<T as TraitName>::Associated: borsh::ser::BorshSerialize"))]
field: <T as TraitName>::Associated,
another: V,
}
```
###### interaction with `#[borsh(skip)]`
`#[borsh(bound(serialize = ...))]` replaces bounds, which are derived automatically,
irrelevant of whether `#[borsh(skip)]` attribute is present.
### 5. `#[borsh(serialize_with = ...)]` (field level attribute)
###### syntax
Attribute takes literal string value, which is a syn's [ExprPath](https://docs.rs/syn/latest/syn/struct.ExprPath.html).
###### usage
Attribute adds possibility to specify full path of function, optionally qualified with generics,
with which to serialize the annotated field.
It may be used when `BorshSerialize` cannot be implemented for field's type, if it's from foreign crate.
It may be used to override the implementation of serialization for some other reason.
```rust
use borsh::BorshSerialize;
use indexmap::IndexMap;
/// this a stub module, representing a 3rd party crate `indexmap`
mod indexmap {
/// this a stub struct, representing a 3rd party `indexmap::IndexMap`
/// or some local type we want to override trait implementation for
pub struct IndexMap<K, V> {
pub(crate) tuples: Vec<(K, V)>,
}
}
mod index_map_impl {
use super::indexmap::IndexMap;
pub fn serialize_index_map<
K: borsh::ser::BorshSerialize,
V: borsh::ser::BorshSerialize,
W: borsh::io::Write,
>(
obj: &IndexMap<K, V>,
writer: &mut W,
) -> ::core::result::Result<(), borsh::io::Error> {
// the line of implementation for type from real `indexmap` crate
// let key_value_tuples = obj.iter().collect::<Vec<_>>();
let key_value_tuples = obj.tuples.iter().collect::<Vec<_>>();
borsh::BorshSerialize::serialize(&key_value_tuples, writer)?;
Ok(())
}
}
#[derive(BorshSerialize)]
struct B<K, V> {
#[borsh(
serialize_with = "index_map_impl::serialize_index_map",
)]
x: IndexMap<K, V>,
y: String,
}
# fn main() {
# }
```
###### usage (comprehensive example)
[borsh/examples/serde_json_value.rs](https://github.com/near/borsh-rs/blob/master/borsh/examples/serde_json_value.rs) is
a more complex example of how the attribute may be used.
###### interaction with `#[borsh(skip)]`
`#[borsh(serialize_with = ...)]` is not allowed to be used simultaneously with `#[borsh(skip)]`.