base64urlsafedata-0.5.4/.cargo_vcs_info.json0000644000000001570000000000100143450ustar { "git": { "sha1": "282d95f20648090bc131cdd33cdced336c33c3d1" }, "path_in_vcs": "base64urlsafedata" }base64urlsafedata-0.5.4/Cargo.lock0000644000000105260000000000100123210ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64urlsafedata" version = "0.5.4" dependencies = [ "base64", "pastey", "serde", "serde_cbor_2", "serde_json", ] [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "crunchy" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "half" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", "zerocopy", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "pastey" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" [[package]] name = "proc-macro2" version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e0f6df8eaa422d97d72edcd152e1451618fed47fabbdbd5a8864167b1d4aff7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_cbor_2" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aec2709de9078e077090abd848e967abab63c9fb3fdb5d4799ad359d8d482c" dependencies = [ "half", "serde", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", "serde_core", ] [[package]] name = "syn" version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" [[package]] name = "zerocopy" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", "syn", ] base64urlsafedata-0.5.4/Cargo.toml0000644000000025100000000000100123360ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.85" name = "base64urlsafedata" version = "0.5.4" authors = [ "William Brown ", "Michael Farrell ", ] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Base 64 Url Safe wrapper for Serde" homepage = "https://github.com/kanidm/webauthn-rs" readme = "README.md" keywords = [ "base64", "serde", ] categories = ["web-programming"] license = "MPL-2.0" repository = "https://github.com/kanidm/webauthn-rs" [lib] name = "base64urlsafedata" path = "src/lib.rs" [dependencies.base64] version = "0.21" [dependencies.pastey] version = "0.1.1" [dependencies.serde] version = "^1.0.228" features = ["derive"] [dev-dependencies.serde_cbor_2] version = "0.13.0" [dev-dependencies.serde_json] version = "^1.0.145" base64urlsafedata-0.5.4/Cargo.toml.orig000064400000000000000000000012201046102023000160140ustar 00000000000000[package] name = "base64urlsafedata" description = "Base 64 Url Safe wrapper for Serde" keywords = ["base64", "serde"] categories = ["web-programming"] readme = "README.md" version = { workspace = true } authors = { workspace = true } rust-version = { workspace = true } edition = { workspace = true } license = { workspace = true } homepage = { workspace = true } repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] serde.workspace = true base64.workspace = true pastey = "0.1.1" [dev-dependencies] serde_cbor_2.workspace = true serde_json.workspace = true base64urlsafedata-0.5.4/README.md000064400000000000000000000001371046102023000144120ustar 00000000000000# Base64 Url Safe Serde Wrapper A wrapper to help inline deserialisation of base64 datatypes. base64urlsafedata-0.5.4/src/common.rs000064400000000000000000000125521046102023000155640ustar 00000000000000/// Macro to declare common functionality for [`Base64UrlSafeData`][0] and /// [`HumanBinaryData`][1] /// /// [0]: crate::Base64UrlSafeData /// [1]: crate::HumanBinaryData macro_rules! common_impls { ($type:ty) => { impl $type { pub const fn new() -> Self { Self(Vec::new()) } pub fn with_capacity(capacity: usize) -> Self { Vec::with_capacity(capacity).into() } } impl Default for $type { fn default() -> Self { Self::new() } } impl std::ops::Deref for $type { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl std::ops::DerefMut for $type { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl std::borrow::Borrow<[u8]> for $type { fn borrow(&self) -> &[u8] { self.0.as_slice() } } impl From> for $type { fn from(value: Vec) -> Self { Self(value) } } impl From<[u8; N]> for $type { fn from(value: [u8; N]) -> Self { Self(value.to_vec()) } } impl From<&[u8]> for $type { fn from(value: &[u8]) -> Self { Self(value.to_vec()) } } impl From<&[u8; N]> for $type { fn from(value: &[u8; N]) -> Self { Self(value.to_vec()) } } impl From<$type> for Vec { fn from(value: $type) -> Self { value.0 } } impl AsRef<[u8]> for $type { fn as_ref(&self) -> &[u8] { &self.0 } } impl AsMut<[u8]> for $type { fn as_mut(&mut self) -> &mut [u8] { &mut self.0 } } macro_rules! partial_eq_impl { ($other:ty) => { impl PartialEq<$other> for $type { fn eq(&self, other: &$other) -> bool { self.as_slice() == &other[..] } } impl PartialEq<$type> for $other { fn eq(&self, other: &$type) -> bool { self.eq(&other.0) } } }; } partial_eq_impl!(Vec); partial_eq_impl!([u8]); partial_eq_impl!(&[u8]); impl PartialEq<[u8; N]> for $type { fn eq(&self, other: &[u8; N]) -> bool { self.0.eq(other) } } impl PartialEq<$type> for [u8; N] { fn eq(&self, other: &$type) -> bool { self.as_slice().eq(&other.0) } } pastey::paste! { #[doc(hidden)] struct [<$type Visitor>]; impl<'de> serde::de::Visitor<'de> for [<$type Visitor>] { type Value = $type; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( formatter, "a url-safe base64-encoded string, bytes, or sequence of integers" ) } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { // Forgive alt base64 decoding formats for config in crate::ALLOWED_DECODING_FORMATS { if let Ok(data) = config.decode(v) { return Ok(<$type>::from(data)); } } Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &self)) } fn visit_seq(self, mut v: A) -> Result where A: serde::de::SeqAccess<'de>, { let mut data = if let Some(sz) = v.size_hint() { Vec::with_capacity(sz) } else { Vec::new() }; while let Some(i) = v.next_element()? { data.push(i) } Ok(<$type>::from(data)) } fn visit_byte_buf(self, v: Vec) -> Result where E: serde::de::Error, { Ok(<$type>::from(v)) } fn visit_bytes(self, v: &[u8]) -> Result where E: serde::de::Error, { Ok(<$type>::from(v)) } } impl<'de> serde::Deserialize<'de> for $type { fn deserialize(deserializer: D) -> Result>::Error> where D: serde::Deserializer<'de>, { // Was previously _str deserializer.deserialize_any([<$type Visitor>]) } } } }; } base64urlsafedata-0.5.4/src/human.rs000064400000000000000000000030421046102023000153760ustar 00000000000000use std::fmt; use crate::{Base64UrlSafeData, URL_SAFE_NO_PAD}; use base64::Engine; use serde::{Serialize, Serializer}; /// Serde wrapper for `Vec` which emits URL-safe, non-padded Base64 for /// *only* human-readable formats, and accepts Base64 and binary formats. /// /// * Deserialisation is described in the [module documentation][crate]. /// /// * Serialisation to [a human-readable format][0] (such as JSON) emits /// URL-safe, non-padded Base64 (per [RFC 4648 §5][sec5]). /// /// * Serialisation to [a non-human-readable format][0] (such as CBOR) emits /// a native "bytes" type, and not encode the value. /// /// [0]: https://docs.rs/serde/latest/serde/trait.Serializer.html#method.is_human_readable /// [sec5]: https://datatracker.ietf.org/doc/html/rfc4648#section-5 #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct HumanBinaryData(Vec); common_impls!(HumanBinaryData); impl From for HumanBinaryData { fn from(value: Base64UrlSafeData) -> Self { Self(value.into()) } } impl PartialEq for HumanBinaryData { fn eq(&self, other: &Base64UrlSafeData) -> bool { self.0.eq(other) } } impl Serialize for HumanBinaryData { fn serialize(&self, serializer: S) -> Result where S: Serializer, { if serializer.is_human_readable() { let encoded = URL_SAFE_NO_PAD.encode(self); serializer.serialize_str(&encoded) } else { serializer.serialize_bytes(self) } } } base64urlsafedata-0.5.4/src/lib.rs000064400000000000000000000124111046102023000150340ustar 00000000000000//! Wrappers for `Vec` to make Serde serialise and deserialise as URL-safe, //! non-padded Base64 (per [RFC 4648 §5][sec5]). //! //! ## Serialisation behaviour //! //! * [`Base64UrlSafeData`] always serialises to URL-safe, non-padded Base64. //! //! * [`HumanBinaryData`] only serialises to URL-safe, non-padded Base64 when //! using a [human-readable format][0]. //! //! Otherwise, it serialises as a "bytes"-like type (like [`serde_bytes`][1]). //! //! This feature is new in `base64urlsafe` v0.1.4. //! //! By comparison, Serde's default behaviour is to serialise `Vec` as a //! sequence of integers. This is a problem for many formats: //! //! * `serde_cbor` encodes as an `array`, rather than a `bytes`. This uses //! zig-zag encoded integers for values > `0x1F`, which averages about 1.88 //! bytes per byte assuming an equal distribution of values. //! //! * `serde_json` encodes as an `Array`, which averages 3.55 bytes per //! byte without whitespace. //! //! Using Base64 encoding averages 1.33 bytes per byte, and most formats pass //! strings nearly-verbatim. //! //! ## Deserialisation behaviour //! //! Both types will deserialise multiple formats, provided the format is //! self-describing (ie: [implements `deserialize_any`][5]): //! //! * Bytes types are passed as-is (new in v0.1.4). //! //! [`HumanBinaryData`] produces this for [non-human-readable formats][0]. //! //! * Sequences of integers are passed as-is. //! //! Serde's default `Vec` serialiser produces this for many formats. //! //! * Strings are decoded Base64 per [RFC 4648 §5 (URL-safe)][sec5] or //! [§4 (standard)][sec4], with optional padding. //! //! [`Base64UrlSafeData`] produces this for all formats, and //! [`HumanBinaryData`] produces this for [human-readable formats][0]. This //! should also be compatible with many other serialisers. //! //! ## Migrating from `Base64UrlSafeData` to `HumanBinaryData` //! //! [`Base64UrlSafeData`] always uses Base64 encoding, which isn't optimal for //! many binary formats. For that reason, it's a good idea to migrate to //! [`HumanBinaryData`] if you're using a binary format. //! //! However, you'll need to make sure *all* readers using [`Base64UrlSafeData`] //! are on `base64urlsafedata` v0.1.4 or later before switching *anything* to //! [`HumanBinaryData`]. Otherwise, they'll not be able to read any data in the //! new format! //! //! Once they're all migrated across, you can start issuing writes in the new //! format. It's a good idea to slowly roll out the change, in case you discover //! something has been left behind. //! //! ## Alternatives //! //! * [`serde_bytes`][1], which implements efficient coding of `Vec` //! [for non-human-readable formats only][2]. //! //! [0]: https://docs.rs/serde/latest/serde/trait.Serializer.html#method.is_human_readable //! [1]: https://docs.rs/serde_bytes //! [2]: https://github.com/serde-rs/bytes/issues/37 //! [5]: https://serde.rs/impl-deserialize.html //! [sec4]: https://datatracker.ietf.org/doc/html/rfc4648#section-4 //! [sec5]: https://datatracker.ietf.org/doc/html/rfc4648#section-5 #![deny(warnings)] #![warn(unused_extern_crates)] #![deny(clippy::todo)] #![deny(clippy::unimplemented)] #![deny(clippy::unwrap_used)] #![deny(clippy::expect_used)] #![deny(clippy::panic)] #![deny(clippy::unreachable)] #![deny(clippy::await_holding_lock)] #![deny(clippy::needless_pass_by_value)] #![deny(clippy::trivially_copy_pass_by_ref)] #[macro_use] mod common; mod human; #[cfg(test)] mod tests; pub use crate::human::HumanBinaryData; use base64::{ engine::general_purpose::{ GeneralPurpose, STANDARD, STANDARD_NO_PAD, URL_SAFE, URL_SAFE_NO_PAD, }, Engine, }; use serde::{Serialize, Serializer}; use std::fmt; use std::hash::Hash; static ALLOWED_DECODING_FORMATS: &[GeneralPurpose] = &[URL_SAFE_NO_PAD, URL_SAFE, STANDARD, STANDARD_NO_PAD]; /// Serde wrapper for `Vec` which always emits URL-safe, non-padded Base64, /// and accepts Base64 and binary formats. /// /// * Deserialisation is described in the [module documentation][crate]. /// /// * Serialisation *always* emits URL-safe, non-padded Base64 (per /// [RFC 4648 §5][sec5]). /// /// Unlike [`HumanBinaryData`], this happens *regardless* of whether the /// underlying serialisation format is [human readable][0]. If you're /// serialising to [non-human-readable formats][0], you should consider /// [migrating to `HumanBinaryData`][crate]. /// /// Otherwise, this type should work as much like a `Vec` as possible. /// /// [0]: https://docs.rs/serde/latest/serde/trait.Serializer.html#method.is_human_readable /// [sec5]: https://datatracker.ietf.org/doc/html/rfc4648#section-5 #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct Base64UrlSafeData(Vec); common_impls!(Base64UrlSafeData); impl From for Base64UrlSafeData { fn from(value: HumanBinaryData) -> Self { Self(value.into()) } } impl PartialEq for Base64UrlSafeData { fn eq(&self, other: &HumanBinaryData) -> bool { self.0.eq(other) } } impl Serialize for Base64UrlSafeData { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let encoded = URL_SAFE_NO_PAD.encode(self); serializer.serialize_str(&encoded) } } base64urlsafedata-0.5.4/src/tests.rs000064400000000000000000000144271046102023000154410ustar 00000000000000use super::*; macro_rules! from_json_test { ($($name:ident: $value:expr,)*) => { $( #[test] fn $name() { let (input, expected): (&str, &[u8]) = $value; assert_eq!(serde_json::from_str::(input).unwrap(), expected); assert_eq!(serde_json::from_str::(input).unwrap(), expected); } )* }; } macro_rules! from_invalid_json_test { ($($name:ident: $value:expr,)*) => { $( #[test] fn $name() { let input: &str = $value; assert!(serde_json::from_str::(input).is_err()); assert!(serde_json::from_str::(input).is_err()); } )* }; } macro_rules! from_cbor_test { ($($name:ident: $value:expr,)*) => { $( #[test] fn $name() { let (input, expected): (&[u8], &[u8]) = $value; assert_eq!(serde_cbor_2::from_slice::(input).unwrap(), expected); assert_eq!(serde_cbor_2::from_slice::(input).unwrap(), expected); } )* }; } macro_rules! from_invalid_cbor_test { ($($name:ident: $value:expr,)*) => { $( #[test] fn $name() { let input: &[u8] = $value; assert!(serde_cbor_2::from_slice::(input).is_err()); assert!(serde_cbor_2::from_slice::(input).is_err()); } )* }; } from_json_test! { from_json_empty_array: ("[]", &[]), from_json_as_array_number: ("[0,1,2,255]", &[0x00, 0x01, 0x02, 0xFF]), from_json_as_array_number_whitespace: ("[0, 1, 2, 255]", &[0x00, 0x01, 0x02, 0xFF]), from_json_empty_string: ("\"\"", &[]), from_json_b64_urlsafe_nonpadded: ("\"AAEC_w\"", &[0x00, 0x01, 0x02, 0xFF]), from_json_b64_urlsafe_padded: ("\"AAEC_w==\"", &[0x00, 0x01, 0x02, 0xFF]), from_json_b64_standard_nonpadded: ("\"AAEC/w\"", &[0x00, 0x01, 0x02, 0xFF]), from_json_b64_standard_padded: ("\"AAEC/w==\"", &[0x00, 0x01, 0x02, 0xFF]), } from_invalid_json_test! { from_json_empty: "", from_json_null: "null", from_json_number: "1", from_json_empty_map: "{}", from_json_map: "{\"1\": \"AAEC_w\"}", } from_cbor_test! { from_cbor_bytes: (&[ 0x44, // bytes(4) 0x00, 0x01, 0x02, 0xFF, ], &[0x00, 0x01, 0x02, 0xFF]), from_cbor_array: (&[ 0x84, // array(4) 0x00, // 0 0x01, // 1 0x02, // 2 0x18, 0xff, // 0xff ], &[0x00, 0x01, 0x02, 0xFF]), from_cbor_empty_array: (&[0x80], &[]), // array(0) from_cbor_empty_string: (&[0x60], &[]), // text(0) from_cbor_string_b64_urlsafe_nonpadded: (&[ 0x66, // text(6) 0x41, 0x41, 0x45, 0x43, 0x5F, 0x77, // "AAEC_w" ], &[0x00, 0x01, 0x02, 0xFF]), from_cbor_string_b64_urlsafe_padded: (&[ 0x68, // text(8) 0x41, 0x41, 0x45, 0x43, 0x5F, 0x77, 0x3D, 0x3D // "AAEC_w==" ], &[0x00, 0x01, 0x02, 0xFF]), from_cbor_string_b64_standard_nonpadded: (&[ 0x66, // text(6) 0x41, 0x41, 0x45, 0x43, 0x2F, 0x77, // "AAEC/w" ], &[0x00, 0x01, 0x02, 0xFF]), from_cbor_string_b64_standard_padded: (&[ 0x68, // text(8) 0x41, 0x41, 0x45, 0x43, 0x2F, 0x77, 0x3D, 0x3D // "AAEC/w==" ], &[0x00, 0x01, 0x02, 0xFF]), } from_invalid_cbor_test! { from_seq_string: &[0x82, 0x61, 0x61, 0x61, 0x62], from_empty: &[], from_positive_int: &[0x01], from_negative_int: &[0x20], from_seq_negative_int: &[0x82, 0x20, 0x21], from_seq_positive_and_negative_int: &[0x82, 0x01, 0x20], } #[test] fn to_json() { let input = [0x00, 0x01, 0x02, 0xff]; // JSON output should always be a base64 string assert_eq!( serde_json::to_string(&Base64UrlSafeData::from(input)).unwrap(), "\"AAEC_w\"", ); assert_eq!( serde_json::to_string(&HumanBinaryData::from(input)).unwrap(), "\"AAEC_w\"", ); } #[test] fn to_cbor() { let input = [0x00, 0x01, 0x02, 0xff]; // Base64UrlSafeData CBOR output should be a base64 encoded string assert_eq!( serde_cbor_2::to_vec(&Base64UrlSafeData::from(input)).unwrap(), vec![ 0x66, // text(6) 0x41, 0x41, 0x45, 0x43, 0x5F, 0x77 // "AAEC_w" ] ); // HumanBinaryData CBOR output should be a bytes assert_eq!( serde_cbor_2::to_vec(&HumanBinaryData::from(input)).unwrap(), vec![ 0x44, // bytes(4) 0x00, 0x01, 0x02, 0xff ] ); } #[test] fn interop_from() { let input = [0x00, 0x01, 0x02, 0xff]; let a = Base64UrlSafeData::from(input.as_ref()); let b = HumanBinaryData::from(input.as_ref()); let c = Base64UrlSafeData::from(b.clone()); assert_eq!(a, c); let d = HumanBinaryData::from(a); assert_eq!(b, d); } #[test] fn interop_equality() { let input = [0x00, 0x01, 0x02, 0xff]; let other = [0xff, 0x00, 0x01, 0x02]; assert_eq!( Base64UrlSafeData::from(input.as_ref()), HumanBinaryData::from(input.as_ref()), ); assert_eq!( HumanBinaryData::from(input.as_ref()), Base64UrlSafeData::from(input.as_ref()), ); assert_eq!(input, Base64UrlSafeData::from(input.as_ref())); assert_eq!(Base64UrlSafeData::from(input.as_ref()), input); assert_eq!(input, HumanBinaryData::from(input.as_ref())); assert_eq!(HumanBinaryData::from(input.as_ref()), input); assert_ne!( Base64UrlSafeData::from(input.as_ref()), HumanBinaryData::from(other.as_ref()), ); assert_ne!( HumanBinaryData::from(input.as_ref()), Base64UrlSafeData::from(other.as_ref()), ); assert_ne!(input, Base64UrlSafeData::from(other.as_ref())); assert_ne!(Base64UrlSafeData::from(other.as_ref()), input); assert_ne!(input, HumanBinaryData::from(other.as_ref())); assert_ne!(HumanBinaryData::from(other.as_ref()), input); } #[test] fn interop_vec() { let mut a = Base64UrlSafeData::from([0, 1, 2, 3]); a.push(4); assert_eq!(vec![0, 1, 2, 3, 4], a); assert_eq!(5, a.len()); let mut a = HumanBinaryData::from([0, 1, 2, 3]); a.push(4); assert_eq!(vec![0, 1, 2, 3, 4], a); assert_eq!(5, a.len()); }