From 4561d4fdda0ed53043c1d514ff8158bb61af0543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= Date: Sat, 28 Dec 2024 16:15:26 +0200 Subject: [PATCH] doc: transfer doc for `BorshSerialize` --- borsh-derive/src/lib.rs | 264 +----------------------- borsh/src/lib.rs | 6 +- docs/rustdoc_include/borsh_serialize.md | 263 +++++++++++++++++++++++ 3 files changed, 269 insertions(+), 264 deletions(-) create mode 100644 docs/rustdoc_include/borsh_serialize.md diff --git a/borsh-derive/src/lib.rs b/borsh-derive/src/lib.rs index 1cebd6f4a..24280bc0a 100644 --- a/borsh-derive/src/lib.rs +++ b/borsh-derive/src/lib.rs @@ -29,269 +29,7 @@ fn check_attrs_get_cratename(input: &TokenStream) -> Result { cratename::get(&derive_input.attrs) } -/** -# derive proc-macro for `borsh::ser::BorshSerialize` trait - -## Bounds - -Generally, `BorshSerialize` adds `borsh::ser::BorshSerialize` bound to any type parameter -found in item's fields. - -```ignore -/// impl borsh::ser::BorshSerialize for A -/// where -/// U: borsh::ser::BorshSerialize, -/// V: borsh::ser::BorshSerialize, -#[derive(BorshSerialize)] -struct A { - x: U, - y: V, -} -``` - -```ignore -/// impl borsh::ser::BorshSerialize for A -/// where -/// U: borsh::ser::BorshSerialize, -#[derive(BorshSerialize)] -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(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: - -```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, -} -``` - -```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=)` (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): -```ignore -#[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. -```ignore -#[derive(BorshSerialize)] -#[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(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: -```ignore -#[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`. - -```ignore -#[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](syn::WherePredicate)-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. - -```ignore -/// additional bound `T: Ord` (required by `HashMap`) is injected into -/// derived trait implementation via attribute to avoid adding the bounds on the struct itself -#[derive(BorshSerialize)] -struct A { - a: String, - #[borsh(bound(serialize = - "T: borsh::ser::BorshSerialize + Ord, - U: borsh::ser::BorshSerialize"))] - b: HashMap, -} -``` - - -```ignore -/// derive here figures the bound erroneously as `T: borsh::ser::BorshSerialize` -#[derive(BorshSerialize)] -struct A -where - T: TraitName, -{ - #[borsh(bound(serialize = "::Associated: borsh::ser::BorshSerialize"))] - field: ::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](syn::ExprPath). - -###### 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. - -```ignore -use indexmap::IndexMap; - -mod index_map_impl { - use super::IndexMap; - use core::hash::Hash; - - pub fn serialize_index_map< - K: borsh::ser::BorshSerialize, - V: borsh::ser::BorshSerialize, - W: borsh::io::Write, - >( - obj: &IndexMap, - writer: &mut W, - ) -> ::core::result::Result<(), borsh::io::Error> { - let key_value_tuples = obj.iter().collect::>(); - borsh::BorshSerialize::serialize(&key_value_tuples, writer)?; - Ok(()) - } -} - -#[derive(BorshSerialize)] -struct B { - #[borsh( - serialize_with = "index_map_impl::serialize_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(serialize_with = ...)]` is not allowed to be used simultaneously with `#[borsh(skip)]`. - -*/ +/// moved to documnetation of **Derive Macro** `BorshSerialize` in `borsh` crate #[proc_macro_derive(BorshSerialize, attributes(borsh))] pub fn borsh_serialize(input: TokenStream) -> TokenStream { let cratename = match check_attrs_get_cratename(&input) { diff --git a/borsh/src/lib.rs b/borsh/src/lib.rs index eafad58a4..9b88404fa 100644 --- a/borsh/src/lib.rs +++ b/borsh/src/lib.rs @@ -79,7 +79,11 @@ pub use borsh_derive::BorshSchema; /// Derive macro available if borsh is built with `features = ["derive"]`. #[cfg(feature = "derive")] -pub use borsh_derive::{BorshDeserialize, BorshSerialize}; +pub use borsh_derive::BorshDeserialize; + +#[doc = include_str!("../../docs/rustdoc_include/borsh_serialize.md")] +#[cfg(feature = "derive")] +pub use borsh_derive::BorshSerialize; pub mod de; diff --git a/docs/rustdoc_include/borsh_serialize.md b/docs/rustdoc_include/borsh_serialize.md new file mode 100644 index 000000000..fde132ace --- /dev/null +++ b/docs/rustdoc_include/borsh_serialize.md @@ -0,0 +1,263 @@ +# derive proc-macro for `borsh::ser::BorshSerialize` trait + +## Bounds + +Generally, `BorshSerialize` adds `borsh::ser::BorshSerialize` bound to any type parameter +found in item's fields. + +```rust +use borsh::BorshSerialize; + +/// impl borsh::ser::BorshSerialize for A +/// where +/// U: borsh::ser::BorshSerialize, +/// V: borsh::ser::BorshSerialize, +#[derive(BorshSerialize)] +struct A { + x: U, + y: V, +} +``` + +```ignore +/// impl borsh::ser::BorshSerialize for A +/// where +/// U: borsh::ser::BorshSerialize, +#[derive(BorshSerialize)] +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(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: + +```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, +} +``` + +```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=)` (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): +```ignore +#[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. +```ignore +#[derive(BorshSerialize)] +#[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(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: +```ignore +#[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`. + +```ignore +#[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](syn::WherePredicate)-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. + +```ignore +/// additional bound `T: Ord` (required by `HashMap`) is injected into +/// derived trait implementation via attribute to avoid adding the bounds on the struct itself +#[derive(BorshSerialize)] +struct A { + a: String, + #[borsh(bound(serialize = + "T: borsh::ser::BorshSerialize + Ord, + U: borsh::ser::BorshSerialize"))] + b: HashMap, +} +``` + + +```ignore +/// derive here figures the bound erroneously as `T: borsh::ser::BorshSerialize` +#[derive(BorshSerialize)] +struct A +where + T: TraitName, +{ + #[borsh(bound(serialize = "::Associated: borsh::ser::BorshSerialize"))] + field: ::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](syn::ExprPath). + +###### 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. + +```ignore +use indexmap::IndexMap; + +mod index_map_impl { + use super::IndexMap; + use core::hash::Hash; + + pub fn serialize_index_map< + K: borsh::ser::BorshSerialize, + V: borsh::ser::BorshSerialize, + W: borsh::io::Write, + >( + obj: &IndexMap, + writer: &mut W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let key_value_tuples = obj.iter().collect::>(); + borsh::BorshSerialize::serialize(&key_value_tuples, writer)?; + Ok(()) + } +} + +#[derive(BorshSerialize)] +struct B { + #[borsh( + serialize_with = "index_map_impl::serialize_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(serialize_with = ...)]` is not allowed to be used simultaneously with `#[borsh(skip)]`. +