From 5f33c594ee18b880fbd903aeb3875675ea8088a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= <26653921+dj8yfo@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:52:48 +0200 Subject: [PATCH] feat: impl `BorshSchema` for `Cow<'a, T>` (#284) * test: correct `test_ultimate_combined_all_features` test expected return type variant for Cow impl<'a, 'b, B, C> PartialEq> for Cow<'a, B> where B: PartialEq + ToOwned + ?Sized, C: ToOwned + ?Sized, allowed to use `Cow::Borrowed` instead of `Cow::Owned` * test: add `test_cow` * feat: impl `BorshSchema` for `Cow<'a, T>` --- borsh/src/schema.rs | 15 ++ .../snapshots/test_cow__cow_byte_slice.snap | 20 +++ .../test_cow__cow_slice_of_cow_str.snap | 55 ++++++ borsh/tests/snapshots/test_cow__cow_str.snap | 22 +++ borsh/tests/test_cow.rs | 157 ++++++++++++++++++ borsh/tests/test_simple_structs.rs | 2 +- 6 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 borsh/tests/snapshots/test_cow__cow_byte_slice.snap create mode 100644 borsh/tests/snapshots/test_cow__cow_slice_of_cow_str.snap create mode 100644 borsh/tests/snapshots/test_cow__cow_str.snap create mode 100644 borsh/tests/test_cow.rs diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs index e795f0eda..07cbc0abd 100644 --- a/borsh/src/schema.rs +++ b/borsh/src/schema.rs @@ -14,6 +14,7 @@ #![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}, format, @@ -382,6 +383,20 @@ pub mod rc { } } +impl BorshSchema for borrow::Cow<'_, T> +where + T: borrow::ToOwned + ?Sized, + T::Owned: BorshSchema, +{ + fn add_definitions_recursively(definitions: &mut BTreeMap) { + ::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + ::declaration() + } +} + macro_rules! impl_for_renamed_primitives { ($($ty: ty : $name: ident => $size: expr);+) => { $( diff --git a/borsh/tests/snapshots/test_cow__cow_byte_slice.snap b/borsh/tests/snapshots/test_cow__cow_byte_slice.snap new file mode 100644 index 000000000..a28105466 --- /dev/null +++ b/borsh/tests/snapshots/test_cow__cow_byte_slice.snap @@ -0,0 +1,20 @@ +--- +source: borsh/tests/test_cow.rs +expression: encoded +--- +[ + 10, + 0, + 0, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, +] diff --git a/borsh/tests/snapshots/test_cow__cow_slice_of_cow_str.snap b/borsh/tests/snapshots/test_cow__cow_slice_of_cow_str.snap new file mode 100644 index 000000000..368354b4b --- /dev/null +++ b/borsh/tests/snapshots/test_cow__cow_slice_of_cow_str.snap @@ -0,0 +1,55 @@ +--- +source: borsh/tests/test_cow.rs +expression: encoded +--- +[ + 2, + 0, + 0, + 0, + 18, + 0, + 0, + 0, + 102, + 105, + 114, + 115, + 116, + 32, + 115, + 116, + 97, + 116, + 105, + 99, + 32, + 105, + 110, + 112, + 117, + 116, + 19, + 0, + 0, + 0, + 115, + 101, + 99, + 111, + 110, + 100, + 32, + 115, + 116, + 97, + 116, + 105, + 99, + 32, + 105, + 110, + 112, + 117, + 116, +] diff --git a/borsh/tests/snapshots/test_cow__cow_str.snap b/borsh/tests/snapshots/test_cow__cow_str.snap new file mode 100644 index 000000000..f810057e3 --- /dev/null +++ b/borsh/tests/snapshots/test_cow__cow_str.snap @@ -0,0 +1,22 @@ +--- +source: borsh/tests/test_cow.rs +expression: encoded +--- +[ + 12, + 0, + 0, + 0, + 115, + 116, + 97, + 116, + 105, + 99, + 32, + 105, + 110, + 112, + 117, + 116, +] diff --git a/borsh/tests/test_cow.rs b/borsh/tests/test_cow.rs new file mode 100644 index 000000000..25c41d2e8 --- /dev/null +++ b/borsh/tests/test_cow.rs @@ -0,0 +1,157 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use borsh::{from_slice, to_vec}; +use core::{matches, ops::Deref}; +extern crate alloc; +use alloc::string::ToString; + +#[cfg(feature = "std")] +use std::borrow::Cow; + +#[cfg(not(feature = "std"))] +use alloc::{borrow::Cow, vec}; + +#[test] +fn test_cow_str() { + let input: Cow<'_, str> = Cow::Borrowed("static input"); + + let encoded = to_vec(&input).unwrap(); + + #[cfg(feature = "std")] + insta::assert_debug_snapshot!(encoded); + + let out: Cow<'_, str> = from_slice(&encoded).unwrap(); + + assert!(matches!(out, Cow::Owned(..))); + + assert_eq!(input, out); + assert_eq!(out, "static input"); +} + +#[test] +fn test_cow_byte_slice() { + let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let input: Cow<'_, [u8]> = Cow::Borrowed(&arr); + + let encoded = to_vec(&input).unwrap(); + + #[cfg(feature = "std")] + insta::assert_debug_snapshot!(encoded); + + let out: Cow<'_, [u8]> = from_slice(&encoded).unwrap(); + + assert!(matches!(out, Cow::Owned(..))); + + assert_eq!(input, out); + assert_eq!(out, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +fn test_cow_slice_of_cow_str() { + let arr = [ + Cow::Borrowed("first static input"), + Cow::Owned("second static input".to_string()), + ]; + let input: Cow<'_, [Cow<'_, str>]> = Cow::Borrowed(&arr); + + let encoded = to_vec(&input).unwrap(); + + #[cfg(feature = "std")] + insta::assert_debug_snapshot!(encoded); + + let out: Cow<'_, [Cow<'_, str>]> = from_slice(&encoded).unwrap(); + + assert!(matches!(out, Cow::Owned(..))); + + for element in out.deref() { + assert!(matches!(element, Cow::Owned(..))); + } + + assert_eq!(input, out); + assert_eq!( + out, + vec![ + Cow::Borrowed("first static input"), + Cow::Borrowed("second static input"), + ] + ); +} + +#[macro_use] +mod common_macro; + +#[cfg(feature = "unstable__schema")] +mod schema { + + use super::common_macro::schema_imports::*; + #[cfg(feature = "std")] + use std::borrow::Cow; + + #[cfg(not(feature = "std"))] + use alloc::borrow::Cow; + + #[test] + fn test_cow_str() { + assert_eq!("String", as BorshSchema>::declaration()); + + let mut actual_defs = schema_map!(); + as BorshSchema>::add_definitions_recursively(&mut actual_defs); + assert_eq!( + schema_map! { + "String" => Definition::Sequence { + length_width: Definition::DEFAULT_LENGTH_WIDTH, + length_range: Definition::DEFAULT_LENGTH_RANGE, + elements: "u8".to_string() + }, + "u8" => Definition::Primitive(1) + }, + actual_defs + ); + } + + #[test] + fn test_cow_byte_slice() { + assert_eq!("Vec", as BorshSchema>::declaration()); + + let mut actual_defs = schema_map!(); + as BorshSchema>::add_definitions_recursively(&mut actual_defs); + assert_eq!( + schema_map! { + "Vec" => Definition::Sequence { + length_width: Definition::DEFAULT_LENGTH_WIDTH, + length_range: Definition::DEFAULT_LENGTH_RANGE, + elements: "u8".to_string(), + }, + "u8" => Definition::Primitive(1) + }, + actual_defs + ); + } + + #[test] + fn test_cow_slice_of_cow_str() { + assert_eq!( + "Vec", + ]> as BorshSchema>::declaration() + ); + + let mut actual_defs = schema_map!(); + ]> as BorshSchema>::add_definitions_recursively(&mut actual_defs); + assert_eq!( + schema_map! { + "Vec" => Definition::Sequence { + length_width: Definition::DEFAULT_LENGTH_WIDTH, + length_range: Definition::DEFAULT_LENGTH_RANGE, + elements: "String".to_string(), + }, + "String" => Definition::Sequence { + length_width: Definition::DEFAULT_LENGTH_WIDTH, + length_range: Definition::DEFAULT_LENGTH_RANGE, + elements: "u8".to_string() + }, + "u8" => Definition::Primitive(1) + }, + actual_defs + ); + } +} diff --git a/borsh/tests/test_simple_structs.rs b/borsh/tests/test_simple_structs.rs index 5385e2a8c..1557536d0 100644 --- a/borsh/tests/test_simple_structs.rs +++ b/borsh/tests/test_simple_structs.rs @@ -159,7 +159,7 @@ fn test_ultimate_combined_all_features() { lazy: Some(5), c: borrow::Cow::Owned("Hello".to_string()), cow_arr: borrow::Cow::Owned(vec![ - borrow::Cow::Borrowed("Hello1"), + borrow::Cow::Owned("Hello1".to_string()), borrow::Cow::Owned("Hello2".to_string()), ]), range_u32: 12..71,