diff --git a/.github/test.sh b/.github/test.sh index 2fdbf58b9..351a87912 100755 --- a/.github/test.sh +++ b/.github/test.sh @@ -15,6 +15,9 @@ pushd borsh cargo test --no-run cargo test cargo test --features derive +cargo test --features derive,unstable__tokio +cargo test --features derive,unstable__async-std +cargo test --features derive,unstable__async 'compile_derives::async_derives' cargo test --features unstable__schema ########## features = ["ascii"] group cargo test --features ascii 'roundtrip::test_ascii_strings' diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 087983950..ce3e62532 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,7 +20,7 @@ jobs: tests: strategy: matrix: - rust_version: [1.67, stable] + rust_version: [1.75.0, stable] runs-on: ubuntu-20.04 steps: @@ -63,7 +63,13 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run clippy - run: cargo clippy --features unstable__schema --benches -- -D clippy::all + run: cargo clippy --features rc,ascii,unstable__schema --benches -- -D clippy::all + - name: Run clippy (async) + run: cargo clippy --features rc,ascii,unstable__schema,unstable__async --benches -- -D clippy::all + - name: Run clippy (tokio) + run: cargo clippy --features rc,ascii,unstable__schema,unstable__tokio --benches -- -D clippy::all + - name: Run clippy (async-std) + run: cargo clippy --features rc,ascii,unstable__schema,unstable__async-std --benches -- -D clippy::all cargo-fmt: runs-on: ubuntu-20.04 @@ -82,7 +88,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: run cargo doc - run: RUSTDOCFLAGS="-D warnings" cargo doc --features derive,unstable__schema + run: RUSTDOCFLAGS="-D warnings" cargo doc --features derive,rc,ascii,unstable__schema,unstable__async release-plz: runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 985557bb4..9977a334f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,9 @@ [workspace] +resolver = "2" members = ["borsh", "borsh-derive", "fuzz/fuzz-run", "benchmarks"] [workspace.package] # shared version of all public crates in the workspace version = "1.5.5" -rust-version = "1.67.0" +edition = "2021" +rust-version = "1.75.0" diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index aad6202b9..241c11d73 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -3,7 +3,7 @@ name = "benchmarks" version.workspace = true rust-version.workspace = true authors = ["Near Inc "] -edition = "2018" +edition.workspace = true publish = false # This is somehow needed for command line arguments to work: https://github.com/bheisler/criterion.rs/issues/193#issuecomment-415740713 @@ -13,7 +13,7 @@ bench = false [dependencies] rand_xorshift = "0.2.0" rand = "0.7.0" -borsh = { path = "../borsh", default-features = false } +borsh = { path = "../borsh", default-features = false, features = ["std", "derive"] } serde = { version = "1.0", features = ["derive"] } speedy-derive = "0.5" speedy = "0.5" @@ -39,6 +39,3 @@ harness = false [[bench]] name = "object_length" harness = false - -[features] -default = ["borsh/std", "borsh/derive"] diff --git a/benchmarks/benches/bench.rs b/benchmarks/benches/bench.rs index 75199bc88..6dde9d754 100644 --- a/benchmarks/benches/bench.rs +++ b/benchmarks/benches/bench.rs @@ -14,8 +14,8 @@ where + BorshDeserialize + SerdeSerialize + SerdeDeserialize<'a> - + Readable<'a, speedy::Endianness> - + Writable + + Readable<'a, Endianness> + + Writable + 'static, { let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); @@ -71,8 +71,8 @@ where + BorshDeserialize + SerdeSerialize + SerdeDeserialize<'a> - + Readable<'a, speedy::Endianness> - + Writable + + Readable<'a, Endianness> + + Writable + 'static, { let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); diff --git a/benchmarks/benches/object_length.rs b/benchmarks/benches/object_length.rs index e69e62a78..ca83b3f38 100644 --- a/benchmarks/benches/object_length.rs +++ b/benchmarks/benches/object_length.rs @@ -1,5 +1,5 @@ use benchmarks::{Generate, ValidatorStake}; -use borsh::{to_vec, BorshSerialize}; +use borsh::BorshSerialize; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use rand::SeedableRng; @@ -11,7 +11,7 @@ where let mut group = c.benchmark_group(group_name); let objects: Vec<_> = (0..num_samples).map(|_| T::generate(&mut rng)).collect(); - let borsh_datas: Vec> = objects.iter().map(|t| to_vec(t).unwrap()).collect(); + let borsh_datas: Vec> = objects.iter().map(|t| borsh::to_vec(t).unwrap()).collect(); let borsh_sizes: Vec<_> = borsh_datas.iter().map(|d| d.len()).collect(); for i in 0..objects.len() { diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs index 36474ba76..35a8e8d21 100644 --- a/benchmarks/src/lib.rs +++ b/benchmarks/src/lib.rs @@ -1,14 +1,15 @@ //! This library contains data structures used for benchmarking. +extern crate speedy_derive; + use borsh::{BorshDeserialize, BorshSerialize}; use rand::distributions::{Alphanumeric, Distribution, Standard}; use rand::Rng; use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; -extern crate speedy_derive; use speedy::{Context, Readable, Reader, Writable, Writer}; pub trait Generate { - fn generate(rng: &mut R) -> Self; + fn generate(rng: &mut R) -> Self; } #[derive( @@ -16,7 +17,7 @@ pub trait Generate { )] pub struct CryptoHash([u8; 32]); impl Generate for CryptoHash { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { let mut res = [0u8; 32]; rng.fill_bytes(&mut res); CryptoHash(res) @@ -27,7 +28,7 @@ impl<'a, C> Readable<'a, C> for CryptoHash where C: Context, { - fn read_from>(reader: &mut R) -> std::result::Result { + fn read_from>(reader: &mut R) -> Result { let mut data = [0u8; 32]; reader.read_bytes(&mut data)?; Ok(Self(data)) @@ -38,7 +39,7 @@ impl Writable for CryptoHash { fn write_to<'a, T: ?Sized + Writer<'a, C>>( &'a self, writer: &mut T, - ) -> std::result::Result<(), std::io::Error> { + ) -> Result<(), std::io::Error> { writer.write_bytes(&self.0).map(|_| ()) } } @@ -48,7 +49,7 @@ impl Writable for CryptoHash { )] pub struct MerkleHash([u8; 32]); impl Generate for MerkleHash { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { let mut res = [0u8; 32]; rng.fill_bytes(&mut res); MerkleHash(res) @@ -59,7 +60,7 @@ impl<'a, C> Readable<'a, C> for MerkleHash where C: Context, { - fn read_from>(reader: &mut R) -> std::result::Result { + fn read_from>(reader: &mut R) -> Result { let mut data = [0u8; 32]; reader.read_bytes(&mut data)?; Ok(Self(data)) @@ -70,7 +71,7 @@ impl Writable for MerkleHash { fn write_to<'a, T: ?Sized + Writer<'a, C>>( &'a self, writer: &mut T, - ) -> std::result::Result<(), std::io::Error> { + ) -> Result<(), std::io::Error> { writer.write_bytes(&self.0).map(|_| ()) } } @@ -80,7 +81,7 @@ impl Writable for MerkleHash { )] pub struct Signature([u8; 32]); impl Generate for Signature { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { let mut res = [0u8; 32]; rng.fill_bytes(&mut res); Signature(res) @@ -91,7 +92,7 @@ impl<'a, C> Readable<'a, C> for Signature where C: Context, { - fn read_from>(reader: &mut R) -> std::result::Result { + fn read_from>(reader: &mut R) -> Result { let mut data = [0u8; 32]; reader.read_bytes(&mut data)?; Ok(Self(data)) @@ -102,7 +103,7 @@ impl Writable for Signature { fn write_to<'a, T: ?Sized + Writer<'a, C>>( &'a self, writer: &mut T, - ) -> std::result::Result<(), std::io::Error> { + ) -> Result<(), std::io::Error> { writer.write_bytes(&self.0).map(|_| ()) } } @@ -122,7 +123,7 @@ impl Writable for Signature { )] pub struct PublicKey([u8; 32]); impl Generate for PublicKey { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { let mut res = [0u8; 32]; rng.fill_bytes(&mut res); PublicKey(res) @@ -130,7 +131,7 @@ impl Generate for PublicKey { } impl Generate for (PublicKey, PublicKey) { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { (PublicKey::generate(rng), PublicKey::generate(rng)) } } @@ -139,7 +140,7 @@ impl<'a, C> Readable<'a, C> for PublicKey where C: Context, { - fn read_from>(reader: &mut R) -> std::result::Result { + fn read_from>(reader: &mut R) -> Result { let mut data = [0u8; 32]; reader.read_bytes(&mut data)?; Ok(Self(data)) @@ -150,7 +151,7 @@ impl Writable for PublicKey { fn write_to<'a, T: ?Sized + Writer<'a, C>>( &'a self, writer: &mut T, - ) -> std::result::Result<(), std::io::Error> { + ) -> Result<(), std::io::Error> { writer.write_bytes(&self.0).map(|_| ()) } } @@ -169,20 +170,20 @@ impl Writable for PublicKey { )] pub struct AccountId(String); impl Generate for String { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { let len: usize = rng.gen_range(5, 200); rng.sample_iter(&Alphanumeric).take(len).collect::() } } impl Generate for (String, String) { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { (String::generate(rng), String::generate(rng)) } } impl Generate for AccountId { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { AccountId(String::generate(rng)) } } @@ -208,7 +209,7 @@ pub struct ValidatorStake { } impl Generate for ValidatorStake { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { account_id: AccountId::generate(rng), public_key: PublicKey::generate(rng), @@ -223,7 +224,7 @@ pub type Weight = u64; pub fn generate_vec_primitives(rng: &mut R, min_number: usize, max_number: usize) -> Vec where Standard: Distribution, - R: rand::Rng, + R: Rng, { let num: usize = rng.gen_range(min_number, max_number + 1); let mut res = vec![]; @@ -233,7 +234,7 @@ where res } -pub fn generate_vec( +pub fn generate_vec( rng: &mut R, min_number: usize, max_number: usize, @@ -272,7 +273,7 @@ pub struct BlockHeaderInner { } impl Generate for BlockHeaderInner { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { height: u64::generate(rng), epoch_hash: CryptoHash::generate(rng), @@ -307,7 +308,7 @@ pub struct BlockHeader { } impl Generate for BlockHeader { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { inner: BlockHeaderInner::generate(rng), signature: Signature::generate(rng), @@ -334,7 +335,7 @@ pub struct Block { } impl Generate for Block { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { header: BlockHeader::generate(rng), transactions: generate_vec(rng, 0, 1000), @@ -361,7 +362,7 @@ pub struct SignedTransaction { } impl Generate for SignedTransaction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { transaction: Transaction::generate(rng), signature: Signature::generate(rng), @@ -393,7 +394,7 @@ pub struct Transaction { } impl Generate for Transaction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { signer_id: AccountId::generate(rng), public_key: PublicKey::generate(rng), @@ -428,7 +429,7 @@ pub enum Action { } impl Generate for Action { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { use Action::*; // Deploy contract action is 1000 times less frequent than other actions. if u64::generate(rng) % 1000 == 0 { @@ -462,7 +463,7 @@ impl Generate for Action { )] pub struct CreateAccountAction {} impl Generate for CreateAccountAction { - fn generate(_rng: &mut R) -> Self { + fn generate(_rng: &mut R) -> Self { Self {} } } @@ -483,7 +484,7 @@ pub struct DeployContractAction { code: Vec, } -pub fn generate_vec_u8(rng: &mut R, min_number: usize, max_number: usize) -> Vec { +pub fn generate_vec_u8(rng: &mut R, min_number: usize, max_number: usize) -> Vec { let num: usize = rng.gen_range(min_number, max_number + 1); let mut res = vec![0u8; num]; rng.fill_bytes(&mut res); @@ -491,7 +492,7 @@ pub fn generate_vec_u8(rng: &mut R, min_number: usize, max_number: } impl Generate for DeployContractAction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { // Between 20KiB and 1MiB. code: generate_vec_u8(rng, 20 * 2usize.pow(10), 2usize.pow(20)), @@ -521,7 +522,7 @@ pub struct FunctionCallAction { } impl Generate for FunctionCallAction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { method_name: String::generate(rng), args: generate_vec_u8(rng, 0, 1000), @@ -546,7 +547,7 @@ pub struct TransferAction { deposit: Balance, } impl Generate for TransferAction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { deposit: u64::generate(rng), } @@ -571,7 +572,7 @@ pub struct StakeAction { } impl Generate for StakeAction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { stake: u64::generate(rng), public_key: PublicKey::generate(rng), @@ -597,7 +598,7 @@ pub struct AddKeyAction { } impl Generate for AddKeyAction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { public_key: PublicKey::generate(rng), access_key: AccessKey::generate(rng), @@ -622,7 +623,7 @@ pub struct DeleteKeyAction { } impl Generate for DeleteKeyAction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { public_key: PublicKey::generate(rng), } @@ -646,7 +647,7 @@ pub struct DeleteAccountAction { } impl Generate for DeleteAccountAction { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { beneficiary_id: AccountId::generate(rng), } @@ -671,7 +672,7 @@ pub struct AccessKey { } impl Generate for AccessKey { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { nonce: u64::generate(rng), permission: AccessKeyPermission::generate(rng), @@ -697,7 +698,7 @@ pub enum AccessKeyPermission { } impl Generate for AccessKeyPermission { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { if u64::generate(rng) % 2 == 0 { AccessKeyPermission::FunctionCall(FunctionCallPermission::generate(rng)) } else { @@ -724,7 +725,7 @@ pub struct FunctionCallPermission { method_names: Vec, } -fn generate_option(rng: &mut R) -> Option { +fn generate_option(rng: &mut R) -> Option { if u64::generate(rng) % 2 == 0 { None } else { @@ -733,13 +734,13 @@ fn generate_option(rng: &mut R) -> Option { } impl Generate for u64 { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { rng.next_u64() } } impl Generate for FunctionCallPermission { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { allowance: generate_option(rng), receiver_id: AccountId::generate(rng), @@ -769,7 +770,7 @@ pub struct Account { } impl Generate for Account { - fn generate(rng: &mut R) -> Self { + fn generate(rng: &mut R) -> Self { Self { amount: u64::generate(rng), staked: u64::generate(rng), diff --git a/borsh-derive/Cargo.toml b/borsh-derive/Cargo.toml index 0bfa2ed8f..d9dfa80b0 100644 --- a/borsh-derive/Cargo.toml +++ b/borsh-derive/Cargo.toml @@ -3,7 +3,7 @@ name = "borsh-derive" version.workspace = true rust-version.workspace = true authors = ["Near Inc "] -edition = "2018" +edition.workspace = true license = "Apache-2.0" readme = "README.md" categories = ["encoding", "network-programming"] @@ -18,14 +18,15 @@ exclude = ["*.snap"] proc-macro = true [dependencies] -syn = { version = "2.0.81", features = ["full", "fold"] } +syn = { version = "2.0.96", features = ["full", "fold"] } proc-macro-crate = "3" -proc-macro2 = "1" -quote = "1" +proc-macro2 = "1.0" +quote = "1.0" once_cell = "1.18.0" +cfg-if = "1.0" [dev-dependencies] -syn = { version = "2.0.81", features = ["full", "fold", "parsing"] } +syn = { version = "2.0.96", features = ["parsing"] } prettyplease = "0.2.9" insta = "1.29.0" @@ -36,5 +37,6 @@ targets = ["x86_64-unknown-linux-gnu"] [features] default = [] +async = [] schema = [] force_exhaustive_checks = [] diff --git a/borsh-derive/src/internals/attributes/field/bounds.rs b/borsh-derive/src/internals/attributes/field/bounds.rs index d383b2327..261a354e5 100644 --- a/borsh-derive/src/internals/attributes/field/bounds.rs +++ b/borsh-derive/src/internals/attributes/field/bounds.rs @@ -1,9 +1,9 @@ use std::collections::BTreeMap; +use once_cell::sync::Lazy; use syn::{meta::ParseNestedMeta, WherePredicate}; use crate::internals::attributes::{parsing::parse_lit_into_vec, Symbol, DESERIALIZE, SERIALIZE}; -use once_cell::sync::Lazy; pub enum Variants { Serialize(Vec), diff --git a/borsh-derive/src/internals/attributes/field/mod.rs b/borsh-derive/src/internals/attributes/field/mod.rs index 0ff2555c1..1d93ba68e 100644 --- a/borsh-derive/src/internals/attributes/field/mod.rs +++ b/borsh-derive/src/internals/attributes/field/mod.rs @@ -1,21 +1,22 @@ use std::collections::BTreeMap; +use cfg_if::cfg_if; use once_cell::sync::Lazy; use syn::{meta::ParseNestedMeta, Attribute, WherePredicate}; +#[cfg(feature = "schema")] +use { + super::schema_keys::{PARAMS, SCHEMA, WITH_FUNCS}, + schema::SCHEMA_FIELD_PARSE_MAP, +}; use self::bounds::BOUNDS_FIELD_PARSE_MAP; - use super::{ get_one_attribute, parsing::{attr_get_by_symbol_keys, meta_get_by_symbol_keys, parse_lit_into}, BoundType, Symbol, BORSH, BOUND, DESERIALIZE_WITH, SERIALIZE_WITH, SKIP, }; - -#[cfg(feature = "schema")] -use { - super::schema_keys::{PARAMS, SCHEMA, WITH_FUNCS}, - schema::SCHEMA_FIELD_PARSE_MAP, -}; +#[cfg(feature = "async")] +use super::{ASYNC_BOUND, DESERIALIZE_WITH_ASYNC, SERIALIZE_WITH_ASYNC}; pub mod bounds; #[cfg(feature = "schema")] @@ -25,6 +26,10 @@ enum Variants { Bounds(bounds::Bounds), SerializeWith(syn::ExprPath), DeserializeWith(syn::ExprPath), + #[cfg(feature = "async")] + SerializeWithAsync(syn::ExprPath), + #[cfg(feature = "async")] + DeserializeWithAsync(syn::ExprPath), Skip(()), #[cfg(feature = "schema")] Schema(schema::Attributes), @@ -42,6 +47,13 @@ static BORSH_FIELD_PARSE_MAP: Lazy>> = Lazy::new(| Ok(Variants::Bounds(bounds_attributes)) }); + #[cfg(feature = "async")] + let f_async_bounds: Box = Box::new(|_attr_name, _meta_item_name, meta| { + let map_result = meta_get_by_symbol_keys(ASYNC_BOUND, meta, &BOUNDS_FIELD_PARSE_MAP)?; + let bounds_attributes: bounds::Bounds = map_result.into(); + Ok(Variants::Bounds(bounds_attributes)) + }); + let f_serialize_with: Box = Box::new(|attr_name, meta_item_name, meta| { parse_lit_into::(attr_name, meta_item_name, meta) .map(Variants::SerializeWith) @@ -52,6 +64,18 @@ static BORSH_FIELD_PARSE_MAP: Lazy>> = Lazy::new(| .map(Variants::DeserializeWith) }); + #[cfg(feature = "async")] + let f_serialize_with_async: Box = Box::new(|attr_name, meta_item_name, meta| { + parse_lit_into::(attr_name, meta_item_name, meta) + .map(Variants::SerializeWithAsync) + }); + + #[cfg(feature = "async")] + let f_deserialize_with_async: Box = Box::new(|attr_name, meta_item_name, meta| { + parse_lit_into::(attr_name, meta_item_name, meta) + .map(Variants::DeserializeWithAsync) + }); + #[cfg(feature = "schema")] let f_schema: Box = Box::new(|_attr_name, _meta_item_name, meta| { let map_result = meta_get_by_symbol_keys(SCHEMA, meta, &SCHEMA_FIELD_PARSE_MAP)?; @@ -62,8 +86,14 @@ static BORSH_FIELD_PARSE_MAP: Lazy>> = Lazy::new(| let f_skip: Box = Box::new(|_attr_name, _meta_item_name, _meta| Ok(Variants::Skip(()))); m.insert(BOUND, f_bounds); + #[cfg(feature = "async")] + m.insert(ASYNC_BOUND, f_async_bounds); m.insert(SERIALIZE_WITH, f_serialize_with); m.insert(DESERIALIZE_WITH, f_deserialize_with); + #[cfg(feature = "async")] + m.insert(SERIALIZE_WITH_ASYNC, f_serialize_with_async); + #[cfg(feature = "async")] + m.insert(DESERIALIZE_WITH_ASYNC, f_deserialize_with_async); m.insert(SKIP, f_skip); #[cfg(feature = "schema")] m.insert(SCHEMA, f_schema); @@ -73,8 +103,14 @@ static BORSH_FIELD_PARSE_MAP: Lazy>> = Lazy::new(| #[derive(Default, Clone)] pub(crate) struct Attributes { pub bounds: Option, + #[cfg(feature = "async")] + pub async_bounds: Option, pub serialize_with: Option, pub deserialize_with: Option, + #[cfg(feature = "async")] + pub serialize_with_async: Option, + #[cfg(feature = "async")] + pub deserialize_with_async: Option, pub skip: bool, #[cfg(feature = "schema")] pub schema: Option, @@ -83,14 +119,27 @@ pub(crate) struct Attributes { impl From> for Attributes { fn from(mut map: BTreeMap) -> Self { let bounds = map.remove(&BOUND); + #[cfg(feature = "async")] + let async_bounds = map.remove(&ASYNC_BOUND); let serialize_with = map.remove(&SERIALIZE_WITH); let deserialize_with = map.remove(&DESERIALIZE_WITH); + #[cfg(feature = "async")] + let serialize_with_async = map.remove(&SERIALIZE_WITH_ASYNC); + #[cfg(feature = "async")] + let deserialize_with_async = map.remove(&DESERIALIZE_WITH_ASYNC); let skip = map.remove(&SKIP); + let bounds = bounds.map(|variant| match variant { Variants::Bounds(bounds) => bounds, _ => unreachable!("only one enum variant is expected to correspond to given map key"), }); + #[cfg(feature = "async")] + let async_bounds = async_bounds.map(|variant| match variant { + Variants::Bounds(bounds) => bounds, + _ => unreachable!("only one enum variant is expected to correspond to given map key"), + }); + let serialize_with = serialize_with.map(|variant| match variant { Variants::SerializeWith(serialize_with) => serialize_with, _ => unreachable!("only one enum variant is expected to correspond to given map key"), @@ -101,6 +150,18 @@ impl From> for Attributes { _ => unreachable!("only one enum variant is expected to correspond to given map key"), }); + #[cfg(feature = "async")] + let serialize_with_async = serialize_with_async.map(|variant| match variant { + Variants::SerializeWithAsync(serialize_with_async) => serialize_with_async, + _ => unreachable!("only one enum variant is expected to correspond to given map key"), + }); + + #[cfg(feature = "async")] + let deserialize_with_async = deserialize_with_async.map(|variant| match variant { + Variants::DeserializeWithAsync(deserialize_with_async) => deserialize_with_async, + _ => unreachable!("only one enum variant is expected to correspond to given map key"), + }); + let skip = skip.map(|variant| match variant { Variants::Skip(skip) => skip, _ => unreachable!("only one enum variant is expected to correspond to given map key"), @@ -116,10 +177,17 @@ impl From> for Attributes { } }) }; + Self { bounds, + #[cfg(feature = "async")] + async_bounds, serialize_with, deserialize_with, + #[cfg(feature = "async")] + serialize_with_async, + #[cfg(feature = "async")] + deserialize_with_async, skip: skip.is_some(), #[cfg(feature = "schema")] schema, @@ -136,14 +204,42 @@ pub(crate) fn filter_attrs( impl Attributes { fn check(&self, attr: &Attribute) -> Result<(), syn::Error> { - if self.skip && (self.serialize_with.is_some() || self.deserialize_with.is_some()) { - return Err(syn::Error::new_spanned( - attr, - format!( - "`{}` cannot be used at the same time as `{}` or `{}`", - SKIP.0, SERIALIZE_WITH.0, DESERIALIZE_WITH.0 - ), - )); + cfg_if! { + if #[cfg(feature = "async")] { + let test_serde_with = || + self.serialize_with.is_some() || + self.deserialize_with.is_some() || + self.serialize_with_async.is_some() || + self.deserialize_with_async.is_some(); + } else { + let test_serde_with = || + self.serialize_with.is_some() || + self.deserialize_with.is_some(); + } + } + + if self.skip && test_serde_with() { + cfg_if! { + if #[cfg(feature = "async")] { + let msg = format!( + "`{}` cannot be used at the same time as `{}`, `{}`, `{}` or `{}`", + SKIP.name, + SERIALIZE_WITH.name, + DESERIALIZE_WITH.name, + SERIALIZE_WITH_ASYNC.name, + DESERIALIZE_WITH_ASYNC.name, + ); + } else { + let msg = format!( + "`{}` cannot be used at the same time as `{}` or `{}`", + SKIP.name, + SERIALIZE_WITH.name, + DESERIALIZE_WITH.name, + ); + } + } + + return Err(syn::Error::new_spanned(attr, msg)); } #[cfg(feature = "schema")] @@ -151,6 +247,7 @@ impl Attributes { Ok(()) } + pub(crate) fn parse(attrs: &[Attribute]) -> Result { let borsh = get_one_attribute(attrs)?; @@ -164,20 +261,35 @@ impl Attributes { Ok(result) } - pub(crate) fn needs_bounds_derive(&self, ty: BoundType) -> bool { - let predicates = self.get_bounds(ty); + + pub(crate) fn needs_bounds_derive(&self, ty: BoundType) -> bool { + let predicates = self.get_bounds::(ty); predicates.is_none() } - fn get_bounds(&self, ty: BoundType) -> Option> { - let bounds = self.bounds.as_ref(); + fn get_bounds(&self, ty: BoundType) -> Option> { + let bounds = if IS_ASYNC { + cfg_if! { + if #[cfg(feature = "async")] { + self.async_bounds.as_ref() + } else { + None + } + } + } else { + self.bounds.as_ref() + }; bounds.and_then(|bounds| match ty { BoundType::Serialize => bounds.serialize.clone(), BoundType::Deserialize => bounds.deserialize.clone(), }) } - pub(crate) fn collect_bounds(&self, ty: BoundType) -> Vec { - let predicates = self.get_bounds(ty); + + pub(crate) fn collect_bounds( + &self, + ty: BoundType, + ) -> Vec { + let predicates = self.get_bounds::(ty); predicates.unwrap_or_default() } } @@ -191,7 +303,7 @@ impl Attributes { attr, format!( "`{}` cannot be used at the same time as `{}({})`", - SKIP.0, SCHEMA.0, PARAMS.1 + SKIP.name, SCHEMA.name, PARAMS.expected ), )); } @@ -201,7 +313,7 @@ impl Attributes { attr, format!( "`{}` cannot be used at the same time as `{}({})`", - SKIP.0, SCHEMA.0, WITH_FUNCS.1 + SKIP.name, SCHEMA.name, WITH_FUNCS.expected ), )); } @@ -239,25 +351,33 @@ impl Attributes { #[cfg(test)] mod tests { - use quote::quote; - use syn::{Attribute, ItemStruct}; - - fn parse_bounds(attrs: &[Attribute]) -> Result, syn::Error> { - // #[borsh(bound(serialize = "...", deserialize = "..."))] - let borsh_attrs = Attributes::parse(attrs)?; - Ok(borsh_attrs.bounds) - } + use syn::{parse_quote, Attribute, ItemStruct}; + use super::{bounds, Attributes}; use crate::internals::test_helpers::{ debug_print_tokenizable, debug_print_vec_of_tokenizable, local_insta_assert_debug_snapshot, local_insta_assert_snapshot, }; - use super::{bounds, Attributes}; + struct ParsedBounds { + sync: Option, + #[cfg(feature = "async")] + r#async: Option, + } + + fn parse_bounds(attrs: &[Attribute]) -> Result { + // #[borsh(bound(serialize = "...", deserialize = "..."), async_bound(serialize = "...", deserialize = "..."))] + let borsh_attrs = Attributes::parse(attrs)?; + Ok(ParsedBounds { + sync: borsh_attrs.bounds, + #[cfg(feature = "async")] + r#async: borsh_attrs.async_bounds, + }) + } #[test] fn test_reject_multiple_borsh_attrs() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(skip)] #[borsh(bound(deserialize = "K: Hash + Ord, @@ -268,8 +388,7 @@ mod tests { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let err = match Attributes::parse(&first_field.attrs) { @@ -281,7 +400,7 @@ mod tests { #[test] fn test_bounds_parsing1() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deserialize = "K: Hash + Ord, V: Eq + Ord", @@ -291,18 +410,17 @@ mod tests { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; - let attrs = parse_bounds(&first_field.attrs).unwrap().unwrap(); - local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize.clone())); + let attrs = parse_bounds(&first_field.attrs).unwrap().sync.unwrap(); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize)); local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); } #[test] fn test_bounds_parsing2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deserialize = "K: Hash + Eq + borsh::de::BorshDeserialize, V: borsh::de::BorshDeserialize", @@ -312,18 +430,17 @@ mod tests { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; - let attrs = parse_bounds(&first_field.attrs).unwrap().unwrap(); - local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize.clone())); + let attrs = parse_bounds(&first_field.attrs).unwrap().sync.unwrap(); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize)); local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); } #[test] fn test_bounds_parsing3() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deserialize = "K: Hash + Eq + borsh::de::BorshDeserialize, V: borsh::de::BorshDeserialize", @@ -332,42 +449,39 @@ mod tests { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; - let attrs = parse_bounds(&first_field.attrs).unwrap().unwrap(); + let attrs = parse_bounds(&first_field.attrs).unwrap().sync.unwrap(); assert_eq!(attrs.serialize.as_ref().unwrap().len(), 0); local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); } #[test] fn test_bounds_parsing4() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deserialize = "K: Hash"))] x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; - let attrs = parse_bounds(&first_field.attrs).unwrap().unwrap(); + let attrs = parse_bounds(&first_field.attrs).unwrap().sync.unwrap(); assert!(attrs.serialize.is_none()); local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); } #[test] fn test_bounds_parsing_error() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deser = "K: Hash"))] x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let err = match parse_bounds(&first_field.attrs) { @@ -379,14 +493,13 @@ mod tests { #[test] fn test_bounds_parsing_error2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deserialize = "K Hash"))] x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let err = match parse_bounds(&first_field.attrs) { @@ -398,14 +511,155 @@ mod tests { #[test] fn test_bounds_parsing_error3() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deserialize = 42))] x: u64, y: String, } - }) - .unwrap(); + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let err = match parse_bounds(&first_field.attrs) { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + } + + #[test] + #[cfg(feature = "async")] + fn test_async_bounds_parsing1() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(async_bound( + deserialize = + "K: Hash + Ord, + V: Eq + Ord", + serialize = + "K: Hash + Eq + Ord, + V: Ord" + ))] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let attrs = parse_bounds(&first_field.attrs).unwrap().r#async.unwrap(); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize)); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); + } + + #[test] + #[cfg(feature = "async")] + fn test_async_bounds_parsing2() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(async_bound(deserialize = + "K: Hash + Eq + borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync", + serialize = + "K: Hash + Eq + borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync" + ))] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let attrs = parse_bounds(&first_field.attrs).unwrap().r#async.unwrap(); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize)); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); + } + + #[test] + #[cfg(feature = "async")] + fn test_async_bounds_parsing3() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(async_bound(deserialize = + "K: Hash + Eq + borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync", + serialize = "" + ))] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let attrs = parse_bounds(&first_field.attrs).unwrap().r#async.unwrap(); + assert_eq!(attrs.serialize.as_ref().unwrap().len(), 0); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); + } + + #[test] + #[cfg(feature = "async")] + fn test_async_bounds_parsing4() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(async_bound(deserialize = "K: Hash"))] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let attrs = parse_bounds(&first_field.attrs).unwrap().r#async.unwrap(); + assert!(attrs.serialize.is_none()); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); + } + + #[test] + #[cfg(feature = "async")] + fn test_async_bounds_parsing_error() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(async_bound(deser = "K: Hash"))] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let err = match parse_bounds(&first_field.attrs) { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + } + + #[test] + #[cfg(feature = "async")] + fn test_async_bounds_parsing_error2() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(async_bound(deserialize = "K Hash"))] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let err = match parse_bounds(&first_field.attrs) { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + } + + #[test] + #[cfg(feature = "async")] + fn test_async_bounds_parsing_error3() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(async_bound(deserialize = 42))] + x: u64, + y: String, + } + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let err = match parse_bounds(&first_field.attrs) { @@ -415,9 +669,47 @@ mod tests { local_insta_assert_debug_snapshot!(err); } + #[test] + #[cfg(feature = "async")] + fn test_both_bounds_parsing() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh( + bound( + deserialize = + "K: Hash + Ord, + V: Eq + Ord", + serialize = + "K: Hash + Eq + Ord, + V: Ord" + ), + async_bound( + deserialize = + "K: Hash + Ord + A, + V: Eq + Ord + AA", + serialize = + "K: Hash + Eq + Ord + A, + V: Ord + AA" + ) + )] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let attrs = parse_bounds(&first_field.attrs).unwrap().sync.unwrap(); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize)); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); + + let attrs = parse_bounds(&first_field.attrs).unwrap().r#async.unwrap(); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.serialize)); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(attrs.deserialize)); + } + #[test] fn test_ser_de_with_parsing1() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh( serialize_with = "third_party_impl::serialize_third_party", @@ -426,39 +718,58 @@ mod tests { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let attrs = Attributes::parse(&first_field.attrs).unwrap(); local_insta_assert_snapshot!(debug_print_tokenizable(attrs.serialize_with.as_ref())); local_insta_assert_snapshot!(debug_print_tokenizable(attrs.deserialize_with)); } + + #[test] + #[cfg(feature = "async")] + fn test_async_ser_de_with_parsing1() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh( + serialize_with_async = "third_party_impl::serialize_third_party", + deserialize_with_async = "third_party_impl::deserialize_third_party", + )] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let attrs = Attributes::parse(&first_field.attrs).unwrap(); + local_insta_assert_snapshot!(debug_print_tokenizable(attrs.serialize_with_async.as_ref())); + local_insta_assert_snapshot!(debug_print_tokenizable(attrs.deserialize_with_async)); + } + #[test] fn test_borsh_skip() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(skip)] x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let result = Attributes::parse(&first_field.attrs).unwrap(); assert!(result.skip); } + #[test] fn test_borsh_no_skip() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; @@ -470,6 +781,9 @@ mod tests { #[cfg(feature = "schema")] #[cfg(test)] mod tests_schema { + use syn::{parse_quote, Attribute, ItemStruct}; + + use super::schema; use crate::internals::{ attributes::field::Attributes, test_helpers::{ @@ -478,10 +792,6 @@ mod tests_schema { }, }; - use quote::quote; - use syn::{Attribute, ItemStruct}; - - use super::schema; fn parse_schema_attrs(attrs: &[Attribute]) -> Result, syn::Error> { // #[borsh(schema(params = "..."))] let borsh_attrs = Attributes::parse(attrs)?; @@ -490,7 +800,7 @@ mod tests_schema { #[test] fn test_root_bounds_and_params_combined() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh( serialize_with = "third_party_impl::serialize_third_party", @@ -500,8 +810,7 @@ mod tests_schema { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; @@ -511,13 +820,13 @@ mod tests_schema { local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(bounds.deserialize)); assert!(attrs.deserialize_with.is_none()); let schema = attrs.schema.clone().unwrap(); - local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(schema.params.clone())); + local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(schema.params)); local_insta_assert_snapshot!(debug_print_tokenizable(attrs.serialize_with)); } #[test] fn test_schema_params_parsing1() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -528,16 +837,16 @@ mod tests_schema { field: ::Associated, another: V, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let schema_attrs = parse_schema_attrs(&first_field.attrs).unwrap(); local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(schema_attrs.unwrap().params)); } + #[test] fn test_schema_params_parsing_error() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -548,8 +857,7 @@ mod tests_schema { field: ::Associated, another: V, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let err = match parse_schema_attrs(&first_field.attrs) { @@ -561,7 +869,7 @@ mod tests_schema { #[test] fn test_schema_params_parsing_error2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -572,8 +880,7 @@ mod tests_schema { field: ::Associated, another: V, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let err = match parse_schema_attrs(&first_field.attrs) { @@ -585,7 +892,7 @@ mod tests_schema { #[test] fn test_schema_params_parsing2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -596,16 +903,16 @@ mod tests_schema { field: ::Associated, another: V, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let schema_attrs = parse_schema_attrs(&first_field.attrs).unwrap(); local_insta_assert_snapshot!(debug_print_vec_of_tokenizable(schema_attrs.unwrap().params)); } + #[test] fn test_schema_params_parsing3() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -614,8 +921,7 @@ mod tests_schema { field: ::Associated, another: V, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let schema_attrs = parse_schema_attrs(&first_field.attrs).unwrap(); @@ -624,7 +930,7 @@ mod tests_schema { #[test] fn test_schema_params_parsing4() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -632,8 +938,7 @@ mod tests_schema { field: ::Associated, another: V, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let schema_attrs = parse_schema_attrs(&first_field.attrs).unwrap(); @@ -642,7 +947,7 @@ mod tests_schema { #[test] fn test_schema_with_funcs_parsing() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(schema(with_funcs( declaration = "third_party_impl::declaration::", @@ -651,22 +956,21 @@ mod tests_schema { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let attrs = Attributes::parse(&first_field.attrs).unwrap(); let schema = attrs.schema.unwrap(); let with_funcs = schema.with_funcs.unwrap(); - local_insta_assert_snapshot!(debug_print_tokenizable(with_funcs.declaration.clone())); + local_insta_assert_snapshot!(debug_print_tokenizable(with_funcs.declaration)); local_insta_assert_snapshot!(debug_print_tokenizable(with_funcs.definitions)); } // both `declaration` and `definitions` have to be specified #[test] fn test_schema_with_funcs_parsing_error() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(schema(with_funcs( declaration = "third_party_impl::declaration::" @@ -674,8 +978,7 @@ mod tests_schema { x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let attrs = Attributes::parse(&first_field.attrs); @@ -688,15 +991,15 @@ mod tests_schema { } #[test] + #[cfg(not(feature = "async"))] fn test_root_error() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(boons)] x: u64, y: String, } - }) - .unwrap(); + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; let err = match Attributes::parse(&first_field.attrs) { @@ -707,8 +1010,28 @@ mod tests_schema { } #[test] + #[cfg(feature = "async")] + fn test_root_error_feature_async() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(boons)] + x: u64, + y: String, + } + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + let err = match Attributes::parse(&first_field.attrs) { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + } + + #[test] + #[cfg(not(feature = "async"))] fn test_root_bounds_and_wrong_key_combined() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(bound(deserialize = "K: Hash"), schhema(params = "T => ::Associated, V => Vec") @@ -716,8 +1039,29 @@ mod tests_schema { x: u64, y: String, } - }) - .unwrap(); + }; + + let first_field = &item_struct.fields.into_iter().collect::>()[0]; + + let err = match Attributes::parse(&first_field.attrs) { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + } + + #[test] + #[cfg(feature = "async")] + fn test_root_bounds_and_wrong_key_combined_feature_async() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(bound(deserialize = "K: Hash"), + schhema(params = "T => ::Associated, V => Vec") + )] + x: u64, + y: String, + } + }; let first_field = &item_struct.fields.into_iter().collect::>()[0]; diff --git a/borsh-derive/src/internals/attributes/field/schema.rs b/borsh-derive/src/internals/attributes/field/schema.rs index 40ebec39d..32210c7b1 100644 --- a/borsh-derive/src/internals/attributes/field/schema.rs +++ b/borsh-derive/src/internals/attributes/field/schema.rs @@ -1,10 +1,5 @@ use std::collections::BTreeMap; -use crate::internals::attributes::{ - parsing::{meta_get_by_symbol_keys, parse_lit_into_vec}, - schema_keys::{DECLARATION, DEFINITIONS, PARAMS, WITH_FUNCS}, - Symbol, -}; use once_cell::sync::Lazy; use quote::ToTokens; use syn::{ @@ -14,6 +9,11 @@ use syn::{ }; use self::with_funcs::{WithFuncs, WITH_FUNCS_FIELD_PARSE_MAP}; +use crate::internals::attributes::{ + parsing::{meta_get_by_symbol_keys, parse_lit_into_vec}, + schema_keys::{DECLARATION, DEFINITIONS, PARAMS, WITH_FUNCS}, + Symbol, +}; pub mod with_funcs; @@ -43,7 +43,7 @@ pub static SCHEMA_FIELD_PARSE_MAP: Lazy>> = Lazy:: &meta.path, format!( "both `{}` and `{}` have to be specified at the same time", - DECLARATION.1, DEFINITIONS.1, + DECLARATION.expected, DEFINITIONS.expected, ), )); } diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing1-2.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing1-2.snap new file mode 100644 index 000000000..75ee80942 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing1-2.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.deserialize) +--- +K : Hash + Ord +V : Eq + Ord diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing1.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing1.snap new file mode 100644 index 000000000..182ab0ab3 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing1.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.serialize) +--- +K : Hash + Eq + Ord +V : Ord diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing2-2.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing2-2.snap new file mode 100644 index 000000000..d3c7c7d51 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing2-2.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.deserialize) +--- +K : Hash + Eq + borsh :: de :: BorshDeserializeAsync +V : borsh :: de :: BorshDeserializeAsync diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing2.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing2.snap new file mode 100644 index 000000000..e27068395 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing2.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.serialize) +--- +K : Hash + Eq + borsh :: ser :: BorshSerializeAsync +V : borsh :: ser :: BorshSerializeAsync diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing3.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing3.snap new file mode 100644 index 000000000..d3c7c7d51 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing3.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.deserialize) +--- +K : Hash + Eq + borsh :: de :: BorshDeserializeAsync +V : borsh :: de :: BorshDeserializeAsync diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing4.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing4.snap new file mode 100644 index 000000000..a68309c1b --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing4.snap @@ -0,0 +1,5 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.deserialize) +--- +K : Hash diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error.snap new file mode 100644 index 000000000..da1748fd0 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: err +--- +Error( + "malformed async_bound attribute, expected `async_bound(deserialize = ..., serialize = ...)`", +) diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error2.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error2.snap new file mode 100644 index 000000000..fc006afe2 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error2.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: err +--- +Error( + "expected `:`", +) diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error3.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error3.snap new file mode 100644 index 000000000..36e9a0b74 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_bounds_parsing_error3.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: err +--- +Error( + "expected borsh async_bound attribute to be a string: `deserialize = \"...\"`", +) diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_ser_de_with_parsing1-2.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_ser_de_with_parsing1-2.snap new file mode 100644 index 000000000..2ca40b4fb --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_ser_de_with_parsing1-2.snap @@ -0,0 +1,5 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_tokenizable(attrs.deserialize_with_async) +--- +third_party_impl :: deserialize_third_party diff --git a/borsh-derive/src/internals/attributes/field/snapshots/async_ser_de_with_parsing1.snap b/borsh-derive/src/internals/attributes/field/snapshots/async_ser_de_with_parsing1.snap new file mode 100644 index 000000000..39366dd42 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/async_ser_de_with_parsing1.snap @@ -0,0 +1,5 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_tokenizable(attrs.serialize_with_async.as_ref()) +--- +third_party_impl :: serialize_third_party diff --git a/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-2.snap b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-2.snap new file mode 100644 index 000000000..75ee80942 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-2.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.deserialize) +--- +K : Hash + Ord +V : Eq + Ord diff --git a/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-3.snap b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-3.snap new file mode 100644 index 000000000..f7702d582 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-3.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.serialize) +--- +K : Hash + Eq + Ord + A +V : Ord + AA diff --git a/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-4.snap b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-4.snap new file mode 100644 index 000000000..24c22921b --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing-4.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.deserialize) +--- +K : Hash + Ord + A +V : Eq + Ord + AA diff --git a/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing.snap b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing.snap new file mode 100644 index 000000000..182ab0ab3 --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/both_bounds_parsing.snap @@ -0,0 +1,6 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: debug_print_vec_of_tokenizable(attrs.serialize) +--- +K : Hash + Eq + Ord +V : Ord diff --git a/borsh-derive/src/internals/attributes/field/snapshots/root_bounds_and_wrong_key_combined_feature_async.snap b/borsh-derive/src/internals/attributes/field/snapshots/root_bounds_and_wrong_key_combined_feature_async.snap new file mode 100644 index 000000000..f62d0826e --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/root_bounds_and_wrong_key_combined_feature_async.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: err +--- +Error( + "malformed borsh attribute, expected `borsh(async_bound(...), bound(...), deserialize_with = ..., deserialize_with_async = ..., schema(...), serialize_with = ..., serialize_with_async = ..., skip)`", +) diff --git a/borsh-derive/src/internals/attributes/field/snapshots/root_error_feature_async.snap b/borsh-derive/src/internals/attributes/field/snapshots/root_error_feature_async.snap new file mode 100644 index 000000000..f62d0826e --- /dev/null +++ b/borsh-derive/src/internals/attributes/field/snapshots/root_error_feature_async.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/attributes/field/mod.rs +expression: err +--- +Error( + "malformed borsh attribute, expected `borsh(async_bound(...), bound(...), deserialize_with = ..., deserialize_with_async = ..., schema(...), serialize_with = ..., serialize_with_async = ..., skip)`", +) diff --git a/borsh-derive/src/internals/attributes/item/mod.rs b/borsh-derive/src/internals/attributes/item/mod.rs index ff3551486..785e6ad05 100644 --- a/borsh-derive/src/internals/attributes/item/mod.rs +++ b/borsh-derive/src/internals/attributes/item/mod.rs @@ -1,10 +1,10 @@ -use crate::internals::attributes::{BORSH, CRATE, INIT, USE_DISCRIMINANT}; use quote::ToTokens; -use syn::{spanned::Spanned, Attribute, DeriveInput, Error, Expr, ItemEnum, Path}; +use syn::{spanned::Spanned, Attribute, DeriveInput, Expr, ItemEnum, Path}; use super::{get_one_attribute, parsing}; +use crate::internals::attributes::{BORSH, CRATE, INIT, USE_DISCRIMINANT}; -pub fn check_attributes(derive_input: &DeriveInput) -> Result<(), Error> { +pub fn check_attributes(derive_input: &DeriveInput) -> syn::Result<()> { let borsh = get_one_attribute(&derive_input.attrs)?; if let Some(attr) = borsh { @@ -33,7 +33,7 @@ pub fn check_attributes(derive_input: &DeriveInput) -> Result<(), Error> { Ok(()) } -pub(crate) fn contains_use_discriminant(input: &ItemEnum) -> Result { +pub(crate) fn contains_use_discriminant(input: &ItemEnum) -> syn::Result { if input.variants.len() > 256 { return Err(syn::Error::new( input.span(), @@ -80,7 +80,7 @@ pub(crate) fn contains_use_discriminant(input: &ItemEnum) -> Result Result, Error> { +pub(crate) fn contains_initialize_with(attrs: &[Attribute]) -> syn::Result> { let mut res = None; let attr = attrs.iter().find(|attr| attr.path() == BORSH); if let Some(attr) = attr { @@ -99,7 +99,7 @@ pub(crate) fn contains_initialize_with(attrs: &[Attribute]) -> Result Result, Error> { +pub(crate) fn get_crate(attrs: &[Attribute]) -> syn::Result> { let mut res = None; let attr = attrs.iter().find(|attr| attr.path() == BORSH); if let Some(attr) = attr { @@ -120,52 +120,50 @@ pub(crate) fn get_crate(attrs: &[Attribute]) -> Result, Error> { #[cfg(test)] mod tests { - use crate::internals::test_helpers::local_insta_assert_debug_snapshot; - use quote::{quote, ToTokens}; - use syn::ItemEnum; + use quote::ToTokens; + use syn::{parse_quote, ItemEnum}; use super::*; + use crate::internals::test_helpers::local_insta_assert_debug_snapshot; + #[test] fn test_use_discriminant() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[derive(BorshDeserialize, Debug)] #[borsh(use_discriminant = false)] enum AWithUseDiscriminantFalse { X, Y, } - }) - .unwrap(); + }; let actual = contains_use_discriminant(&item_enum); assert!(!actual.unwrap()); } #[test] fn test_use_discriminant_true() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[derive(BorshDeserialize, Debug)] #[borsh(use_discriminant = true)] enum AWithUseDiscriminantTrue { X, Y, } - }) - .unwrap(); + }; let actual = contains_use_discriminant(&item_enum); assert!(actual.unwrap()); } #[test] fn test_use_discriminant_wrong_value() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[derive(BorshDeserialize, Debug)] #[borsh(use_discriminant = 111)] enum AWithUseDiscriminantFalse { X, Y, } - }) - .unwrap(); + }; let actual = contains_use_discriminant(&item_enum); let err = match actual { Ok(..) => unreachable!("expecting error here"), @@ -175,64 +173,60 @@ mod tests { } #[test] fn test_check_attrs_use_discriminant_on_struct() { - let item_enum: DeriveInput = syn::parse2(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshDeserialize, Debug)] #[borsh(use_discriminant = false)] struct AWithUseDiscriminantFalse { x: X, y: Y, } - }) - .unwrap(); - let actual = check_attributes(&item_enum); + }; + let actual = check_attributes(&derive_input); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn test_check_attrs_borsh_skip_on_whole_item() { - let item_enum: DeriveInput = syn::parse2(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshDeserialize, Debug)] #[borsh(skip)] struct AWithUseDiscriminantFalse { x: X, y: Y, } - }) - .unwrap(); - let actual = check_attributes(&item_enum); + }; + let actual = check_attributes(&derive_input); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn test_check_attrs_borsh_invalid_on_whole_item() { - let item_enum: DeriveInput = syn::parse2(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshDeserialize, Debug)] #[borsh(invalid)] enum AWithUseDiscriminantFalse { X, Y, } - }) - .unwrap(); - let actual = check_attributes(&item_enum); + }; + let actual = check_attributes(&derive_input); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn test_check_attrs_init_function() { - let item_struct = syn::parse2::(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init = initialization_method)] struct A<'a> { x: u64, } - }) - .unwrap(); + }; - let actual = check_attributes(&item_struct); + let actual = check_attributes(&derive_input); assert!(actual.is_ok()); } #[test] fn test_check_attrs_init_function_with_use_discriminant_reversed() { - let item_struct = syn::parse2::(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(use_discriminant=true, init = initialization_method)] enum A { @@ -240,16 +234,15 @@ mod tests { C, D= 10, } - }) - .unwrap(); + }; - let actual = check_attributes(&item_struct); + let actual = check_attributes(&derive_input); assert!(actual.is_ok()); } #[test] fn test_reject_multiple_borsh_attrs() { - let item_struct = syn::parse2::(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(use_discriminant=true)] #[borsh(init = initialization_method)] @@ -258,16 +251,15 @@ mod tests { C, D= 10, } - }) - .unwrap(); + }; - let actual = check_attributes(&item_struct); + let actual = check_attributes(&derive_input); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn test_check_attrs_init_function_with_use_discriminant() { - let item_struct = syn::parse2::(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init = initialization_method, use_discriminant=true)] enum A { @@ -275,16 +267,15 @@ mod tests { C, D= 10, } - }) - .unwrap(); + }; - let actual = check_attributes(&item_struct); + let actual = check_attributes(&derive_input); assert!(actual.is_ok()); } #[test] fn test_check_attrs_init_function_wrong_format() { - let item_struct: DeriveInput = syn::parse2(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshDeserialize, Debug)] #[borsh(init_func = initialization_method)] struct A<'a> { @@ -295,23 +286,21 @@ mod tests { v: Vec, } - }) - .unwrap(); - let actual = check_attributes(&item_struct); + }; + let actual = check_attributes(&derive_input); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn test_init_function() { - let item_struct = syn::parse2::(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init = initialization_method)] struct A<'a> { x: u64, } - }) - .unwrap(); + }; - let actual = contains_initialize_with(&item_struct.attrs); + let actual = contains_initialize_with(&derive_input.attrs); assert_eq!( actual.unwrap().to_token_stream().to_string(), "initialization_method" @@ -320,16 +309,15 @@ mod tests { #[test] fn test_init_function_parsing_error() { - let item_struct = syn::parse2::(quote! { + let derive_input: DeriveInput = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init={strange; blocky})] struct A { lazy: Option, } - }) - .unwrap(); + }; - let actual = contains_initialize_with(&item_struct.attrs); + let actual = contains_initialize_with(&derive_input.attrs); let err = match actual { Ok(..) => unreachable!("expecting error here"), Err(err) => err, @@ -339,7 +327,7 @@ mod tests { #[test] fn test_init_function_with_use_discriminant() { - let item_struct = syn::parse2::(quote! { + let item_enum: ItemEnum = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init = initialization_method, use_discriminant=true)] enum A { @@ -347,21 +335,20 @@ mod tests { C, D, } - }) - .unwrap(); + }; - let actual = contains_initialize_with(&item_struct.attrs); + let actual = contains_initialize_with(&item_enum.attrs); assert_eq!( actual.unwrap().to_token_stream().to_string(), "initialization_method" ); - let actual = contains_use_discriminant(&item_struct); + let actual = contains_use_discriminant(&item_enum); assert!(actual.unwrap()); } #[test] fn test_init_function_with_use_discriminant_reversed() { - let item_struct = syn::parse2::(quote! { + let item_enum: ItemEnum = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(use_discriminant=true, init = initialization_method)] enum A { @@ -369,21 +356,20 @@ mod tests { C, D, } - }) - .unwrap(); + }; - let actual = contains_initialize_with(&item_struct.attrs); + let actual = contains_initialize_with(&item_enum.attrs); assert_eq!( actual.unwrap().to_token_stream().to_string(), "initialization_method" ); - let actual = contains_use_discriminant(&item_struct); + let actual = contains_use_discriminant(&item_enum); assert!(actual.unwrap()); } #[test] fn test_init_function_with_use_discriminant_with_crate() { - let item_struct = syn::parse2::(quote! { + let item_enum: ItemEnum = parse_quote! { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init = initialization_method, crate = "reexporter::borsh", use_discriminant=true)] enum A { @@ -391,18 +377,17 @@ mod tests { C, D, } - }) - .unwrap(); + }; - let actual = contains_initialize_with(&item_struct.attrs); + let actual = contains_initialize_with(&item_enum.attrs); assert_eq!( actual.unwrap().to_token_stream().to_string(), "initialization_method" ); - let actual = contains_use_discriminant(&item_struct); + let actual = contains_use_discriminant(&item_enum); assert!(actual.unwrap()); - let crate_ = get_crate(&item_struct.attrs); + let crate_ = get_crate(&item_enum.attrs); assert_eq!( crate_.unwrap().to_token_stream().to_string(), "reexporter :: borsh" diff --git a/borsh-derive/src/internals/attributes/mod.rs b/borsh-derive/src/internals/attributes/mod.rs index 4c5a69d4d..8b76a7f41 100644 --- a/borsh-derive/src/internals/attributes/mod.rs +++ b/borsh-derive/src/internals/attributes/mod.rs @@ -4,47 +4,106 @@ pub mod field; pub mod item; pub mod parsing; -/// first field is attr name -/// second field is its expected value format representation for error printing #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct Symbol(pub &'static str, pub &'static str); +pub struct Symbol { + /// Name of the attribute + pub name: &'static str, + /// Expected value format representation for error printing + pub expected: &'static str, + support: AsyncSupport, +} + +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] +enum AsyncSupport { + Sync, + Async, + Both, +} + +impl Symbol { + pub const fn new(name: &'static str, expected: &'static str) -> Self { + Self { + name, + expected, + support: AsyncSupport::Both, + } + } + + pub const fn new_sync(name: &'static str, expected: &'static str) -> Self { + Self { + name, + expected, + support: AsyncSupport::Sync, + } + } + + pub const fn new_async(name: &'static str, expected: &'static str) -> Self { + Self { + name, + expected, + support: AsyncSupport::Async, + } + } + + pub const fn test_support(&self) -> bool { + if ASYNC_OR_SYNC_SWITCH { + matches!(self.support, AsyncSupport::Async | AsyncSupport::Both) + } else { + matches!(self.support, AsyncSupport::Sync | AsyncSupport::Both) + } + } +} -/// borsh - top level prefix in nested meta attribute -pub const BORSH: Symbol = Symbol("borsh", "borsh(...)"); -/// bound - sub-borsh nested meta, field-level only, `BorshSerialize` and `BorshDeserialize` contexts -pub const BOUND: Symbol = Symbol("bound", "bound(...)"); -// use_discriminant - sub-borsh nested meta, item-level only, enums only, `BorshSerialize` and `BorshDeserialize` contexts -pub const USE_DISCRIMINANT: Symbol = Symbol("use_discriminant", "use_discriminant = ..."); -/// serialize - sub-bound nested meta attribute -pub const SERIALIZE: Symbol = Symbol("serialize", "serialize = ..."); -/// deserialize - sub-bound nested meta attribute -pub const DESERIALIZE: Symbol = Symbol("deserialize", "deserialize = ..."); -/// skip - sub-borsh nested meta, field-level only attribute, `BorshSerialize`, `BorshDeserialize`, `BorshSchema` contexts -pub const SKIP: Symbol = Symbol("skip", "skip"); -/// init - sub-borsh nested meta, item-level only attribute `BorshDeserialize` context -pub const INIT: Symbol = Symbol("init", "init = ..."); -/// serialize_with - sub-borsh nested meta, field-level only, `BorshSerialize` context -pub const SERIALIZE_WITH: Symbol = Symbol("serialize_with", "serialize_with = ..."); -/// deserialize_with - sub-borsh nested meta, field-level only, `BorshDeserialize` context -pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with", "deserialize_with = ..."); -/// crate - sub-borsh nested meta, item-level only, `BorshSerialize`, `BorshDeserialize`, `BorshSchema` contexts -pub const CRATE: Symbol = Symbol("crate", "crate = ..."); +/// `borsh` - top level prefix in nested meta attribute +pub const BORSH: Symbol = Symbol::new("borsh", "borsh(...)"); +/// `bound` - sub-borsh nested meta, field-level only; `BorshSerialize` and `BorshDeserialize` contexts +pub const BOUND: Symbol = Symbol::new_sync("bound", "bound(...)"); +/// `async_bound` - sub-borsh nested meta, field-level only; `BorshSerializeAsync` and `BorshDeserializeAsync` contexts +#[cfg(feature = "async")] +pub const ASYNC_BOUND: Symbol = Symbol::new_async("async_bound", "async_bound(...)"); +/// `use_discriminant` - sub-borsh nested meta, item-level only, enums only; +/// `BorshSerialize`, `BorshDeserialize`, `BorshSerializeAsync` and `BorshDeserializeAsync` contexts +pub const USE_DISCRIMINANT: Symbol = Symbol::new("use_discriminant", "use_discriminant = ..."); +/// `serialize` - sub-bound nested meta attribute +pub const SERIALIZE: Symbol = Symbol::new("serialize", "serialize = ..."); +/// `deserialize` - sub-bound nested meta attribute +pub const DESERIALIZE: Symbol = Symbol::new("deserialize", "deserialize = ..."); +/// `skip` - sub-borsh nested meta, field-level only attribute; +/// `BorshSerialize`, `BorshDeserialize`, `BorshSerializeAsync`, `BorshDeserializeAsync` and `BorshSchema` contexts +pub const SKIP: Symbol = Symbol::new("skip", "skip"); +/// `init` - sub-borsh nested meta, item-level only attribute; `BorshDeserialize` and `BorshDeserializeAsync` contexts +pub const INIT: Symbol = Symbol::new("init", "init = ..."); +/// `serialize_with` - sub-borsh nested meta, field-level only; `BorshSerialize` context +pub const SERIALIZE_WITH: Symbol = Symbol::new_sync("serialize_with", "serialize_with = ..."); +/// `deserialize_with` - sub-borsh nested meta, field-level only; `BorshDeserialize` context +pub const DESERIALIZE_WITH: Symbol = Symbol::new_sync("deserialize_with", "deserialize_with = ..."); +/// `serialize_with_async` - sub-borsh nested meta, field-level only; `BorshSerializeAsync` context +#[cfg(feature = "async")] +pub const SERIALIZE_WITH_ASYNC: Symbol = + Symbol::new_async("serialize_with_async", "serialize_with_async = ..."); +/// `deserialize_with_async` - sub-borsh nested meta, field-level only; `BorshDeserializeAsync` context +#[cfg(feature = "async")] +pub const DESERIALIZE_WITH_ASYNC: Symbol = + Symbol::new_async("deserialize_with_async", "deserialize_with_async = ..."); +/// `crate` - sub-borsh nested meta, item-level only; +/// `BorshSerialize`, `BorshDeserialize`, `BorshSerializeAsync`, `BorshDeserializeAsync` and `BorshSchema` contexts +pub const CRATE: Symbol = Symbol::new("crate", "crate = ..."); #[cfg(feature = "schema")] pub mod schema_keys { use super::Symbol; - /// schema - sub-borsh nested meta, `BorshSchema` context - pub const SCHEMA: Symbol = Symbol("schema", "schema(...)"); - /// params - sub-schema nested meta, field-level only attribute - pub const PARAMS: Symbol = Symbol("params", "params = ..."); - /// serialize_with - sub-borsh nested meta, field-level only, `BorshSerialize` context - /// with_funcs - sub-schema nested meta, field-level only attribute - pub const WITH_FUNCS: Symbol = Symbol("with_funcs", "with_funcs(...)"); - /// declaration - sub-with_funcs nested meta, field-level only attribute - pub const DECLARATION: Symbol = Symbol("declaration", "declaration = ..."); - /// definitions - sub-with_funcs nested meta, field-level only attribute - pub const DEFINITIONS: Symbol = Symbol("definitions", "definitions = ..."); + /// `schema` - sub-borsh nested meta, `BorshSchema` context + pub const SCHEMA: Symbol = Symbol::new("schema", "schema(...)"); + /// `params` - sub-schema nested meta, field-level only attribute + pub const PARAMS: Symbol = Symbol::new("params", "params = ..."); + /// `serialize_with` - sub-borsh nested meta, field-level only, `BorshSerialize` context + /// `with_funcs` - sub-schema nested meta, field-level only attribute + pub const WITH_FUNCS: Symbol = Symbol::new("with_funcs", "with_funcs(...)"); + /// `declaration` - sub-with_funcs nested meta, field-level only attribute + pub const DECLARATION: Symbol = Symbol::new("declaration", "declaration = ..."); + /// `definitions` - sub-with_funcs nested meta, field-level only attribute + pub const DEFINITIONS: Symbol = Symbol::new("definitions", "definitions = ..."); } #[derive(Clone, Copy)] @@ -54,23 +113,23 @@ pub enum BoundType { } impl PartialEq for Path { fn eq(&self, word: &Symbol) -> bool { - self.is_ident(word.0) + self.is_ident(word.name) } } impl<'a> PartialEq for &'a Path { fn eq(&self, word: &Symbol) -> bool { - self.is_ident(word.0) + self.is_ident(word.name) } } fn get_one_attribute(attrs: &[Attribute]) -> syn::Result> { - let count = attrs.iter().filter(|attr| attr.path() == BORSH).count(); - let borsh = attrs.iter().find(|attr| attr.path() == BORSH); - if count > 1 { + let mut attrs = attrs.iter().filter(|attr| attr.path() == BORSH); + let borsh = attrs.next(); + if let Some(other_borsh) = attrs.next() { return Err(syn::Error::new_spanned( - borsh.unwrap(), - format!("multiple `{}` attributes not allowed", BORSH.0), + other_borsh, + format!("multiple `{}` attributes not allowed", BORSH.name), )); } Ok(borsh) diff --git a/borsh-derive/src/internals/attributes/parsing.rs b/borsh-derive/src/internals/attributes/parsing.rs index 7ebf8e778..4eebee807 100644 --- a/borsh-derive/src/internals/attributes/parsing.rs +++ b/borsh-derive/src/internals/attributes/parsing.rs @@ -27,7 +27,7 @@ fn get_lit_str2( expr, format!( "expected borsh {} attribute to be a string: `{} = \"...\"`", - attr_name.0, meta_item_name.0 + attr_name.name, meta_item_name.name ), )) } @@ -77,11 +77,11 @@ where } } if !match_ { - let keys_strs = map.keys().map(|symbol| symbol.1).collect::>(); + let keys_strs = map.keys().map(|symbol| symbol.expected).collect::>(); let keys_strs = keys_strs.join(", "); return Err(meta.error(format_args!( "malformed {0} attribute, expected `{0}({1})`", - attr_name.0, keys_strs + attr_name.name, keys_strs ))); } Ok(()) diff --git a/borsh-derive/src/internals/deserialize/enums/mod.rs b/borsh-derive/src/internals/deserialize/enums/mod.rs index fb405e90d..5e5826448 100644 --- a/borsh-derive/src/internals/deserialize/enums/mod.rs +++ b/borsh-derive/src/internals/deserialize/enums/mod.rs @@ -1,65 +1,87 @@ -use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Fields, ItemEnum, Path, Variant}; +use syn::{Fields, ItemEnum, Path, Token, Variant}; use crate::internals::{attributes::item, deserialize, enum_discriminant::Discriminants, generics}; -pub fn process(input: &ItemEnum, cratename: Path) -> syn::Result { +pub fn process( + input: ItemEnum, + cratename: Path, +) -> syn::Result { let name = &input.ident; - let generics = generics::without_defaults(&input.generics); + let use_discriminant = item::contains_use_discriminant(&input)?; + let generics = generics::without_defaults(input.generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let mut where_clause = generics::default_where(where_clause); let mut variant_arms = TokenStream2::new(); - let use_discriminant = item::contains_use_discriminant(input)?; let discriminants = Discriminants::new(&input.variants); let mut generics_output = deserialize::GenericsOutput::new(&generics); - for (variant_idx, variant) in input.variants.iter().enumerate() { - let variant_body = process_variant(variant, &cratename, &mut generics_output)?; - let variant_ident = &variant.ident; + for (variant_idx, variant) in input.variants.into_iter().enumerate() { + let variant_body = process_variant::(&variant, &cratename, &mut generics_output)?; + let variant_ident = variant.ident; + + let discriminant_value = + discriminants.get(&variant_ident, use_discriminant, variant_idx)?; - let discriminant_value = discriminants.get(variant_ident, use_discriminant, variant_idx)?; + // `if` branches are used instead of `match` branches, because `discriminant_value` might be a function call variant_arms.extend(quote! { if variant_tag == #discriminant_value { #name::#variant_ident #variant_body } else }); } - let init = if let Some(method_ident) = item::contains_initialize_with(&input.attrs)? { - quote! { - return_value.#method_ident(); - } + let init = item::contains_initialize_with(&input.attrs)? + .map(|method_ident| quote! { return_value.#method_ident(); }); + let r#mut = init.is_some().then(|| Token![mut](Span::call_site())); + generics_output.extend::(&mut where_clause, &cratename); + + let deserialize_trait = Ident::new( + if IS_ASYNC { + "BorshDeserializeAsync" + } else { + "BorshDeserialize" + }, + Span::call_site(), + ); + let enum_ext = Ident::new( + if IS_ASYNC { "EnumExtAsync" } else { "EnumExt" }, + Span::call_site(), + ); + let read_trait_path = if IS_ASYNC { + quote! { async_io::AsyncRead } } else { - quote! {} + quote! { io::Read } }; - generics_output.extend(&mut where_clause, &cratename); + let r#async = IS_ASYNC.then(|| Token![async](Span::call_site())); + let dot_await = IS_ASYNC.then(|| quote! { .await }); Ok(quote! { - impl #impl_generics #cratename::de::BorshDeserialize for #name #ty_generics #where_clause { - fn deserialize_reader<__R: #cratename::io::Read>(reader: &mut __R) -> ::core::result::Result { - let tag = ::deserialize_reader(reader)?; - ::deserialize_variant(reader, tag) + impl #impl_generics #cratename::de::#deserialize_trait for #name #ty_generics #where_clause { + #r#async fn deserialize_reader<__R: #cratename::#read_trait_path>(reader: &mut __R) -> ::core::result::Result { + let tag = ::deserialize_reader(reader)#dot_await?; + ::deserialize_variant(reader, tag)#dot_await } } - impl #impl_generics #cratename::de::EnumExt for #name #ty_generics #where_clause { - fn deserialize_variant<__R: #cratename::io::Read>( + impl #impl_generics #cratename::de::#enum_ext for #name #ty_generics #where_clause { + #r#async fn deserialize_variant<__R: #cratename::#read_trait_path>( reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = + let #r#mut return_value = #variant_arms { - return Err(#cratename::io::Error::new( + return ::core::result::Result::Err(#cratename::io::Error::new( #cratename::io::ErrorKind::InvalidData, #cratename::__private::maybestd::format!("Unexpected variant tag: {:?}", variant_tag), )) }; #init - Ok(return_value) + ::core::result::Result::Ok(return_value) } } }) } -fn process_variant( +fn process_variant( variant: &Variant, cratename: &Path, generics: &mut deserialize::GenericsOutput, @@ -68,13 +90,13 @@ fn process_variant( match &variant.fields { Fields::Named(fields) => { for field in &fields.named { - deserialize::process_field(field, cratename, &mut body, generics)?; + deserialize::process_field::(field, cratename, &mut body, generics)?; } body = quote! { { #body }}; } Fields::Unnamed(fields) => { for field in fields.unnamed.iter() { - deserialize::process_field(field, cratename, &mut body, generics)?; + deserialize::process_field::(field, cratename, &mut body, generics)?; } body = quote! { ( #body )}; } @@ -85,15 +107,16 @@ fn process_variant( #[cfg(test)] mod tests { + use syn::parse_quote; + + use super::*; use crate::internals::test_helpers::{ default_cratename, local_insta_assert_snapshot, pretty_print_syn_str, }; - use super::*; - #[test] fn borsh_skip_struct_variant_field() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum AA { B { #[borsh(skip)] @@ -104,16 +127,21 @@ mod tests { beta: u8, } } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn borsh_skip_tuple_variant_field() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum AAT { B(#[borsh(skip)] i32, u32), @@ -121,16 +149,21 @@ mod tests { beta: u8, } } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_enum_with_custom_crate() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { B { x: HashMap, @@ -138,17 +171,23 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; + + let crate_: Path = parse_quote! { reexporter::borsh }; - let crate_: Path = syn::parse2(quote! { reexporter::borsh }).unwrap(); - let actual = process(&item_struct, crate_).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_enum.clone(), crate_.clone()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_enum, crate_).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_generics() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { B { x: HashMap, @@ -156,16 +195,21 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn bound_generics() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { x: HashMap, @@ -173,16 +217,21 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn recursive_enum() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { x: HashMap, @@ -190,16 +239,20 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_borsh_skip_struct_field() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { #[borsh(skip)] @@ -208,17 +261,21 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_borsh_skip_tuple_field() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { x: HashMap, @@ -226,17 +283,21 @@ mod tests { }, C(K, #[borsh(skip)] Vec), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_deserialize_bound() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { C { a: String, @@ -248,17 +309,45 @@ mod tests { }, D(u32, u32), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn generic_deserialize_async_bound() { + let item_enum: ItemEnum = parse_quote! { + enum A { + C { + a: String, + #[borsh(async_bound(deserialize = + "T: PartialOrd + Hash + Eq + borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync" + ))] + b: HashMap, + }, + D(u32, u32), + } + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn check_deserialize_with_attr() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum C { C3(u64, u64), C4 { @@ -267,17 +356,42 @@ mod tests { y: ThirdParty }, } - }) - .unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn check_deserialize_with_async_attr() { + let item_enum: ItemEnum = parse_quote! { + enum C { + C3(u64, u64), + C4 { + x: u64, + #[borsh(deserialize_with_async = "third_party_impl::deserialize_third_party")] + y: ThirdParty + }, + } + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn borsh_discriminant_false() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[borsh(use_discriminant = false)] enum X { A, @@ -287,15 +401,21 @@ mod tests { E = 10, F, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } + #[test] fn borsh_discriminant_true() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[borsh(use_discriminant = true)] enum X { A, @@ -305,15 +425,21 @@ mod tests { E = 10, F, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } + #[test] fn borsh_init_func() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[borsh(init = initialization_method)] enum A { A, @@ -323,9 +449,15 @@ mod tests { E, F, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_false-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_false-2.snap new file mode 100644 index 000000000..2d8c2ec53 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_false-2.snap @@ -0,0 +1,43 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for X { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for X { + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + X::A + } else if variant_tag == 1u8 { + X::B + } else if variant_tag == 2u8 { + X::C + } else if variant_tag == 3u8 { + X::D + } else if variant_tag == 4u8 { + X::E + } else if variant_tag == 5u8 { + X::F + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_false.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_false.snap index 1e0446681..14c718d83 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_false.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_false.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for X { fn deserialize_reader<__R: borsh::io::Read>( @@ -15,7 +15,7 @@ impl borsh::de::EnumExt for X { reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { X::A } else if variant_tag == 1u8 { X::B @@ -28,7 +28,7 @@ impl borsh::de::EnumExt for X { } else if variant_tag == 5u8 { X::F } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -37,6 +37,6 @@ impl borsh::de::EnumExt for X { ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_true-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_true-2.snap new file mode 100644 index 000000000..5a617705e --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_true-2.snap @@ -0,0 +1,43 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for X { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for X { + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0 { + X::A + } else if variant_tag == 20 { + X::B + } else if variant_tag == 20 + 1 { + X::C + } else if variant_tag == 20 + 1 + 1 { + X::D + } else if variant_tag == 10 { + X::E + } else if variant_tag == 10 + 1 { + X::F + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_true.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_true.snap index add0f62ed..65e363b07 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_true.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_discriminant_true.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for X { fn deserialize_reader<__R: borsh::io::Read>( @@ -15,7 +15,7 @@ impl borsh::de::EnumExt for X { reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0 { + let return_value = if variant_tag == 0 { X::A } else if variant_tag == 20 { X::B @@ -28,7 +28,7 @@ impl borsh::de::EnumExt for X { } else if variant_tag == 10 + 1 { X::F } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -37,6 +37,6 @@ impl borsh::de::EnumExt for X { ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_init_func-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_init_func-2.snap new file mode 100644 index 000000000..dec3fd255 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_init_func-2.snap @@ -0,0 +1,44 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A { + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let mut return_value = if variant_tag == 0u8 { + A::A + } else if variant_tag == 1u8 { + A::B + } else if variant_tag == 2u8 { + A::C + } else if variant_tag == 3u8 { + A::D + } else if variant_tag == 4u8 { + A::E + } else if variant_tag == 5u8 { + A::F + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + return_value.initialization_method(); + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_init_func.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_init_func.snap index 28dd7dbce..54a14823e 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_init_func.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_init_func.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A { fn deserialize_reader<__R: borsh::io::Read>( @@ -28,7 +28,7 @@ impl borsh::de::EnumExt for A { } else if variant_tag == 5u8 { A::F } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -38,6 +38,6 @@ impl borsh::de::EnumExt for A { ) }; return_value.initialization_method(); - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_struct_variant_field-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_struct_variant_field-2.snap new file mode 100644 index 000000000..b95070208 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_struct_variant_field-2.snap @@ -0,0 +1,42 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for AA { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for AA { + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + AA::B { + c: ::core::default::Default::default(), + d: ::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + AA::NegatedVariant { + beta: ::deserialize_reader(reader) + .await?, + } + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_struct_variant_field.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_struct_variant_field.snap index a8a2d9e1a..5938210dc 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_struct_variant_field.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_struct_variant_field.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for AA { fn deserialize_reader<__R: borsh::io::Read>( @@ -15,17 +15,17 @@ impl borsh::de::EnumExt for AA { reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { AA::B { - c: core::default::Default::default(), - d: borsh::BorshDeserialize::deserialize_reader(reader)?, + c: ::core::default::Default::default(), + d: ::deserialize_reader(reader)?, } } else if variant_tag == 1u8 { AA::NegatedVariant { - beta: borsh::BorshDeserialize::deserialize_reader(reader)?, + beta: ::deserialize_reader(reader)?, } } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -34,6 +34,6 @@ impl borsh::de::EnumExt for AA { ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_tuple_variant_field-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_tuple_variant_field-2.snap new file mode 100644 index 000000000..51758af91 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_tuple_variant_field-2.snap @@ -0,0 +1,41 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for AAT { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for AAT { + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + AAT::B( + ::core::default::Default::default(), + ::deserialize_reader(reader).await?, + ) + } else if variant_tag == 1u8 { + AAT::NegatedVariant { + beta: ::deserialize_reader(reader) + .await?, + } + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_tuple_variant_field.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_tuple_variant_field.snap index 60149fc60..7303215f7 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_tuple_variant_field.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/borsh_skip_tuple_variant_field.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for AAT { fn deserialize_reader<__R: borsh::io::Read>( @@ -15,17 +15,17 @@ impl borsh::de::EnumExt for AAT { reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { AAT::B( - core::default::Default::default(), - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::default::Default::default(), + ::deserialize_reader(reader)?, ) } else if variant_tag == 1u8 { AAT::NegatedVariant { - beta: borsh::BorshDeserialize::deserialize_reader(reader)?, + beta: ::deserialize_reader(reader)?, } } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -34,6 +34,6 @@ impl borsh::de::EnumExt for AAT { ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/bound_generics-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/bound_generics-2.snap new file mode 100644 index 000000000..1b3648e82 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/bound_generics-2.snap @@ -0,0 +1,59 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::B { + x: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + y: ::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + A::C( + ::deserialize_reader(reader).await?, + as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/bound_generics.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/bound_generics.snap index 6b31f4bc6..abae695c1 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/bound_generics.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/bound_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where @@ -27,18 +27,21 @@ where reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { A::B { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + x: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, } } else if variant_tag == 1u8 { A::C( - borsh::BorshDeserialize::deserialize_reader(reader)?, - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + as borsh::BorshDeserialize>::deserialize_reader(reader)?, ) } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -47,6 +50,6 @@ where ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_async_attr-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_async_attr-2.snap new file mode 100644 index 000000000..6ae29e32e --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_async_attr-2.snap @@ -0,0 +1,50 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for C +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for C +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + C::C3( + ::deserialize_reader(reader).await?, + ::deserialize_reader(reader).await?, + ) + } else if variant_tag == 1u8 { + C::C4 { + x: ::deserialize_reader(reader) + .await?, + y: third_party_impl::deserialize_third_party(reader).await?, + } + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_async_attr.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_async_attr.snap new file mode 100644 index 000000000..63c7c21d3 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_async_attr.snap @@ -0,0 +1,51 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserialize for C +where + K: borsh::de::BorshDeserialize, + V: borsh::de::BorshDeserialize, +{ + fn deserialize_reader<__R: borsh::io::Read>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader)?; + ::deserialize_variant(reader, tag) + } +} +impl borsh::de::EnumExt for C +where + K: borsh::de::BorshDeserialize, + V: borsh::de::BorshDeserialize, +{ + fn deserialize_variant<__R: borsh::io::Read>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + C::C3( + ::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + ) + } else if variant_tag == 1u8 { + C::C4 { + x: ::deserialize_reader(reader)?, + y: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + } + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_attr-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_attr-2.snap new file mode 100644 index 000000000..4be6ba10b --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_attr-2.snap @@ -0,0 +1,54 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for C +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for C +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + C::C3( + ::deserialize_reader(reader).await?, + ::deserialize_reader(reader).await?, + ) + } else if variant_tag == 1u8 { + C::C4 { + x: ::deserialize_reader(reader) + .await?, + y: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + } + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_attr.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_attr.snap index 968e0c3b5..fe50c66c2 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_attr.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/check_deserialize_with_attr.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for C where @@ -23,18 +23,18 @@ where reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { C::C3( - borsh::BorshDeserialize::deserialize_reader(reader)?, - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, ) } else if variant_tag == 1u8 { C::C4 { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, + x: ::deserialize_reader(reader)?, y: third_party_impl::deserialize_third_party(reader)?, } } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -43,6 +43,6 @@ where ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_struct_field-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_struct_field-2.snap new file mode 100644 index 000000000..4329633ca --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_struct_field-2.snap @@ -0,0 +1,57 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, + K: ::core::default::Default, + V: ::core::default::Default, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, + K: ::core::default::Default, + V: ::core::default::Default, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::B { + x: ::core::default::Default::default(), + y: ::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + A::C( + ::deserialize_reader(reader).await?, + as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_struct_field.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_struct_field.snap index cde62e488..5c6546e30 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_struct_field.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_struct_field.snap @@ -1,14 +1,14 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where V: Value, K: borsh::de::BorshDeserialize, U: borsh::de::BorshDeserialize, - K: core::default::Default, - V: core::default::Default, + K: ::core::default::Default, + V: ::core::default::Default, { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, @@ -22,25 +22,25 @@ where V: Value, K: borsh::de::BorshDeserialize, U: borsh::de::BorshDeserialize, - K: core::default::Default, - V: core::default::Default, + K: ::core::default::Default, + V: ::core::default::Default, { fn deserialize_variant<__R: borsh::io::Read>( reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { A::B { - x: core::default::Default::default(), - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + x: ::core::default::Default::default(), + y: ::deserialize_reader(reader)?, } } else if variant_tag == 1u8 { A::C( - borsh::BorshDeserialize::deserialize_reader(reader)?, - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + as borsh::BorshDeserialize>::deserialize_reader(reader)?, ) } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -49,6 +49,6 @@ where ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_tuple_field-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_tuple_field-2.snap new file mode 100644 index 000000000..0910625cb --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_tuple_field-2.snap @@ -0,0 +1,58 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, + U: ::core::default::Default, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, + U: ::core::default::Default, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::B { + x: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + y: ::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + A::C( + ::deserialize_reader(reader).await?, + ::core::default::Default::default(), + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_tuple_field.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_tuple_field.snap index 0cc108681..d4087a395 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_tuple_field.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_borsh_skip_tuple_field.snap @@ -1,13 +1,13 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where V: Value, K: borsh::de::BorshDeserialize, V: borsh::de::BorshDeserialize, - U: core::default::Default, + U: ::core::default::Default, { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, @@ -21,24 +21,27 @@ where V: Value, K: borsh::de::BorshDeserialize, V: borsh::de::BorshDeserialize, - U: core::default::Default, + U: ::core::default::Default, { fn deserialize_variant<__R: borsh::io::Read>( reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { A::B { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + x: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, } } else if variant_tag == 1u8 { A::C( - borsh::BorshDeserialize::deserialize_reader(reader)?, - core::default::Default::default(), + ::deserialize_reader(reader)?, + ::core::default::Default::default(), ) } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -47,6 +50,6 @@ where ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_async_bound-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_async_bound-2.snap new file mode 100644 index 000000000..0635c01ac --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_async_bound-2.snap @@ -0,0 +1,54 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + T: PartialOrd + Hash + Eq + borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A +where + T: PartialOrd + Hash + Eq + borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::C { + a: ::deserialize_reader(reader) + .await?, + b: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + A::D( + ::deserialize_reader(reader).await?, + ::deserialize_reader(reader).await?, + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_async_bound.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_async_bound.snap new file mode 100644 index 000000000..7463da4e0 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_async_bound.snap @@ -0,0 +1,51 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserialize for A +where + T: borsh::de::BorshDeserialize, + U: borsh::de::BorshDeserialize, +{ + fn deserialize_reader<__R: borsh::io::Read>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader)?; + ::deserialize_variant(reader, tag) + } +} +impl borsh::de::EnumExt for A +where + T: borsh::de::BorshDeserialize, + U: borsh::de::BorshDeserialize, +{ + fn deserialize_variant<__R: borsh::io::Read>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::C { + a: ::deserialize_reader(reader)?, + b: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + } + } else if variant_tag == 1u8 { + A::D( + ::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_bound-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_bound-2.snap new file mode 100644 index 000000000..0eedb6981 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_bound-2.snap @@ -0,0 +1,54 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + T: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A +where + T: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::C { + a: ::deserialize_reader(reader) + .await?, + b: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + A::D( + ::deserialize_reader(reader).await?, + ::deserialize_reader(reader).await?, + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_bound.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_bound.snap index adf641118..75ac1987e 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_bound.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/generic_deserialize_bound.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where @@ -23,18 +23,21 @@ where reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { A::C { - a: borsh::BorshDeserialize::deserialize_reader(reader)?, - b: borsh::BorshDeserialize::deserialize_reader(reader)?, + a: ::deserialize_reader(reader)?, + b: as borsh::BorshDeserialize>::deserialize_reader(reader)?, } } else if variant_tag == 1u8 { A::D( - borsh::BorshDeserialize::deserialize_reader(reader)?, - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, ) } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -43,6 +46,6 @@ where ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/recursive_enum-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/recursive_enum-2.snap new file mode 100644 index 000000000..705858356 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/recursive_enum-2.snap @@ -0,0 +1,57 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::B { + x: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + y: ::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + A::C( + ::deserialize_reader(reader).await?, + as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/recursive_enum.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/recursive_enum.snap index b3c8f7790..852382274 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/recursive_enum.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/recursive_enum.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where @@ -25,18 +25,21 @@ where reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { A::B { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + x: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, } } else if variant_tag == 1u8 { A::C( - borsh::BorshDeserialize::deserialize_reader(reader)?, - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + as borsh::BorshDeserialize>::deserialize_reader(reader)?, ) } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -45,6 +48,6 @@ where ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/simple_enum_with_custom_crate-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_enum_with_custom_crate-2.snap new file mode 100644 index 000000000..a6eb2c787 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_enum_with_custom_crate-2.snap @@ -0,0 +1,61 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl reexporter::borsh::de::BorshDeserializeAsync for A { + async fn deserialize_reader<__R: reexporter::borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader( + reader, + ) + .await?; + ::deserialize_variant(reader, tag) + .await + } +} +impl reexporter::borsh::de::EnumExtAsync for A { + async fn deserialize_variant<__R: reexporter::borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::B { + x: as reexporter::borsh::BorshDeserializeAsync>::deserialize_reader( + reader, + ) + .await?, + y: ::deserialize_reader( + reader, + ) + .await?, + } + } else if variant_tag == 1u8 { + A::C( + ::deserialize_reader( + reader, + ) + .await?, + as reexporter::borsh::BorshDeserializeAsync>::deserialize_reader( + reader, + ) + .await?, + ) + } else { + return ::core::result::Result::Err( + reexporter::borsh::io::Error::new( + reexporter::borsh::io::ErrorKind::InvalidData, + reexporter::borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/simple_enum_with_custom_crate.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_enum_with_custom_crate.snap index 88457ee99..16400f884 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/simple_enum_with_custom_crate.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_enum_with_custom_crate.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl reexporter::borsh::de::BorshDeserialize for A { fn deserialize_reader<__R: reexporter::borsh::io::Read>( @@ -17,18 +17,25 @@ impl reexporter::borsh::de::EnumExt for A { reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { A::B { - x: reexporter::borsh::BorshDeserialize::deserialize_reader(reader)?, - y: reexporter::borsh::BorshDeserialize::deserialize_reader(reader)?, + x: as reexporter::borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader( + reader, + )?, } } else if variant_tag == 1u8 { A::C( - reexporter::borsh::BorshDeserialize::deserialize_reader(reader)?, - reexporter::borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + as reexporter::borsh::BorshDeserialize>::deserialize_reader(reader)?, ) } else { - return Err( + return ::core::result::Result::Err( reexporter::borsh::io::Error::new( reexporter::borsh::io::ErrorKind::InvalidData, reexporter::borsh::__private::maybestd::format!( @@ -37,6 +44,6 @@ impl reexporter::borsh::de::EnumExt for A { ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/simple_generics-2.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_generics-2.snap new file mode 100644 index 000000000..e39c1335d --- /dev/null +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_generics-2.snap @@ -0,0 +1,57 @@ +--- +source: borsh-derive/src/internals/deserialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let tag = ::deserialize_reader(reader) + .await?; + ::deserialize_variant(reader, tag).await + } +} +impl borsh::de::EnumExtAsync for A +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_variant<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + variant_tag: u8, + ) -> ::core::result::Result { + let return_value = if variant_tag == 0u8 { + A::B { + x: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + y: ::deserialize_reader(reader) + .await?, + } + } else if variant_tag == 1u8 { + A::C( + ::deserialize_reader(reader).await?, + as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + ) + } else { + return ::core::result::Result::Err( + borsh::io::Error::new( + borsh::io::ErrorKind::InvalidData, + borsh::__private::maybestd::format!( + "Unexpected variant tag: {:?}", variant_tag + ), + ), + ) + }; + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/enums/snapshots/simple_generics.snap b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_generics.snap index d8b85259c..ef9765ea6 100644 --- a/borsh-derive/src/internals/deserialize/enums/snapshots/simple_generics.snap +++ b/borsh-derive/src/internals/deserialize/enums/snapshots/simple_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where @@ -25,18 +25,21 @@ where reader: &mut __R, variant_tag: u8, ) -> ::core::result::Result { - let mut return_value = if variant_tag == 0u8 { + let return_value = if variant_tag == 0u8 { A::B { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + x: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, } } else if variant_tag == 1u8 { A::C( - borsh::BorshDeserialize::deserialize_reader(reader)?, - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + as borsh::BorshDeserialize>::deserialize_reader(reader)?, ) } else { - return Err( + return ::core::result::Result::Err( borsh::io::Error::new( borsh::io::ErrorKind::InvalidData, borsh::__private::maybestd::format!( @@ -45,6 +48,6 @@ where ), ) }; - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/mod.rs b/borsh-derive/src/internals/deserialize/mod.rs index f9732c05f..bb79f9c5e 100644 --- a/borsh-derive/src/internals/deserialize/mod.rs +++ b/borsh-derive/src/internals/deserialize/mod.rs @@ -1,6 +1,7 @@ +use cfg_if::cfg_if; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{ExprPath, Generics, Ident, Path}; +use syn::{parse_quote, ExprPath, Generics, Ident, Path, Type}; use super::{ attributes::{field, BoundType}, @@ -25,9 +26,14 @@ impl GenericsOutput { default_visitor: generics::FindTyParams::new(generics), } } - fn extend(self, where_clause: &mut syn::WhereClause, cratename: &Path) { - let de_trait: Path = syn::parse2(quote! { #cratename::de::BorshDeserialize }).unwrap(); - let default_trait: Path = syn::parse2(quote! { core::default::Default }).unwrap(); + + fn extend(self, where_clause: &mut syn::WhereClause, cratename: &Path) { + let de_trait: Path = if IS_ASYNC { + parse_quote! { #cratename::de::BorshDeserializeAsync } + } else { + parse_quote! { #cratename::de::BorshDeserialize } + }; + let default_trait: Path = parse_quote! { ::core::default::Default }; let de_predicates = generics::compute_predicates(self.deserialize_visitor.process_for_bounds(), &de_trait); let default_predicates = @@ -38,7 +44,7 @@ impl GenericsOutput { } } -fn process_field( +fn process_field( field: &syn::Field, cratename: &Path, body: &mut TokenStream2, @@ -48,8 +54,8 @@ fn process_field( generics .overrides - .extend(parsed.collect_bounds(BoundType::Deserialize)); - let needs_bounds_derive = parsed.needs_bounds_derive(BoundType::Deserialize); + .extend(parsed.collect_bounds::(BoundType::Deserialize)); + let needs_bounds_derive = parsed.needs_bounds_derive::(BoundType::Deserialize); let field_name = field.ident.as_ref(); let delta = if parsed.skip { @@ -61,7 +67,22 @@ fn process_field( if needs_bounds_derive { generics.deserialize_visitor.visit_field(field); } - field_output(field_name, cratename, parsed.deserialize_with) + field_output::( + field_name, + &field.ty, + cratename, + if IS_ASYNC { + cfg_if! { + if #[cfg(feature = "async")] { + parsed.deserialize_with_async + } else { + None + } + } + } else { + parsed.deserialize_with + }, + ) }; body.extend(delta); Ok(()) @@ -69,22 +90,31 @@ fn process_field( /// function which computes derive output [proc_macro2::TokenStream] /// of code, which deserializes single field -fn field_output( +fn field_output( field_name: Option<&Ident>, + field_type: &Type, cratename: &Path, deserialize_with: Option, ) -> TokenStream2 { - let default_path: ExprPath = - syn::parse2(quote! { #cratename::BorshDeserialize::deserialize_reader }).unwrap(); - let path: ExprPath = deserialize_with.unwrap_or(default_path); + let default_path = || { + let deserialize_trait = Ident::new( + if IS_ASYNC { + "BorshDeserializeAsync" + } else { + "BorshDeserialize" + }, + proc_macro2::Span::call_site(), + ); + parse_quote! { <#field_type as #cratename::#deserialize_trait>::deserialize_reader } + }; + + let path: ExprPath = deserialize_with.unwrap_or_else(default_path); + let dot_await = IS_ASYNC.then(|| quote! { .await }); + if let Some(field_name) = field_name { - quote! { - #field_name: #path(reader)?, - } + quote! { #field_name: #path(reader)#dot_await?, } } else { - quote! { - #path(reader)?, - } + quote! { #path(reader)#dot_await?, } } } @@ -92,10 +122,8 @@ fn field_output( /// of code, which deserializes single skipped field fn field_default_output(field_name: Option<&Ident>) -> TokenStream2 { if let Some(field_name) = field_name { - quote! { - #field_name: core::default::Default::default(), - } + quote! { #field_name: ::core::default::Default::default(), } } else { - quote! { core::default::Default::default(), } + quote! { ::core::default::Default::default(), } } } diff --git a/borsh-derive/src/internals/deserialize/structs/mod.rs b/borsh-derive/src/internals/deserialize/structs/mod.rs index 0faaec309..7ca999d64 100644 --- a/borsh-derive/src/internals/deserialize/structs/mod.rs +++ b/borsh-derive/src/internals/deserialize/structs/mod.rs @@ -1,12 +1,15 @@ -use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Fields, ItemStruct, Path}; +use syn::{Fields, ItemStruct, Path, Token}; use crate::internals::{attributes::item, deserialize, generics}; -pub fn process(input: &ItemStruct, cratename: Path) -> syn::Result { +pub fn process( + input: ItemStruct, + cratename: Path, +) -> syn::Result { let name = &input.ident; - let generics = generics::without_defaults(&input.generics); + let generics = generics::without_defaults(input.generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let mut where_clause = generics::default_where(where_clause); let mut body = TokenStream2::new(); @@ -15,193 +18,249 @@ pub fn process(input: &ItemStruct, cratename: Path) -> syn::Result let return_value = match &input.fields { Fields::Named(fields) => { for field in &fields.named { - deserialize::process_field(field, &cratename, &mut body, &mut generics_output)?; - } - quote! { - Self { #body } + deserialize::process_field::( + field, + &cratename, + &mut body, + &mut generics_output, + )?; } + quote! { Self { #body } } } Fields::Unnamed(fields) => { for field in fields.unnamed.iter() { - deserialize::process_field(field, &cratename, &mut body, &mut generics_output)?; - } - quote! { - Self( #body ) - } - } - Fields::Unit => { - quote! { - Self {} + deserialize::process_field::( + field, + &cratename, + &mut body, + &mut generics_output, + )?; } + quote! { Self( #body ) } } + Fields::Unit => quote! { Self {} }, }; - generics_output.extend(&mut where_clause, &cratename); - - if let Some(method_ident) = item::contains_initialize_with(&input.attrs)? { - Ok(quote! { - impl #impl_generics #cratename::de::BorshDeserialize for #name #ty_generics #where_clause { - fn deserialize_reader<__R: #cratename::io::Read>(reader: &mut __R) -> ::core::result::Result { - let mut return_value = #return_value; - return_value.#method_ident(); - Ok(return_value) - } - } - }) + generics_output.extend::(&mut where_clause, &cratename); + + let deserialize_trait = Ident::new( + if IS_ASYNC { + "BorshDeserializeAsync" + } else { + "BorshDeserialize" + }, + Span::call_site(), + ); + let read_trait_path = if IS_ASYNC { + quote! { async_io::AsyncRead } + } else { + quote! { io::Read } + }; + let r#async = IS_ASYNC.then(|| Token![async](Span::call_site())); + + let body = if let Some(method_ident) = item::contains_initialize_with(&input.attrs)? { + quote! { + let mut return_value = #return_value; + return_value.#method_ident(); + ::core::result::Result::Ok(return_value) + } } else { - Ok(quote! { - impl #impl_generics #cratename::de::BorshDeserialize for #name #ty_generics #where_clause { - fn deserialize_reader<__R: #cratename::io::Read>(reader: &mut __R) -> ::core::result::Result { - Ok(#return_value) - } + quote! { ::core::result::Result::Ok(#return_value) } + }; + + Ok(quote! { + impl #impl_generics #cratename::de::#deserialize_trait for #name #ty_generics #where_clause { + #r#async fn deserialize_reader<__R: #cratename::#read_trait_path>(reader: &mut __R) -> ::core::result::Result { + #body } - }) - } + } + }) } #[cfg(test)] mod tests { + use syn::parse_quote; + + use super::*; use crate::internals::test_helpers::{ default_cratename, local_insta_assert_snapshot, pretty_print_syn_str, }; - use super::*; - #[test] fn simple_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: u64, y: String, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_struct_with_custom_crate() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: u64, y: String, } - }) - .unwrap(); + }; + + let crate_: Path = parse_quote! { reexporter::borsh }; - let crate_: Path = syn::parse2(quote! { reexporter::borsh }).unwrap(); - let actual = process(&item_struct, crate_).unwrap(); + let actual = process::(item_struct.clone(), crate_.clone()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, crate_).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_generics() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: HashMap, y: String, } - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_generic_tuple_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct TupleA(T, u32); - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn bound_generics() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A where V: Value { x: HashMap, y: String, } - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn recursive_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct CRecC { a: String, b: HashMap, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_tuple_struct_borsh_skip1() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G ( #[borsh(skip)] HashMap, U, ); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_tuple_struct_borsh_skip2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G ( HashMap, #[borsh(skip)] U, ); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_named_fields_struct_borsh_skip() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G { #[borsh(skip)] x: HashMap, y: U, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_deserialize_bound() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct C { a: String, #[borsh(bound(deserialize = @@ -210,56 +269,132 @@ mod tests { ))] b: HashMap, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn generic_deserialize_async_bound() { + let item_struct: ItemStruct = parse_quote! { + struct C { + a: String, + #[borsh(async_bound(deserialize = + "T: PartialOrd + Hash + Eq + borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync" + ))] + b: HashMap, + } + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn test_override_automatically_added_default_trait() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G1( #[borsh(skip,bound(deserialize = ""))] HashMap, U ); - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn test_override_automatically_added_default_trait_async() { + let item_struct: ItemStruct = parse_quote! { + struct G1( + #[borsh(skip,async_bound(deserialize = ""))] + HashMap, + U + ); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn check_deserialize_with_attr() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(deserialize_with = "third_party_impl::deserialize_third_party")] x: ThirdParty, y: u64, } - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn check_deserialize_with_async_attr() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(deserialize_with_async = "third_party_impl::deserialize_third_party")] + x: ThirdParty, + y: u64, + } + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } + #[test] fn borsh_init_func() { - let item_enum: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { #[borsh(init=initialization_method)] struct A { x: u64, y: String, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/borsh_init_func-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/borsh_init_func-2.snap new file mode 100644 index 000000000..5c52c0507 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/borsh_init_func-2.snap @@ -0,0 +1,17 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + let mut return_value = Self { + x: ::deserialize_reader(reader).await?, + y: ::deserialize_reader(reader) + .await?, + }; + return_value.initialization_method(); + ::core::result::Result::Ok(return_value) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/borsh_init_func.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/borsh_init_func.snap index 61f51e48e..e449e90f7 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/borsh_init_func.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/borsh_init_func.snap @@ -1,16 +1,16 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { let mut return_value = Self { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + x: ::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, }; return_value.initialization_method(); - Ok(return_value) + ::core::result::Result::Ok(return_value) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/bound_generics-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/bound_generics-2.snap new file mode 100644 index 000000000..ef7898f88 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/bound_generics-2.snap @@ -0,0 +1,24 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + V: Value, + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + y: ::deserialize_reader(reader) + .await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/bound_generics.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/bound_generics.snap index dc4514d64..81363ce72 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/bound_generics.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/bound_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where @@ -11,9 +11,9 @@ where fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::result::Result::Ok(Self { + x: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, }) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_async_attr-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_async_attr-2.snap new file mode 100644 index 000000000..a4a594f06 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_async_attr-2.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: third_party_impl::deserialize_third_party(reader).await?, + y: ::deserialize_reader(reader).await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_async_attr.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_async_attr.snap new file mode 100644 index 000000000..7d0159738 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_async_attr.snap @@ -0,0 +1,21 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserialize for A +where + K: borsh::de::BorshDeserialize, + V: borsh::de::BorshDeserialize, +{ + fn deserialize_reader<__R: borsh::io::Read>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_attr-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_attr-2.snap new file mode 100644 index 000000000..f9f1899de --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_attr-2.snap @@ -0,0 +1,22 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + y: ::deserialize_reader(reader).await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_attr.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_attr.snap index 79a844803..8eb7e2997 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_attr.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/check_deserialize_with_attr.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where @@ -10,9 +10,9 @@ where fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { + ::core::result::Result::Ok(Self { x: third_party_impl::deserialize_third_party(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, }) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_async_bound-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_async_bound-2.snap new file mode 100644 index 000000000..48f831c7c --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_async_bound-2.snap @@ -0,0 +1,23 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for C +where + T: PartialOrd + Hash + Eq + borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + a: ::deserialize_reader(reader) + .await?, + b: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_async_bound.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_async_bound.snap new file mode 100644 index 000000000..634f837ce --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_async_bound.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserialize for C +where + T: borsh::de::BorshDeserialize, + U: borsh::de::BorshDeserialize, +{ + fn deserialize_reader<__R: borsh::io::Read>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + a: ::deserialize_reader(reader)?, + b: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_bound-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_bound-2.snap new file mode 100644 index 000000000..808a8ee96 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_bound-2.snap @@ -0,0 +1,23 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for C +where + T: borsh::de::BorshDeserializeAsync, + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + a: ::deserialize_reader(reader) + .await?, + b: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_bound.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_bound.snap index 7e4a0317b..0eaeeed68 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_bound.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_deserialize_bound.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for C where @@ -10,9 +10,9 @@ where fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { - a: borsh::BorshDeserialize::deserialize_reader(reader)?, - b: borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::result::Result::Ok(Self { + a: ::deserialize_reader(reader)?, + b: as borsh::BorshDeserialize>::deserialize_reader(reader)?, }) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_named_fields_struct_borsh_skip-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_named_fields_struct_borsh_skip-2.snap new file mode 100644 index 000000000..aece060e2 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_named_fields_struct_borsh_skip-2.snap @@ -0,0 +1,19 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for G +where + U: borsh::de::BorshDeserializeAsync, + K: ::core::default::Default, + V: ::core::default::Default, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: ::core::default::Default::default(), + y: ::deserialize_reader(reader).await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap index c094081c6..380e5b217 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap @@ -1,19 +1,19 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for G where U: borsh::de::BorshDeserialize, - K: core::default::Default, - V: core::default::Default, + K: ::core::default::Default, + V: ::core::default::Default, { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { - x: core::default::Default::default(), - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::result::Result::Ok(Self { + x: ::core::default::Default::default(), + y: ::deserialize_reader(reader)?, }) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip1-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip1-2.snap new file mode 100644 index 000000000..e3b9f09c3 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip1-2.snap @@ -0,0 +1,21 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for G +where + U: borsh::de::BorshDeserializeAsync, + K: ::core::default::Default, + V: ::core::default::Default, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + Self( + ::core::default::Default::default(), + ::deserialize_reader(reader).await?, + ), + ) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap index 492cc2b14..a9a2aec29 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap @@ -1,20 +1,20 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for G where U: borsh::de::BorshDeserialize, - K: core::default::Default, - V: core::default::Default, + K: ::core::default::Default, + V: ::core::default::Default, { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok( + ::core::result::Result::Ok( Self( - core::default::Default::default(), - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::default::Default::default(), + ::deserialize_reader(reader)?, ), ) } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip2-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip2-2.snap new file mode 100644 index 000000000..d2c09ca42 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip2-2.snap @@ -0,0 +1,25 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for G +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, + U: ::core::default::Default, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + Self( + as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + ::core::default::Default::default(), + ), + ) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap index 5abdae6be..3caa0dc1c 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap @@ -1,20 +1,20 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for G where K: borsh::de::BorshDeserialize, V: borsh::de::BorshDeserialize, - U: core::default::Default, + U: ::core::default::Default, { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok( + ::core::result::Result::Ok( Self( - borsh::BorshDeserialize::deserialize_reader(reader)?, - core::default::Default::default(), + as borsh::BorshDeserialize>::deserialize_reader(reader)?, + ::core::default::Default::default(), ), ) } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait-2.snap new file mode 100644 index 000000000..edc0aaa54 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait-2.snap @@ -0,0 +1,21 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for G1 +where + U: borsh::de::BorshDeserializeAsync, + K: ::core::default::Default, + V: ::core::default::Default, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + Self( + ::core::default::Default::default(), + ::deserialize_reader(reader).await?, + ), + ) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait.snap index 546931471..7110ab816 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for G1 where @@ -9,10 +9,10 @@ where fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok( + ::core::result::Result::Ok( Self( - core::default::Default::default(), - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::default::Default::default(), + ::deserialize_reader(reader)?, ), ) } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait_async-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait_async-2.snap new file mode 100644 index 000000000..d6174b721 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait_async-2.snap @@ -0,0 +1,19 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for G1 +where + U: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + Self( + ::core::default::Default::default(), + ::deserialize_reader(reader).await?, + ), + ) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait_async.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait_async.snap new file mode 100644 index 000000000..3b6968bc9 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/override_automatically_added_default_trait_async.snap @@ -0,0 +1,21 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserialize for G1 +where + U: borsh::de::BorshDeserialize, + K: ::core::default::Default, + V: ::core::default::Default, +{ + fn deserialize_reader<__R: borsh::io::Read>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + Self( + ::core::default::Default::default(), + ::deserialize_reader(reader)?, + ), + ) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/recursive_struct-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/recursive_struct-2.snap new file mode 100644 index 000000000..1a9ab454b --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/recursive_struct-2.snap @@ -0,0 +1,19 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for CRecC { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + a: ::deserialize_reader(reader) + .await?, + b: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/recursive_struct.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/recursive_struct.snap index 0dd795c39..2336313ce 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/recursive_struct.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/recursive_struct.snap @@ -1,14 +1,17 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for CRecC { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { - a: borsh::BorshDeserialize::deserialize_reader(reader)?, - b: borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::result::Result::Ok(Self { + a: ::deserialize_reader(reader)?, + b: as borsh::BorshDeserialize>::deserialize_reader(reader)?, }) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generic_tuple_struct-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generic_tuple_struct-2.snap new file mode 100644 index 000000000..5b8829d6b --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generic_tuple_struct-2.snap @@ -0,0 +1,19 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for TupleA +where + T: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok( + Self( + ::deserialize_reader(reader).await?, + ::deserialize_reader(reader).await?, + ), + ) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generic_tuple_struct.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generic_tuple_struct.snap index c1d69d951..8a5cdabd4 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generic_tuple_struct.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generic_tuple_struct.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for TupleA where @@ -9,10 +9,10 @@ where fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok( + ::core::result::Result::Ok( Self( - borsh::BorshDeserialize::deserialize_reader(reader)?, - borsh::BorshDeserialize::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, + ::deserialize_reader(reader)?, ), ) } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generics-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generics-2.snap new file mode 100644 index 000000000..482878f49 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generics-2.snap @@ -0,0 +1,23 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A +where + K: borsh::de::BorshDeserializeAsync, + V: borsh::de::BorshDeserializeAsync, +{ + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: as borsh::BorshDeserializeAsync>::deserialize_reader(reader) + .await?, + y: ::deserialize_reader(reader) + .await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generics.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generics.snap index f2a8c7c4b..e1566514c 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generics.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A where @@ -10,9 +10,9 @@ where fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::result::Result::Ok(Self { + x: as borsh::BorshDeserialize>::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, }) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct-2.snap new file mode 100644 index 000000000..c6676bac8 --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct-2.snap @@ -0,0 +1,15 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::de::BorshDeserializeAsync for A { + async fn deserialize_reader<__R: borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: ::deserialize_reader(reader).await?, + y: ::deserialize_reader(reader) + .await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct.snap index 6026d09f0..feaaa0d5e 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct.snap @@ -1,14 +1,14 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::de::BorshDeserialize for A { fn deserialize_reader<__R: borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { - x: borsh::BorshDeserialize::deserialize_reader(reader)?, - y: borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::result::Result::Ok(Self { + x: ::deserialize_reader(reader)?, + y: ::deserialize_reader(reader)?, }) } } diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct_with_custom_crate-2.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct_with_custom_crate-2.snap new file mode 100644 index 000000000..8cc625bae --- /dev/null +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct_with_custom_crate-2.snap @@ -0,0 +1,20 @@ +--- +source: borsh-derive/src/internals/deserialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl reexporter::borsh::de::BorshDeserializeAsync for A { + async fn deserialize_reader<__R: reexporter::borsh::async_io::AsyncRead>( + reader: &mut __R, + ) -> ::core::result::Result { + ::core::result::Result::Ok(Self { + x: ::deserialize_reader( + reader, + ) + .await?, + y: ::deserialize_reader( + reader, + ) + .await?, + }) + } +} diff --git a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct_with_custom_crate.snap b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct_with_custom_crate.snap index ad7fb3788..5d2e208e6 100644 --- a/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct_with_custom_crate.snap +++ b/borsh-derive/src/internals/deserialize/structs/snapshots/simple_struct_with_custom_crate.snap @@ -1,14 +1,16 @@ --- source: borsh-derive/src/internals/deserialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl reexporter::borsh::de::BorshDeserialize for A { fn deserialize_reader<__R: reexporter::borsh::io::Read>( reader: &mut __R, ) -> ::core::result::Result { - Ok(Self { - x: reexporter::borsh::BorshDeserialize::deserialize_reader(reader)?, - y: reexporter::borsh::BorshDeserialize::deserialize_reader(reader)?, + ::core::result::Result::Ok(Self { + x: ::deserialize_reader(reader)?, + y: ::deserialize_reader( + reader, + )?, }) } } diff --git a/borsh-derive/src/internals/deserialize/unions/mod.rs b/borsh-derive/src/internals/deserialize/unions/mod.rs index 3806821bd..89200b4ea 100644 --- a/borsh-derive/src/internals/deserialize/unions/mod.rs +++ b/borsh-derive/src/internals/deserialize/unions/mod.rs @@ -1,6 +1,9 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ItemUnion, Path}; -pub fn process(_input: &ItemUnion, _cratename: Path) -> syn::Result { +pub fn process( + _input: ItemUnion, + _cratename: Path, +) -> syn::Result { unimplemented!() } diff --git a/borsh-derive/src/internals/enum_discriminant.rs b/borsh-derive/src/internals/enum_discriminant.rs index 03b3f5829..a3e8fcc94 100644 --- a/borsh-derive/src/internals/enum_discriminant.rs +++ b/borsh-derive/src/internals/enum_discriminant.rs @@ -1,8 +1,7 @@ -use std::collections::HashMap; -use std::convert::TryFrom; +use std::{collections::HashMap, convert::TryFrom}; use proc_macro2::{Ident, TokenStream}; -use quote::quote; +use quote::{quote, ToTokens}; use syn::{punctuated::Punctuated, token::Comma, Variant}; pub struct Discriminants(HashMap); @@ -15,8 +14,8 @@ impl Discriminants { for variant in variants { let this_discriminant = variant.discriminant.clone().map_or_else( - || quote! { #next_discriminant_if_not_specified }, - |(_, e)| quote! { #e }, + || next_discriminant_if_not_specified, + |(_, e)| e.into_token_stream(), ); next_discriminant_if_not_specified = quote! { #this_discriminant + 1 }; @@ -39,8 +38,7 @@ impl Discriminants { ) })?; let result = if use_discriminant { - let discriminant_value = self.0.get(variant_ident).unwrap(); - quote! { #discriminant_value } + self.0.get(variant_ident).unwrap().clone() // discriminant value } else { quote! { #variant_idx } }; diff --git a/borsh-derive/src/internals/generics.rs b/borsh-derive/src/internals/generics.rs index 7914b3b00..3b29bb4f2 100644 --- a/borsh-derive/src/internals/generics.rs +++ b/borsh-derive/src/internals/generics.rs @@ -1,9 +1,10 @@ use std::collections::{HashMap, HashSet}; -use quote::{quote, ToTokens}; +use quote::ToTokens; use syn::{ - punctuated::Pair, Field, GenericArgument, Generics, Ident, Macro, Path, PathArguments, - PathSegment, ReturnType, Type, TypeParamBound, TypePath, WhereClause, WherePredicate, + parse_quote, punctuated::Pair, Field, GenericArgument, Generics, Ident, Macro, Path, + PathArguments, PathSegment, ReturnType, Type, TypeParamBound, TypePath, WhereClause, + WherePredicate, }; pub fn default_where(where_clause: Option<&WhereClause>) -> WhereClause { @@ -20,10 +21,7 @@ pub fn compute_predicates(params: Vec, traitname: &Path) -> Vec, traitname: &Path) -> Vec Generics { - syn::Generics { +pub fn without_defaults(generics: Generics) -> Generics { + Generics { params: generics .params - .iter() + .into_iter() .map(|param| match param { syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam { eq_token: None, default: None, - ..param.clone() + ..param }), - _ => param.clone(), + _ => param, }) .collect(), - ..generics.clone() + ..generics } } diff --git a/borsh-derive/src/internals/schema/enums/mod.rs b/borsh-derive/src/internals/schema/enums/mod.rs index 95bbed06b..0d5394986 100644 --- a/borsh-derive/src/internals/schema/enums/mod.rs +++ b/borsh-derive/src/internals/schema/enums/mod.rs @@ -1,6 +1,7 @@ +use std::collections::HashSet; + use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; -use std::collections::HashSet; use syn::{Fields, Generics, Ident, ItemEnum, ItemStruct, Path, Variant, Visibility}; use crate::internals::{ @@ -28,14 +29,14 @@ fn transform_variant_fields(mut input: Fields) -> Fields { input } -pub fn process(input: &ItemEnum, cratename: Path) -> syn::Result { +pub fn process(input: ItemEnum, cratename: Path) -> syn::Result { let name = &input.ident; let enum_name = name.to_token_stream().to_string(); - let generics = generics::without_defaults(&input.generics); + let use_discriminant = item::contains_use_discriminant(&input)?; + let generics = generics::without_defaults(input.generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let mut where_clause = generics::default_where(where_clause); let mut generics_output = schema::GenericsOutput::new(&generics); - let use_discriminant = item::contains_use_discriminant(input)?; let discriminants = Discriminants::new(&input.variants); // Generate functions that return the schema for variants. @@ -137,8 +138,8 @@ fn process_variant( }, variant_entry: quote! { (u8::from(#discriminant_value) as i64, - #variant_name.into(), - #variant_type::declaration()) + #variant_name.into(), + #variant_type::declaration()) }, }) } @@ -181,47 +182,46 @@ fn inner_struct_definition( #[cfg(test)] mod tests { + use syn::parse_quote; + + use super::*; use crate::internals::test_helpers::{ default_cratename, local_insta_assert_debug_snapshot, local_insta_assert_snapshot, pretty_print_syn_str, }; - use super::*; - #[test] fn simple_enum() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { Bacon, Eggs } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn simple_enum_with_custom_crate() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { Bacon, Eggs } - }) - .unwrap(); + }; - let crate_: Path = syn::parse2(quote! { reexporter::borsh }).unwrap(); - let actual = process(&item_enum, crate_).unwrap(); + let crate_: Path = parse_quote! { reexporter::borsh }; + let actual = process(item_enum, crate_).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn borsh_discriminant_false() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[borsh(use_discriminant = false)] enum X { A, @@ -231,15 +231,14 @@ mod tests { E = 10, F, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn borsh_discriminant_true() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[borsh(use_discriminant = true)] enum X { A, @@ -249,61 +248,57 @@ mod tests { E = 10, F, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn single_field_enum() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { Bacon, } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn complex_enum() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { Bacon, Eggs, Salad(Tomatoes, Cucumber, Oil), Sausage{wrapper: Wrapper, filling: Filling}, } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn complex_enum_generics() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { Bacon, Eggs, Salad(Tomatoes, C, Oil), Sausage{wrapper: W, filling: Filling}, } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn trailing_comma_generics() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum Side where A: Display + Debug, @@ -312,16 +307,15 @@ mod tests { Left(A), Right(B), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn test_filter_foreign_attrs() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { #[serde(rename = "ab")] B { @@ -335,33 +329,31 @@ mod tests { beta: String, } } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn complex_enum_generics_borsh_skip_tuple_field() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where W: Hash { Bacon, Eggs, Salad(Tomatoes, #[borsh(skip)] C, Oil), Sausage{wrapper: W, filling: Filling}, } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn complex_enum_generics_borsh_skip_named_field() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { Bacon, Eggs, @@ -373,16 +365,15 @@ mod tests { unexpected: U, }, } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn recursive_enum() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { x: HashMap, @@ -390,17 +381,16 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_associated_type() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum EnumParametrized where K: TraitName, @@ -415,16 +405,15 @@ mod tests { }, C(T, u16), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_associated_type_param_override() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum EnumParametrized where K: TraitName, @@ -440,17 +429,16 @@ mod tests { }, C(T, u16), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_associated_type_param_override_conflict() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum EnumParametrized where K: TraitName, @@ -462,17 +450,16 @@ mod tests { }, C(T, u16), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()); + let actual = process(item_enum, default_cratename()); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn check_with_funcs_skip_conflict() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum C { C3(u64, u64), C4( @@ -484,17 +471,16 @@ mod tests { ThirdParty, ), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()); + let actual = process(item_enum, default_cratename()); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn with_funcs_attr() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum C { C3(u64, u64), C4( @@ -506,11 +492,10 @@ mod tests { ThirdParty, ), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_enum, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } } diff --git a/borsh-derive/src/internals/schema/mod.rs b/borsh-derive/src/internals/schema/mod.rs index b094a4452..d1104d9d0 100644 --- a/borsh-derive/src/internals/schema/mod.rs +++ b/borsh-derive/src/internals/schema/mod.rs @@ -3,8 +3,8 @@ use std::collections::HashSet; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{ - punctuated::Punctuated, token::Comma, Field, Fields, GenericParam, Generics, Ident, Path, Type, - WherePredicate, + parse_quote, punctuated::Punctuated, token::Comma, Field, Fields, GenericParam, Generics, + Ident, Path, Type, WherePredicate, }; use crate::internals::{attributes::field, generics}; @@ -22,8 +22,9 @@ impl GenericsOutput { params_visitor: generics::FindTyParams::new(generics), } } + fn result(self, item_name: &str, cratename: &Path) -> (Vec, TokenStream2) { - let trait_path: Path = syn::parse2(quote! { #cratename::BorshSchema }).unwrap(); + let trait_path: Path = parse_quote! { #cratename::BorshSchema }; let predicates = generics::compute_predicates( self.params_visitor.clone().process_for_bounds(), &trait_path, @@ -48,13 +49,11 @@ fn declaration(ident_str: &str, cratename: Path, params_for_bounds: Vec) - }); } if declaration_params.is_empty() { - quote! { - #ident_str.to_string() - } + quote! { #ident_str.to_string() } } else { quote! { - let params = #cratename::__private::maybestd::vec![#(#declaration_params),*]; - format!(r#"{}<{}>"#, #ident_str, params.join(", ")) + let params = #cratename::__private::maybestd::vec![#(#declaration_params),*]; + format!(r#"{}<{}>"#, #ident_str, params.join(", ")) } } } diff --git a/borsh-derive/src/internals/schema/structs/mod.rs b/borsh-derive/src/internals/schema/structs/mod.rs index 8e388430f..19b09f5b6 100644 --- a/borsh-derive/src/internals/schema/structs/mod.rs +++ b/borsh-derive/src/internals/schema/structs/mod.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; -use syn::{ExprPath, Fields, Ident, ItemStruct, Path, Type}; +use syn::{parse_quote, ExprPath, Fields, Ident, ItemStruct, Path, Type}; use crate::internals::{attributes::field, generics, schema}; @@ -13,10 +13,9 @@ fn field_declaration_output( cratename: &Path, declaration_override: Option, ) -> TokenStream2 { - let default_path: ExprPath = - syn::parse2(quote! { <#field_type as #cratename::BorshSchema>::declaration }).unwrap(); + let default_path = || parse_quote! { <#field_type as #cratename::BorshSchema>::declaration }; - let path = declaration_override.unwrap_or(default_path); + let path = declaration_override.unwrap_or_else(default_path); if let Some(field_name) = field_name { let field_name = field_name.to_token_stream().to_string(); @@ -37,21 +36,19 @@ fn field_definitions_output( cratename: &Path, definitions_override: Option, ) -> TokenStream2 { - let default_path: ExprPath = syn::parse2( - quote! { <#field_type as #cratename::BorshSchema>::add_definitions_recursively }, - ) - .unwrap(); - let path = definitions_override.unwrap_or(default_path); + let default_path = + || parse_quote! { <#field_type as #cratename::BorshSchema>::add_definitions_recursively }; + let path = definitions_override.unwrap_or_else(default_path); quote! { #path(definitions); } } -pub fn process(input: &ItemStruct, cratename: Path) -> syn::Result { +pub fn process(input: ItemStruct, cratename: Path) -> syn::Result { let name = &input.ident; let struct_name = name.to_token_stream().to_string(); - let generics = generics::without_defaults(&input.generics); + let generics = generics::without_defaults(input.generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let mut where_clause = generics::default_where(where_clause); let mut generics_output = schema::GenericsOutput::new(&generics); @@ -135,6 +132,7 @@ fn process_fields( } Ok((struct_fields, add_definitions_recursively)) } + fn process_field( field: &syn::Field, cratename: &Path, @@ -162,103 +160,97 @@ fn process_field( #[cfg(test)] mod tests { + use syn::parse_quote; + + use super::*; use crate::internals::test_helpers::{ default_cratename, local_insta_assert_debug_snapshot, local_insta_assert_snapshot, pretty_print_syn_str, }; - use super::*; - #[test] fn unit_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A; - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn wrapper_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A(T); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn tuple_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A(u64, String); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn tuple_struct_params() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A(K, V); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn simple_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: u64, y: String, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn simple_struct_with_custom_crate() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: u64, y: String, } - }) - .unwrap(); + }; - let crate_: Path = syn::parse2(quote! { reexporter::borsh }).unwrap(); - let actual = process(&item_struct, crate_).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let crate_: Path = parse_quote! { reexporter::borsh }; + let actual = process(item_struct, crate_).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn simple_generics() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: HashMap, y: String, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn trailing_comma_generics() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A where K: Display + Debug, @@ -266,130 +258,121 @@ mod tests { x: HashMap, y: String, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn tuple_struct_whole_skip() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A(#[borsh(skip)] String); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn tuple_struct_partial_skip() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A(#[borsh(skip)] u64, String); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_tuple_struct_borsh_skip1() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G ( #[borsh(skip)] HashMap, U, ); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_tuple_struct_borsh_skip2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G ( HashMap, #[borsh(skip)] U, ); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_tuple_struct_borsh_skip3() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G ( #[borsh(skip)] HashMap, U, K, ); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_tuple_struct_borsh_skip4() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct ASalad(Tomatoes, #[borsh(skip)] C, Oil); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_named_fields_struct_borsh_skip() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G { #[borsh(skip)] x: HashMap, y: U, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn recursive_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct CRecC { a: String, b: HashMap, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_associated_type() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -397,17 +380,16 @@ mod tests { field: T::Associated, another: V, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_associated_type_param_override() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -418,17 +400,16 @@ mod tests { field: ::Associated, another: V, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_associated_type_param_override2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -439,17 +420,16 @@ mod tests { field: (::Associated, T), another: V, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn generic_associated_type_param_override_conflict() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -460,17 +440,16 @@ mod tests { field: ::Associated, another: V, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()); + let actual = process(item_struct, default_cratename()); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn check_with_funcs_skip_conflict() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(skip,schema(with_funcs( declaration = "third_party_impl::declaration::", @@ -479,17 +458,16 @@ mod tests { x: ThirdParty, y: u64, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()); + let actual = process(item_struct, default_cratename()); local_insta_assert_debug_snapshot!(actual.unwrap_err()); } #[test] fn with_funcs_attr() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(schema(with_funcs( declaration = "third_party_impl::declaration::", @@ -498,17 +476,16 @@ mod tests { x: ThirdParty, y: u64, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn schema_param_override3() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh( schema( @@ -518,11 +495,10 @@ mod tests { x: PrimaryMap, y: String, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process(item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } } diff --git a/borsh-derive/src/internals/serialize/enums/mod.rs b/borsh-derive/src/internals/serialize/enums/mod.rs index 4e86ca2d4..729441d97 100644 --- a/borsh-derive/src/internals/serialize/enums/mod.rs +++ b/borsh-derive/src/internals/serialize/enums/mod.rs @@ -1,6 +1,7 @@ -use proc_macro2::TokenStream as TokenStream2; +use cfg_if::cfg_if; +use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Fields, Ident, ItemEnum, Path, Variant}; +use syn::{Fields, Ident, ItemEnum, Lifetime, Path, Token, Variant}; use crate::internals::{ attributes::{field, item, BoundType}, @@ -8,22 +9,25 @@ use crate::internals::{ generics, serialize, }; -pub fn process(input: &ItemEnum, cratename: Path) -> syn::Result { +pub fn process( + input: ItemEnum, + cratename: Path, +) -> syn::Result { let enum_ident = &input.ident; - let generics = generics::without_defaults(&input.generics); + let use_discriminant = item::contains_use_discriminant(&input)?; + let generics = generics::without_defaults(input.generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let mut where_clause = generics::default_where(where_clause); let mut generics_output = serialize::GenericsOutput::new(&generics); let mut all_variants_idx_body = TokenStream2::new(); let mut fields_body = TokenStream2::new(); - let use_discriminant = item::contains_use_discriminant(input)?; let discriminants = Discriminants::new(&input.variants); let mut has_unit_variant = false; for (variant_idx, variant) in input.variants.iter().enumerate() { let variant_ident = &variant.ident; let discriminant_value = discriminants.get(variant_ident, use_discriminant, variant_idx)?; - let variant_output = process_variant( + let variant_output = process_variant::( variant, enum_ident, &discriminant_value, @@ -41,18 +45,44 @@ pub fn process(input: &ItemEnum, cratename: Path) -> syn::Result { } } let fields_body = optimize_fields_body(fields_body, has_unit_variant); - generics_output.extend(&mut where_clause, &cratename); + generics_output.extend::(&mut where_clause, &cratename); + + let serialize_trait = Ident::new( + if IS_ASYNC { + "BorshSerializeAsync" + } else { + "BorshSerialize" + }, + Span::call_site(), + ); + let writer_trait = if IS_ASYNC { + quote! { async_io::AsyncWrite } + } else { + quote! { io::Write } + }; + let r#async = IS_ASYNC.then(|| Token![async](Span::call_site())); + let lifetime = IS_ASYNC.then(|| Lifetime::new("'async_variant", Span::call_site())); + let lt_comma = IS_ASYNC.then(|| Token![,](Span::call_site())); + + let write_variant_idx = if IS_ASYNC { + quote! { writer.write_u8(variant_idx).await } + } else { + quote! { writer.write_all(&variant_idx.to_le_bytes()) } + }; Ok(quote! { - impl #impl_generics #cratename::ser::BorshSerialize for #enum_ident #ty_generics #where_clause { - fn serialize<__W: #cratename::io::Write>(&self, writer: &mut __W) -> ::core::result::Result<(), #cratename::io::Error> { + impl #impl_generics #cratename::ser::#serialize_trait for #enum_ident #ty_generics #where_clause { + #r#async fn serialize<#lifetime #lt_comma __W: #cratename::#writer_trait>( + &#lifetime self, + writer: &#lifetime mut __W, + ) -> ::core::result::Result<(), #cratename::io::Error> { let variant_idx: u8 = match self { #all_variants_idx_body }; - writer.write_all(&variant_idx.to_le_bytes())?; + #write_variant_idx?; #fields_body - Ok(()) + ::core::result::Result::Ok(()) } } }) @@ -123,7 +153,7 @@ struct VariantOutput { variant_idx_body: TokenStream2, } -fn process_variant( +fn process_variant( variant: &Variant, enum_ident: &Ident, discriminant_value: &TokenStream2, @@ -136,7 +166,13 @@ fn process_variant( let mut variant_fields = VariantFields::default(); for field in &fields.named { let field_id = serialize::FieldId::Enum(field.ident.clone().unwrap()); - process_field(field, field_id, cratename, generics, &mut variant_fields)?; + process_field::( + field, + field_id, + cratename, + generics, + &mut variant_fields, + )?; } VariantOutput { body: VariantBody::Fields(variant_fields.named_header()), @@ -149,7 +185,13 @@ fn process_variant( let mut variant_fields = VariantFields::default(); for (field_idx, field) in fields.unnamed.iter().enumerate() { let field_id = serialize::FieldId::new_enum_unnamed(field_idx)?; - process_field(field, field_id, cratename, generics, &mut variant_fields)?; + process_field::( + field, + field_id, + cratename, + generics, + &mut variant_fields, + )?; } VariantOutput { body: VariantBody::Fields(variant_fields.unnamed_header()), @@ -168,7 +210,7 @@ fn process_variant( Ok(variant_output) } -fn process_field( +fn process_field( field: &syn::Field, field_id: serialize::FieldId, cratename: &Path, @@ -177,10 +219,10 @@ fn process_field( ) -> syn::Result<()> { let parsed = field::Attributes::parse(&field.attrs)?; - let needs_bounds_derive = parsed.needs_bounds_derive(BoundType::Serialize); + let needs_bounds_derive = parsed.needs_bounds_derive::(BoundType::Serialize); generics .overrides - .extend(parsed.collect_bounds(BoundType::Serialize)); + .extend(parsed.collect_bounds::(BoundType::Serialize)); let field_variant_header = field_id.enum_variant_header(parsed.skip); if let Some(field_variant_header) = field_variant_header { @@ -188,7 +230,21 @@ fn process_field( } if !parsed.skip { - let delta = field_id.serialize_output(cratename, parsed.serialize_with); + let delta = field_id.serialize_output::( + &field.ty, + cratename, + if IS_ASYNC { + cfg_if! { + if #[cfg(feature = "async")] { + parsed.serialize_with_async + } else { + None + } + } + } else { + parsed.serialize_with + }, + ); output.body.extend(delta); if needs_bounds_derive { generics.serialize_visitor.visit_field(field); @@ -199,14 +255,15 @@ fn process_field( #[cfg(test)] mod tests { + use syn::parse_quote; + + use super::*; use crate::internals::test_helpers::{ default_cratename, local_insta_assert_snapshot, pretty_print_syn_str, }; - - use super::*; #[test] fn borsh_skip_tuple_variant_field() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum AATTB { B(#[borsh(skip)] i32, #[borsh(skip)] u32), @@ -214,16 +271,21 @@ mod tests { beta: u8, } } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn struct_variant_field() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum AB { B { c: i32, @@ -234,17 +296,21 @@ mod tests { beta: String, } } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_enum_with_custom_crate() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum AB { B { c: i32, @@ -255,18 +321,23 @@ mod tests { beta: String, } } - }) - .unwrap(); + }; + + let crate_: Path = parse_quote! { reexporter::borsh }; - let crate_: Path = syn::parse2(quote! { reexporter::borsh }).unwrap(); - let actual = process(&item_enum, crate_).unwrap(); + let actual = process::(item_enum.clone(), crate_.clone()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, crate_).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn borsh_skip_struct_variant_field() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum AB { B { @@ -280,17 +351,21 @@ mod tests { beta: String, } } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn borsh_skip_struct_variant_all_fields() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum AAB { B { @@ -305,17 +380,21 @@ mod tests { beta: String, } } - }) - .unwrap(); + }; - let actual = process(&item_enum, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_generics() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { B { x: HashMap, @@ -323,16 +402,21 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn bound_generics() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { x: HashMap, @@ -340,16 +424,21 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn recursive_enum() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { x: HashMap, @@ -357,17 +446,21 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_borsh_skip_struct_field() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { #[borsh(skip)] @@ -376,17 +469,21 @@ mod tests { }, C(K, Vec), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_borsh_skip_tuple_field() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A where V: Value { B { x: HashMap, @@ -394,17 +491,21 @@ mod tests { }, C(K, #[borsh(skip)] Vec), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_serialize_bound() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum A { C { a: String, @@ -416,17 +517,45 @@ mod tests { }, D(u32, u32), } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn generic_serialize_async_bound() { + let item_enum: ItemEnum = parse_quote! { + enum A { + C { + a: String, + #[borsh(async_bound(serialize = + "T: borsh::ser::BorshSerializeAsync + PartialOrd, + U: borsh::ser::BorshSerializeAsync" + ))] + b: HashMap, + }, + D(u32, u32), + } + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn check_serialize_with_attr() { - let item_struct: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum C { C3(u64, u64), C4 { @@ -435,17 +564,42 @@ mod tests { y: ThirdParty }, } - }) - .unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn check_serialize_with_async_attr() { + let item_enum: ItemEnum = parse_quote! { + enum C { + C3(u64, u64), + C4 { + x: u64, + #[borsh(serialize_with_async = "third_party_impl::serialize_third_party")] + y: ThirdParty + }, + } + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn borsh_discriminant_false() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[borsh(use_discriminant = false)] enum X { A, @@ -455,15 +609,20 @@ mod tests { E = 10, F, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn borsh_discriminant_true() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { #[borsh(use_discriminant = true)] enum X { A, @@ -473,26 +632,36 @@ mod tests { E = 10, F, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; + + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn mixed_with_unit_variants() { - let item_enum: ItemEnum = syn::parse2(quote! { + let item_enum: ItemEnum = parse_quote! { enum X { A(u16), B, C {x: i32, y: i32}, D, } - }) - .unwrap(); - let actual = process(&item_enum, default_cratename()).unwrap(); + }; - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_enum.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_enum, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_false-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_false-2.snap new file mode 100644 index 000000000..0722ddbcd --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_false-2.snap @@ -0,0 +1,21 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for X { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + X::A => 0u8, + X::B => 1u8, + X::C => 2u8, + X::D => 3u8, + X::E => 4u8, + X::F => 5u8, + }; + writer.write_u8(variant_idx).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_false.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_false.snap index 05e1f88e9..f7d142a0a 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_false.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_false.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for X { fn serialize<__W: borsh::io::Write>( @@ -16,6 +16,6 @@ impl borsh::ser::BorshSerialize for X { X::F => 5u8, }; writer.write_all(&variant_idx.to_le_bytes())?; - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_true-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_true-2.snap new file mode 100644 index 000000000..f2067c71f --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_true-2.snap @@ -0,0 +1,21 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for X { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + X::A => 0, + X::B => 20, + X::C => 20 + 1, + X::D => 20 + 1 + 1, + X::E => 10, + X::F => 10 + 1, + }; + writer.write_u8(variant_idx).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_true.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_true.snap index ee3371232..51e3dedaa 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_true.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_discriminant_true.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for X { fn serialize<__W: borsh::io::Write>( @@ -16,6 +16,6 @@ impl borsh::ser::BorshSerialize for X { X::F => 10 + 1, }; writer.write_all(&variant_idx.to_le_bytes())?; - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_all_fields-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_all_fields-2.snap new file mode 100644 index 000000000..9160687b9 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_all_fields-2.snap @@ -0,0 +1,23 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for AAB { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + AAB::B { .. } => 0u8, + AAB::NegatedVariant { .. } => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + AAB::B { .. } => {} + AAB::NegatedVariant { beta, .. } => { + ::serialize(beta, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_all_fields.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_all_fields.snap index b8943a467..359f454b5 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_all_fields.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_all_fields.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for AAB { fn serialize<__W: borsh::io::Write>( @@ -15,9 +15,9 @@ impl borsh::ser::BorshSerialize for AAB { match self { AAB::B { .. } => {} AAB::NegatedVariant { beta, .. } => { - borsh::BorshSerialize::serialize(beta, writer)?; + ::serialize(beta, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_field-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_field-2.snap new file mode 100644 index 000000000..1a0b50e04 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_field-2.snap @@ -0,0 +1,25 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for AB { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + AB::B { .. } => 0u8, + AB::NegatedVariant { .. } => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + AB::B { d, .. } => { + ::serialize(d, writer).await?; + } + AB::NegatedVariant { beta, .. } => { + ::serialize(beta, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_field.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_field.snap index f5ff6946d..a8b419de0 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_field.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_struct_variant_field.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for AB { fn serialize<__W: borsh::io::Write>( @@ -14,12 +14,12 @@ impl borsh::ser::BorshSerialize for AB { writer.write_all(&variant_idx.to_le_bytes())?; match self { AB::B { d, .. } => { - borsh::BorshSerialize::serialize(d, writer)?; + ::serialize(d, writer)?; } AB::NegatedVariant { beta, .. } => { - borsh::BorshSerialize::serialize(beta, writer)?; + ::serialize(beta, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_tuple_variant_field-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_tuple_variant_field-2.snap new file mode 100644 index 000000000..cef5665fe --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_tuple_variant_field-2.snap @@ -0,0 +1,23 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for AATTB { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + AATTB::B(..) => 0u8, + AATTB::NegatedVariant { .. } => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + AATTB::B(_id0, _id1) => {} + AATTB::NegatedVariant { beta, .. } => { + ::serialize(beta, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_tuple_variant_field.snap b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_tuple_variant_field.snap index fc3be0087..82b5f7b48 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_tuple_variant_field.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/borsh_skip_tuple_variant_field.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for AATTB { fn serialize<__W: borsh::io::Write>( @@ -15,9 +15,9 @@ impl borsh::ser::BorshSerialize for AATTB { match self { AATTB::B(_id0, _id1) => {} AATTB::NegatedVariant { beta, .. } => { - borsh::BorshSerialize::serialize(beta, writer)?; + ::serialize(beta, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/bound_generics-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/bound_generics-2.snap new file mode 100644 index 000000000..86c05d8ca --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/bound_generics-2.snap @@ -0,0 +1,34 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + V: Value, + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::B { .. } => 0u8, + A::C(..) => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + A::B { x, y, .. } => { + as borsh::BorshSerializeAsync>::serialize(x, writer) + .await?; + ::serialize(y, writer).await?; + } + A::C(id0, id1) => { + ::serialize(id0, writer).await?; + as borsh::BorshSerializeAsync>::serialize(id1, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/bound_generics.snap b/borsh-derive/src/internals/serialize/enums/snapshots/bound_generics.snap index 9c1630967..10e186ddc 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/bound_generics.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/bound_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -20,14 +20,14 @@ where writer.write_all(&variant_idx.to_le_bytes())?; match self { A::B { x, y, .. } => { - borsh::BorshSerialize::serialize(x, writer)?; - borsh::BorshSerialize::serialize(y, writer)?; + as borsh::BorshSerialize>::serialize(x, writer)?; + ::serialize(y, writer)?; } A::C(id0, id1) => { - borsh::BorshSerialize::serialize(id0, writer)?; - borsh::BorshSerialize::serialize(id1, writer)?; + ::serialize(id0, writer)?; + as borsh::BorshSerialize>::serialize(id1, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_async_attr-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_async_attr-2.snap new file mode 100644 index 000000000..e7cb8b20d --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_async_attr-2.snap @@ -0,0 +1,31 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for C +where + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + C::C3(..) => 0u8, + C::C4 { .. } => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + C::C3(id0, id1) => { + ::serialize(id0, writer).await?; + ::serialize(id1, writer).await?; + } + C::C4 { x, y, .. } => { + ::serialize(x, writer).await?; + third_party_impl::serialize_third_party(y, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_async_attr.snap b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_async_attr.snap new file mode 100644 index 000000000..9206b7b6d --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_async_attr.snap @@ -0,0 +1,31 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerialize for C +where + K: borsh::ser::BorshSerialize, + V: borsh::ser::BorshSerialize, +{ + fn serialize<__W: borsh::io::Write>( + &self, + writer: &mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + C::C3(..) => 0u8, + C::C4 { .. } => 1u8, + }; + writer.write_all(&variant_idx.to_le_bytes())?; + match self { + C::C3(id0, id1) => { + ::serialize(id0, writer)?; + ::serialize(id1, writer)?; + } + C::C4 { x, y, .. } => { + ::serialize(x, writer)?; + as borsh::BorshSerialize>::serialize(y, writer)?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_attr-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_attr-2.snap new file mode 100644 index 000000000..9bd53ef66 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_attr-2.snap @@ -0,0 +1,32 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for C +where + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + C::C3(..) => 0u8, + C::C4 { .. } => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + C::C3(id0, id1) => { + ::serialize(id0, writer).await?; + ::serialize(id1, writer).await?; + } + C::C4 { x, y, .. } => { + ::serialize(x, writer).await?; + as borsh::BorshSerializeAsync>::serialize(y, writer) + .await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_attr.snap b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_attr.snap index 29f1619c1..84fc5e517 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_attr.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/check_serialize_with_attr.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for C where @@ -18,14 +18,14 @@ where writer.write_all(&variant_idx.to_le_bytes())?; match self { C::C3(id0, id1) => { - borsh::BorshSerialize::serialize(id0, writer)?; - borsh::BorshSerialize::serialize(id1, writer)?; + ::serialize(id0, writer)?; + ::serialize(id1, writer)?; } C::C4 { x, y, .. } => { - borsh::BorshSerialize::serialize(x, writer)?; + ::serialize(x, writer)?; third_party_impl::serialize_third_party(y, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_struct_field-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_struct_field-2.snap new file mode 100644 index 000000000..8d186deb4 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_struct_field-2.snap @@ -0,0 +1,31 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + V: Value, + K: borsh::ser::BorshSerializeAsync, + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::B { .. } => 0u8, + A::C(..) => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + A::B { y, .. } => { + ::serialize(y, writer).await?; + } + A::C(id0, id1) => { + ::serialize(id0, writer).await?; + as borsh::BorshSerializeAsync>::serialize(id1, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_struct_field.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_struct_field.snap index 051033a64..0fc2c601a 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_struct_field.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_struct_field.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -19,13 +19,13 @@ where writer.write_all(&variant_idx.to_le_bytes())?; match self { A::B { y, .. } => { - borsh::BorshSerialize::serialize(y, writer)?; + ::serialize(y, writer)?; } A::C(id0, id1) => { - borsh::BorshSerialize::serialize(id0, writer)?; - borsh::BorshSerialize::serialize(id1, writer)?; + ::serialize(id0, writer)?; + as borsh::BorshSerialize>::serialize(id1, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_tuple_field-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_tuple_field-2.snap new file mode 100644 index 000000000..b260874c3 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_tuple_field-2.snap @@ -0,0 +1,32 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + V: Value, + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::B { .. } => 0u8, + A::C(..) => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + A::B { x, y, .. } => { + as borsh::BorshSerializeAsync>::serialize(x, writer) + .await?; + ::serialize(y, writer).await?; + } + A::C(id0, _id1) => { + ::serialize(id0, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_tuple_field.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_tuple_field.snap index 4f0ae41b7..ca61b5fb9 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_tuple_field.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_borsh_skip_tuple_field.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -19,13 +19,13 @@ where writer.write_all(&variant_idx.to_le_bytes())?; match self { A::B { x, y, .. } => { - borsh::BorshSerialize::serialize(x, writer)?; - borsh::BorshSerialize::serialize(y, writer)?; + as borsh::BorshSerialize>::serialize(x, writer)?; + ::serialize(y, writer)?; } A::C(id0, _id1) => { - borsh::BorshSerialize::serialize(id0, writer)?; + ::serialize(id0, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_async_bound-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_async_bound-2.snap new file mode 100644 index 000000000..9d4f4b821 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_async_bound-2.snap @@ -0,0 +1,32 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + T: borsh::ser::BorshSerializeAsync + PartialOrd, + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::C { .. } => 0u8, + A::D(..) => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + A::C { a, b, .. } => { + ::serialize(a, writer).await?; + as borsh::BorshSerializeAsync>::serialize(b, writer) + .await?; + } + A::D(id0, id1) => { + ::serialize(id0, writer).await?; + ::serialize(id1, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_async_bound.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_async_bound.snap new file mode 100644 index 000000000..23806f9c5 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_async_bound.snap @@ -0,0 +1,31 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerialize for A +where + T: borsh::ser::BorshSerialize, + U: borsh::ser::BorshSerialize, +{ + fn serialize<__W: borsh::io::Write>( + &self, + writer: &mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::C { .. } => 0u8, + A::D(..) => 1u8, + }; + writer.write_all(&variant_idx.to_le_bytes())?; + match self { + A::C { a, b, .. } => { + ::serialize(a, writer)?; + as borsh::BorshSerialize>::serialize(b, writer)?; + } + A::D(id0, id1) => { + ::serialize(id0, writer)?; + ::serialize(id1, writer)?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_bound-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_bound-2.snap new file mode 100644 index 000000000..e3374b186 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_bound-2.snap @@ -0,0 +1,32 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + T: borsh::ser::BorshSerializeAsync, + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::C { .. } => 0u8, + A::D(..) => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + A::C { a, b, .. } => { + ::serialize(a, writer).await?; + as borsh::BorshSerializeAsync>::serialize(b, writer) + .await?; + } + A::D(id0, id1) => { + ::serialize(id0, writer).await?; + ::serialize(id1, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_bound.snap b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_bound.snap index 1e53690ec..91fd4f8fe 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_bound.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/generic_serialize_bound.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -18,14 +18,14 @@ where writer.write_all(&variant_idx.to_le_bytes())?; match self { A::C { a, b, .. } => { - borsh::BorshSerialize::serialize(a, writer)?; - borsh::BorshSerialize::serialize(b, writer)?; + ::serialize(a, writer)?; + as borsh::BorshSerialize>::serialize(b, writer)?; } A::D(id0, id1) => { - borsh::BorshSerialize::serialize(id0, writer)?; - borsh::BorshSerialize::serialize(id1, writer)?; + ::serialize(id0, writer)?; + ::serialize(id1, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/mixed_with_unit_variants-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/mixed_with_unit_variants-2.snap new file mode 100644 index 000000000..c1155e80c --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/mixed_with_unit_variants-2.snap @@ -0,0 +1,29 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for X { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + X::A(..) => 0u8, + X::B => 1u8, + X::C { .. } => 2u8, + X::D => 3u8, + }; + writer.write_u8(variant_idx).await?; + match self { + X::A(id0) => { + ::serialize(id0, writer).await?; + } + X::C { x, y, .. } => { + ::serialize(x, writer).await?; + ::serialize(y, writer).await?; + } + _ => {} + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/mixed_with_unit_variants.snap b/borsh-derive/src/internals/serialize/enums/snapshots/mixed_with_unit_variants.snap index 69a56d5b9..687e4d815 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/mixed_with_unit_variants.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/mixed_with_unit_variants.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for X { fn serialize<__W: borsh::io::Write>( @@ -16,14 +16,14 @@ impl borsh::ser::BorshSerialize for X { writer.write_all(&variant_idx.to_le_bytes())?; match self { X::A(id0) => { - borsh::BorshSerialize::serialize(id0, writer)?; + ::serialize(id0, writer)?; } X::C { x, y, .. } => { - borsh::BorshSerialize::serialize(x, writer)?; - borsh::BorshSerialize::serialize(y, writer)?; + ::serialize(x, writer)?; + ::serialize(y, writer)?; } _ => {} } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/recursive_enum-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/recursive_enum-2.snap new file mode 100644 index 000000000..124234ea6 --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/recursive_enum-2.snap @@ -0,0 +1,33 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + V: Value, + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::B { .. } => 0u8, + A::C(..) => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + A::B { x, y, .. } => { + as borsh::BorshSerializeAsync>::serialize(x, writer) + .await?; + ::serialize(y, writer).await?; + } + A::C(id0, id1) => { + ::serialize(id0, writer).await?; + as borsh::BorshSerializeAsync>::serialize(id1, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/recursive_enum.snap b/borsh-derive/src/internals/serialize/enums/snapshots/recursive_enum.snap index d7aeab233..6724928e5 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/recursive_enum.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/recursive_enum.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -19,14 +19,14 @@ where writer.write_all(&variant_idx.to_le_bytes())?; match self { A::B { x, y, .. } => { - borsh::BorshSerialize::serialize(x, writer)?; - borsh::BorshSerialize::serialize(y, writer)?; + as borsh::BorshSerialize>::serialize(x, writer)?; + ::serialize(y, writer)?; } A::C(id0, id1) => { - borsh::BorshSerialize::serialize(id0, writer)?; - borsh::BorshSerialize::serialize(id1, writer)?; + ::serialize(id0, writer)?; + as borsh::BorshSerialize>::serialize(id1, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/simple_enum_with_custom_crate-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/simple_enum_with_custom_crate-2.snap new file mode 100644 index 000000000..4789105cf --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/simple_enum_with_custom_crate-2.snap @@ -0,0 +1,32 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl reexporter::borsh::ser::BorshSerializeAsync for AB { + async fn serialize<'async_variant, __W: reexporter::borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), reexporter::borsh::io::Error> { + let variant_idx: u8 = match self { + AB::B { .. } => 0u8, + AB::NegatedVariant { .. } => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + AB::B { c, d, .. } => { + ::serialize(c, writer) + .await?; + ::serialize(d, writer) + .await?; + } + AB::NegatedVariant { beta, .. } => { + ::serialize( + beta, + writer, + ) + .await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/simple_enum_with_custom_crate.snap b/borsh-derive/src/internals/serialize/enums/snapshots/simple_enum_with_custom_crate.snap index fcfc55df6..d16e4ea5e 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/simple_enum_with_custom_crate.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/simple_enum_with_custom_crate.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl reexporter::borsh::ser::BorshSerialize for AB { fn serialize<__W: reexporter::borsh::io::Write>( @@ -14,13 +14,13 @@ impl reexporter::borsh::ser::BorshSerialize for AB { writer.write_all(&variant_idx.to_le_bytes())?; match self { AB::B { c, d, .. } => { - reexporter::borsh::BorshSerialize::serialize(c, writer)?; - reexporter::borsh::BorshSerialize::serialize(d, writer)?; + ::serialize(c, writer)?; + ::serialize(d, writer)?; } AB::NegatedVariant { beta, .. } => { - reexporter::borsh::BorshSerialize::serialize(beta, writer)?; + ::serialize(beta, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/simple_generics-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/simple_generics-2.snap new file mode 100644 index 000000000..3f495c06c --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/simple_generics-2.snap @@ -0,0 +1,33 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + A::B { .. } => 0u8, + A::C(..) => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + A::B { x, y, .. } => { + as borsh::BorshSerializeAsync>::serialize(x, writer) + .await?; + ::serialize(y, writer).await?; + } + A::C(id0, id1) => { + ::serialize(id0, writer).await?; + as borsh::BorshSerializeAsync>::serialize(id1, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/simple_generics.snap b/borsh-derive/src/internals/serialize/enums/snapshots/simple_generics.snap index 70e2f6498..8d056af5e 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/simple_generics.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/simple_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -19,14 +19,14 @@ where writer.write_all(&variant_idx.to_le_bytes())?; match self { A::B { x, y, .. } => { - borsh::BorshSerialize::serialize(x, writer)?; - borsh::BorshSerialize::serialize(y, writer)?; + as borsh::BorshSerialize>::serialize(x, writer)?; + ::serialize(y, writer)?; } A::C(id0, id1) => { - borsh::BorshSerialize::serialize(id0, writer)?; - borsh::BorshSerialize::serialize(id1, writer)?; + ::serialize(id0, writer)?; + as borsh::BorshSerialize>::serialize(id1, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/struct_variant_field-2.snap b/borsh-derive/src/internals/serialize/enums/snapshots/struct_variant_field-2.snap new file mode 100644 index 000000000..4c8428aaf --- /dev/null +++ b/borsh-derive/src/internals/serialize/enums/snapshots/struct_variant_field-2.snap @@ -0,0 +1,26 @@ +--- +source: borsh-derive/src/internals/serialize/enums/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for AB { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + let variant_idx: u8 = match self { + AB::B { .. } => 0u8, + AB::NegatedVariant { .. } => 1u8, + }; + writer.write_u8(variant_idx).await?; + match self { + AB::B { c, d, .. } => { + ::serialize(c, writer).await?; + ::serialize(d, writer).await?; + } + AB::NegatedVariant { beta, .. } => { + ::serialize(beta, writer).await?; + } + } + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/enums/snapshots/struct_variant_field.snap b/borsh-derive/src/internals/serialize/enums/snapshots/struct_variant_field.snap index 0ba345286..893d4794b 100644 --- a/borsh-derive/src/internals/serialize/enums/snapshots/struct_variant_field.snap +++ b/borsh-derive/src/internals/serialize/enums/snapshots/struct_variant_field.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/enums/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for AB { fn serialize<__W: borsh::io::Write>( @@ -14,13 +14,13 @@ impl borsh::ser::BorshSerialize for AB { writer.write_all(&variant_idx.to_le_bytes())?; match self { AB::B { c, d, .. } => { - borsh::BorshSerialize::serialize(c, writer)?; - borsh::BorshSerialize::serialize(d, writer)?; + ::serialize(c, writer)?; + ::serialize(d, writer)?; } AB::NegatedVariant { beta, .. } => { - borsh::BorshSerialize::serialize(beta, writer)?; + ::serialize(beta, writer)?; } } - Ok(()) + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/mod.rs b/borsh-derive/src/internals/serialize/mod.rs index b4b7a20dc..1ae0d4172 100644 --- a/borsh-derive/src/internals/serialize/mod.rs +++ b/borsh-derive/src/internals/serialize/mod.rs @@ -1,7 +1,8 @@ +use std::convert::TryFrom; + use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use std::convert::TryFrom; -use syn::{Expr, ExprPath, Generics, Ident, Index, Path}; +use syn::{parse_quote, Expr, ExprPath, Generics, Ident, Index, Path, Type}; use super::generics; @@ -21,8 +22,12 @@ impl GenericsOutput { serialize_visitor: generics::FindTyParams::new(generics), } } - fn extend(self, where_clause: &mut syn::WhereClause, cratename: &Path) { - let trait_path: Path = syn::parse2(quote! { #cratename::ser::BorshSerialize }).unwrap(); + fn extend(self, where_clause: &mut syn::WhereClause, cratename: &Path) { + let trait_path: Path = if IS_ASYNC { + parse_quote! { #cratename::ser::BorshSerializeAsync } + } else { + parse_quote! { #cratename::ser::BorshSerialize } + }; let predicates = generics::compute_predicates(self.serialize_visitor.process_for_bounds(), &trait_path); where_clause.predicates.extend(predicates); @@ -65,39 +70,54 @@ impl FieldId { impl FieldId { fn serialize_arg(&self) -> Expr { match self { - Self::Struct(name) => syn::parse2(quote! { &self.#name }).unwrap(), - Self::StructUnnamed(index) => syn::parse2(quote! { &self.#index }).unwrap(), - Self::Enum(name) => syn::parse2(quote! { #name }).unwrap(), + Self::Struct(name) => parse_quote! { &self.#name }, + Self::StructUnnamed(index) => parse_quote! { &self.#index }, + Self::Enum(name) => parse_quote! { #name }, Self::EnumUnnamed(ind) => { let field = Ident::new(&format!("id{}", ind.index), Span::mixed_site()); - syn::parse2(quote! { #field }).unwrap() + parse_quote! { #field } } } } + /// function which computes derive output [proc_macro2::TokenStream] /// of code, which serializes single field - pub fn serialize_output( + pub fn serialize_output( &self, + field_type: &Type, cratename: &Path, serialize_with: Option, ) -> TokenStream2 { let arg: Expr = self.serialize_arg(); + let dot_await = IS_ASYNC.then(|| quote! { .await }); if let Some(func) = serialize_with { - quote! { #func(#arg, writer)?; } + quote! { #func(#arg, writer)#dot_await?; } } else { - quote! { #cratename::BorshSerialize::serialize(#arg, writer)?; } + let serialize_trait = Ident::new( + if IS_ASYNC { + "BorshSerializeAsync" + } else { + "BorshSerialize" + }, + Span::call_site(), + ); + quote! { <#field_type as #cratename::#serialize_trait>::serialize(#arg, writer)#dot_await?; } } } + pub fn enum_variant_header(&self, skipped: bool) -> Option { match self { Self::Struct(..) | Self::StructUnnamed(..) => unreachable!("no variant header"), Self::Enum(name) => (!skipped).then_some(quote! { #name, }), Self::EnumUnnamed(index) => { - let field_ident = if skipped { - Ident::new(&format!("_id{}", index.index), Span::mixed_site()) - } else { - Ident::new(&format!("id{}", index.index), Span::mixed_site()) - }; + let field_ident = Ident::new( + &if skipped { + format!("_id{}", index.index) + } else { + format!("id{}", index.index) + }, + Span::mixed_site(), + ); Some(quote! { #field_ident, }) } } diff --git a/borsh-derive/src/internals/serialize/structs/mod.rs b/borsh-derive/src/internals/serialize/structs/mod.rs index 2864c78e9..add3a767f 100644 --- a/borsh-derive/src/internals/serialize/structs/mod.rs +++ b/borsh-derive/src/internals/serialize/structs/mod.rs @@ -1,15 +1,19 @@ -use proc_macro2::TokenStream as TokenStream2; +use cfg_if::cfg_if; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Fields, ItemStruct, Path}; +use syn::{Fields, ItemStruct, Lifetime, Path, Token}; use crate::internals::{ attributes::{field, BoundType}, generics, serialize, }; -pub fn process(input: &ItemStruct, cratename: Path) -> syn::Result { +pub fn process( + input: ItemStruct, + cratename: Path, +) -> syn::Result { let name = &input.ident; - let generics = generics::without_defaults(&input.generics); + let generics = generics::without_defaults(input.generics); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let mut where_clause = generics::default_where(where_clause); let mut body = TokenStream2::new(); @@ -19,31 +23,63 @@ pub fn process(input: &ItemStruct, cratename: Path) -> syn::Result for field in &fields.named { let field_id = serialize::FieldId::Struct(field.ident.clone().unwrap()); - process_field(field, field_id, &cratename, &mut generics_output, &mut body)?; + process_field::( + field, + field_id, + &cratename, + &mut generics_output, + &mut body, + )?; } } Fields::Unnamed(fields) => { for (field_idx, field) in fields.unnamed.iter().enumerate() { let field_id = serialize::FieldId::new_struct_unnamed(field_idx)?; - process_field(field, field_id, &cratename, &mut generics_output, &mut body)?; + process_field::( + field, + field_id, + &cratename, + &mut generics_output, + &mut body, + )?; } } Fields::Unit => {} } - generics_output.extend(&mut where_clause, &cratename); + generics_output.extend::(&mut where_clause, &cratename); + + let serialize_trait = Ident::new( + if IS_ASYNC { + "BorshSerializeAsync" + } else { + "BorshSerialize" + }, + Span::call_site(), + ); + let writer_trait_path = if IS_ASYNC { + quote! { async_io::AsyncWrite } + } else { + quote! { io::Write } + }; + let r#async = IS_ASYNC.then(|| Token![async](Span::call_site())); + let lifetime = IS_ASYNC.then(|| Lifetime::new("'async_variant", Span::call_site())); + let lt_comma = IS_ASYNC.then(|| Token![,](Span::call_site())); Ok(quote! { - impl #impl_generics #cratename::ser::BorshSerialize for #name #ty_generics #where_clause { - fn serialize<__W: #cratename::io::Write>(&self, writer: &mut __W) -> ::core::result::Result<(), #cratename::io::Error> { + impl #impl_generics #cratename::ser::#serialize_trait for #name #ty_generics #where_clause { + #r#async fn serialize<#lifetime #lt_comma __W: #cratename::#writer_trait_path>( + &#lifetime self, + writer: &#lifetime mut __W, + ) -> ::core::result::Result<(), #cratename::io::Error> { #body - Ok(()) + ::core::result::Result::Ok(()) } } }) } -fn process_field( +fn process_field( field: &syn::Field, field_id: serialize::FieldId, cratename: &Path, @@ -51,13 +87,28 @@ fn process_field( body: &mut TokenStream2, ) -> syn::Result<()> { let parsed = field::Attributes::parse(&field.attrs)?; - let needs_bounds_derive = parsed.needs_bounds_derive(BoundType::Serialize); + let needs_bounds_derive = parsed.needs_bounds_derive::(BoundType::Serialize); generics .overrides - .extend(parsed.collect_bounds(BoundType::Serialize)); + .extend(parsed.collect_bounds::(BoundType::Serialize)); + if !parsed.skip { - let delta = field_id.serialize_output(cratename, parsed.serialize_with); + let delta = field_id.serialize_output::( + &field.ty, + cratename, + if IS_ASYNC { + cfg_if! { + if #[cfg(feature = "async")] { + parsed.serialize_with_async + } else { + None + } + } + } else { + parsed.serialize_with + }, + ); body.extend(delta); if needs_bounds_derive { @@ -69,149 +120,190 @@ fn process_field( #[cfg(test)] mod tests { + use syn::parse_quote; + + use super::*; use crate::internals::test_helpers::{ default_cratename, local_insta_assert_debug_snapshot, local_insta_assert_snapshot, pretty_print_syn_str, }; - use super::*; - #[test] fn simple_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: u64, y: String, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_struct_with_custom_crate() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: u64, y: String, } - }) - .unwrap(); + }; + + let crate_: Path = parse_quote! { reexporter::borsh }; - let crate_: Path = syn::parse2(quote! { reexporter::borsh }).unwrap(); - let actual = process(&item_struct, crate_).unwrap(); + let actual = process::(item_struct.clone(), crate_.clone()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, crate_).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_generics() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { x: HashMap, y: String, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn simple_generic_tuple_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct TupleA(T, u32); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn bound_generics() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A where V: Value { x: HashMap, y: String, } - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - let actual = process(&item_struct, default_cratename()).unwrap(); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn recursive_struct() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct CRecC { a: String, b: HashMap, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_tuple_struct_borsh_skip1() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G ( #[borsh(skip)] HashMap, U, ); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_tuple_struct_borsh_skip2() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G ( HashMap, #[borsh(skip)] U, ); - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_named_fields_struct_borsh_skip() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct G { #[borsh(skip)] x: HashMap, y: U, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_associated_type() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName, @@ -219,17 +311,21 @@ mod tests { field: T::Associated, another: V, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } } #[test] fn generic_serialize_bound() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct C { a: String, #[borsh(bound(serialize = @@ -238,17 +334,42 @@ mod tests { ))] b: HashMap, } - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn generic_serialize_async_bound() { + let item_struct: ItemStruct = parse_quote! { + struct C { + a: String, + #[borsh(async_bound(serialize = + "T: borsh::ser::BorshSerializeAsync + PartialOrd, + U: borsh::ser::BorshSerializeAsync" + ))] + b: HashMap, + } + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn override_generic_associated_type_wrong_derive() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct Parametrized where T: TraitName { #[borsh(bound(serialize = "::Associated: borsh::ser::BorshSerialize" @@ -256,42 +377,144 @@ mod tests { field: ::Associated, another: V, } - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn async_override_generic_associated_type_wrong_derive() { + let item_struct: ItemStruct = parse_quote! { + struct Parametrized where T: TraitName { + #[borsh(async_bound(serialize = + "::Associated: borsh::ser::BorshSerializeAsync" + ))] + field: ::Associated, + another: V, + } + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] fn check_serialize_with_attr() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { struct A { #[borsh(serialize_with = "third_party_impl::serialize_third_party")] x: ThirdParty, y: u64, } - }) - .unwrap(); + }; - let actual = process(&item_struct, default_cratename()).unwrap(); + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); - local_insta_assert_snapshot!(pretty_print_syn_str(&actual).unwrap()); + #[cfg(feature = "async")] + { + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + } + } + + #[test] + #[cfg(feature = "async")] + fn check_serialize_with_async_attr() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(serialize_with_async = "third_party_impl::serialize_third_party")] + x: ThirdParty, + y: u64, + } + }; + + let actual = process::(item_struct.clone(), default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); + + let actual = process::(item_struct, default_cratename()).unwrap(); + local_insta_assert_snapshot!(pretty_print_syn_str(actual).unwrap()); } #[test] + #[cfg(not(feature = "async"))] fn check_serialize_with_skip_conflict() { - let item_struct: ItemStruct = syn::parse2(quote! { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(skip, serialize_with = "third_party_impl::serialize_third_party")] + x: ThirdParty, + y: u64, + } + }; + + let actual = process::(item_struct.clone(), default_cratename()); + + let err = match actual { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + } + + #[test] + #[cfg(feature = "async")] + fn check_serialize_with_skip_conflict_feature_async() { + let item_struct: ItemStruct = parse_quote! { + struct A { + #[borsh(skip, serialize_with = "third_party_impl::serialize_third_party")] + x: ThirdParty, + y: u64, + } + }; + + let actual = process::(item_struct.clone(), default_cratename()); + + let err = match actual { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + + let actual = process::(item_struct, default_cratename()); + + let err = match actual { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); + } + + #[test] + #[cfg(feature = "async")] + fn check_serialize_with_async_skip_conflict() { + let item_struct: ItemStruct = parse_quote! { struct A { - #[borsh(skip,serialize_with = "third_party_impl::serialize_third_party")] + #[borsh(skip, serialize_with_async = "third_party_impl::serialize_third_party")] x: ThirdParty, y: u64, } - }) - .unwrap(); + }; + + let actual = process::(item_struct.clone(), default_cratename()); + + let err = match actual { + Ok(..) => unreachable!("expecting error here"), + Err(err) => err, + }; + local_insta_assert_debug_snapshot!(err); - let actual = process(&item_struct, default_cratename()); + let actual = process::(item_struct, default_cratename()); let err = match actual { Ok(..) => unreachable!("expecting error here"), diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/async_override_generic_associated_type_wrong_derive-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/async_override_generic_associated_type_wrong_derive-2.snap new file mode 100644 index 000000000..9958fedc1 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/async_override_generic_associated_type_wrong_derive-2.snap @@ -0,0 +1,23 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for Parametrized +where + T: TraitName, + V: borsh::ser::BorshSerializeAsync, + ::Associated: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + <::Associated as borsh::BorshSerializeAsync>::serialize( + &self.field, + writer, + ) + .await?; + ::serialize(&self.another, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/async_override_generic_associated_type_wrong_derive.snap b/borsh-derive/src/internals/serialize/structs/snapshots/async_override_generic_associated_type_wrong_derive.snap new file mode 100644 index 000000000..c6ad3da20 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/async_override_generic_associated_type_wrong_derive.snap @@ -0,0 +1,22 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerialize for Parametrized +where + T: TraitName, + T: borsh::ser::BorshSerialize, + V: borsh::ser::BorshSerialize, +{ + fn serialize<__W: borsh::io::Write>( + &self, + writer: &mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + <::Associated as borsh::BorshSerialize>::serialize( + &self.field, + writer, + )?; + ::serialize(&self.another, writer)?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/bound_generics-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/bound_generics-2.snap new file mode 100644 index 000000000..1b4293232 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/bound_generics-2.snap @@ -0,0 +1,19 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + V: Value, + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + as borsh::BorshSerializeAsync>::serialize(&self.x, writer).await?; + ::serialize(&self.y, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/bound_generics.snap b/borsh-derive/src/internals/serialize/structs/snapshots/bound_generics.snap index 5c7c6926b..15a8d413d 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/bound_generics.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/bound_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -12,8 +12,8 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.x, writer)?; - borsh::BorshSerialize::serialize(&self.y, writer)?; - Ok(()) + as borsh::BorshSerialize>::serialize(&self.x, writer)?; + ::serialize(&self.y, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_attr-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_attr-2.snap new file mode 100644 index 000000000..bbf2faf4c --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_attr-2.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + third_party_impl::serialize_third_party(&self.x, writer).await?; + ::serialize(&self.y, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_attr.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_attr.snap new file mode 100644 index 000000000..7816a3a1a --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_attr.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerialize for A +where + K: borsh::ser::BorshSerialize, + V: borsh::ser::BorshSerialize, +{ + fn serialize<__W: borsh::io::Write>( + &self, + writer: &mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + as borsh::BorshSerialize>::serialize(&self.x, writer)?; + ::serialize(&self.y, writer)?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_skip_conflict-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_skip_conflict-2.snap new file mode 100644 index 000000000..3b40ba358 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_skip_conflict-2.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: err +--- +Error( + "`skip` cannot be used at the same time as `serialize_with`, `deserialize_with`, `serialize_with_async` or `deserialize_with_async`", +) diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_skip_conflict.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_skip_conflict.snap new file mode 100644 index 000000000..3b40ba358 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_async_skip_conflict.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: err +--- +Error( + "`skip` cannot be used at the same time as `serialize_with`, `deserialize_with`, `serialize_with_async` or `deserialize_with_async`", +) diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_attr-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_attr-2.snap new file mode 100644 index 000000000..5e5d2dac9 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_attr-2.snap @@ -0,0 +1,19 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + as borsh::BorshSerializeAsync>::serialize(&self.x, writer) + .await?; + ::serialize(&self.y, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_attr.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_attr.snap index 6d977d521..2370ac97c 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_attr.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_attr.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -12,7 +12,7 @@ where writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { third_party_impl::serialize_third_party(&self.x, writer)?; - borsh::BorshSerialize::serialize(&self.y, writer)?; - Ok(()) + ::serialize(&self.y, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict-2.snap new file mode 100644 index 000000000..3b40ba358 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict-2.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: err +--- +Error( + "`skip` cannot be used at the same time as `serialize_with`, `deserialize_with`, `serialize_with_async` or `deserialize_with_async`", +) diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict_feature_async-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict_feature_async-2.snap new file mode 100644 index 000000000..3b40ba358 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict_feature_async-2.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: err +--- +Error( + "`skip` cannot be used at the same time as `serialize_with`, `deserialize_with`, `serialize_with_async` or `deserialize_with_async`", +) diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict_feature_async.snap b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict_feature_async.snap new file mode 100644 index 000000000..3b40ba358 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/check_serialize_with_skip_conflict_feature_async.snap @@ -0,0 +1,7 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: err +--- +Error( + "`skip` cannot be used at the same time as `serialize_with`, `deserialize_with`, `serialize_with_async` or `deserialize_with_async`", +) diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_associated_type-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_associated_type-2.snap new file mode 100644 index 000000000..1adbfacf5 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_associated_type-2.snap @@ -0,0 +1,20 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for Parametrized +where + T: TraitName, + T::Associated: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.field, writer) + .await?; + ::serialize(&self.another, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_associated_type.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_associated_type.snap index 33b203f80..322493711 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/generic_associated_type.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_associated_type.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for Parametrized where @@ -12,8 +12,8 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.field, writer)?; - borsh::BorshSerialize::serialize(&self.another, writer)?; - Ok(()) + ::serialize(&self.field, writer)?; + ::serialize(&self.another, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_named_fields_struct_borsh_skip-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_named_fields_struct_borsh_skip-2.snap new file mode 100644 index 000000000..225be6bc6 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_named_fields_struct_borsh_skip-2.snap @@ -0,0 +1,16 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for G +where + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.y, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap index 5351f166f..0526e2c5b 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_named_fields_struct_borsh_skip.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for G where @@ -10,7 +10,7 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.y, writer)?; - Ok(()) + ::serialize(&self.y, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_async_bound-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_async_bound-2.snap new file mode 100644 index 000000000..dea73c518 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_async_bound-2.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for C +where + T: borsh::ser::BorshSerializeAsync + PartialOrd, + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.a, writer).await?; + as borsh::BorshSerializeAsync>::serialize(&self.b, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_async_bound.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_async_bound.snap new file mode 100644 index 000000000..b960585ae --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_async_bound.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerialize for C +where + T: borsh::ser::BorshSerialize, + U: borsh::ser::BorshSerialize, +{ + fn serialize<__W: borsh::io::Write>( + &self, + writer: &mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.a, writer)?; + as borsh::BorshSerialize>::serialize(&self.b, writer)?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_bound-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_bound-2.snap new file mode 100644 index 000000000..62754e200 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_bound-2.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for C +where + T: borsh::ser::BorshSerializeAsync, + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.a, writer).await?; + as borsh::BorshSerializeAsync>::serialize(&self.b, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_bound.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_bound.snap index c0309d69a..ad8e7908a 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_bound.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_serialize_bound.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for C where @@ -11,8 +11,8 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.a, writer)?; - borsh::BorshSerialize::serialize(&self.b, writer)?; - Ok(()) + ::serialize(&self.a, writer)?; + as borsh::BorshSerialize>::serialize(&self.b, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip1-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip1-2.snap new file mode 100644 index 000000000..56479f61c --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip1-2.snap @@ -0,0 +1,16 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for G +where + U: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.1, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap index 447fcb32e..2ba771ccb 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip1.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for G where @@ -10,7 +10,7 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.1, writer)?; - Ok(()) + ::serialize(&self.1, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip2-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip2-2.snap new file mode 100644 index 000000000..1ff89c2d7 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip2-2.snap @@ -0,0 +1,17 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for G +where + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + as borsh::BorshSerializeAsync>::serialize(&self.0, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap index 8c5800a25..59b43cd7f 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/generic_tuple_struct_borsh_skip2.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for G where @@ -11,7 +11,7 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.0, writer)?; - Ok(()) + as borsh::BorshSerialize>::serialize(&self.0, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/override_generic_associated_type_wrong_derive-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/override_generic_associated_type_wrong_derive-2.snap new file mode 100644 index 000000000..568115c56 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/override_generic_associated_type_wrong_derive-2.snap @@ -0,0 +1,23 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for Parametrized +where + T: TraitName, + T: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + <::Associated as borsh::BorshSerializeAsync>::serialize( + &self.field, + writer, + ) + .await?; + ::serialize(&self.another, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/override_generic_associated_type_wrong_derive.snap b/borsh-derive/src/internals/serialize/structs/snapshots/override_generic_associated_type_wrong_derive.snap index 9b2bf20b3..c7bd3fd42 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/override_generic_associated_type_wrong_derive.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/override_generic_associated_type_wrong_derive.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for Parametrized where @@ -12,8 +12,11 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.field, writer)?; - borsh::BorshSerialize::serialize(&self.another, writer)?; - Ok(()) + <::Associated as borsh::BorshSerialize>::serialize( + &self.field, + writer, + )?; + ::serialize(&self.another, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/recursive_struct-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/recursive_struct-2.snap new file mode 100644 index 000000000..6f25d5914 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/recursive_struct-2.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for CRecC { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.a, writer).await?; + as borsh::BorshSerializeAsync>::serialize(&self.b, writer) + .await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/recursive_struct.snap b/borsh-derive/src/internals/serialize/structs/snapshots/recursive_struct.snap index 510b71b77..33be64902 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/recursive_struct.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/recursive_struct.snap @@ -1,14 +1,14 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for CRecC { fn serialize<__W: borsh::io::Write>( &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.a, writer)?; - borsh::BorshSerialize::serialize(&self.b, writer)?; - Ok(()) + ::serialize(&self.a, writer)?; + as borsh::BorshSerialize>::serialize(&self.b, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_generic_tuple_struct-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generic_tuple_struct-2.snap new file mode 100644 index 000000000..a94669553 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generic_tuple_struct-2.snap @@ -0,0 +1,17 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for TupleA +where + T: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.0, writer).await?; + ::serialize(&self.1, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_generic_tuple_struct.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generic_tuple_struct.snap index 984defae9..7486c2358 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/simple_generic_tuple_struct.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generic_tuple_struct.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for TupleA where @@ -10,8 +10,8 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.0, writer)?; - borsh::BorshSerialize::serialize(&self.1, writer)?; - Ok(()) + ::serialize(&self.0, writer)?; + ::serialize(&self.1, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_generics-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generics-2.snap new file mode 100644 index 000000000..299dcfd67 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generics-2.snap @@ -0,0 +1,18 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A +where + K: borsh::ser::BorshSerializeAsync, + V: borsh::ser::BorshSerializeAsync, +{ + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + as borsh::BorshSerializeAsync>::serialize(&self.x, writer).await?; + ::serialize(&self.y, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_generics.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generics.snap index 3518ba3a4..47667b80d 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/simple_generics.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_generics.snap @@ -1,6 +1,6 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A where @@ -11,8 +11,8 @@ where &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.x, writer)?; - borsh::BorshSerialize::serialize(&self.y, writer)?; - Ok(()) + as borsh::BorshSerialize>::serialize(&self.x, writer)?; + ::serialize(&self.y, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct-2.snap new file mode 100644 index 000000000..5e9b3b15e --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct-2.snap @@ -0,0 +1,14 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl borsh::ser::BorshSerializeAsync for A { + async fn serialize<'async_variant, __W: borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), borsh::io::Error> { + ::serialize(&self.x, writer).await?; + ::serialize(&self.y, writer).await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct.snap index 8904dca85..eac613281 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct.snap @@ -1,14 +1,14 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl borsh::ser::BorshSerialize for A { fn serialize<__W: borsh::io::Write>( &self, writer: &mut __W, ) -> ::core::result::Result<(), borsh::io::Error> { - borsh::BorshSerialize::serialize(&self.x, writer)?; - borsh::BorshSerialize::serialize(&self.y, writer)?; - Ok(()) + ::serialize(&self.x, writer)?; + ::serialize(&self.y, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct_with_custom_crate-2.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct_with_custom_crate-2.snap new file mode 100644 index 000000000..8361c83c3 --- /dev/null +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct_with_custom_crate-2.snap @@ -0,0 +1,16 @@ +--- +source: borsh-derive/src/internals/serialize/structs/mod.rs +expression: pretty_print_syn_str(actual).unwrap() +--- +impl reexporter::borsh::ser::BorshSerializeAsync for A { + async fn serialize<'async_variant, __W: reexporter::borsh::async_io::AsyncWrite>( + &'async_variant self, + writer: &'async_variant mut __W, + ) -> ::core::result::Result<(), reexporter::borsh::io::Error> { + ::serialize(&self.x, writer) + .await?; + ::serialize(&self.y, writer) + .await?; + ::core::result::Result::Ok(()) + } +} diff --git a/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct_with_custom_crate.snap b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct_with_custom_crate.snap index bd331f30d..462572e22 100644 --- a/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct_with_custom_crate.snap +++ b/borsh-derive/src/internals/serialize/structs/snapshots/simple_struct_with_custom_crate.snap @@ -1,14 +1,14 @@ --- source: borsh-derive/src/internals/serialize/structs/mod.rs -expression: pretty_print_syn_str(&actual).unwrap() +expression: pretty_print_syn_str(actual).unwrap() --- impl reexporter::borsh::ser::BorshSerialize for A { fn serialize<__W: reexporter::borsh::io::Write>( &self, writer: &mut __W, ) -> ::core::result::Result<(), reexporter::borsh::io::Error> { - reexporter::borsh::BorshSerialize::serialize(&self.x, writer)?; - reexporter::borsh::BorshSerialize::serialize(&self.y, writer)?; - Ok(()) + ::serialize(&self.x, writer)?; + ::serialize(&self.y, writer)?; + ::core::result::Result::Ok(()) } } diff --git a/borsh-derive/src/internals/serialize/unions/mod.rs b/borsh-derive/src/internals/serialize/unions/mod.rs index 3806821bd..89200b4ea 100644 --- a/borsh-derive/src/internals/serialize/unions/mod.rs +++ b/borsh-derive/src/internals/serialize/unions/mod.rs @@ -1,6 +1,9 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ItemUnion, Path}; -pub fn process(_input: &ItemUnion, _cratename: Path) -> syn::Result { +pub fn process( + _input: ItemUnion, + _cratename: Path, +) -> syn::Result { unimplemented!() } diff --git a/borsh-derive/src/internals/test_helpers.rs b/borsh-derive/src/internals/test_helpers.rs index 806b7c826..a4ce8ae4a 100644 --- a/borsh-derive/src/internals/test_helpers.rs +++ b/borsh-derive/src/internals/test_helpers.rs @@ -1,37 +1,36 @@ -use super::cratename::BORSH; -use proc_macro2::Span; -use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; -use std::fmt::Write; +use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; use syn::{Ident, Path}; -pub fn pretty_print_syn_str(input: &TokenStream) -> syn::Result { - let input = format!("{}", quote!(#input)); - let syn_file = syn::parse_str::(&input)?; +use super::cratename::BORSH; + +pub fn pretty_print_syn_str(input: TokenStream) -> syn::Result { + let syn_file = syn::parse2::(input)?; Ok(prettyplease::unparse(&syn_file)) } pub fn debug_print_vec_of_tokenizable(optional: Option>) -> String { - let mut s = String::new(); if let Some(vec) = optional { + let mut s = String::new(); for element in vec { - writeln!(&mut s, "{}", element.to_token_stream()).unwrap(); + s.push_str(&element.to_token_stream().to_string()); + s.push('\n'); } + s } else { - write!(&mut s, "None").unwrap(); + "None".to_owned() } - s } pub fn debug_print_tokenizable(optional: Option) -> String { - let mut s = String::new(); if let Some(type_) = optional { - writeln!(&mut s, "{}", type_.to_token_stream()).unwrap(); + let mut s = type_.to_token_stream().to_string(); + s.push('\n'); + s } else { - write!(&mut s, "None").unwrap(); + "None".to_owned() } - s } macro_rules! local_insta_assert_debug_snapshot { @@ -57,4 +56,5 @@ pub(crate) fn default_cratename() -> Path { cratename.into() } -pub(crate) use {local_insta_assert_debug_snapshot, local_insta_assert_snapshot}; +pub(crate) use local_insta_assert_debug_snapshot; +pub(crate) use local_insta_assert_snapshot; diff --git a/borsh-derive/src/lib.rs b/borsh-derive/src/lib.rs index f12dd3992..ee409fc78 100644 --- a/borsh-derive/src/lib.rs +++ b/borsh-derive/src/lib.rs @@ -14,12 +14,12 @@ use syn::{DeriveInput, Error, ItemEnum, ItemStruct, ItemUnion, Path}; /// by convention, local to borsh-derive crate, imports from proc_macro (1) are not allowed in `internals` module or in any of its submodules. mod internals; -use crate::internals::attributes::item; - #[cfg(feature = "schema")] use internals::schema; use internals::{cratename, deserialize, serialize}; +use crate::internals::attributes::item; + fn check_attrs_get_cratename(input: &TokenStream) -> Result { let input = input.clone(); @@ -35,6 +35,19 @@ fn check_attrs_get_cratename(input: &TokenStream) -> Result { /// moved to docs of **Derive Macro** `BorshSerialize` in `borsh` crate #[proc_macro_derive(BorshSerialize, attributes(borsh))] pub fn borsh_serialize(input: TokenStream) -> TokenStream { + borsh_serialize_generic::(input) +} + +/// --- +/// +/// moved to docs of **Derive Macro** `BorshSerializeAsync` in `borsh` crate +#[cfg(feature = "async")] +#[proc_macro_derive(BorshSerializeAsync, attributes(borsh))] +pub fn borsh_serialize_async(input: TokenStream) -> TokenStream { + borsh_serialize_generic::(input) +} + +fn borsh_serialize_generic(input: TokenStream) -> TokenStream { let cratename = match check_attrs_get_cratename(&input) { Ok(cratename) => cratename, Err(err) => { @@ -43,19 +56,16 @@ pub fn borsh_serialize(input: TokenStream) -> TokenStream { }; let res = if let Ok(input) = syn::parse::(input.clone()) { - serialize::structs::process(&input, cratename) + serialize::structs::process::(input, cratename) } else if let Ok(input) = syn::parse::(input.clone()) { - serialize::enums::process(&input, cratename) + serialize::enums::process::(input, cratename) } else if let Ok(input) = syn::parse::(input) { - serialize::unions::process(&input, cratename) + serialize::unions::process::(input, cratename) } else { // Derive macros can only be defined on structs, enums, and unions. unreachable!() }; - TokenStream::from(match res { - Ok(res) => res, - Err(err) => err.to_compile_error(), - }) + TokenStream::from(res.unwrap_or_else(|err| err.to_compile_error())) } /// --- @@ -63,6 +73,19 @@ pub fn borsh_serialize(input: TokenStream) -> TokenStream { /// moved to docs of **Derive Macro** `BorshDeserialize` in `borsh` crate #[proc_macro_derive(BorshDeserialize, attributes(borsh))] pub fn borsh_deserialize(input: TokenStream) -> TokenStream { + borsh_deserialize_generic::(input) +} + +/// --- +/// +/// moved to docs of **Derive Macro** `BorshDeserializeAsync` in `borsh` crate +#[cfg(feature = "async")] +#[proc_macro_derive(BorshDeserializeAsync, attributes(borsh))] +pub fn borsh_deserialize_async(input: TokenStream) -> TokenStream { + borsh_deserialize_generic::(input) +} + +fn borsh_deserialize_generic(input: TokenStream) -> TokenStream { let cratename = match check_attrs_get_cratename(&input) { Ok(cratename) => cratename, Err(err) => { @@ -71,19 +94,16 @@ pub fn borsh_deserialize(input: TokenStream) -> TokenStream { }; let res = if let Ok(input) = syn::parse::(input.clone()) { - deserialize::structs::process(&input, cratename) + deserialize::structs::process::(input, cratename) } else if let Ok(input) = syn::parse::(input.clone()) { - deserialize::enums::process(&input, cratename) + deserialize::enums::process::(input, cratename) } else if let Ok(input) = syn::parse::(input) { - deserialize::unions::process(&input, cratename) + deserialize::unions::process::(input, cratename) } else { // Derive macros can only be defined on structs, enums, and unions. unreachable!() }; - TokenStream::from(match res { - Ok(res) => res, - Err(err) => err.to_compile_error(), - }) + TokenStream::from(res.unwrap_or_else(|err| err.to_compile_error())) } /// --- @@ -100,11 +120,11 @@ pub fn borsh_schema(input: TokenStream) -> TokenStream { }; let res = if let Ok(input) = syn::parse::(input.clone()) { - schema::structs::process(&input, cratename) + schema::structs::process(input, cratename) } else if let Ok(input) = syn::parse::(input.clone()) { - schema::enums::process(&input, cratename) + schema::enums::process(input, cratename) } else if syn::parse::(input).is_ok() { - Err(syn::Error::new( + Err(Error::new( Span::call_site(), "Borsh schema does not support unions yet.", )) @@ -112,8 +132,5 @@ pub fn borsh_schema(input: TokenStream) -> TokenStream { // Derive macros can only be defined on structs, enums, and unions. unreachable!() }; - TokenStream::from(match res { - Ok(res) => res, - Err(err) => err.to_compile_error(), - }) + TokenStream::from(res.unwrap_or_else(|err| err.to_compile_error())) } diff --git a/borsh/Cargo.toml b/borsh/Cargo.toml index 4a7cd7bcc..c26278716 100644 --- a/borsh/Cargo.toml +++ b/borsh/Cargo.toml @@ -3,7 +3,7 @@ name = "borsh" version.workspace = true rust-version.workspace = true authors = ["Near Inc "] -edition = "2018" +edition.workspace = true license = "MIT OR Apache-2.0" readme = "README.md" categories = ["encoding", "network-programming"] @@ -41,17 +41,26 @@ hashbrown = { version = ">=0.11,<0.16.0", optional = true } bytes = { version = "1", optional = true } bson = { version = "2", optional = true } +async-generic = { git = "https://github.com/DanikVitek/async-generic.git", rev = "392e8ef432c9bf4ca381262f56e192a27136e04f" } +tokio = { version = "1", default-features = false, features = ["io-util"], optional = true } +async-std = { version = "1", default-features = false, features = ["std"], optional = true } + [dev-dependencies] +tokio-test = "0.4.4" +tokio = { version = "1", features = ["io-std", "macros"] } insta = "1.29.0" serde_json = { version = "1" } [package.metadata.docs.rs] -features = ["derive", "unstable__schema", "rc"] +features = ["derive", "rc", "ascii", "unstable__schema", "unstable__async"] targets = ["x86_64-unknown-linux-gnu"] [features] default = ["std"] -derive = ["borsh-derive"] +derive = ["dep:borsh-derive"] +unstable__async = ["borsh-derive?/async"] +unstable__tokio = ["unstable__async", "dep:tokio", "std"] +unstable__async-std = ["unstable__async", "dep:async-std", "std"] unstable__schema = ["derive", "borsh-derive/schema"] std = [] # Opt into impls for Rc and Arc. Serializing and deserializing these types diff --git a/borsh/docs/rustdoc_include/borsh_crate_top_level.md b/borsh/docs/rustdoc_include/borsh_crate_top_level.md index 29624c193..e4e35d82b 100644 --- a/borsh/docs/rustdoc_include/borsh_crate_top_level.md +++ b/borsh/docs/rustdoc_include/borsh_crate_top_level.md @@ -7,12 +7,12 @@ 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 + 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. + `std::io` items. Otherwise they are borsh-specific types which mimic + behavior of corresponding standard types. ### Default features @@ -20,55 +20,71 @@ ### 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] +* **`derive`** - + Gates derive macros of [`BorshSerialize`] and + [`BorshDeserialize`] traits, as well as of [`BorshSchema`], + [`BorshSerializeAsync`] and [`BorshDeserializeAsync`] traits + if corresponding features are enabled. +* **`unstable__schema`** - + Gates [`BorshSchema`] trait and its derive macro. + Gates [`schema`] module. + This feature requires **`derive`** to be enabled too. +* **`unstable__async`** - + Gates [`BorshSerializeAsync`] and [`BorshDeserializeAsync`] traits. + Gates [`async_io`] module. + This feature is meant to allow implementing serialization/deserialization + using async I/O. +* **`unstable__tokio`** / **`unstable__async-std`** - + Gates implementation of [`async_io::AsyncWrite`] and [`async_io::AsyncRead`] + for `tokio` and `async-std` runtimes respectively. + Enabling either of these features will also enable **`unstable__async`** feature. + These features are set to be mutually exclusive. +* **`rc`** - + Gates implementation of [`BorshSerialize`] and [`BorshDeserialize`] for [`Rc`](std::rc::Rc)/[`Arc`](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). -* **bson** - - Gates implementation of [BorshSerialize] and [BorshDeserialize] - for [ObjectId](https://docs.rs/bson/2.9.0/bson/oid/struct.ObjectId.html). -* **ascii** - - Gates implementation of [BorshSerialize], [BorshDeserialize], [BorshSchema] for - types from [ascii](https://docs.rs/ascii/1.1.0/ascii/) crate. -* **de_strict_order** - +* **`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). +* **`bson`** - + Gates implementation of [`BorshSerialize`] and [`BorshDeserialize`] + for [`ObjectId`](https://docs.rs/bson/2.9.0/bson/oid/struct.ObjectId.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. + [`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. + If this feature is not enabled, it is possible that two different byte slices could deserialize into the same + [`HashMap`](std::collections::HashMap)/[`HashSet`](std::collections::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). +* **`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. +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) +- [Derive Macro `BorshSerializeAsync`](macro@crate::BorshSerializeAsync) +- [Derive Macro `BorshDeserializeAsync`](macro@crate::BorshDeserializeAsync) diff --git a/borsh/docs/rustdoc_include/borsh_deserialize.md b/borsh/docs/rustdoc_include/borsh_deserialize.md index 0232d661c..b3e22dcfb 100644 --- a/borsh/docs/rustdoc_include/borsh_deserialize.md +++ b/borsh/docs/rustdoc_include/borsh_deserialize.md @@ -109,6 +109,7 @@ This adds a lot of convenience for objects that are architectured to be used as type CryptoHash = String; use borsh::BorshDeserialize; + #[derive(BorshDeserialize)] #[borsh(init=init)] struct Message { @@ -140,6 +141,7 @@ You must specify `use_discriminant` for all enums with explicit discriminants in 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 { @@ -151,6 +153,7 @@ enum A { 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 { @@ -165,6 +168,7 @@ This case is not supported: ```rust,compile_fail use borsh::BorshDeserialize; + const fn discrim() -> isize { 0x14 } @@ -186,6 +190,8 @@ enum X { This is not supported: ```rust,compile_fail +use borsh::BorshDeserialize; + #[derive(BorshDeserialize)] #[borsh(use_discriminant = true)] enum X { @@ -211,6 +217,7 @@ parameters encountered in annotated field. ```rust use borsh::BorshDeserialize; + #[derive(BorshDeserialize)] struct A { x: u64, @@ -241,6 +248,7 @@ 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"))] @@ -259,10 +267,12 @@ struct A { ```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 @@ -286,6 +296,7 @@ use borsh::BorshDeserialize; 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"))] diff --git a/borsh/docs/rustdoc_include/borsh_serialize.md b/borsh/docs/rustdoc_include/borsh_serialize.md index db87e6865..3f5117898 100644 --- a/borsh/docs/rustdoc_include/borsh_serialize.md +++ b/borsh/docs/rustdoc_include/borsh_serialize.md @@ -125,6 +125,8 @@ enum B { ###### borsh, expressions, evaluating to `isize`, as discriminant This case is not supported: ```rust,compile_fail +use borsh::BorshSerialize; + const fn discrim() -> isize { 0x14 } @@ -144,6 +146,8 @@ enum X { ###### borsh explicit discriminant does not support literal values outside of u8 range This is not supported: ```rust,compile_fail +use borsh::BorshSerialize; + #[derive(BorshSerialize)] #[borsh(use_discriminant = true)] enum X { @@ -192,6 +196,7 @@ use borsh::BorshSerialize; 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"))] @@ -208,10 +213,12 @@ struct A { ```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 diff --git a/borsh/examples/serde_json_value.rs b/borsh/examples/serde_json_value.rs index 21cadf758..dbbd599d8 100644 --- a/borsh/examples/serde_json_value.rs +++ b/borsh/examples/serde_json_value.rs @@ -3,14 +3,18 @@ use std::collections::HashMap; use borsh::{BorshDeserialize, BorshSerialize}; mod serde_json_value { + #[allow(unused_imports)] // used in attribute pub use de::deserialize_value; + #[allow(unused_imports)] // used in attribute pub use ser::serialize_value; + mod ser { + use core::convert::TryFrom; + use borsh::{ io::{ErrorKind, Result, Write}, BorshSerialize, }; - use core::convert::TryFrom; /// this is mutually recursive with `serialize_array` and `serialize_map` pub fn serialize_value(value: &serde_json::Value, writer: &mut W) -> Result<()> { diff --git a/borsh/src/async_io.rs b/borsh/src/async_io.rs new file mode 100644 index 000000000..a0c45235e --- /dev/null +++ b/borsh/src/async_io.rs @@ -0,0 +1,420 @@ +use core::future::Future; + +use crate::io::Result as BorshIoResult; + +/// Asynchronous read trait. +/// +/// `read_` methods imply little-endian byte order, +/// otherwise it's incorrect in the context of `borsh`. +/// +/// Blanked implementations for `tokio` and `async-std` are provided. +pub trait AsyncRead: Unpin + Send { + fn read<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> impl Future> + Send + 'a; + + fn read_exact<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> impl Future> + Send + 'a { + async { + let mut offset = 0; + while offset < buf.len() { + let read = self.read(&mut buf[offset..]).await?; + if read == 0 { + return Err(crate::io::Error::new( + crate::io::ErrorKind::UnexpectedEof, + "failed to fill the whole buffer", + )); + } + offset += read; + } + Ok(()) + } + } + + fn read_u8(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 1]; + self.read_exact(&mut buf).await?; + Ok(buf[0]) + } + } + + fn read_u16(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 2]; + self.read_exact(&mut buf).await?; + Ok(u16::from_le_bytes(buf)) + } + } + + fn read_u32(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf).await?; + Ok(u32::from_le_bytes(buf)) + } + } + + fn read_u64(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf).await?; + Ok(u64::from_le_bytes(buf)) + } + } + + fn read_u128(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 16]; + self.read_exact(&mut buf).await?; + Ok(u128::from_le_bytes(buf)) + } + } + + fn read_i8(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 1]; + self.read_exact(&mut buf).await?; + Ok(buf[0] as i8) + } + } + + fn read_i16(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 2]; + self.read_exact(&mut buf).await?; + Ok(i16::from_le_bytes(buf)) + } + } + + fn read_i32(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf).await?; + Ok(i32::from_le_bytes(buf)) + } + } + + fn read_i64(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf).await?; + Ok(i64::from_le_bytes(buf)) + } + } + + fn read_i128(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 16]; + self.read_exact(&mut buf).await?; + Ok(i128::from_le_bytes(buf)) + } + } + + fn read_f32(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf).await?; + Ok(f32::from_le_bytes(buf)) + } + } + + fn read_f64(&mut self) -> impl Future> + Send { + async { + let mut buf = [0u8; 8]; + self.read_exact(&mut buf).await?; + Ok(f64::from_le_bytes(buf)) + } + } +} + +#[cfg(feature = "unstable__tokio")] +impl AsyncRead for R { + #[inline] + fn read<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> impl Future> + Send + 'a { + tokio::io::AsyncReadExt::read(self, buf) + } + + #[inline] + async fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> BorshIoResult<()> { + tokio::io::AsyncReadExt::read_exact(self, buf) + .await + .map(|_| ()) + } + + #[inline] + fn read_u8(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_u8(self) + } + + #[inline] + fn read_u16(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_u16_le(self) + } + + #[inline] + fn read_u32(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_u32_le(self) + } + + #[inline] + fn read_u64(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_u64_le(self) + } + + #[inline] + fn read_u128(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_u128_le(self) + } + + #[inline] + fn read_i8(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_i8(self) + } + + #[inline] + fn read_i16(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_i16_le(self) + } + + #[inline] + fn read_i32(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_i32_le(self) + } + + #[inline] + fn read_i64(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_i64_le(self) + } + + #[inline] + fn read_i128(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_i128_le(self) + } + + #[inline] + fn read_f32(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_f32_le(self) + } + + #[inline] + fn read_f64(&mut self) -> impl Future> + Send { + tokio::io::AsyncReadExt::read_f64_le(self) + } +} + +#[cfg(feature = "unstable__async-std")] +impl AsyncRead for R { + #[inline] + fn read<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> impl Future> + Send + 'a { + async_std::io::ReadExt::read(self, buf) + } + + #[inline] + fn read_exact<'a>( + &'a mut self, + buf: &'a mut [u8], + ) -> impl Future> + Send + 'a { + async_std::io::ReadExt::read_exact(self, buf) + } +} + +/// Asynchronous write trait. +/// +/// `write_` methods imply little-endian byte order, +/// otherwise it's incorrect in the context of `borsh`. +/// +/// Blanked implementations for `tokio` and `async-std` are provided. +pub trait AsyncWrite: Unpin + Send { + fn write_all<'a>( + &'a mut self, + buf: &'a [u8], + ) -> impl Future> + Send + 'a; + + fn write_u8(&mut self, n: u8) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_u16(&mut self, n: u16) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_u32(&mut self, n: u32) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_u64(&mut self, n: u64) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_u128(&mut self, n: u128) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_i8(&mut self, n: i8) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_i16(&mut self, n: i16) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_i32(&mut self, n: i32) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_i64(&mut self, n: i64) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_i128(&mut self, n: i128) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_f32(&mut self, n: f32) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } + + fn write_f64(&mut self, n: f64) -> impl Future> + Send { + async move { + let bytes = n.to_le_bytes(); + self.write_all(&bytes).await?; + Ok(()) + } + } +} + +#[cfg(feature = "unstable__tokio")] +impl AsyncWrite for R { + #[inline] + fn write_all<'a>( + &'a mut self, + buf: &'a [u8], + ) -> impl Future> + Send + 'a { + tokio::io::AsyncWriteExt::write_all(self, buf) + } + + #[inline] + fn write_u8(&mut self, n: u8) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_u8(self, n) + } + + #[inline] + fn write_u16(&mut self, n: u16) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_u16_le(self, n) + } + + #[inline] + fn write_u32(&mut self, n: u32) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_u32_le(self, n) + } + + #[inline] + fn write_u64(&mut self, n: u64) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_u64_le(self, n) + } + + #[inline] + fn write_u128(&mut self, n: u128) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_u128_le(self, n) + } + + #[inline] + fn write_i8(&mut self, n: i8) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_i8(self, n) + } + + #[inline] + fn write_i16(&mut self, n: i16) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_i16_le(self, n) + } + + #[inline] + fn write_i32(&mut self, n: i32) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_i32_le(self, n) + } + + #[inline] + fn write_i64(&mut self, n: i64) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_i64_le(self, n) + } + + #[inline] + fn write_i128(&mut self, n: i128) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_i128_le(self, n) + } + + #[inline] + fn write_f32(&mut self, n: f32) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_f32_le(self, n) + } + + #[inline] + fn write_f64(&mut self, n: f64) -> impl Future> + Send { + tokio::io::AsyncWriteExt::write_f64_le(self, n) + } +} + +#[cfg(feature = "unstable__async-std")] +impl AsyncWrite for R { + #[inline] + fn write_all<'a>( + &'a mut self, + buf: &'a [u8], + ) -> impl Future> + Send + 'a { + async_std::io::WriteExt::write_all(self, buf) + } +} diff --git a/borsh/src/de/mod.rs b/borsh/src/de/mod.rs index 0abb36e3e..3f9a2c6ee 100644 --- a/borsh/src/de/mod.rs +++ b/borsh/src/de/mod.rs @@ -1,25 +1,30 @@ -use core::marker::PhantomData; -use core::mem::MaybeUninit; +#[cfg(feature = "unstable__async")] +use core::future::Future; use core::{ convert::{TryFrom, TryInto}, - mem::size_of, + marker::PhantomData, + mem::{size_of, MaybeUninit}, }; +use async_generic::async_generic; #[cfg(feature = "bytes")] use bytes::{BufMut, BytesMut}; -use crate::__private::maybestd::{ - borrow::{Borrow, Cow, ToOwned}, - boxed::Box, - collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, - format, - string::{String, ToString}, - vec, - vec::Vec, +#[cfg(feature = "unstable__async")] +use crate::async_io::AsyncRead; +use crate::{ + __private::maybestd::{ + borrow::{Borrow, Cow, ToOwned}, + boxed::Box, + collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, + format, + string::String, + vec, + vec::Vec, + }, + error::check_zst, + io::{Error, ErrorKind, Read, Result}, }; -use crate::io::{Error, ErrorKind, Read, Result}; - -use crate::error::check_zst; mod hint; @@ -54,6 +59,7 @@ pub trait BorshDeserialize: Sized { fn try_from_reader(reader: &mut R) -> Result { let result = Self::deserialize_reader(reader)?; + let mut buf = [0u8; 1]; match reader.read_exact(&mut buf) { Err(f) if f.kind() == ErrorKind::UnexpectedEof => Ok(result), @@ -77,12 +83,53 @@ pub trait BorshDeserialize: Sized { } } +/// A data-structure that can be asynchronously de-serialized from binary format by NBOR. +#[cfg(feature = "unstable__async")] +pub trait BorshDeserializeAsync: Sized + Send { + fn deserialize_reader( + reader: &mut R, + ) -> impl Future> + Send; + + fn try_from_reader(reader: &mut R) -> impl Future> + Send { + async move { + let result = Self::deserialize_reader(reader).await?; + + let mut buf = [0u8; 1]; + let res = reader.read_exact(&mut buf).await; + match res { + Err(f) if f.kind() == ErrorKind::UnexpectedEof => Ok(result), + _ => Err(Error::new(ErrorKind::InvalidData, ERROR_NOT_ALL_BYTES_READ)), + } + } + } + + #[inline] + #[doc(hidden)] + fn vec_from_reader( + len: u32, + reader: &mut R, + ) -> impl Future>>> + Send { + let _ = len; + let _ = reader; + core::future::ready(Ok(None)) + } + + #[inline] + #[doc(hidden)] + fn array_from_reader( + reader: &mut R, + ) -> impl Future>> + Send { + let _ = reader; + core::future::ready(Ok(None)) + } +} + /// Additional methods offered on enums which is used by `[derive(BorshDeserialize)]`. pub trait EnumExt: BorshDeserialize { /// Deserialises given variant of an enum from the reader. /// /// This may be used to perform validation or filtering based on what - /// variant is being deserialised. + /// variant is being deserialized. /// /// ``` /// use borsh::BorshDeserialize; @@ -135,6 +182,70 @@ pub trait EnumExt: BorshDeserialize { fn deserialize_variant(reader: &mut R, tag: u8) -> Result; } +/// Additional methods offered on enums which is used by `[derive(BorshDeserializeAsync)]`. +#[cfg(feature = "unstable__async")] +pub trait EnumExtAsync: BorshDeserializeAsync { + /// Deserialises given variant of an enum from the async reader. + /// + /// This may be used to perform validation or filtering based on what + /// variant is being deserialized. + /// + /// ``` + /// use borsh::BorshDeserializeAsync; + /// use borsh::de::EnumExtAsync as _; + /// + /// /// derive is only available if borsh is built with `features = ["derive"]` + /// # #[cfg(feature = "derive")] + /// #[derive(Debug, PartialEq, Eq, BorshDeserializeAsync)] + /// enum MyEnum { + /// Zero, + /// One(u8), + /// Many(Vec) + /// } + /// + /// # #[cfg(feature = "derive")] + /// #[derive(Debug, PartialEq, Eq)] + /// struct OneOrZero(MyEnum); + /// + /// # #[cfg(feature = "derive")] + /// impl borsh::de::BorshDeserializeAsync for OneOrZero { + /// async fn deserialize_reader( + /// reader: &mut R, + /// ) -> borsh::io::Result { + /// use borsh::de::EnumExtAsync; + /// let tag = u8::deserialize_reader(reader).await?; + /// if tag == 2 { + /// Err(borsh::io::Error::new( + /// borsh::io::ErrorKind::InvalidData, + /// "MyEnum::Many not allowed here", + /// )) + /// } else { + /// MyEnum::deserialize_variant(reader, tag).await.map(Self) + /// } + /// } + /// } + /// + /// use borsh::from_reader_async; + /// # tokio_test::block_on(async { + /// let data = b"\0"; + /// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] + /// assert_eq!(MyEnum::Zero, from_reader_async::<_, MyEnum>(&mut &data[..]).await.unwrap()); + /// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] + /// assert_eq!(MyEnum::Zero, from_reader_async::<_, OneOrZero>(&mut &data[..]).await.unwrap().0); + /// + /// let data = b"\x02\0\0\0\0"; + /// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] + /// assert_eq!(MyEnum::Many(Vec::new()), from_reader_async::<_, MyEnum>(&mut &data[..]).await.unwrap()); + /// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] + /// assert!(from_reader_async::<_, OneOrZero>(&mut &data[..]).await.is_err()); + /// # }); + /// ``` + fn deserialize_variant( + reader: &mut R, + tag: u8, + ) -> impl Future> + Send; +} + fn unexpected_eof_to_unexpected_length_of_input(e: Error) -> Error { if e.kind() == ErrorKind::UnexpectedEof { Error::new(ErrorKind::InvalidData, ERROR_UNEXPECTED_LENGTH_OF_INPUT) @@ -143,18 +254,32 @@ fn unexpected_eof_to_unexpected_length_of_input(e: Error) -> Error { } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for u8 { #[inline] + #[async_generic( + async_signature[impl_fut](reader: &mut R) -> impl Future> + Send + )] fn deserialize_reader(reader: &mut R) -> Result { - let mut buf = [0u8; 1]; - reader - .read_exact(&mut buf) - .map_err(unexpected_eof_to_unexpected_length_of_input)?; - Ok(buf[0]) + if _sync { + let mut buf = [0u8; 1]; + reader + .read_exact(&mut buf) + .map_err(unexpected_eof_to_unexpected_length_of_input)?; + Ok(buf[0]) + } else { + reader.read_u8() + } } #[inline] #[doc(hidden)] + #[async_generic( + async_signature(len: u32, reader: &mut R) -> Result>> + )] fn vec_from_reader(len: u32, reader: &mut R) -> Result>> { let len: usize = len.try_into().map_err(|_| ErrorKind::InvalidData)?; // Avoid OOM by limiting the size of allocation. This makes the read @@ -168,7 +293,12 @@ impl BorshDeserialize for u8 { vec.resize(vec.len().saturating_mul(2).min(len), 0) } // TODO(mina86): Convert this to read_buf once that stabilises. - match reader.read(&mut vec.as_mut_slice()[pos..])? { + let res = if _sync { + reader.read(&mut vec.as_mut_slice()[pos..])? + } else { + reader.read(&mut vec.as_mut_slice()[pos..]).await? + }; + match res { 0 => { return Err(Error::new( ErrorKind::InvalidData, @@ -185,11 +315,17 @@ impl BorshDeserialize for u8 { #[inline] #[doc(hidden)] + #[async_generic( + async_signature(reader: &mut R) -> Result> + )] fn array_from_reader(reader: &mut R) -> Result> { let mut arr = [0u8; N]; - reader - .read_exact(&mut arr) - .map_err(unexpected_eof_to_unexpected_length_of_input)?; + if _sync { + reader.read_exact(&mut arr) + } else { + reader.read_exact(&mut arr).await + } + .map_err(unexpected_eof_to_unexpected_length_of_input)?; Ok(Some(arr)) } } @@ -220,68 +356,130 @@ impl_for_integer!(u32); impl_for_integer!(u64); impl_for_integer!(u128); +macro_rules! impl_async_for_integer { + ($type: ident, $method: ident) => { + #[cfg(feature = "unstable__async")] + impl BorshDeserializeAsync for $type { + #[inline] + fn deserialize_reader( + reader: &mut R, + ) -> impl Future> + Send { + reader.$method() + } + } + }; +} + +impl_async_for_integer!(i8, read_i8); +impl_async_for_integer!(i16, read_i16); +impl_async_for_integer!(i32, read_i32); +impl_async_for_integer!(i64, read_i64); +impl_async_for_integer!(i128, read_i128); +impl_async_for_integer!(u16, read_u16); +impl_async_for_integer!(u32, read_u32); +impl_async_for_integer!(u64, read_u64); +impl_async_for_integer!(u128, read_u128); + macro_rules! impl_for_nonzero_integer { - ($type: ty) => { + ($type: ty, $repr: ty) => { impl BorshDeserialize for $type { #[inline] fn deserialize_reader(reader: &mut R) -> Result { - <$type>::new(BorshDeserialize::deserialize_reader(reader)?) + <$type>::new(<$repr as BorshDeserialize>::deserialize_reader(reader)?) .ok_or_else(|| Error::new(ErrorKind::InvalidData, ERROR_INVALID_ZERO_VALUE)) } } }; } -impl_for_nonzero_integer!(core::num::NonZeroI8); -impl_for_nonzero_integer!(core::num::NonZeroI16); -impl_for_nonzero_integer!(core::num::NonZeroI32); -impl_for_nonzero_integer!(core::num::NonZeroI64); -impl_for_nonzero_integer!(core::num::NonZeroI128); -impl_for_nonzero_integer!(core::num::NonZeroU8); -impl_for_nonzero_integer!(core::num::NonZeroU16); -impl_for_nonzero_integer!(core::num::NonZeroU32); -impl_for_nonzero_integer!(core::num::NonZeroU64); -impl_for_nonzero_integer!(core::num::NonZeroU128); -impl_for_nonzero_integer!(core::num::NonZeroUsize); - -impl BorshDeserialize for isize { - fn deserialize_reader(reader: &mut R) -> Result { - let i: i64 = BorshDeserialize::deserialize_reader(reader)?; - let i = isize::try_from(i).map_err(|_| { - Error::new( - ErrorKind::InvalidData, - ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_ISIZE, - ) - })?; - Ok(i) - } +impl_for_nonzero_integer!(core::num::NonZeroI8, i8); +impl_for_nonzero_integer!(core::num::NonZeroI16, i16); +impl_for_nonzero_integer!(core::num::NonZeroI32, i32); +impl_for_nonzero_integer!(core::num::NonZeroI64, i64); +impl_for_nonzero_integer!(core::num::NonZeroI128, i128); +impl_for_nonzero_integer!(core::num::NonZeroIsize, isize); +impl_for_nonzero_integer!(core::num::NonZeroU8, u8); +impl_for_nonzero_integer!(core::num::NonZeroU16, u16); +impl_for_nonzero_integer!(core::num::NonZeroU32, u32); +impl_for_nonzero_integer!(core::num::NonZeroU64, u64); +impl_for_nonzero_integer!(core::num::NonZeroU128, u128); +impl_for_nonzero_integer!(core::num::NonZeroUsize, usize); + +macro_rules! impl_async_for_nonzero_integer { + ($type: ty, $repr: ty) => { + #[cfg(feature = "unstable__async")] + impl BorshDeserializeAsync for $type { + #[inline] + async fn deserialize_reader(reader: &mut R) -> Result { + <$type>::new(<$repr as BorshDeserializeAsync>::deserialize_reader(reader).await?) + .ok_or_else(|| Error::new(ErrorKind::InvalidData, ERROR_INVALID_ZERO_VALUE)) + } + } + }; } -impl BorshDeserialize for usize { - fn deserialize_reader(reader: &mut R) -> Result { - let u: u64 = BorshDeserialize::deserialize_reader(reader)?; - let u = usize::try_from(u).map_err(|_| { - Error::new( - ErrorKind::InvalidData, - ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_USIZE, - ) - })?; - Ok(u) - } +impl_async_for_nonzero_integer!(core::num::NonZeroI8, i8); +impl_async_for_nonzero_integer!(core::num::NonZeroI16, i16); +impl_async_for_nonzero_integer!(core::num::NonZeroI32, i32); +impl_async_for_nonzero_integer!(core::num::NonZeroI64, i64); +impl_async_for_nonzero_integer!(core::num::NonZeroI128, i128); +impl_async_for_nonzero_integer!(core::num::NonZeroIsize, isize); +impl_async_for_nonzero_integer!(core::num::NonZeroU8, u8); +impl_async_for_nonzero_integer!(core::num::NonZeroU16, u16); +impl_async_for_nonzero_integer!(core::num::NonZeroU32, u32); +impl_async_for_nonzero_integer!(core::num::NonZeroU64, u64); +impl_async_for_nonzero_integer!(core::num::NonZeroU128, u128); +impl_async_for_nonzero_integer!(core::num::NonZeroUsize, usize); + +macro_rules! impl_for_size_integer { + ($type: ty: $temp_type: ty, $msg: expr) => { + impl BorshDeserialize for $type { + #[inline] + fn deserialize_reader(reader: &mut R) -> Result { + let i = <$temp_type as BorshDeserialize>::deserialize_reader(reader)?; + let i = + <$type>::try_from(i).map_err(|_| Error::new(ErrorKind::InvalidData, $msg))?; + Ok(i) + } + } + }; +} + +impl_for_size_integer!(isize: i64, ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_ISIZE); +impl_for_size_integer!(usize: u64, ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_USIZE); + +macro_rules! impl_async_for_size_integer { + ($type: ty: $temp_type: ty, $msg: expr) => { + #[cfg(feature = "unstable__async")] + impl BorshDeserializeAsync for $type { + #[inline] + async fn deserialize_reader(reader: &mut R) -> Result { + let i = <$temp_type as BorshDeserializeAsync>::deserialize_reader(reader).await?; + let i = + <$type>::try_from(i).map_err(|_| Error::new(ErrorKind::InvalidData, $msg))?; + Ok(i) + } + } + }; } +impl_async_for_size_integer!(isize: i64, ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_ISIZE); +impl_async_for_size_integer!(usize: u64, ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_USIZE); + // Note NaNs have a portability issue. Specifically, signalling NaNs on MIPS are quiet NaNs on x86, -// and vice-versa. We disallow NaNs to avoid this issue. +// and vice versa. We disallow NaNs to avoid this issue. macro_rules! impl_for_float { ($type: ident, $int_type: ident) => { impl BorshDeserialize for $type { #[inline] fn deserialize_reader(reader: &mut R) -> Result { - let mut buf = [0u8; size_of::<$type>()]; - reader - .read_exact(&mut buf) - .map_err(unexpected_eof_to_unexpected_length_of_input)?; - let res = $type::from_bits($int_type::from_le_bytes(buf.try_into().unwrap())); + let res = { + let mut buf = [0u8; size_of::<$type>()]; + reader + .read_exact(&mut buf) + .map_err(unexpected_eof_to_unexpected_length_of_input)?; + $type::from_bits($int_type::from_le_bytes(buf.try_into().unwrap())) + }; if res.is_nan() { return Err(Error::new( ErrorKind::InvalidData, @@ -297,124 +495,276 @@ macro_rules! impl_for_float { impl_for_float!(f32, u32); impl_for_float!(f64, u64); +// Note NaNs have a portability issue. Specifically, signalling NaNs on MIPS are quiet NaNs on x86, +// and vice versa. We disallow NaNs to avoid this issue. +macro_rules! impl_async_for_float { + ($type: ident, $method: ident) => { + #[cfg(feature = "unstable__async")] + impl BorshDeserializeAsync for $type { + #[inline] + async fn deserialize_reader(reader: &mut R) -> Result { + let res = reader.$method().await?; + if res.is_nan() { + return Err(Error::new( + ErrorKind::InvalidData, + "For portability reasons we do not allow to deserialize NaNs.", + )); + } + Ok(res) + } + } + }; +} + +impl_async_for_float!(f32, read_f32); +impl_async_for_float!(f64, read_f64); + +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for bool { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let b: u8 = BorshDeserialize::deserialize_reader(reader)?; - if b == 0 { - Ok(false) - } else if b == 1 { - Ok(true) + let b = if _sync { + ::deserialize_reader(reader)? } else { - let msg = format!("Invalid bool representation: {}", b); - - Err(Error::new(ErrorKind::InvalidData, msg)) + ::deserialize_reader(reader).await? + }; + match b { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(Error::new( + ErrorKind::InvalidData, + format!("Invalid bool representation: {}", b), + )), } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, +)] impl BorshDeserialize for Option where T: BorshDeserialize, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let flag: u8 = BorshDeserialize::deserialize_reader(reader)?; - if flag == 0 { - Ok(None) - } else if flag == 1 { - Ok(Some(T::deserialize_reader(reader)?)) + let flag = if _sync { + ::deserialize_reader(reader)? } else { - let msg = format!( - "Invalid Option representation: {}. The first byte must be 0 or 1", - flag - ); - - Err(Error::new(ErrorKind::InvalidData, msg)) + ::deserialize_reader(reader).await? + }; + match flag { + 0 => Ok(None), + 1 => { + if _sync { + T::deserialize_reader(reader).map(Some) + } else { + T::deserialize_reader(reader).await.map(Some) + } + } + _ => Err(Error::new( + ErrorKind::InvalidData, + format!( + "Invalid Option representation: {}. The first byte must be 0 or 1", + flag + ), + )), } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, + E: BorshDeserializeAsync, +)] impl BorshDeserialize for core::result::Result where T: BorshDeserialize, E: BorshDeserialize, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let flag: u8 = BorshDeserialize::deserialize_reader(reader)?; - if flag == 0 { - Ok(Err(E::deserialize_reader(reader)?)) - } else if flag == 1 { - Ok(Ok(T::deserialize_reader(reader)?)) + let flag = if _sync { + ::deserialize_reader(reader)? } else { - let msg = format!( - "Invalid Result representation: {}. The first byte must be 0 or 1", - flag - ); - - Err(Error::new(ErrorKind::InvalidData, msg)) + ::deserialize_reader(reader).await? + }; + match flag { + 0 => { + if _sync { + E::deserialize_reader(reader).map(Err) + } else { + E::deserialize_reader(reader).await.map(Err) + } + } + 1 => { + if _sync { + T::deserialize_reader(reader).map(Ok) + } else { + T::deserialize_reader(reader).await.map(Ok) + } + } + _ => Err(Error::new( + ErrorKind::InvalidData, + format!( + "Invalid Result representation: {}. The first byte must be 0 or 1", + flag + ), + )), } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for String { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - String::from_utf8(Vec::::deserialize_reader(reader)?).map_err(|err| { - let msg = err.to_string(); - Error::new(ErrorKind::InvalidData, msg) + String::from_utf8(if _sync { + as BorshDeserialize>::deserialize_reader(reader)? + } else { + as BorshDeserializeAsync>::deserialize_reader(reader).await? + }) + .map_err(|err| { + #[cfg(feature = "std")] + { + Error::new(ErrorKind::InvalidData, err) + } + #[cfg(not(feature = "std"))] + { + use crate::__private::maybestd::string::ToString; + Error::new(ErrorKind::InvalidData, err.to_string()) + } }) } } /// Module is available if borsh is built with `features = ["ascii"]`. +/// +/// Module defines [`BorshDeserialize`] +#[cfg_attr(feature = "unstable__async", doc = " and [`BorshDeserializeAsync`]")] +/// implementation for some types from [`ascii`](::ascii) crate. #[cfg(feature = "ascii")] pub mod ascii { - //! - //! Module defines [BorshDeserialize] implementation for - //! some types from [ascii](::ascii) crate. - use crate::BorshDeserialize; - use crate::__private::maybestd::{string::ToString, vec::Vec}; - use crate::io::{Error, ErrorKind, Read, Result}; + use async_generic::async_generic; + + use super::BorshDeserialize; + #[cfg(feature = "unstable__async")] + use super::{AsyncRead, BorshDeserializeAsync}; + use crate::{ + __private::maybestd::vec::Vec, + io::{Error, ErrorKind, Read, Result}, + }; + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshDeserialize for ascii::AsciiString { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let bytes = Vec::::deserialize_reader(reader)?; - ascii::AsciiString::from_ascii(bytes) - .map_err(|err| Error::new(ErrorKind::InvalidData, err.to_string())) + let bytes = if _sync { + as BorshDeserialize>::deserialize_reader(reader)? + } else { + as BorshDeserializeAsync>::deserialize_reader(reader).await? + }; + ascii::AsciiString::from_ascii(bytes).map_err(|err| { + #[cfg(feature = "std")] + { + Error::new(ErrorKind::InvalidData, err) + } + #[cfg(not(feature = "std"))] + { + use crate::__private::maybestd::string::ToString; + Error::new(ErrorKind::InvalidData, err.to_string()) + } + }) } } + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshDeserialize for ascii::AsciiChar { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let byte = u8::deserialize_reader(reader)?; - ascii::AsciiChar::from_ascii(byte) - .map_err(|err| Error::new(ErrorKind::InvalidData, err.to_string())) + let byte = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; + ascii::AsciiChar::from_ascii(byte).map_err(|err| { + #[cfg(feature = "std")] + { + Error::new(ErrorKind::InvalidData, err) + } + #[cfg(not(feature = "std"))] + { + use crate::__private::maybestd::string::ToString; + Error::new(ErrorKind::InvalidData, err.to_string()) + } + }) } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, +)] impl BorshDeserialize for Vec where T: BorshDeserialize, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; - let len = u32::deserialize_reader(reader)?; + let len = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; if len == 0 { Ok(Vec::new()) - } else if let Some(vec_bytes) = T::vec_from_reader(len, reader)? { + } else if let Some(vec_bytes) = { + if _sync { + T::vec_from_reader(len, reader)? + } else { + T::vec_from_reader(len, reader).await? + } + } { Ok(vec_bytes) } else { // TODO(16): return capacity allocation when we can safely do that. let mut result = Vec::with_capacity(hint::cautious::(len)); for _ in 0..len { - result.push(T::deserialize_reader(reader)?); + result.push(if _sync { + T::deserialize_reader(reader)? + } else { + T::deserialize_reader(reader).await? + }); } Ok(result) } @@ -422,105 +772,190 @@ where } #[cfg(feature = "bytes")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for bytes::Bytes { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let vec = >::deserialize_reader(reader)?; + let vec = if _sync { + as BorshDeserialize>::deserialize_reader(reader)? + } else { + as BorshDeserializeAsync>::deserialize_reader(reader).await? + }; Ok(vec.into()) } } #[cfg(feature = "bytes")] -impl BorshDeserialize for bytes::BytesMut { +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] +impl BorshDeserialize for BytesMut { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let len = u32::deserialize_reader(reader)?; + let len = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; let mut out = BytesMut::with_capacity(hint::cautious::(len)); for _ in 0..len { - out.put_u8(u8::deserialize_reader(reader)?); + out.put_u8(if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }); } Ok(out) } } #[cfg(feature = "bson")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for bson::oid::ObjectId { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; 12]; - reader.read_exact(&mut buf)?; + if _sync { + reader.read_exact(&mut buf)? + } else { + reader.read_exact(&mut buf).await? + }; Ok(bson::oid::ObjectId::from_bytes(buf)) } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: ToOwned + ?Sized, + T::Owned: BorshDeserializeAsync, + for<'a> &'a T: Send, +)] impl BorshDeserialize for Cow<'_, T> where T: ToOwned + ?Sized, T::Owned: BorshDeserialize, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - Ok(Cow::Owned(BorshDeserialize::deserialize_reader(reader)?)) + Ok(Cow::Owned(if _sync { + T::Owned::deserialize_reader(reader)? + } else { + T::Owned::deserialize_reader(reader).await? + })) } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, +)] impl BorshDeserialize for VecDeque where T: BorshDeserialize, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let vec = >::deserialize_reader(reader)?; - Ok(vec.into()) + if _sync { + >::deserialize_reader(reader).map(Into::into) + } else { + >::deserialize_reader(reader).await.map(Into::into) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, +)] impl BorshDeserialize for LinkedList where T: BorshDeserialize, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let vec = >::deserialize_reader(reader)?; + let vec = if _sync { + >::deserialize_reader(reader)? + } else { + >::deserialize_reader(reader).await? + }; Ok(vec.into_iter().collect::>()) } } /// Module is available if borsh is built with `features = ["std"]` or `features = ["hashbrown"]`. /// -/// Module defines [BorshDeserialize] implementation for -/// [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet). +/// Module defines [`BorshDeserialize`] +#[cfg_attr(feature = "unstable__async", doc = " and [`BorshDeserializeAsync`]")] +/// implementation for [`HashMap`](std::collections::HashMap)/[`HashSet`](std::collections::HashSet). #[cfg(hash_collections)] pub mod hashes { use core::hash::{BuildHasher, Hash}; - use crate::BorshDeserialize; - use crate::__private::maybestd::collections::{HashMap, HashSet}; - use crate::__private::maybestd::vec::Vec; - use crate::io::{Read, Result}; + use async_generic::async_generic; - #[cfg(feature = "de_strict_order")] - const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order"; - use crate::error::check_zst; + use super::BorshDeserialize; + #[cfg(feature = "unstable__async")] + use super::{AsyncRead, BorshDeserializeAsync}; #[cfg(feature = "de_strict_order")] use crate::io::{Error, ErrorKind}; + use crate::{ + __private::maybestd::{ + collections::{HashMap, HashSet}, + vec::Vec, + }, + error::check_zst, + io::{Read, Result}, + }; + #[cfg(feature = "de_strict_order")] + const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order"; + + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync + Eq + Hash + Ord, + H: BuildHasher + Default + Send, + )] impl BorshDeserialize for HashSet where T: BorshDeserialize + Eq + Hash + Ord, H: BuildHasher + Default, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the elements // which may fail `cmp()` checks // NOTE: deserialize first to `Vec` is faster - let vec = >::deserialize_reader(reader)?; + let vec = if _sync { + >::deserialize_reader(reader)? + } else { + >::deserialize_reader(reader).await? + }; #[cfg(feature = "de_strict_order")] - // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 - // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 + // TODO: replace with `is_sorted` api once MSRV >= 1.82 for pair in vec.windows(2) { let [a, b] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); @@ -538,6 +973,14 @@ pub mod hashes { } } + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + K: BorshDeserializeAsync + Eq + Hash + Ord, + V: BorshDeserializeAsync, + H: BuildHasher + Default + Send, + )] impl BorshDeserialize for HashMap where K: BorshDeserialize + Eq + Hash + Ord, @@ -545,17 +988,21 @@ pub mod hashes { H: BuildHasher + Default, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the entries // which may fail `cmp()` checks // NOTE: deserialize first to `Vec<(K, V)>` is faster - let vec = >::deserialize_reader(reader)?; + let vec = if _sync { + >::deserialize_reader(reader)? + } else { + >::deserialize_reader(reader).await? + }; #[cfg(feature = "de_strict_order")] - // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 - // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 + // TODO: replace with `is_sorted` api once MSRV >= 1.82 for pair in vec.windows(2) { let [(a_k, _a_v), (b_k, _b_v)] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); @@ -574,21 +1021,31 @@ pub mod hashes { } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync + Ord, +)] impl BorshDeserialize for BTreeSet where T: BorshDeserialize + Ord, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the elements // which may fail `cmp()` checks // NOTE: deserialize first to `Vec` is faster - let vec = >::deserialize_reader(reader)?; + let vec = if _sync { + >::deserialize_reader(reader)? + } else { + >::deserialize_reader(reader).await? + }; #[cfg(feature = "de_strict_order")] - // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 - // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 + // TODO: replace with `is_sorted` api once MSRV >= 1.82 for pair in vec.windows(2) { let [a, b] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); @@ -607,23 +1064,34 @@ where } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + K: BorshDeserializeAsync + Ord, + V: BorshDeserializeAsync, +)] impl BorshDeserialize for BTreeMap where K: BorshDeserialize + Ord, V: BorshDeserialize, { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the entries // which may fail `cmp()` checks // NOTE: deserialize first to `Vec<(K, V)>` is faster - let vec = >::deserialize_reader(reader)?; + let vec = if _sync { + >::deserialize_reader(reader)? + } else { + >::deserialize_reader(reader).await? + }; #[cfg(feature = "de_strict_order")] - // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 - // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 + // TODO: replace with `is_sorted` api once MSRV >= 1.82 for pair in vec.windows(2) { let [(a_k, _a_v), (b_k, _b_v)] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); @@ -644,13 +1112,32 @@ where } #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for std::net::SocketAddr { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let kind = u8::deserialize_reader(reader)?; + let kind = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; match kind { - 0 => std::net::SocketAddrV4::deserialize_reader(reader).map(std::net::SocketAddr::V4), - 1 => std::net::SocketAddrV6::deserialize_reader(reader).map(std::net::SocketAddr::V6), + 0 => if _sync { + ::deserialize_reader(reader) + } else { + ::deserialize_reader(reader).await + } + .map(std::net::SocketAddr::V4), + 1 => if _sync { + ::deserialize_reader(reader) + } else { + ::deserialize_reader(reader).await + } + .map(std::net::SocketAddr::V6), value => Err(Error::new( ErrorKind::InvalidData, format!("Invalid SocketAddr variant: {}", value), @@ -660,21 +1147,32 @@ impl BorshDeserialize for std::net::SocketAddr { } #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for std::net::IpAddr { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let kind = u8::deserialize_reader(reader)?; + let kind = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; match kind { - 0u8 => { - // Deserialize an Ipv4Addr and convert it to IpAddr::V4 - let ipv4_addr = std::net::Ipv4Addr::deserialize_reader(reader)?; - Ok(std::net::IpAddr::V4(ipv4_addr)) + 0 => if _sync { + ::deserialize_reader(reader) + } else { + ::deserialize_reader(reader).await } - 1u8 => { - // Deserialize an Ipv6Addr and convert it to IpAddr::V6 - let ipv6_addr = std::net::Ipv6Addr::deserialize_reader(reader)?; - Ok(std::net::IpAddr::V6(ipv6_addr)) + .map(std::net::IpAddr::V4), + 1 => if _sync { + ::deserialize_reader(reader) + } else { + ::deserialize_reader(reader).await } + .map(std::net::IpAddr::V6), value => Err(Error::new( ErrorKind::InvalidData, format!("Invalid IpAddr variant: {}", value), @@ -684,57 +1182,114 @@ impl BorshDeserialize for std::net::IpAddr { } #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for std::net::SocketAddrV4 { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let ip = std::net::Ipv4Addr::deserialize_reader(reader)?; - let port = u16::deserialize_reader(reader)?; + let ip = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; + let port = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; Ok(std::net::SocketAddrV4::new(ip, port)) } } #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for std::net::SocketAddrV6 { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let ip = std::net::Ipv6Addr::deserialize_reader(reader)?; - let port = u16::deserialize_reader(reader)?; + let ip = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; + let port = if _sync { + ::deserialize_reader(reader)? + } else { + ::deserialize_reader(reader).await? + }; Ok(std::net::SocketAddrV6::new(ip, port, 0, 0)) } } #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for std::net::Ipv4Addr { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; 4]; - reader - .read_exact(&mut buf) - .map_err(unexpected_eof_to_unexpected_length_of_input)?; + if _sync { + reader.read_exact(&mut buf) + } else { + reader.read_exact(&mut buf).await + } + .map_err(unexpected_eof_to_unexpected_length_of_input)?; Ok(std::net::Ipv4Addr::from(buf)) } } #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for std::net::Ipv6Addr { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; 16]; - reader - .read_exact(&mut buf) - .map_err(unexpected_eof_to_unexpected_length_of_input)?; + if _sync { + reader.read_exact(&mut buf) + } else { + reader.read_exact(&mut buf).await + } + .map_err(unexpected_eof_to_unexpected_length_of_input)?; Ok(std::net::Ipv6Addr::from(buf)) } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + U: Into> + Borrow, + T: ToOwned + ?Sized + Send, + T::Owned: BorshDeserializeAsync, +)] impl BorshDeserialize for Box where U: Into> + Borrow, T: ToOwned + ?Sized, T::Owned: BorshDeserialize, { + #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - Ok(T::Owned::deserialize_reader(reader)?.into()) + if _sync { + T::Owned::deserialize_reader(reader) + } else { + T::Owned::deserialize_reader(reader).await + } + .map(Into::into) } } @@ -781,63 +1336,78 @@ where } if let Some(arr) = T::array_from_reader(reader)? { - Ok(arr) - } else { - let mut result = ArrayDropGuard { - buffer: unsafe { MaybeUninit::uninit().assume_init() }, - init_count: 0, - }; + return Ok(arr); + } + let mut result = ArrayDropGuard { + buffer: unsafe { MaybeUninit::uninit().assume_init() }, + init_count: 0, + }; - result.fill_buffer(|| T::deserialize_reader(reader))?; + result.fill_buffer(|| T::deserialize_reader(reader))?; - // SAFETY: The elements up to `i` have been initialized in `fill_buffer`. - Ok(unsafe { result.transmute_to_array() }) - } + // SAFETY: The elements up to `i` have been initialized in `fill_buffer`. + Ok(unsafe { result.transmute_to_array() }) } } -#[test] -fn array_deserialization_doesnt_leak() { - use core::sync::atomic::{AtomicUsize, Ordering}; - - static DESERIALIZE_COUNT: AtomicUsize = AtomicUsize::new(0); - static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); - - #[allow(unused)] - struct MyType(u8); - impl BorshDeserialize for MyType { - fn deserialize_reader(reader: &mut R) -> Result { - let val = u8::deserialize_reader(reader)?; - let v = DESERIALIZE_COUNT.fetch_add(1, Ordering::SeqCst); - if v >= 7 { - panic!("panic in deserialize"); +#[cfg(feature = "unstable__async")] +impl BorshDeserializeAsync for [T; N] +where + T: BorshDeserializeAsync, +{ + #[inline] + async fn deserialize_reader(reader: &mut R) -> Result { + struct ArrayDropGuard<'r, T: BorshDeserializeAsync, const N: usize, R: AsyncRead> { + buffer: [MaybeUninit; N], + init_count: usize, + reader: &'r mut R, + } + impl Drop for ArrayDropGuard<'_, T, N, R> { + fn drop(&mut self) { + let init_range = &mut self.buffer[..self.init_count]; + // SAFETY: Elements up to self.init_count have been initialized. Assumes this value + // is only incremented in `fill_buffer`, which writes the element before + // increasing the init_count. + unsafe { + core::ptr::drop_in_place(init_range as *mut _ as *mut [T]); + }; } - Ok(MyType(val)) } - } - impl Drop for MyType { - fn drop(&mut self) { - DROP_COUNT.fetch_add(1, Ordering::SeqCst); + + impl ArrayDropGuard<'_, T, N, R> { + unsafe fn transmute_to_array(mut self) -> [T; N] { + debug_assert_eq!(self.init_count, N); + // Set init_count to 0 so that the values do not get dropped twice. + self.init_count = 0; + // SAFETY: This cast is required because `mem::transmute` does not work with + // const generics https://github.com/rust-lang/rust/issues/61956. This + // array is guaranteed to be initialized by this point. + core::ptr::read(&self.buffer as *const _ as *const [T; N]) + } + async fn fill_buffer(&mut self) -> Result<()> { + // TODO: replace with `core::array::try_from_fn` when stabilized to avoid manually + // dropping uninitialized values through the guard drop. + for elem in self.buffer.iter_mut() { + elem.write(T::deserialize_reader(self.reader).await?); + self.init_count += 1; + } + Ok(()) + } } - } - assert!(<[MyType; 5] as BorshDeserialize>::deserialize(&mut &[0u8; 3][..]).is_err()); - assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 3); - assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3); + if let Some(arr) = T::array_from_reader(reader).await? { + return Ok(arr); + } + let mut result = ArrayDropGuard { + buffer: unsafe { MaybeUninit::uninit().assume_init() }, + init_count: 0, + reader, + }; - assert!(<[MyType; 2] as BorshDeserialize>::deserialize(&mut &[0u8; 2][..]).is_ok()); - assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 5); - assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 5); + result.fill_buffer().await?; - #[cfg(feature = "std")] - { - // Test that during a panic in deserialize, the values are still dropped. - let result = std::panic::catch_unwind(|| { - <[MyType; 3] as BorshDeserialize>::deserialize(&mut &[0u8; 3][..]).unwrap(); - }); - assert!(result.is_err()); - assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 8); - assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 7); // 5 because 6 panicked and was not init + // SAFETY: The elements up to `i` have been initialized in `fill_buffer`. + Ok(unsafe { result.transmute_to_array() }) } } @@ -845,22 +1415,22 @@ macro_rules! impl_tuple { (@unit $name:ty) => { impl BorshDeserialize for $name { #[inline] - fn deserialize_reader(_reader: &mut R) -> Result { + fn deserialize_reader(_: &mut R) -> Result { Ok(<$name>::default()) } } }; ($($name:ident)+) => { - impl<$($name),+> BorshDeserialize for ($($name,)+) - where $($name: BorshDeserialize,)+ - { - #[inline] - fn deserialize_reader(reader: &mut R) -> Result { - - Ok(($($name::deserialize_reader(reader)?,)+)) + impl<$($name),+> BorshDeserialize for ($($name,)+) + where + $($name: BorshDeserialize,)+ + { + #[inline] + fn deserialize_reader(reader: &mut R) -> Result { + Ok(($(<$name as BorshDeserialize>::deserialize_reader(reader)?,)+)) + } } - } }; } @@ -888,33 +1458,100 @@ impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19); +macro_rules! impl_async_tuple { + (@unit $name:ty) => { + #[cfg(feature = "unstable__async")] + impl BorshDeserializeAsync for $name { + #[inline] + fn deserialize_reader(_: &mut R) -> impl Future> + Send { + core::future::ready(Ok(<$name>::default())) + } + } + }; + + ($($name:ident)+) => { + #[cfg(feature = "unstable__async")] + impl<$($name),+> BorshDeserializeAsync for ($($name,)+) + where + $($name: BorshDeserializeAsync,)+ + { + #[inline] + async fn deserialize_reader(reader: &mut R) -> Result { + Ok(($(<$name as BorshDeserializeAsync>::deserialize_reader(reader).await?,)+)) + } + } + }; +} + +impl_async_tuple!(@unit ()); +impl_async_tuple!(@unit core::ops::RangeFull); + +impl_async_tuple!(T0); +impl_async_tuple!(T0 T1); +impl_async_tuple!(T0 T1 T2); +impl_async_tuple!(T0 T1 T2 T3); +impl_async_tuple!(T0 T1 T2 T3 T4); +impl_async_tuple!(T0 T1 T2 T3 T4 T5); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18); +impl_async_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19); + macro_rules! impl_range { - ($type:ident, $make:expr, $($side:ident),*) => { + ($type:ident, $make:expr, $n: literal, $($side:ident),*) => { + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, + )] impl BorshDeserialize for core::ops::$type { #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - let ($($side,)*) = <_>::deserialize_reader(reader)?; + let [$($side,)*] = if _sync { + <[T; $n]>::deserialize_reader(reader)? + } else { + <[T; $n]>::deserialize_reader(reader).await? + }; Ok($make) } } }; } -impl_range!(Range, start..end, start, end); -impl_range!(RangeInclusive, start..=end, start, end); -impl_range!(RangeFrom, start.., start); -impl_range!(RangeTo, ..end, end); -impl_range!(RangeToInclusive, ..=end, end); +impl_range!(Range, start..end, 2, start, end); +impl_range!(RangeInclusive, start..=end, 2, start, end); +impl_range!(RangeFrom, start.., 1, start); +impl_range!(RangeTo, ..end, 1, end); +impl_range!(RangeToInclusive, ..=end, 1, end); /// Module is available if borsh is built with `features = ["rc"]`. +/// +/// Module defines [`BorshDeserialize`] +#[cfg_attr(feature = "unstable__async", doc = " and [`BorshDeserializeAsync`]")] +/// implementation for [`alloc::rc::Rc`](std::rc::Rc) and [`alloc::sync::Arc`](std::sync::Arc). #[cfg(feature = "rc")] pub mod rc { - //! - //! Module defines [BorshDeserialize] implementation for - //! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc). - use crate::__private::maybestd::{boxed::Box, rc::Rc, sync::Arc}; - use crate::io::{Read, Result}; - use crate::BorshDeserialize; + use async_generic::async_generic; + + use super::BorshDeserialize; + #[cfg(feature = "unstable__async")] + use super::{AsyncRead, BorshDeserializeAsync}; + use crate::{ + __private::maybestd::{boxed::Box, rc::Rc, sync::Arc}, + io::{Read, Result}, + }; /// This impl requires the [`"rc"`] Cargo feature of borsh. /// @@ -925,8 +1562,9 @@ pub mod rc { where Box: BorshDeserialize, { + #[inline] fn deserialize_reader(reader: &mut R) -> Result { - Ok(Box::::deserialize_reader(reader)?.into()) + >::deserialize_reader(reader).map(Into::into) } } @@ -935,37 +1573,85 @@ pub mod rc { /// Deserializing a data structure containing `Arc` will not attempt to /// deduplicate `Arc` references to the same data. Every deserialized `Arc` /// will end up with a strong count of 1. + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + Box: BorshDeserializeAsync, + T: ?Sized + Sync + Send, + )] impl BorshDeserialize for Arc where Box: BorshDeserialize, { + #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - Ok(Box::::deserialize_reader(reader)?.into()) + if _sync { + >::deserialize_reader(reader) + } else { + >::deserialize_reader(reader).await + } + .map(Into::into) } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshDeserialize for PhantomData { + #[inline] + #[async_generic( + async_signature[ready](_: &mut R) -> impl Future> + Send + )] fn deserialize_reader(_: &mut R) -> Result { Ok(PhantomData) } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, +)] impl BorshDeserialize for core::cell::Cell where - T: BorshDeserialize + Copy, + T: BorshDeserialize, { + #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - ::deserialize_reader(reader).map(core::cell::Cell::new) + if _sync { + T::deserialize_reader(reader) + } else { + T::deserialize_reader(reader).await + } + .map(core::cell::Cell::new) } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshDeserializeAsync, +)] impl BorshDeserialize for core::cell::RefCell where T: BorshDeserialize, { + #[inline] + #[async_generic(async_signature(reader: &mut R) -> Result)] fn deserialize_reader(reader: &mut R) -> Result { - ::deserialize_reader(reader).map(core::cell::RefCell::new) + if _sync { + T::deserialize_reader(reader) + } else { + T::deserialize_reader(reader).await + } + .map(core::cell::RefCell::new) } } @@ -1001,41 +1687,182 @@ pub fn from_slice(v: &[u8]) -> Result { let mut v_mut = v; let object = T::deserialize(&mut v_mut)?; if !v_mut.is_empty() { - return Err(Error::new( - ErrorKind::InvalidData, - crate::de::ERROR_NOT_ALL_BYTES_READ, - )); + return Err(Error::new(ErrorKind::InvalidData, ERROR_NOT_ALL_BYTES_READ)); } Ok(object) } /// Deserializes an object from a reader. /// # Example -/// ``` -/// use borsh::{BorshDeserialize, BorshSerialize, from_reader, to_vec}; -/// -/// /// derive is only available if borsh is built with `features = ["derive"]` -/// # #[cfg(feature = "derive")] -/// #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] -/// struct MyStruct { -/// a: u64, -/// b: Vec, -/// } -/// -/// # #[cfg(feature = "derive")] -/// let original = MyStruct { a: 10, b: vec![1, 2, 3] }; -/// # #[cfg(feature = "derive")] -/// let encoded = to_vec(&original).unwrap(); -/// # #[cfg(feature = "derive")] -/// let decoded = from_reader::<_, MyStruct>(&mut encoded.as_slice()).unwrap(); -/// # #[cfg(feature = "derive")] -/// assert_eq!(original, decoded); -/// ``` +#[async_generic( + /// ``` + /// use borsh::{BorshDeserialize, BorshSerialize, from_reader, to_vec}; + /// + /// /// derive is only available if borsh is built with `features = ["derive"]` + /// # #[cfg(feature = "derive")] + /// #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] + /// struct MyStruct { + /// a: u64, + /// b: Vec, + /// } + /// + /// # #[cfg(feature = "derive")] + /// let original = MyStruct { a: 10, b: vec![1, 2, 3] }; + /// # #[cfg(feature = "derive")] + /// let encoded = to_vec(&original).unwrap(); + /// # #[cfg(feature = "derive")] + /// let decoded = from_reader::<_, MyStruct>(&mut encoded.as_slice()).unwrap(); + /// # #[cfg(feature = "derive")] + /// assert_eq!(original, decoded); + /// ``` + sync_signature; + + /// ``` + /// use borsh::{BorshDeserializeAsync, BorshSerialize, from_reader_async, to_vec}; + /// + /// /// derive is only available if borsh is built with `features = ["derive"]` + /// # #[cfg(feature = "derive")] + /// #[derive(BorshSerialize, BorshDeserializeAsync, PartialEq, Debug)] + /// struct MyStruct { + /// a: u64, + /// b: Vec, + /// } + /// + /// # tokio_test::block_on(async { + /// # #[cfg(feature = "derive")] + /// let original = MyStruct { a: 10, b: vec![1, 2, 3] }; + /// # #[cfg(feature = "derive")] + /// let encoded = to_vec(&original).unwrap(); + /// # #[cfg(feature = "derive")] + /// let decoded = from_reader_async::<_, MyStruct>(&mut encoded.as_slice()).await.unwrap(); + /// # #[cfg(feature = "derive")] + /// assert_eq!(original, decoded); + /// # }); + /// ``` + #[cfg(feature = "unstable__async")] + async_signature[impl_fut]( + reader: &mut R, + ) -> impl Future> + Send + Captures<&mut R> +)] pub fn from_reader(reader: &mut R) -> Result { - let result = T::deserialize_reader(reader)?; - let mut buf = [0u8; 1]; - match reader.read_exact(&mut buf) { - Err(f) if f.kind() == ErrorKind::UnexpectedEof => Ok(result), - _ => Err(Error::new(ErrorKind::InvalidData, ERROR_NOT_ALL_BYTES_READ)), + T::try_from_reader(reader) +} + +#[cfg(feature = "unstable__async")] +use captures::Captures; +#[cfg(feature = "unstable__async")] +pub mod captures { + /// This is a [trick](https://github.com/rust-lang/rfcs/blob/master/text/3498-lifetime-capture-rules-2024.md#the-captures-trick), + /// used to not over-restrict the lifetime and trait bounds of a RPIT. + /// + /// TODO: Once the MSRV is >=1.82, this should be removed and replaced with `use<>` notation for precise capturing. + pub trait Captures {} + + impl Captures for U {} +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn array_deserialization_doesnt_leak() { + use core::sync::atomic::{AtomicUsize, Ordering}; + + static DESERIALIZE_COUNT: AtomicUsize = AtomicUsize::new(0); + static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + + #[allow(unused)] + struct MyType(u8); + + impl BorshDeserialize for MyType { + fn deserialize_reader(reader: &mut R) -> Result { + let val = ::deserialize_reader(reader)?; + let v = DESERIALIZE_COUNT.fetch_add(1, Ordering::SeqCst); + if v >= 7 { + panic!("panic in deserialize"); + } + Ok(MyType(val)) + } + } + impl Drop for MyType { + fn drop(&mut self) { + DROP_COUNT.fetch_add(1, Ordering::SeqCst); + } + } + + assert!(<[MyType; 5] as BorshDeserialize>::deserialize(&mut &[0u8; 3][..]).is_err()); + assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 3); + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3); + + assert!(<[MyType; 2] as BorshDeserialize>::deserialize(&mut &[0u8; 2][..]).is_ok()); + assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 5); + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 5); + + #[cfg(feature = "std")] + { + // Test that during a panic in deserialize, the values are still dropped. + let result = std::panic::catch_unwind(|| { + <[MyType; 3] as BorshDeserialize>::deserialize(&mut &[0u8; 3][..]).unwrap(); + }); + assert!(result.is_err()); + assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 8); + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 7); // 5 because 6 panicked and was not init + } + } + + #[cfg(feature = "unstable__tokio")] + #[tokio::test] + async fn async_array_deserialization_doesnt_leak() { + use core::sync::atomic::{AtomicUsize, Ordering}; + + static DESERIALIZE_COUNT: AtomicUsize = AtomicUsize::new(0); + static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + + #[allow(unused)] + struct MyType(u8); + + impl BorshDeserializeAsync for MyType { + async fn deserialize_reader(reader: &mut R) -> Result { + let val = ::deserialize_reader(reader).await?; + let v = DESERIALIZE_COUNT.fetch_add(1, Ordering::SeqCst); + if v >= 7 { + panic!("panic in deserialize"); + } + Ok(MyType(val)) + } + } + impl Drop for MyType { + fn drop(&mut self) { + DROP_COUNT.fetch_add(1, Ordering::SeqCst); + } + } + + assert!( + <[MyType; 5] as BorshDeserializeAsync>::deserialize_reader(&mut &[0u8; 3][..]) + .await + .is_err() + ); + assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 3); + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3); + + assert!( + <[MyType; 2] as BorshDeserializeAsync>::deserialize_reader(&mut &[0u8; 2][..]) + .await + .is_ok() + ); + assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 5); + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 5); + + // Test that during a panic in deserialize, the values are still dropped. + let result = tokio::spawn(async { + <[MyType; 3] as BorshDeserializeAsync>::deserialize_reader(&mut &[0u8; 3][..]) + .await + .unwrap(); + }) + .await; + assert!(result.is_err_and(|err| err.is_panic())); + assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 8); + assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 7); // 5 because 6 panicked and was not init } } diff --git a/borsh/src/error.rs b/borsh/src/error.rs index 9afc88c09..615ff6edc 100644 --- a/borsh/src/error.rs +++ b/borsh/src/error.rs @@ -1,5 +1,7 @@ -use crate::io::{Error, ErrorKind, Result}; use core::mem::size_of; + +use crate::io::{Error, ErrorKind, Result}; + pub const ERROR_ZST_FORBIDDEN: &str = "Collections of zero-sized types are not allowed due to deny-of-service concerns on deserialization."; pub(crate) fn check_zst() -> Result<()> { diff --git a/borsh/src/generate_schema_schema.rs b/borsh/src/generate_schema_schema.rs index 647816318..f2baaa98c 100644 --- a/borsh/src/generate_schema_schema.rs +++ b/borsh/src/generate_schema_schema.rs @@ -1,9 +1,9 @@ //! Generate `BorshSchemaCointainer` for `BorshSchemaContainer` and save it into a file. #![cfg_attr(not(feature = "std"), no_std)] +use std::{fs::File, io::Write}; + use borsh::schema_container_of; -use std::fs::File; -use std::io::Write; fn main() { let container = schema_container_of::(); diff --git a/borsh/src/lib.rs b/borsh/src/lib.rs index ba4ce6501..84069bec2 100644 --- a/borsh/src/lib.rs +++ b/borsh/src/lib.rs @@ -4,17 +4,21 @@ #[cfg(not(feature = "std"))] extern crate alloc; -#[doc = include_str!("../docs/rustdoc_include/borsh_schema.md")] -#[cfg(feature = "unstable__schema")] -pub use borsh_derive::BorshSchema; - #[doc = include_str!("../docs/rustdoc_include/borsh_deserialize.md")] #[cfg(feature = "derive")] pub use borsh_derive::BorshDeserialize; - +// TODO: add docs +#[cfg(all(feature = "derive", feature = "unstable__async"))] +pub use borsh_derive::BorshDeserializeAsync; +#[doc = include_str!("../docs/rustdoc_include/borsh_schema.md")] +#[cfg(feature = "unstable__schema")] +pub use borsh_derive::BorshSchema; #[doc = include_str!("../docs/rustdoc_include/borsh_serialize.md")] #[cfg(feature = "derive")] pub use borsh_derive::BorshSerialize; +// TODO: add docs +#[cfg(all(feature = "derive", feature = "unstable__async"))] +pub use borsh_derive::BorshSerializeAsync; pub mod de; @@ -26,33 +30,51 @@ pub mod schema; pub(crate) mod schema_helpers; pub mod ser; -pub use de::BorshDeserialize; -pub use de::{from_reader, from_slice}; +pub use de::{from_reader, from_slice, BorshDeserialize}; +#[cfg(feature = "unstable__async")] +pub use de::{from_reader_async, BorshDeserializeAsync}; #[cfg(feature = "unstable__schema")] pub use schema::BorshSchema; #[cfg(feature = "unstable__schema")] pub use schema_helpers::{ max_serialized_size, schema_container_of, try_from_slice_with_schema, try_to_vec_with_schema, }; -pub use ser::helpers::{object_length, to_vec, to_writer}; -pub use ser::BorshSerialize; +#[cfg(feature = "unstable__async")] +pub use ser::{helpers::to_writer_async, BorshSerializeAsync}; +pub use ser::{ + helpers::{object_length, to_vec, to_writer}, + BorshSerialize, +}; pub mod error; #[cfg(all(feature = "std", feature = "hashbrown"))] compile_error!("feature \"std\" and feature \"hashbrown\" don't make sense at the same time"); +#[cfg(all(feature = "unstable__tokio", feature = "unstable__async-std"))] +compile_error!( + "Cannot enable both `unstable__tokio` and `unstable__async-std` features at the same time" +); + #[cfg(feature = "std")] use std::io as io_impl; + +/// Provides traits for async I/O operations. +/// +/// Module is available if `borsh` is built with `features = ["unstable__async"]`. +#[cfg(feature = "unstable__async")] +pub mod async_io; + #[cfg(not(feature = "std"))] mod nostd_io; + #[cfg(not(feature = "std"))] use nostd_io as io_impl; -/// Subset of `std::io` which is used as part of borsh public API. +/// Subset of `std::io` which is used as part of `borsh` public API. /// /// When crate is built with `std` feature disabled (it’s enabled by default), -/// the exported types are custom borsh types which try to mimic behaviour of -/// corresponding standard types usually offering subset of features. +/// the exported types are custom `borsh` types that try to mimic the behavior of +/// corresponding standard types, usually offering a subset of features. pub mod io { pub use super::io_impl::{Error, ErrorKind, Read, Result, Write}; } @@ -66,19 +88,18 @@ pub mod __private { #[cfg(feature = "std")] pub mod maybestd { pub use std::{borrow, boxed, collections, format, string, vec}; - #[cfg(feature = "rc")] pub use std::{rc, sync}; } #[cfg(not(feature = "std"))] pub mod maybestd { pub use alloc::{borrow, boxed, format, string, vec}; - #[cfg(feature = "rc")] pub use alloc::{rc, sync}; pub mod collections { pub use alloc::collections::{btree_map, BTreeMap, BTreeSet, LinkedList, VecDeque}; + #[cfg(feature = "hashbrown")] pub use hashbrown::*; } diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs index 49bee3824..6a1589e5a 100644 --- a/borsh/src/schema.rs +++ b/borsh/src/schema.rs @@ -6,27 +6,30 @@ //! on Rust side. //! //! The important components are: `BorshSchema` trait, `Definition` and `Declaration` types, and `BorshSchemaContainer` struct. -//! * `BorshSchema` trait allows any type that implements it to be self-descriptive, i.e. generate it's own schema; +//! * `BorshSchema` trait allows any type that implements it to be self-descriptive, i.e. generate its own schema; //! * `Declaration` is used to describe the type identifier, e.g. `HashMap`; //! * `Definition` is used to describe the structure of the type; //! * `BorshSchemaContainer` is used to store all declarations and definitions that are needed to work with a single type. #![allow(dead_code)] // Unclear why rust check complains on fields of `Definition` variants. -use crate as borsh; // For `#[derive(BorshSerialize, BorshDeserialize)]`. -use crate::__private::maybestd::{ - borrow, - boxed::Box, - collections::{btree_map::Entry, BTreeMap, BTreeSet, LinkedList, VecDeque}, - format, - string::{String, ToString}, - vec, - vec::Vec, + +use core::{borrow::Borrow, cmp::Ord, marker::PhantomData}; + +use crate as borsh; +// For `#[derive(BorshSerialize, BorshDeserialize, BorshSchema)]`. +use crate::{ + __private::maybestd::{ + borrow, + boxed::Box, + collections::{btree_map::Entry, BTreeMap, BTreeSet, LinkedList, VecDeque}, + format, + string::{String, ToString}, + vec, + vec::Vec, + }, + io::{Read, Result as IOResult, Write}, + BorshDeserialize, BorshSchema as BorshSchemaMacro, BorshSerialize, }; -use crate::io::{Read, Result as IOResult, Write}; -use crate::{BorshDeserialize, BorshSchema as BorshSchemaMacro, BorshSerialize}; -use core::borrow::Borrow; -use core::cmp::Ord; -use core::marker::PhantomData; mod container_ext; @@ -344,17 +347,18 @@ where T::declaration() } } + /// Module is available if borsh is built with `features = ["rc"]`. +/// +/// Module defines [BorshSchema] implementation for +/// [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc). #[cfg(feature = "rc")] pub mod rc { - //! - //! Module defines [BorshSchema] implementation for - //! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc). - use crate::BorshSchema; - use super::{Declaration, Definition}; - use crate::__private::maybestd::collections::BTreeMap; - use crate::__private::maybestd::{rc::Rc, sync::Arc}; + use crate::{ + BorshSchema, + __private::maybestd::{collections::BTreeMap, rc::Rc, sync::Arc}, + }; impl BorshSchema for Rc where @@ -482,15 +486,13 @@ impl BorshSchema for str { } /// Module is available if borsh is built with `features = ["ascii"]`. +/// +/// Module defines [BorshSchema] implementation for +/// some types from [ascii](::ascii) crate. #[cfg(feature = "ascii")] pub mod ascii { - //! - //! Module defines [BorshSchema] implementation for - //! some types from [ascii](::ascii) crate. - use crate::BorshSchema; - use super::{add_definition, Declaration, Definition}; - use crate::__private::maybestd::collections::BTreeMap; + use crate::{BorshSchema, __private::maybestd::collections::BTreeMap}; impl BorshSchema for ascii::AsciiString { #[inline] @@ -575,7 +577,6 @@ where T: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { - use core::convert::TryFrom; let length = u64::try_from(N).unwrap(); let definition = Definition::Sequence { length_width: Definition::ARRAY_LENGTH_WIDTH, @@ -687,15 +688,15 @@ where /// [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet). #[cfg(hash_collections)] pub mod hashes { - use crate::BorshSchema; - - use super::{add_definition, Declaration, Definition}; - use crate::__private::maybestd::collections::BTreeMap; - - use crate::__private::maybestd::collections::{HashMap, HashSet}; #[cfg(not(feature = "std"))] use alloc::format; + use super::{add_definition, Declaration, Definition}; + use crate::{ + BorshSchema, + __private::maybestd::collections::{BTreeMap, HashMap, HashSet}, + }; + // S is not serialized, so we ignore it in schema too // forcing S to be BorshSchema forces to define Definition // which must be empty, but if not - it will fail diff --git a/borsh/src/schema/container_ext.rs b/borsh/src/schema/container_ext.rs index ba1f95982..e348a9e09 100644 --- a/borsh/src/schema/container_ext.rs +++ b/borsh/src/schema/container_ext.rs @@ -1,8 +1,8 @@ -use super::{BorshSchemaContainer, Declaration, Definition, Fields}; - pub use max_size::Error as SchemaMaxSerializedSizeError; use max_size::{is_zero_size, ZeroSizeError}; pub use validate::Error as SchemaContainerValidateError; +use super::{BorshSchemaContainer, Declaration, Definition, Fields}; + mod max_size; mod validate; diff --git a/borsh/src/schema/container_ext/max_size.rs b/borsh/src/schema/container_ext/max_size.rs index 193f3be1e..3be5d3936 100644 --- a/borsh/src/schema/container_ext/max_size.rs +++ b/borsh/src/schema/container_ext/max_size.rs @@ -1,8 +1,8 @@ +use core::num::NonZeroUsize; + use super::{BorshSchemaContainer, Declaration, Definition, Fields}; use crate::__private::maybestd::{string::ToString, vec::Vec}; -use core::num::NonZeroUsize; - /// NonZeroUsize of value one. // TODO: Replace usage by NonZeroUsize::MIN once MSRV is 1.70+. const ONE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) }; @@ -13,7 +13,7 @@ impl BorshSchemaContainer { /// Even when if returned upper bound is correct, the theoretical value may be /// *much* larger than any practical length. For example, maximum encoded /// length of `String` is 4 GiB while in practice one may encounter strings of - /// at most dozen of characters. + /// at most a dozen of characters. /// /// # Example /// @@ -66,8 +66,6 @@ fn max_serialized_size_impl<'a>( schema: &'a BorshSchemaContainer, stack: &mut Vec<&'a str>, ) -> Result { - use core::convert::TryFrom; - /// Maximum number of elements in a vector or length of a string which can /// be serialised. const MAX_LEN: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(u32::MAX as usize) }; @@ -263,7 +261,6 @@ fn is_zero_size_impl<'a>( #[cfg(test)] mod tests { use super::*; - // this is not integration test module, so can use __private for ease of imports; // it cannot be made integration, as it tests `is_zero_size` function, chosen to be non-pub use crate::__private::maybestd::{boxed::Box, string::ToString}; diff --git a/borsh/src/schema/container_ext/validate.rs b/borsh/src/schema/container_ext/validate.rs index 6bd2738fb..e9798e75c 100644 --- a/borsh/src/schema/container_ext/validate.rs +++ b/borsh/src/schema/container_ext/validate.rs @@ -1,5 +1,4 @@ -use super::{is_zero_size, ZeroSizeError}; -use super::{BorshSchemaContainer, Declaration, Definition, Fields}; +use super::{is_zero_size, BorshSchemaContainer, Declaration, Definition, Fields, ZeroSizeError}; use crate::__private::maybestd::{string::ToString, vec::Vec}; impl BorshSchemaContainer { diff --git a/borsh/src/schema_helpers.rs b/borsh/src/schema_helpers.rs index 422c36138..7cacabca4 100644 --- a/borsh/src/schema_helpers.rs +++ b/borsh/src/schema_helpers.rs @@ -1,8 +1,10 @@ -use crate::__private::maybestd::vec::Vec; -use crate::from_slice; -use crate::io::{Error, ErrorKind, Result}; -use crate::schema::{BorshSchemaContainer, SchemaMaxSerializedSizeError}; -use crate::{BorshDeserialize, BorshSchema, BorshSerialize}; +use crate::{ + __private::maybestd::vec::Vec, + from_slice, + io::{Error, ErrorKind, Result}, + schema::{BorshSchemaContainer, SchemaMaxSerializedSizeError}, + BorshDeserialize, BorshSchema, BorshSerialize, +}; /// Deserialize this instance from a slice of bytes, but assume that at the beginning we have /// bytes describing the schema of the type. We deserialize this schema and verify that it is diff --git a/borsh/src/ser/helpers.rs b/borsh/src/ser/helpers.rs index da4da27da..4375ce1d6 100644 --- a/borsh/src/ser/helpers.rs +++ b/borsh/src/ser/helpers.rs @@ -1,6 +1,12 @@ -use crate::BorshSerialize; -use crate::__private::maybestd::vec::Vec; -use crate::io::{ErrorKind, Result, Write}; +use async_generic::async_generic; + +#[cfg(feature = "unstable__async")] +use crate::{async_io::AsyncWrite, BorshSerializeAsync}; +use crate::{ + BorshSerialize, + __private::maybestd::vec::Vec, + io::{ErrorKind, Result, Write}, +}; pub(super) const DEFAULT_SERIALIZER_CAPACITY: usize = 1024; @@ -20,19 +26,40 @@ where } /// Serializes an object directly into a `Writer`. -/// # Example /// -/// ``` -/// # #[cfg(feature = "std")] -/// let stderr = std::io::stderr(); -/// # #[cfg(feature = "std")] -/// assert_eq!((), borsh::to_writer(&stderr, "hello_0x0a").unwrap()); -/// ``` +/// # Example +#[async_generic( + /// ``` + /// # #[cfg(feature = "std")] + /// let stderr = std::io::stderr(); + /// # #[cfg(feature = "std")] + /// borsh::to_writer(&stderr, "hello_0x0a").unwrap(); + /// ``` + sync_signature; + + /// ``` + /// # tokio_test::block_on(async { + /// # #[cfg(feature = "unstable__tokio")] + /// let mut stderr = tokio::io::stderr(); + /// # #[cfg(feature = "unstable__tokio")] + /// borsh::to_writer_async(&mut stderr, "hello_0x0a").await.unwrap(); + /// # }) + /// ``` + #[cfg(feature = "unstable__async")] + async_signature(mut writer: W, value: &T) -> Result<()> + where + T: BorshSerializeAsync + ?Sized, +)] pub fn to_writer(mut writer: W, value: &T) -> Result<()> where T: BorshSerialize + ?Sized, { - value.serialize(&mut writer) + let res = value.serialize(&mut writer); + if _sync { + res + } else { + res.await + } } /// Serializes an object without allocation to compute and return its length @@ -47,7 +74,7 @@ where /// struct A { /// tag: String, /// value: u64, -/// }; +/// } /// /// # #[cfg(feature = "derive")] /// let a = A { tag: "hello".to_owned(), value: 42 }; diff --git a/borsh/src/ser/mod.rs b/borsh/src/ser/mod.rs index b7da5ebd7..49a05198d 100644 --- a/borsh/src/ser/mod.rs +++ b/borsh/src/ser/mod.rs @@ -1,15 +1,22 @@ -use core::convert::TryFrom; -use core::marker::PhantomData; - -use crate::__private::maybestd::{ - borrow::{Cow, ToOwned}, - boxed::Box, - collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, - string::String, - vec::Vec, +#[cfg(feature = "unstable__async")] +use core::future::Future; +use core::{convert::TryFrom, marker::PhantomData}; + +use async_generic::async_generic; + +#[cfg(feature = "unstable__async")] +use crate::async_io::AsyncWrite; +use crate::{ + __private::maybestd::{ + borrow::{Cow, ToOwned}, + boxed::Box, + collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, + string::String, + vec::Vec, + }, + error::check_zst, + io::{Error, ErrorKind, Result, Write}, }; -use crate::error::check_zst; -use crate::io::{Error, ErrorKind, Result, Write}; pub(crate) mod helpers; @@ -27,7 +34,6 @@ const FLOAT_NAN_ERR: &str = "For portability reasons we do not allow to serializ /// value: String, /// } /// -/// /// # #[cfg(feature = "derive")] /// let x = MyBorshSerializableStruct { value: "hello".to_owned() }; /// let mut buffer: Vec = Vec::new(); @@ -62,10 +68,82 @@ pub trait BorshSerialize { } } +/// A data-structure that can be serialized into binary format by NBOR. +/// +/// ``` +/// # tokio_test::block_on(async { +/// use borsh::BorshSerializeAsync; +/// # #[cfg(all(feature = "derive", feature = "unstable__tokio"))] +/// # use std::io; +/// # #[cfg(all(feature = "derive", feature = "unstable__async-std"))] +/// # use async_std::io; +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// use io::Cursor; +/// +/// /// derive is only available if borsh is built with `features = ["derive"]` +/// # #[cfg(feature = "derive")] +/// #[derive(BorshSerializeAsync)] +/// struct MyBorshSerializableStruct { +/// value: String, +/// } +/// +/// # #[cfg(feature = "derive")] +/// let x = MyBorshSerializableStruct { value: "hello".to_owned() }; +/// let mut buffer: Vec = Vec::new(); +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// x.serialize(&mut buffer).await.unwrap(); +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// let single_serialized_buffer_len = buffer.len(); +/// +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// x.serialize(&mut buffer).await.unwrap(); +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// assert_eq!(buffer.len(), single_serialized_buffer_len * 2); +/// +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// let mut buffer: Cursor> = Cursor::new(vec![0; 1024 + single_serialized_buffer_len]); +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// buffer.set_position(1024); +/// # #[cfg(all(feature = "derive", any(feature = "unstable__tokio", feature = "unstable__async-std")))] +/// x.serialize(&mut buffer).await.unwrap(); +/// # }) +/// ``` +#[cfg(feature = "unstable__async")] +pub trait BorshSerializeAsync: Sync { + fn serialize<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a; + + #[inline] + #[doc(hidden)] + fn u8_slice(slice: &[Self]) -> Option<&[u8]> + where + Self: Sized, + { + let _ = slice; + None + } +} + +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant(copy_sync) +)] impl BorshSerialize for u8 { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - writer.write_all(core::slice::from_ref(self)) + if _sync { + writer.write_all(core::slice::from_ref(self)) + } else { + writer.write_u8(*self) + } } #[inline] @@ -74,279 +152,620 @@ impl BorshSerialize for u8 { } } +#[rustfmt::skip] macro_rules! impl_for_integer { - ($type: ident) => { + ($type: ident, $method: ident) => { + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshSerialize for $type { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - let bytes = self.to_le_bytes(); - writer.write_all(&bytes) + if _sync { + let bytes = self.to_le_bytes(); + writer.write_all(&bytes) + } else { + writer.$method(*self) + } } } }; } -impl_for_integer!(i8); -impl_for_integer!(i16); -impl_for_integer!(i32); -impl_for_integer!(i64); -impl_for_integer!(i128); -impl_for_integer!(u16); -impl_for_integer!(u32); -impl_for_integer!(u64); -impl_for_integer!(u128); - +impl_for_integer!(i8, write_i8); +impl_for_integer!(i16, write_i16); +impl_for_integer!(i32, write_i32); +impl_for_integer!(i64, write_i64); +impl_for_integer!(i128, write_i128); +impl_for_integer!(u16, write_u16); +impl_for_integer!(u32, write_u32); +impl_for_integer!(u64, write_u64); +impl_for_integer!(u128, write_u128); + +#[rustfmt::skip] macro_rules! impl_for_nonzero_integer { - ($type: ty) => { + ($type: ty, $method: ident $(, $repr: ty)?) => { + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshSerialize for $type { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - BorshSerialize::serialize(&self.get(), writer) + if _sync { + BorshSerialize::serialize(&self.get(), writer) + } else { + let value = self.get(); + writer.$method(value $(as $repr)?) + } } } }; } -impl_for_nonzero_integer!(core::num::NonZeroI8); -impl_for_nonzero_integer!(core::num::NonZeroI16); -impl_for_nonzero_integer!(core::num::NonZeroI32); -impl_for_nonzero_integer!(core::num::NonZeroI64); -impl_for_nonzero_integer!(core::num::NonZeroI128); -impl_for_nonzero_integer!(core::num::NonZeroU8); -impl_for_nonzero_integer!(core::num::NonZeroU16); -impl_for_nonzero_integer!(core::num::NonZeroU32); -impl_for_nonzero_integer!(core::num::NonZeroU64); -impl_for_nonzero_integer!(core::num::NonZeroU128); -impl_for_nonzero_integer!(core::num::NonZeroUsize); - -impl BorshSerialize for isize { - fn serialize(&self, writer: &mut W) -> Result<()> { - BorshSerialize::serialize(&(*self as i64), writer) - } +impl_for_nonzero_integer!(core::num::NonZeroI8, write_i8); +impl_for_nonzero_integer!(core::num::NonZeroI16, write_i16); +impl_for_nonzero_integer!(core::num::NonZeroI32, write_i32); +impl_for_nonzero_integer!(core::num::NonZeroI64, write_i64); +impl_for_nonzero_integer!(core::num::NonZeroI128, write_i128); +impl_for_nonzero_integer!(core::num::NonZeroIsize, write_i64, i64); +impl_for_nonzero_integer!(core::num::NonZeroU8, write_u8); +impl_for_nonzero_integer!(core::num::NonZeroU16, write_u16); +impl_for_nonzero_integer!(core::num::NonZeroU32, write_u32); +impl_for_nonzero_integer!(core::num::NonZeroU64, write_u64); +impl_for_nonzero_integer!(core::num::NonZeroU128, write_u128); +impl_for_nonzero_integer!(core::num::NonZeroUsize, write_u64, u64); + +#[rustfmt::skip] +macro_rules! impl_for_size_integer { + ($type:ty: $repr:ty, $method: ident) => { + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] + impl BorshSerialize for $type { + #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] + fn serialize(&self, writer: &mut W) -> Result<()> { + if _sync { + BorshSerialize::serialize(&(*self as $repr), writer) + } else { + let value = *self as $repr; + writer.$method(value) + } + } + } + }; } -impl BorshSerialize for usize { - fn serialize(&self, writer: &mut W) -> Result<()> { - BorshSerialize::serialize(&(*self as u64), writer) - } -} +impl_for_size_integer!(usize: u64, write_u64); +impl_for_size_integer!(isize: i64, write_i64); // Note NaNs have a portability issue. Specifically, signalling NaNs on MIPS are quiet NaNs on x86, -// and vice-versa. We disallow NaNs to avoid this issue. +// and vice versa. We disallow NaNs to avoid this issue. +#[rustfmt::skip] macro_rules! impl_for_float { - ($type: ident) => { + ($type: ident, $method: ident) => { + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshSerialize for $type { #[inline] + #[async_generic( + async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()> + )] fn serialize(&self, writer: &mut W) -> Result<()> { if self.is_nan() { return Err(Error::new(ErrorKind::InvalidData, FLOAT_NAN_ERR)); } - writer.write_all(&self.to_bits().to_le_bytes()) + if _sync{ + let bytes = self.to_bits().to_le_bytes(); + writer.write_all(&bytes) + } else { + writer.$method(*self).await + } } } }; } -impl_for_float!(f32); -impl_for_float!(f64); +impl_for_float!(f32, write_f32); +impl_for_float!(f64, write_f64); +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for bool { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - (u8::from(*self)).serialize(writer) + let byte = u8::from(*self); + if _sync { + BorshSerialize::serialize(&byte, writer) + } else { + writer.write_u8(byte) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync, +)] impl BorshSerialize for Option where T: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { match self { - None => 0u8.serialize(writer), + None => { + if _sync { + BorshSerialize::serialize(&0u8, writer) + } else { + BorshSerializeAsync::serialize(&0u8, writer).await + } + } Some(value) => { - 1u8.serialize(writer)?; - value.serialize(writer) + if _sync { + BorshSerialize::serialize(&1u8, writer)?; + BorshSerialize::serialize(value, writer) + } else { + BorshSerializeAsync::serialize(&1u8, writer).await?; + BorshSerializeAsync::serialize(value, writer).await + } } } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync, + E: BorshSerializeAsync, +)] impl BorshSerialize for core::result::Result where T: BorshSerialize, E: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { match self { Err(e) => { - 0u8.serialize(writer)?; - e.serialize(writer) + if _sync { + BorshSerialize::serialize(&0u8, writer)?; + BorshSerialize::serialize(e, writer) + } else { + BorshSerializeAsync::serialize(&0u8, writer).await?; + BorshSerializeAsync::serialize(e, writer).await + } } Ok(v) => { - 1u8.serialize(writer)?; - v.serialize(writer) + if _sync { + BorshSerialize::serialize(&1u8, writer)?; + BorshSerialize::serialize(v, writer) + } else { + BorshSerializeAsync::serialize(&1u8, writer).await?; + BorshSerializeAsync::serialize(v, writer).await + } } } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for str { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_bytes().serialize(writer) + let bytes = self.as_bytes(); + if _sync { + BorshSerialize::serialize(bytes, writer) + } else { + BorshSerializeAsync::serialize(bytes, writer) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for String { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_bytes().serialize(writer) + if _sync { + BorshSerialize::serialize(&**self, writer) + } else { + BorshSerializeAsync::serialize(&**self, writer) + } } } /// Module is available if borsh is built with `features = ["ascii"]`. +/// +/// Module defines [`BorshSerialize`] +#[cfg_attr(feature = "unstable__async", doc = " and [`BorshSerializeAsync`]")] +/// implementation for some types from [`ascii`](::ascii) crate. #[cfg(feature = "ascii")] pub mod ascii { - //! - //! Module defines [BorshSerialize] implementation for - //! some types from [ascii](::ascii) crate. + #[cfg(feature = "unstable__async")] + use core::future::Future; + + use async_generic::async_generic; + use super::BorshSerialize; + #[cfg(feature = "unstable__async")] + use super::{AsyncWrite, BorshSerializeAsync}; use crate::io::{Result, Write}; + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshSerialize for ascii::AsciiChar { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_byte().serialize(writer) + let byte = self.as_byte(); + if _sync { + BorshSerialize::serialize(&byte, writer) + } else { + writer.write_u8(byte) + } } } + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshSerialize for ascii::AsciiStr { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_bytes().serialize(writer) + let bytes = self.as_bytes(); + if _sync { + BorshSerialize::serialize(bytes, writer) + } else { + BorshSerializeAsync::serialize(bytes, writer) + } } } + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshSerialize for ascii::AsciiString { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_bytes().serialize(writer) + if _sync { + BorshSerialize::serialize(&**self, writer) + } else { + BorshSerializeAsync::serialize(&**self, writer) + } } } } /// Helper method that is used to serialize a slice of data (without the length marker). #[inline] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_signature(data: &[T], writer: &mut W) -> Result<()> +)] fn serialize_slice(data: &[T], writer: &mut W) -> Result<()> { if let Some(u8_slice) = T::u8_slice(data) { - writer.write_all(u8_slice)?; + if _sync { + writer.write_all(u8_slice) + } else { + writer.write_all(u8_slice).await + }?; } else { for item in data { - item.serialize(writer)?; + if _sync { + BorshSerialize::serialize(item, writer) + } else { + BorshSerializeAsync::serialize(item, writer).await + }?; } } Ok(()) } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync + Sync, +)] impl BorshSerialize for [T] where T: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { - writer.write_all( - &(u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?).to_le_bytes(), - )?; - serialize_slice(self, writer) + let len = u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?; + if _sync { + writer.write_all(&len.to_le_bytes())?; + serialize_slice(self, writer) + } else { + writer.write_u32(len).await?; + serialize_slice_async(self, writer).await + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for &T { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - (*self).serialize(writer) + if _sync { + BorshSerialize::serialize(*self, writer) + } else { + BorshSerializeAsync::serialize(*self, writer) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync + ToOwned + ?Sized, + ::Owned: Sync, +)] impl BorshSerialize for Cow<'_, T> where T: BorshSerialize + ToOwned + ?Sized, { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_ref().serialize(writer) + let r#ref = self.as_ref(); + if _sync { + BorshSerialize::serialize(r#ref, writer) + } else { + BorshSerializeAsync::serialize(r#ref, writer) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync, +)] impl BorshSerialize for Vec where T: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; - - self.as_slice().serialize(writer) + let slice = self.as_slice(); + if _sync { + BorshSerialize::serialize(slice, writer) + } else { + // don't remove `.await`, because `async_signature` will remove the implicit `async` + // block and you would need to setup the state machine manually anyway, + // because of `?` in `check_zst::()?` + BorshSerializeAsync::serialize(slice, writer).await + } } } #[cfg(feature = "bytes")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for bytes::Bytes { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_ref().serialize(writer) + let bytes = self.as_ref(); + if _sync { + BorshSerialize::serialize(bytes, writer) + } else { + BorshSerializeAsync::serialize(bytes, writer) + } } } #[cfg(feature = "bytes")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for bytes::BytesMut { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_ref().serialize(writer) + let bytes = self.as_ref(); + if _sync { + BorshSerialize::serialize(bytes, writer) + } else { + BorshSerializeAsync::serialize(bytes, writer) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] #[cfg(feature = "bson")] impl BorshSerialize for bson::oid::ObjectId { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.bytes().serialize(writer) + #[inline(always)] + fn as_bytes(r#ref: &bson::oid::ObjectId) -> &[u8; 12] { + // SAFETY: `ObjectId` is a `12` byte array. + unsafe { &*(r#ref as *const bson::oid::ObjectId as *const [u8; 12]) } + } + + let bytes = as_bytes(self); + if _sync { + writer.write_all(bytes) + } else { + writer.write_all(bytes) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync, +)] impl BorshSerialize for VecDeque where T: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; - writer.write_all( - &(u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?).to_le_bytes(), - )?; + let len = u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?; let slices = self.as_slices(); - serialize_slice(slices.0, writer)?; - serialize_slice(slices.1, writer) + if _sync { + writer.write_all(&len.to_le_bytes())?; + serialize_slice(slices.0, writer)?; + serialize_slice(slices.1, writer) + } else { + writer.write_u32(len).await?; + serialize_slice_async(slices.0, writer).await?; + serialize_slice_async(slices.1, writer).await + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync, +)] impl BorshSerialize for LinkedList where T: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; - writer.write_all( - &(u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?).to_le_bytes(), - )?; + let len = u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?; + if _sync { + BorshSerialize::serialize(&len, writer) + } else { + writer.write_u32(len).await + }?; for item in self { - item.serialize(writer)?; + if _sync { + BorshSerialize::serialize(item, writer) + } else { + BorshSerializeAsync::serialize(item, writer).await + }?; } Ok(()) } @@ -354,21 +773,34 @@ where /// Module is available if borsh is built with `features = ["std"]` or `features = ["hashbrown"]`. /// -/// Module defines [BorshSerialize] implementation for -/// [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet). +/// Module defines [`BorshSerialize`] +#[cfg_attr(feature = "unstable__async", doc = " and [`BorshSerializeAsync`]")] +/// implementation for [`HashMap`](std::collections::HashMap)/[`HashSet`](std::collections::HashSet). #[cfg(hash_collections)] pub mod hashes { - use crate::__private::maybestd::vec::Vec; - use crate::error::check_zst; + use async_generic::async_generic; + use core::{convert::TryFrom, hash::BuildHasher}; + + use super::BorshSerialize; + #[cfg(feature = "unstable__async")] + use super::{AsyncWrite, BorshSerializeAsync}; use crate::{ - BorshSerialize, - __private::maybestd::collections::{HashMap, HashSet}, + __private::maybestd::{ + collections::{HashMap, HashSet}, + vec::Vec, + }, + error::check_zst, + io::{ErrorKind, Result, Write}, }; - use core::convert::TryFrom; - use core::hash::BuildHasher; - - use crate::io::{ErrorKind, Result, Write}; + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + K: BorshSerializeAsync + Ord, + V: BorshSerializeAsync, + H: BuildHasher + Sync, + )] impl BorshSerialize for HashMap where K: BorshSerialize + Ord, @@ -376,196 +808,410 @@ pub mod hashes { H: BuildHasher, { #[inline] + #[async_generic( + async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()> + )] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; let mut vec = self.iter().collect::>(); vec.sort_by(|(a, _), (b, _)| a.cmp(b)); - u32::try_from(vec.len()) - .map_err(|_| ErrorKind::InvalidData)? - .serialize(writer)?; + let len = u32::try_from(vec.len()).map_err(|_| ErrorKind::InvalidData)?; + if _sync { + BorshSerialize::serialize(&len, writer) + } else { + writer.write_u32(len).await + }?; for kv in vec { - kv.serialize(writer)?; + if _sync { + BorshSerialize::serialize(&kv, writer) + } else { + BorshSerializeAsync::serialize(&kv, writer).await + }?; } Ok(()) } } + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync + Ord, + H: BuildHasher + Sync, + )] impl BorshSerialize for HashSet where T: BorshSerialize + Ord, H: BuildHasher, { #[inline] + #[async_generic( + async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()> + )] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; let mut vec = self.iter().collect::>(); vec.sort(); - u32::try_from(vec.len()) - .map_err(|_| ErrorKind::InvalidData)? - .serialize(writer)?; + let len = u32::try_from(vec.len()).map_err(|_| ErrorKind::InvalidData)?; + if _sync { + BorshSerialize::serialize(&len, writer) + } else { + writer.write_u32(len).await + }?; for item in vec { - item.serialize(writer)?; + if _sync { + BorshSerialize::serialize(&item, writer) + } else { + BorshSerializeAsync::serialize(&item, writer).await + }?; } Ok(()) } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + K: BorshSerializeAsync, + V: BorshSerializeAsync, +)] impl BorshSerialize for BTreeMap where K: BorshSerialize, V: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; // NOTE: BTreeMap iterates over the entries that are sorted by key, so the serialization // result will be consistent without a need to sort the entries as we do for HashMap // serialization. - u32::try_from(self.len()) - .map_err(|_| ErrorKind::InvalidData)? - .serialize(writer)?; + let len = u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?; + if _sync { + BorshSerialize::serialize(&len, writer) + } else { + writer.write_u32(len).await + }?; for (key, value) in self { - key.serialize(writer)?; - value.serialize(writer)?; + if _sync { + BorshSerialize::serialize(&key, writer)?; + BorshSerialize::serialize(&value, writer) + } else { + BorshSerializeAsync::serialize(&key, writer).await?; + BorshSerializeAsync::serialize(&value, writer).await + }?; } Ok(()) } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync + Sync, +)] impl BorshSerialize for BTreeSet where T: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; // NOTE: BTreeSet iterates over the items that are sorted, so the serialization result will // be consistent without a need to sort the entries as we do for HashSet serialization. - u32::try_from(self.len()) - .map_err(|_| ErrorKind::InvalidData)? - .serialize(writer)?; + let len = u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?; + if _sync { + BorshSerialize::serialize(&len, writer) + } else { + writer.write_u32(len).await + }?; for item in self { - item.serialize(writer)?; + if _sync { + BorshSerialize::serialize(&item, writer) + } else { + BorshSerializeAsync::serialize(&item, writer).await + }?; } Ok(()) } } +// TODO: use `core` once MSRV >= 1.77 #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for std::net::SocketAddr { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { - match *self { - std::net::SocketAddr::V4(ref addr) => { - 0u8.serialize(writer)?; - addr.serialize(writer) + match self { + std::net::SocketAddr::V4(addr) => { + if _sync { + BorshSerialize::serialize(&0u8, writer)?; + BorshSerialize::serialize(addr, writer) + } else { + BorshSerializeAsync::serialize(&0u8, writer).await?; + BorshSerializeAsync::serialize(addr, writer).await + } } - std::net::SocketAddr::V6(ref addr) => { - 1u8.serialize(writer)?; - addr.serialize(writer) + std::net::SocketAddr::V6(addr) => { + if _sync { + BorshSerialize::serialize(&1u8, writer)?; + BorshSerialize::serialize(addr, writer) + } else { + BorshSerializeAsync::serialize(&1u8, writer).await?; + BorshSerializeAsync::serialize(addr, writer).await + } } } } } +// TODO: use `core` once MSRV >= 1.77 #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for std::net::SocketAddrV4 { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { - self.ip().serialize(writer)?; - self.port().serialize(writer) + if _sync { + BorshSerialize::serialize(self.ip(), writer)?; + BorshSerialize::serialize(&self.port(), writer) + } else { + BorshSerializeAsync::serialize(self.ip(), writer).await?; + BorshSerializeAsync::serialize(&self.port(), writer).await + } } } +// TODO: use `core` once MSRV >= 1.77 #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for std::net::SocketAddrV6 { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { - self.ip().serialize(writer)?; - self.port().serialize(writer) + if _sync { + BorshSerialize::serialize(self.ip(), writer)?; + BorshSerialize::serialize(&self.port(), writer) + } else { + BorshSerializeAsync::serialize(self.ip(), writer).await?; + BorshSerializeAsync::serialize(&self.port(), writer).await + } } } +// TODO: use `core` once MSRV >= 1.77 #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for std::net::Ipv4Addr { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - writer.write_all(&self.octets()) + #[inline(always)] + fn as_bytes(ip: &std::net::Ipv4Addr) -> &[u8; 4] { + // SAFETY: `Ipv4Addr` is a `4` byte array. + unsafe { &*(ip as *const std::net::Ipv4Addr as *const [u8; 4]) } + } + + let bytes = as_bytes(self); + if _sync { + writer.write_all(bytes) + } else { + writer.write_all(bytes) + } } } +// TODO: use `core` once MSRV >= 1.77 #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for std::net::Ipv6Addr { #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - writer.write_all(&self.octets()) + #[inline(always)] + fn as_bytes(ip: &std::net::Ipv6Addr) -> &[u8; 16] { + // SAFETY: `Ipv4Addr` is a `16` byte array. + unsafe { &*(ip as *const std::net::Ipv6Addr as *const [u8; 16]) } + } + + let bytes = as_bytes(self); + if _sync { + writer.write_all(bytes) + } else { + writer.write_all(bytes) + } } } +// TODO: use `core` once MSRV >= 1.77 #[cfg(feature = "std")] +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant +)] impl BorshSerialize for std::net::IpAddr { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { match self { std::net::IpAddr::V4(ipv4) => { - writer.write_all(&0u8.to_le_bytes())?; - ipv4.serialize(writer) + if _sync { + writer.write_all(&0u8.to_le_bytes())?; + BorshSerialize::serialize(ipv4, writer) + } else { + writer.write_all(&0u8.to_le_bytes()).await?; + BorshSerializeAsync::serialize(ipv4, writer).await + } } std::net::IpAddr::V6(ipv6) => { - writer.write_all(&1u8.to_le_bytes())?; - ipv6.serialize(writer) + if _sync { + writer.write_all(&1u8.to_le_bytes())?; + BorshSerialize::serialize(ipv6, writer) + } else { + writer.write_all(&1u8.to_le_bytes()).await?; + BorshSerializeAsync::serialize(ipv6, writer).await + } } } } } + +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync + ?Sized, +)] impl BorshSerialize for Box { + #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { - self.as_ref().serialize(writer) + let r#ref = self.as_ref(); + if _sync { + BorshSerialize::serialize(r#ref, writer) + } else { + BorshSerializeAsync::serialize(r#ref, writer) + } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync, +)] impl BorshSerialize for [T; N] where T: BorshSerialize, { #[inline] + #[async_generic(async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()>)] fn serialize(&self, writer: &mut W) -> Result<()> { if N == 0 { - return Ok(()); + Ok(()) } else if let Some(u8_slice) = T::u8_slice(self) { - writer.write_all(u8_slice)?; + if _sync { + writer.write_all(u8_slice) + } else { + writer.write_all(u8_slice).await + } } else { - for el in self.iter() { - el.serialize(writer)?; + for el in self { + if _sync { + BorshSerialize::serialize(el, writer) + } else { + BorshSerializeAsync::serialize(el, writer).await + }?; } + Ok(()) } - Ok(()) } } +#[rustfmt::skip] macro_rules! impl_tuple { (@unit $name:ty) => { + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + )] impl BorshSerialize for $name { #[inline] - fn serialize(&self, _writer: &mut W) -> Result<()> { + #[async_generic( + async_signature[ready]<'a, W: AsyncWrite>( + &'a self, + _: &'a mut W, + ) -> impl Future> + Send + 'a + )] + fn serialize(&self, _: &mut W) -> Result<()> { Ok(()) } } }; ($($idx:tt $name:ident)+) => { - impl<$($name),+> BorshSerialize for ($($name,)+) - where $($name: BorshSerialize,)+ - { - #[inline] - fn serialize(&self, writer: &mut W) -> Result<()> { - $(self.$idx.serialize(writer)?;)+ - Ok(()) + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant<$($name),+> + where + $($name: BorshSerializeAsync,)+ + )] + impl<$($name),+> BorshSerialize for ($($name,)+) + where + $($name: BorshSerialize,)+ + { + #[inline] + #[async_generic( + async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()> + )] + fn serialize(&self, writer: &mut W) -> Result<()> { + if _sync { + $(BorshSerialize::serialize(&self.$idx, writer)?;)+ + } else { + $(BorshSerializeAsync::serialize(&self.$idx, writer).await?;)+ + } + Ok(()) + } } - } }; } @@ -593,13 +1239,27 @@ impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16 17 T17 18 T18); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16 17 T17 18 T18 19 T19); +#[rustfmt::skip] macro_rules! impl_range { ($type:ident, $this:ident, $($field:expr),*) => { + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync, + )] impl BorshSerialize for core::ops::$type { #[inline] + #[async_generic( + async_signature<'a, W: AsyncWrite>(&'a self, writer: &'a mut W) -> Result<()> + )] fn serialize(&self, writer: &mut W) -> Result<()> { let $this = self; - $( $field.serialize(writer)?; )* + if _sync { + $( let _ = $field.serialize(writer)?; )* + } else { + $( let _ = $field.serialize(writer).await?; )* + } Ok(()) } } @@ -613,14 +1273,24 @@ impl_range!(RangeTo, this, &this.end); impl_range!(RangeToInclusive, this, &this.end); /// Module is available if borsh is built with `features = ["rc"]`. +/// +/// Module defines [`BorshSerialize`] +#[cfg_attr(feature = "unstable__async", doc = " and [`BorshSerializeAsync`]")] +/// implementation for [`alloc::rc::Rc`](std::rc::Rc) and [`alloc::sync::Arc`](std::sync::Arc). #[cfg(feature = "rc")] pub mod rc { - //! - //! Module defines [BorshSerialize] implementation for - //! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc). - use crate::__private::maybestd::{rc::Rc, sync::Arc}; - use crate::io::{Result, Write}; - use crate::BorshSerialize; + #[cfg(feature = "unstable__async")] + use core::future::Future; + + use async_generic::async_generic; + + #[cfg(feature = "unstable__async")] + use super::{AsyncWrite, BorshSerializeAsync}; + use crate::{ + __private::maybestd::{rc::Rc, sync::Arc}, + io::{Result, Write}, + BorshSerialize, + }; /// This impl requires the [`"rc"`] Cargo feature of borsh. /// @@ -640,14 +1310,40 @@ pub mod rc { /// the contents of the `Arc` each time the `Arc` is referenced within the /// data structure. Serialization will not attempt to deduplicate these /// repeated data. + #[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: BorshSerializeAsync + ?Sized + Send, + )] impl BorshSerialize for Arc { + #[inline] + #[async_generic( + async_signature[impl_fut]<'a, W: AsyncWrite>( + &'a self, + writer: &'a mut W, + ) -> impl Future> + Send + 'a + )] fn serialize(&self, writer: &mut W) -> Result<()> { (**self).serialize(writer) } } } +#[async_generic( + #[cfg(feature = "unstable__async")] + async_variant + where + T: ?Sized + Sync, +)] impl BorshSerialize for PhantomData { + #[inline] + #[async_generic( + async_signature[ready]<'a, W: AsyncWrite>( + &'a self, + _: &'a mut W, + ) -> impl Future> + Send + )] fn serialize(&self, _: &mut W) -> Result<()> { Ok(()) } @@ -657,14 +1353,15 @@ impl BorshSerialize for core::cell::Cell where T: BorshSerialize + Copy, { + #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { - ::serialize(&self.get(), writer) + T::serialize(&self.get(), writer) } } impl BorshSerialize for core::cell::RefCell where - T: BorshSerialize + Sized, + T: BorshSerialize + ?Sized, { fn serialize(&self, writer: &mut W) -> Result<()> { match self.try_borrow() { diff --git a/borsh/tests/common_macro.rs b/borsh/tests/common_macro.rs index bc5503ffb..dd82b0bf8 100644 --- a/borsh/tests/common_macro.rs +++ b/borsh/tests/common_macro.rs @@ -137,9 +137,11 @@ pub mod schema_imports { vec::Vec, }; - pub use borsh::schema::{ - add_definition, BorshSchemaContainer, Declaration, Definition, Fields, - SchemaContainerValidateError, SchemaMaxSerializedSizeError, + pub use borsh::{ + schema::{ + add_definition, BorshSchemaContainer, Declaration, Definition, Fields, + SchemaContainerValidateError, SchemaMaxSerializedSizeError, + }, + schema_container_of, BorshSchema, }; - pub use borsh::{schema_container_of, BorshSchema}; } diff --git a/borsh/tests/compile_derives/async_derives/test_generic_enums.rs b/borsh/tests/compile_derives/async_derives/test_generic_enums.rs new file mode 100644 index 000000000..854cbe343 --- /dev/null +++ b/borsh/tests/compile_derives/async_derives/test_generic_enums.rs @@ -0,0 +1,47 @@ +use alloc::collections::BTreeMap; +#[allow(unused)] +use alloc::{string::String, vec::Vec}; +#[cfg(hash_collections)] +use core::{cmp::Eq, hash::Hash}; +#[cfg(feature = "std")] +use std::collections::HashMap; + +use borsh::{BorshDeserializeAsync, BorshSerializeAsync}; +#[cfg(feature = "hashbrown")] +use hashbrown::HashMap; + +/// `T: Ord` bound is required for `BorshDeserialize` derive to be successful +#[derive(BorshSerializeAsync, BorshDeserializeAsync, PartialEq, Debug)] +enum E { + X { f: BTreeMap }, + Y(W), +} + +#[cfg(hash_collections)] +#[derive(BorshSerializeAsync, BorshDeserializeAsync, Debug)] +enum I1 { + B { + #[allow(unused)] + #[borsh(skip, async_bound(serialize = "V: Sync", deserialize = "V: Send"))] + x: HashMap, + y: String, + }, + C(K, Vec), +} + +#[cfg(hash_collections)] +#[derive(BorshSerializeAsync, BorshDeserializeAsync, Debug)] +enum I2 { + B { + x: HashMap, + y: String, + }, + C( + K, + #[borsh( + skip, + async_bound(serialize = "U: Sync", deserialize = "U: Default + Send") + )] + U, + ), +} diff --git a/borsh/tests/compile_derives/async_derives/test_generic_structs.rs b/borsh/tests/compile_derives/async_derives/test_generic_structs.rs new file mode 100644 index 000000000..e69de29bb diff --git a/borsh/tests/compile_derives/async_derives/test_macro_namespace_collisions.rs b/borsh/tests/compile_derives/async_derives/test_macro_namespace_collisions.rs new file mode 100644 index 000000000..121966ff5 --- /dev/null +++ b/borsh/tests/compile_derives/async_derives/test_macro_namespace_collisions.rs @@ -0,0 +1,21 @@ +// Borsh macros should not collide with the local modules: +// https://github.com/near/borsh-rs/issues/11 +mod std {} +mod core {} + +#[derive(borsh::BorshSerializeAsync, borsh::BorshDeserializeAsync)] +struct A; + +#[derive(borsh::BorshSerializeAsync, borsh::BorshDeserializeAsync)] +enum B { + C, + D, +} + +#[derive(borsh::BorshSerializeAsync, borsh::BorshDeserializeAsync)] +struct C { + x: u64, + #[allow(unused)] + #[borsh(skip)] + y: u64, +} diff --git a/borsh/tests/compile_derives/async_derives/test_recursive_structs.rs b/borsh/tests/compile_derives/async_derives/test_recursive_structs.rs new file mode 100644 index 000000000..e69de29bb diff --git a/borsh/tests/custom_reader/test_custom_reader.rs b/borsh/tests/custom_reader/test_custom_reader.rs index e0c6dc7de..aadd3f934 100644 --- a/borsh/tests/custom_reader/test_custom_reader.rs +++ b/borsh/tests/custom_reader/test_custom_reader.rs @@ -1,10 +1,10 @@ -use borsh::{from_reader, to_vec, BorshDeserialize, BorshSerialize}; - use alloc::{ string::{String, ToString}, vec::Vec, }; +use borsh::{from_reader, to_vec, BorshDeserialize, BorshSerialize}; + const ERROR_NOT_ALL_BYTES_READ: &str = "Not all bytes read"; const ERROR_UNEXPECTED_LENGTH_OF_INPUT: &str = "Unexpected length of input"; diff --git a/borsh/tests/deserialization_errors/test_ascii_strings.rs b/borsh/tests/deserialization_errors/test_ascii_strings.rs index ffcf90d8e..8f8ef786c 100644 --- a/borsh/tests/deserialization_errors/test_ascii_strings.rs +++ b/borsh/tests/deserialization_errors/test_ascii_strings.rs @@ -1,4 +1,5 @@ use alloc::string::ToString; + use borsh::from_slice; #[test] diff --git a/borsh/tests/deserialization_errors/test_initial.rs b/borsh/tests/deserialization_errors/test_initial.rs index 0fef5c5eb..33a33a086 100644 --- a/borsh/tests/deserialization_errors/test_initial.rs +++ b/borsh/tests/deserialization_errors/test_initial.rs @@ -1,8 +1,3 @@ -use borsh::from_slice; - -#[cfg(feature = "derive")] -use borsh::BorshDeserialize; - use alloc::{ format, string::{String, ToString}, @@ -10,6 +5,10 @@ use alloc::{ vec::Vec, }; +use borsh::from_slice; +#[cfg(feature = "derive")] +use borsh::BorshDeserialize; + #[cfg(feature = "derive")] #[derive(BorshDeserialize, Debug)] #[borsh(use_discriminant = true)] diff --git a/borsh/tests/tests.rs b/borsh/tests/tests.rs index ea8cad5d4..e82d7e98b 100644 --- a/borsh/tests/tests.rs +++ b/borsh/tests/tests.rs @@ -22,6 +22,14 @@ mod compile_derives { mod test_generic_enums; mod test_recursive_structs; + #[cfg(feature = "unstable__async")] + mod async_derives { + mod test_generic_structs; + mod test_generic_enums; + mod test_recursive_structs; + mod test_macro_namespace_collisions; + } + #[cfg(feature = "unstable__schema")] mod schema { mod test_generic_enums; diff --git a/borsh/tests/zero_sized_types/test_zero_sized_types_forbidden.rs b/borsh/tests/zero_sized_types/test_zero_sized_types_forbidden.rs index c48b9cf77..a88528f96 100644 --- a/borsh/tests/zero_sized_types/test_zero_sized_types_forbidden.rs +++ b/borsh/tests/zero_sized_types/test_zero_sized_types_forbidden.rs @@ -1,19 +1,15 @@ -use alloc::{string::ToString, vec, vec::Vec}; - +use alloc::{ + collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, + string::ToString, + vec, + vec::Vec, +}; #[cfg(feature = "std")] use std::collections::{HashMap, HashSet}; +use borsh::{error::ERROR_ZST_FORBIDDEN, from_slice, to_vec, BorshDeserialize, BorshSerialize}; #[cfg(feature = "hashbrown")] use hashbrown::{HashMap, HashSet}; - -use alloc::collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}; - -use borsh::from_slice; -use borsh::to_vec; -use borsh::BorshDeserialize; -use borsh::BorshSerialize; - -use borsh::error::ERROR_ZST_FORBIDDEN; #[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug, Eq, PartialOrd, Ord, Hash)] struct A(); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..36c419bb3 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +edition = "2021" \ No newline at end of file