From 5cbfd3ecd2baf2ed911ecff3ae8a52a965c92950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 10 Jan 2024 14:41:33 +0100 Subject: [PATCH 1/5] impl Index --- common/src/zalgo_string.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/common/src/zalgo_string.rs b/common/src/zalgo_string.rs index 6065126..0ef5b54 100644 --- a/common/src/zalgo_string.rs +++ b/common/src/zalgo_string.rs @@ -2,7 +2,10 @@ use crate::{decode_byte_pair, fmt, zalgo_encode, Error}; -use core::iter::{ExactSizeIterator, FusedIterator}; +use core::{ + iter::{ExactSizeIterator, FusedIterator}, + ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, +}; #[cfg(not(feature = "std"))] use alloc::{borrow::Cow, string::String, vec::Vec}; @@ -631,6 +634,26 @@ impl fmt::Display for ZalgoString { } } +// region: impl index + +macro_rules! impl_index { + ($($range:ty),+) => { + $( + impl Index<$range> for ZalgoString { + type Output = str; + #[inline] + fn index(&self, index: $range) -> &Self::Output { + &self.0[index] + } + } + )+ + }; +} + +impl_index! {Range, RangeTo, RangeFrom, RangeInclusive, RangeToInclusive, RangeFull} + +// endregion: impl index + #[cfg(test)] mod test { use super::*; From 3bc8501f11942abf601a2b3804ca0ab5e27c597d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 10 Jan 2024 15:07:21 +0100 Subject: [PATCH 2/5] Add get as a non-panicking alternative to indexing --- common/src/zalgo_string.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/common/src/zalgo_string.rs b/common/src/zalgo_string.rs index 0ef5b54..fb3a9ef 100644 --- a/common/src/zalgo_string.rs +++ b/common/src/zalgo_string.rs @@ -5,6 +5,7 @@ use crate::{decode_byte_pair, fmt, zalgo_encode, Error}; use core::{ iter::{ExactSizeIterator, FusedIterator}, ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, + slice::SliceIndex, }; #[cfg(not(feature = "std"))] @@ -74,6 +75,25 @@ impl ZalgoString { &self.0 } + /// Returns a subslice of `self`. + /// + /// This is the non-panicking alternative to indexing the `ZalgoString`. Returns [`None`] whenever + /// the equivalent indexing operation would panic. + /// + /// # Example + /// + /// ``` + /// # use zalgo_codec_common::{Error, ZalgoString}; + /// let zs = ZalgoString::new("Zalgo")?; + /// assert_eq!(zs.get(0..1), Some("E")); + /// assert_eq!(zs.get(1..=2), Some("\u{33a}")); + /// # Ok::<(), Error>(()) + /// ``` + #[inline] + pub fn get>(&self, index: I) -> Option<&>::Output> { + self.0.get(index) + } + /// Returns an iterator over the encoded characters of the `ZalgoString`. /// /// The first character is an "E", the others are unicode combining characters. From 90217fdd05edc64623157fe59776224123e6fae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 10 Jan 2024 15:23:09 +0100 Subject: [PATCH 3/5] Add tests for get --- common/src/zalgo_string.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/common/src/zalgo_string.rs b/common/src/zalgo_string.rs index fb3a9ef..f734ca0 100644 --- a/common/src/zalgo_string.rs +++ b/common/src/zalgo_string.rs @@ -77,6 +77,8 @@ impl ZalgoString { /// Returns a subslice of `self`. /// + /// Same as [`str::get`]. + /// /// This is the non-panicking alternative to indexing the `ZalgoString`. Returns [`None`] whenever /// the equivalent indexing operation would panic. /// @@ -85,8 +87,13 @@ impl ZalgoString { /// ``` /// # use zalgo_codec_common::{Error, ZalgoString}; /// let zs = ZalgoString::new("Zalgo")?; - /// assert_eq!(zs.get(0..1), Some("E")); - /// assert_eq!(zs.get(1..=2), Some("\u{33a}")); + /// assert_eq!(zs.get(0..3), Some("E\u{33a}")); + /// + /// // indices not on UTF-8 sequence boundaries + /// assert!(zs.get(0..4).is_none()); + /// + /// // out of bounds + /// assert!(zs.get(..42).is_none()); /// # Ok::<(), Error>(()) /// ``` #[inline] @@ -788,4 +795,12 @@ mod test { assert_eq!(zs.decoded_len(), 0); assert!(zs.into_decoded_string().is_empty()); } + + #[test] + fn test_get() { + let zs = ZalgoString::new("Zalgo").unwrap(); + assert_eq!(zs.get(0..3), Some("E\u{33a}")); + assert!(zs.get(0..2).is_none()); + assert!(zs.get(0..42).is_none()); + } } From e28a6bd0c03de43d59129cd1f9b37782bab5cb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 10 Jan 2024 15:27:06 +0100 Subject: [PATCH 4/5] Add tests to indexing --- common/src/zalgo_string.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/common/src/zalgo_string.rs b/common/src/zalgo_string.rs index f734ca0..79e9292 100644 --- a/common/src/zalgo_string.rs +++ b/common/src/zalgo_string.rs @@ -803,4 +803,21 @@ mod test { assert!(zs.get(0..2).is_none()); assert!(zs.get(0..42).is_none()); } + + #[test] + fn test_indexing() { + let zs = ZalgoString::new("Zalgo").unwrap(); + assert_eq!(&zs[0..3], "E\u{33a}"); + assert_eq!(&zs[..3], "E\u{33a}"); + assert_eq!(&zs[0..=2], "E\u{33a}"); + assert_eq!(&zs[..=2], "E\u{33a}"); + assert_eq!(zs[..], zs); + } + + #[test] + #[should_panic] + fn test_index_panic() { + let zs = ZalgoString::new("Zalgo").unwrap(); + let _a = &zs[0..2]; + } } From 1fc2a40d5d697a726c677b714db31367b6ad0fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 10 Jan 2024 15:28:58 +0100 Subject: [PATCH 5/5] format get generic the same as the get function for str in std --- common/src/zalgo_string.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/zalgo_string.rs b/common/src/zalgo_string.rs index 79e9292..aed0379 100644 --- a/common/src/zalgo_string.rs +++ b/common/src/zalgo_string.rs @@ -97,7 +97,10 @@ impl ZalgoString { /// # Ok::<(), Error>(()) /// ``` #[inline] - pub fn get>(&self, index: I) -> Option<&>::Output> { + pub fn get(&self, index: I) -> Option<&>::Output> + where + I: SliceIndex, + { self.0.get(index) }