From 4098923868fcf1a6713479ba54af688d77f52154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Sat, 28 Dec 2024 22:21:18 +0200 Subject: [PATCH] doc: transfer doc for `BorshDeserialize` --- borsh-derive/src/lib.rs | 320 +--------------------- borsh/src/lib.rs | 2 +- docs/rustdoc_include/borsh_deserialize.md | 319 +++++++++++++++++++++ docs/rustdoc_include/borsh_serialize.md | 2 + 4 files changed, 323 insertions(+), 320 deletions(-) create mode 100644 docs/rustdoc_include/borsh_deserialize.md diff --git a/borsh-derive/src/lib.rs b/borsh-derive/src/lib.rs index f465e844e..ff1a875be 100644 --- a/borsh-derive/src/lib.rs +++ b/borsh-derive/src/lib.rs @@ -56,325 +56,7 @@ pub fn borsh_serialize(input: TokenStream) -> TokenStream { }) } -/** -# derive proc-macro for `borsh::de::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. - -```ignore -/// impl borsh::de::BorshDeserialize for A -/// where -/// U: borsh::de::BorshDeserialize, -/// V: borsh::de::BorshDeserialize, -#[derive(BorshDeserialize)] -struct A { - x: U, - y: V, -} -``` - -```ignore -/// impl borsh::de::BorshDeserialize for A -/// where -/// U: borsh::de::BorshDeserialize, -/// V: core::default::Default, -#[derive(BorshDeserialize)] -struct A { - x: U, - #[borsh(skip)] - y: V, -} -``` - - -## Attributes - -### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute) - -###### syntax - -Attribute takes literal string value, which is the syn's [Path] to `borsh` crate used. - -###### usage - -Attribute is optional. - -1. If the attribute is not provided, [crate_name](proc_macro_crate::crate_name) 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: - -```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, -} -``` - -```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]-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. - -```ignore -#[derive(BorshDeserialize)] -#[borsh(init=init)] -struct Message { - message: String, - timestamp: u64, - public_key: CryptoKey, - signature: CryptoSignature, - hash: CryptoHash, -} - -impl Message { - pub fn init(&mut self) { - self.hash = CryptoHash::new().write_string(self.message).write_u64(self.timestamp); - self.signature.verify(self.hash, self.public_key); - } -} -``` - -### 3. `borsh(use_discriminant=)` (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): -```ignore -#[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. -```ignore -#[derive(BorshDeserialize)] -#[borsh(use_discriminant = true)] -enum B { - A - B = 10, -} -``` - - -###### borsh, expressions, evaluating to `isize`, as discriminant -This case is not supported: -```ignore -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: -```ignore -#[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. - - -```ignore -#[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](syn::WherePredicate)-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. - -```ignore -/// 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 -#[derive(BorshDeserialize)] -struct A { - a: String, - #[borsh(bound( - deserialize = - "T: Ord + Hash + Eq + borsh::de::BorshDeserialize, - U: borsh::de::BorshDeserialize" - ))] - b: HashMap, -} -``` - - -```ignore -// derive here figures the bound erroneously as `T: borsh::de::BorshDeserialize,` -#[derive(BorshDeserialize)] -struct A -where - T: TraitName, -{ - #[borsh(bound(deserialize = "::Associated: borsh::de::BorshDeserialize"))] - field: ::Associated, - another: V, -} -``` - -###### interaction with `#[borsh(skip)]` - -`#[borsh(bound(deserialize = ...))]` replaces bounds, which are derived automatically, -irrelevant of whether `#[borsh(skip)]` attribute is present. - -```ignore -/// 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 -#[derive(BorshDeserialize)] -struct A( - #[borsh(skip, bound(deserialize = ""))] - HashMap, - U, -); -``` - -### 6. `#[borsh(deserialize_with = ...)]` (field level attribute) - -###### syntax - -Attribute takes literal string value, which is a syn's [ExprPath](syn::ExprPath). - -###### 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. - -```ignore -use indexmap::IndexMap; - -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, borsh::io::Error> { - let vec: Vec<(K, V)> = borsh::BorshDeserialize::deserialize_reader(reader)?; - let result: IndexMap = vec.into_iter().collect(); - Ok(result) - } -} - -#[derive(BorshDeserialize)] -struct B { - #[borsh( - deserialize_with = "index_map_impl::deserialize_index_map", - )] - x: IndexMap, - y: String, -} -``` - -###### 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)]`. - -*/ +/// moved to docs of **Derive Macro** `BorshDeserialize` in `borsh` crate #[proc_macro_derive(BorshDeserialize, attributes(borsh))] pub fn borsh_deserialize(input: TokenStream) -> TokenStream { let cratename = match check_attrs_get_cratename(&input) { diff --git a/borsh/src/lib.rs b/borsh/src/lib.rs index 9b88404fa..a6e679bc3 100644 --- a/borsh/src/lib.rs +++ b/borsh/src/lib.rs @@ -77,7 +77,7 @@ extern crate alloc; #[cfg(feature = "unstable__schema")] pub use borsh_derive::BorshSchema; -/// Derive macro available if borsh is built with `features = ["derive"]`. +#[doc = include_str!("../../docs/rustdoc_include/borsh_deserialize.md")] #[cfg(feature = "derive")] pub use borsh_derive::BorshDeserialize; diff --git a/docs/rustdoc_include/borsh_deserialize.md b/docs/rustdoc_include/borsh_deserialize.md new file mode 100644 index 000000000..b5306f90a --- /dev/null +++ b/docs/rustdoc_include/borsh_deserialize.md @@ -0,0 +1,319 @@ +Derive macro available if borsh is built with `features = ["derive"]`. + +# derive proc-macro for `borsh::de::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. + +```ignore +/// impl borsh::de::BorshDeserialize for A +/// where +/// U: borsh::de::BorshDeserialize, +/// V: borsh::de::BorshDeserialize, +#[derive(BorshDeserialize)] +struct A { + x: U, + y: V, +} +``` + +```ignore +/// impl borsh::de::BorshDeserialize for A +/// where +/// U: borsh::de::BorshDeserialize, +/// V: core::default::Default, +#[derive(BorshDeserialize)] +struct A { + x: U, + #[borsh(skip)] + y: V, +} +``` + + +## Attributes + +### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute) + +###### syntax + +Attribute takes literal string value, which is the syn's [Path] to `borsh` crate used. + +###### usage + +Attribute is optional. + +1. If the attribute is not provided, [crate_name](proc_macro_crate::crate_name) 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: + +```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, +} +``` + +```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]-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. + +```ignore +#[derive(BorshDeserialize)] +#[borsh(init=init)] +struct Message { + message: String, + timestamp: u64, + public_key: CryptoKey, + signature: CryptoSignature, + hash: CryptoHash, +} + +impl Message { + pub fn init(&mut self) { + self.hash = CryptoHash::new().write_string(self.message).write_u64(self.timestamp); + self.signature.verify(self.hash, self.public_key); + } +} +``` + +### 3. `borsh(use_discriminant=)` (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): +```ignore +#[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. +```ignore +#[derive(BorshDeserialize)] +#[borsh(use_discriminant = true)] +enum B { + A + B = 10, +} +``` + + +###### borsh, expressions, evaluating to `isize`, as discriminant +This case is not supported: +```ignore +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: +```ignore +#[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. + + +```ignore +#[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](syn::WherePredicate)-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. + +```ignore +/// 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 +#[derive(BorshDeserialize)] +struct A { + a: String, + #[borsh(bound( + deserialize = + "T: Ord + Hash + Eq + borsh::de::BorshDeserialize, + U: borsh::de::BorshDeserialize" + ))] + b: HashMap, +} +``` + + +```ignore +// derive here figures the bound erroneously as `T: borsh::de::BorshDeserialize,` +#[derive(BorshDeserialize)] +struct A +where + T: TraitName, +{ + #[borsh(bound(deserialize = "::Associated: borsh::de::BorshDeserialize"))] + field: ::Associated, + another: V, +} +``` + +###### interaction with `#[borsh(skip)]` + +`#[borsh(bound(deserialize = ...))]` replaces bounds, which are derived automatically, +irrelevant of whether `#[borsh(skip)]` attribute is present. + +```ignore +/// 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 +#[derive(BorshDeserialize)] +struct A( + #[borsh(skip, bound(deserialize = ""))] + HashMap, + U, +); +``` + +### 6. `#[borsh(deserialize_with = ...)]` (field level attribute) + +###### syntax + +Attribute takes literal string value, which is a syn's [ExprPath](syn::ExprPath). + +###### 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. + +```ignore +use indexmap::IndexMap; + +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, borsh::io::Error> { + let vec: Vec<(K, V)> = borsh::BorshDeserialize::deserialize_reader(reader)?; + let result: IndexMap = vec.into_iter().collect(); + Ok(result) + } +} + +#[derive(BorshDeserialize)] +struct B { + #[borsh( + deserialize_with = "index_map_impl::deserialize_index_map", + )] + x: IndexMap, + y: String, +} +``` + +###### 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)]`. + diff --git a/docs/rustdoc_include/borsh_serialize.md b/docs/rustdoc_include/borsh_serialize.md index 89eb1e8e7..0d5cdc4ef 100644 --- a/docs/rustdoc_include/borsh_serialize.md +++ b/docs/rustdoc_include/borsh_serialize.md @@ -1,3 +1,5 @@ +Derive macro available if borsh is built with `features = ["derive"]`. + # derive proc-macro for `borsh::ser::BorshSerialize` trait ## Bounds