From 2d4cf209518554a42884c905b873a0321b3a6df3 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 13 Nov 2023 16:01:26 +0100 Subject: [PATCH] feat: add support for types from `ascii` crate (#255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add support for types from ascii ccrate * review * review * schema tests * wip * wip * wip * Update .github/test.sh Co-authored-by: dj8yf0μl <26653921+dj8yfo@users.noreply.github.com> * fix * no_std * fix * unused * test: add missing assert * chore: improve impl/test modularity --------- Co-authored-by: dj8yf0μl <26653921+dj8yfo@users.noreply.github.com> Co-authored-by: dj8yf0μl --- .github/test.sh | 4 +- borsh/Cargo.toml | 3 +- borsh/src/de/mod.rs | 29 + borsh/src/lib.rs | 3 + borsh/src/schema.rs | 52 + borsh/src/ser/mod.rs | 31 + .../snapshots/test_ascii_strings__a.snap | 11 + .../test_ascii_strings__empty_string.snap | 10 + .../test_ascii_strings__hello_10.snap | 370 ++++++ .../test_ascii_strings__hello_world.snap | 21 + .../snapshots/test_ascii_strings__x_1024.snap | 1034 +++++++++++++++++ borsh/tests/test_ascii_strings.rs | 134 +++ borsh/tests/test_strings.rs | 30 +- 13 files changed, 1720 insertions(+), 12 deletions(-) create mode 100644 borsh/tests/snapshots/test_ascii_strings__a.snap create mode 100644 borsh/tests/snapshots/test_ascii_strings__empty_string.snap create mode 100644 borsh/tests/snapshots/test_ascii_strings__hello_10.snap create mode 100644 borsh/tests/snapshots/test_ascii_strings__hello_world.snap create mode 100644 borsh/tests/snapshots/test_ascii_strings__x_1024.snap create mode 100644 borsh/tests/test_ascii_strings.rs diff --git a/.github/test.sh b/.github/test.sh index 00391729a..3816b3e34 100755 --- a/.github/test.sh +++ b/.github/test.sh @@ -5,16 +5,18 @@ export INSTA_UPDATE=no pushd borsh cargo test --no-run cargo test +cargo test --features unstable__schema,ascii --test test_ascii_strings cargo test --features derive cargo test --features unstable__schema cargo test --test test_rc --features rc cargo test --test test_hash_map --test test_btree_map --features de_strict_order cargo test --no-default-features +cargo test --no-default-features --features unstable__schema,ascii --test test_ascii_strings cargo test --no-default-features --features derive cargo test --no-default-features --features unstable__schema cargo test --no-default-features --test test_rc --features rc cargo test --no-default-features --features hashbrown popd pushd borsh-derive -cargo test --features schema \ No newline at end of file +cargo test --features schema diff --git a/borsh/Cargo.toml b/borsh/Cargo.toml index a9db8ad82..30583e629 100644 --- a/borsh/Cargo.toml +++ b/borsh/Cargo.toml @@ -27,10 +27,11 @@ required-features = ["std", "unstable__schema"] cfg_aliases = "0.1.0" [dependencies] +ascii = { version = "1.1", optional = true } borsh-derive = { path = "../borsh-derive", version = "~1.1.2", optional = true } # hashbrown can be used in no-std context. -# NOTE: There is no reason to restrict use of older versions, but we don't want to get +# NOTE: There is no reason to restrict use of older versions, but we don't want to get # sudden breaking changes with an open range of versions, so we limit the range by not yet released 0.15.0 version: hashbrown = { version = ">=0.11,<0.15.0", optional = true } bytes = { version = "1", optional = true } diff --git a/borsh/src/de/mod.rs b/borsh/src/de/mod.rs index ed57938e1..bb6630c99 100644 --- a/borsh/src/de/mod.rs +++ b/borsh/src/de/mod.rs @@ -370,6 +370,35 @@ impl BorshDeserialize for String { } } +/// Module is available if borsh is built with `features = ["ascii"]`. +#[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}; + + impl BorshDeserialize for ascii::AsciiString { + #[inline] + 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())) + } + } + + impl BorshDeserialize for ascii::AsciiChar { + #[inline] + 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())) + } + } +} + impl BorshDeserialize for Vec where T: BorshDeserialize, diff --git a/borsh/src/lib.rs b/borsh/src/lib.rs index 9091b6272..3b7cdc99b 100644 --- a/borsh/src/lib.rs +++ b/borsh/src/lib.rs @@ -44,6 +44,9 @@ * **bson** - Gates implementation of [BorshSerialize] and [BorshDeserialize] for [ObjectId](bson::oid::ObjectId). +* **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 diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs index 9657f1a5a..5c0825037 100644 --- a/borsh/src/schema.rs +++ b/borsh/src/schema.rs @@ -384,6 +384,7 @@ impl BorshSchema for String { str::declaration() } } + impl BorshSchema for str { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { @@ -401,6 +402,57 @@ impl BorshSchema for str { } } +/// Module is available if borsh is built with `features = ["ascii"]`. +#[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; + + impl BorshSchema for ascii::AsciiString { + #[inline] + fn add_definitions_recursively(definitions: &mut BTreeMap) { + ascii::AsciiStr::add_definitions_recursively(definitions); + } + #[inline] + fn declaration() -> Declaration { + ascii::AsciiStr::declaration() + } + } + + impl BorshSchema for ascii::AsciiStr { + #[inline] + fn add_definitions_recursively(definitions: &mut BTreeMap) { + let definition = Definition::Sequence { + length_width: Definition::DEFAULT_LENGTH_WIDTH, + length_range: Definition::DEFAULT_LENGTH_RANGE, + elements: ascii::AsciiChar::declaration(), + }; + add_definition(Self::declaration(), definition, definitions); + ascii::AsciiChar::add_definitions_recursively(definitions); + } + #[inline] + fn declaration() -> Declaration { + "AsciiString".into() + } + } + + impl BorshSchema for ascii::AsciiChar { + #[inline] + fn add_definitions_recursively(definitions: &mut BTreeMap) { + add_definition(Self::declaration(), Definition::Primitive(1), definitions); + } + #[inline] + fn declaration() -> Declaration { + "AsciiChar".into() + } + } +} + impl BorshSchema for core::ops::RangeFull { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { diff --git a/borsh/src/ser/mod.rs b/borsh/src/ser/mod.rs index 1410edf1a..86da9bd72 100644 --- a/borsh/src/ser/mod.rs +++ b/borsh/src/ser/mod.rs @@ -210,6 +210,37 @@ impl BorshSerialize for String { } } +/// Module is available if borsh is built with `features = ["ascii"]`. +#[cfg(feature = "ascii")] +pub mod ascii { + //! + //! Module defines [BorshSerialize] implementation for + //! some types from [ascii](::ascii) crate. + use super::BorshSerialize; + use crate::io::{Result, Write}; + + impl BorshSerialize for ascii::AsciiChar { + #[inline] + fn serialize(&self, writer: &mut W) -> Result<()> { + self.as_byte().serialize(writer) + } + } + + impl BorshSerialize for ascii::AsciiStr { + #[inline] + fn serialize(&self, writer: &mut W) -> Result<()> { + self.as_bytes().serialize(writer) + } + } + + impl BorshSerialize for ascii::AsciiString { + #[inline] + fn serialize(&self, writer: &mut W) -> Result<()> { + self.as_bytes().serialize(writer) + } + } +} + /// Helper method that is used to serialize a slice of data (without the length marker). #[inline] fn serialize_slice(data: &[T], writer: &mut W) -> Result<()> { diff --git a/borsh/tests/snapshots/test_ascii_strings__a.snap b/borsh/tests/snapshots/test_ascii_strings__a.snap new file mode 100644 index 000000000..4ae7c4b7d --- /dev/null +++ b/borsh/tests/snapshots/test_ascii_strings__a.snap @@ -0,0 +1,11 @@ +--- +source: borsh/tests/test_ascii_strings.rs +expression: buf +--- +[ + 1, + 0, + 0, + 0, + 97, +] diff --git a/borsh/tests/snapshots/test_ascii_strings__empty_string.snap b/borsh/tests/snapshots/test_ascii_strings__empty_string.snap new file mode 100644 index 000000000..a3af6fd23 --- /dev/null +++ b/borsh/tests/snapshots/test_ascii_strings__empty_string.snap @@ -0,0 +1,10 @@ +--- +source: borsh/tests/test_ascii_strings.rs +expression: buf +--- +[ + 0, + 0, + 0, + 0, +] diff --git a/borsh/tests/snapshots/test_ascii_strings__hello_10.snap b/borsh/tests/snapshots/test_ascii_strings__hello_10.snap new file mode 100644 index 000000000..fca374e34 --- /dev/null +++ b/borsh/tests/snapshots/test_ascii_strings__hello_10.snap @@ -0,0 +1,370 @@ +--- +source: borsh/tests/test_ascii_strings.rs +expression: buf +--- +[ + 104, + 1, + 0, + 0, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, + 33, +] diff --git a/borsh/tests/snapshots/test_ascii_strings__hello_world.snap b/borsh/tests/snapshots/test_ascii_strings__hello_world.snap new file mode 100644 index 000000000..b2d10ca5a --- /dev/null +++ b/borsh/tests/snapshots/test_ascii_strings__hello_world.snap @@ -0,0 +1,21 @@ +--- +source: borsh/tests/test_ascii_strings.rs +expression: buf +--- +[ + 11, + 0, + 0, + 0, + 104, + 101, + 108, + 108, + 111, + 32, + 119, + 111, + 114, + 108, + 100, +] diff --git a/borsh/tests/snapshots/test_ascii_strings__x_1024.snap b/borsh/tests/snapshots/test_ascii_strings__x_1024.snap new file mode 100644 index 000000000..4fbe58283 --- /dev/null +++ b/borsh/tests/snapshots/test_ascii_strings__x_1024.snap @@ -0,0 +1,1034 @@ +--- +source: borsh/tests/test_ascii_strings.rs +expression: buf +--- +[ + 0, + 4, + 0, + 0, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, +] diff --git a/borsh/tests/test_ascii_strings.rs b/borsh/tests/test_ascii_strings.rs new file mode 100644 index 000000000..ea8774c17 --- /dev/null +++ b/borsh/tests/test_ascii_strings.rs @@ -0,0 +1,134 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg(feature = "ascii")] + +use borsh::{from_slice, to_vec}; + +extern crate alloc; +use alloc::string::String; + +/// Verifies serialisation and deserialisation of an ASCII string `value`. +fn check_ascii(value: &str) -> alloc::vec::Vec { + // Caller promises value is ASCII. + let ascii_str = ascii::AsciiStr::from_ascii(&value).unwrap(); + let buf = to_vec(ascii_str).unwrap(); + // AsciiStr and AsciiString serialise the same way String does. + assert_eq!(buf, to_vec(&ascii::AsciiString::from(ascii_str)).unwrap()); + // Check round trip. + let got = from_slice::(&buf).unwrap(); + assert_eq!(ascii_str, got); + buf +} + +macro_rules! test_ascii_string { + ($test_name: ident, $str: expr, $snap:expr) => { + #[test] + fn $test_name() { + let value = String::from($str); + let _buf = check_ascii(&value); + #[cfg(feature = "std")] + if $snap { + insta::assert_debug_snapshot!(_buf); + } + } + }; +} + +test_ascii_string!(test_empty_string, "", true); +test_ascii_string!(test_a, "a", true); +test_ascii_string!(test_hello_world, "hello world", true); +test_ascii_string!(test_x_1024, "x".repeat(1024), true); +test_ascii_string!(test_x_4096, "x".repeat(4096), false); +test_ascii_string!(test_x_65535, "x".repeat(65535), false); +test_ascii_string!(test_hello_10, "hello world!".repeat(30), true); +test_ascii_string!(test_hello_1000, "hello Achilles!".repeat(1000), false); + +#[test] +fn test_ascii_char() { + use ascii::AsciiChar; + + let buf = to_vec(&AsciiChar::Dot).unwrap(); + assert_eq!(".".as_bytes(), buf); + assert_eq!(AsciiChar::Dot, from_slice::(&buf).unwrap()); + + from_slice::(&[b'\x80']).unwrap_err(); +} + +mod de_errors { + use alloc::string::ToString; + use borsh::from_slice; + + #[test] + fn test_non_ascii() { + let buf = borsh::to_vec(&[0xbf, 0xf3, 0xb3, 0x77][..]).unwrap(); + assert_eq!( + from_slice::(&buf) + .unwrap_err() + .to_string(), + "the byte at index 0 is not ASCII" + ); + + let buf = borsh::to_vec("żółw").unwrap(); + assert_eq!( + from_slice::(&buf) + .unwrap_err() + .to_string(), + "the byte at index 0 is not ASCII" + ); + + assert_eq!( + from_slice::(&[0xbf]) + .unwrap_err() + .to_string(), + "not an ASCII character" + ); + } +} + +#[cfg(feature = "unstable__schema")] +mod schema { + use alloc::{collections::BTreeMap, string::ToString}; + use borsh::schema::{BorshSchema, Definition}; + macro_rules! map( + () => { BTreeMap::new() }; + { $($key:expr => $value:expr),+ } => { + { + let mut m = BTreeMap::new(); + $( + m.insert($key.to_string(), $value); + )+ + m + } + }; + ); + + #[test] + fn test_ascii_strings() { + assert_eq!("AsciiString", ascii::AsciiStr::declaration()); + assert_eq!("AsciiString", ascii::AsciiString::declaration()); + assert_eq!("AsciiChar", ascii::AsciiChar::declaration()); + + let want_char = map! { + "AsciiChar" => Definition::Primitive(1) + }; + let mut actual_defs = map!(); + ascii::AsciiChar::add_definitions_recursively(&mut actual_defs); + assert_eq!(want_char, actual_defs); + + let want = map! { + "AsciiString" => Definition::Sequence { + length_width: Definition::DEFAULT_LENGTH_WIDTH, + length_range: Definition::DEFAULT_LENGTH_RANGE, + elements: "AsciiChar".to_string() + }, + "AsciiChar" => Definition::Primitive(1) + }; + + let mut actual_defs = map!(); + ascii::AsciiStr::add_definitions_recursively(&mut actual_defs); + assert_eq!(want, actual_defs); + + let mut actual_defs = map!(); + ascii::AsciiString::add_definitions_recursively(&mut actual_defs); + assert_eq!(want, actual_defs); + } +} diff --git a/borsh/tests/test_strings.rs b/borsh/tests/test_strings.rs index 6bf084a6e..e3bbe79bf 100644 --- a/borsh/tests/test_strings.rs +++ b/borsh/tests/test_strings.rs @@ -1,24 +1,33 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "std"))] extern crate alloc; -#[cfg(not(feature = "std"))] -use alloc::string::{String, ToString}; + +use alloc::string::String; use borsh::{from_slice, to_vec}; +/// Verifies serialisation and deserialisation of the given string. +/// +/// Returns serialised representation of the string. +fn check_string(value: &str) -> alloc::vec::Vec { + // Encoding is the same as Vec with UTF-8 encoded string. + let buf = to_vec(value.as_bytes()).unwrap(); + assert_eq!(buf, to_vec(value).unwrap()); + assert_eq!(buf, to_vec(&String::from(value)).unwrap()); + // Check round trip. + assert_eq!(value, from_slice::(&buf).unwrap()); + buf +} + macro_rules! test_string { - ($test_name: ident, $str: expr, $snap: expr) => { + ($test_name: ident, $str: expr, $snap:expr) => { #[test] fn $test_name() { - let s = $str.to_string(); - - let buf = to_vec(&s).unwrap(); + let value = String::from($str); + let _buf = check_string(&value); #[cfg(feature = "std")] if $snap { - insta::assert_debug_snapshot!(buf); + insta::assert_debug_snapshot!(_buf); } - let actual_s = from_slice::(&buf).expect("failed to deserialize a string"); - assert_eq!(actual_s, s); } }; } @@ -31,4 +40,5 @@ test_string!(test_x_4096, "x".repeat(4096), false); test_string!(test_x_65535, "x".repeat(65535), false); test_string!(test_hello_10, "hello world!".repeat(30), true); test_string!(test_hello_1000, "hello world!".repeat(1000), false); + test_string!(test_non_ascii, "💩", true);