use core::{
fmt::Debug,
ops::{Deref, Index},
};
use crate::FixedCompactBytestrings;
#[repr(transparent)]
#[derive(Clone)]
pub struct FixedCompactStrings(pub(crate) FixedCompactBytestrings);
impl FixedCompactStrings {
#[must_use]
pub const fn new() -> Self {
Self(FixedCompactBytestrings::new())
}
#[must_use]
pub fn with_capacity(data_capacity: usize, capacity_meta: usize) -> Self {
Self(FixedCompactBytestrings::with_capacity(
data_capacity,
capacity_meta,
))
}
pub fn push<S>(&mut self, string: S)
where
S: Deref<Target = str>,
{
self.0.push(string.as_bytes());
}
#[must_use]
pub fn get(&self, index: usize) -> Option<&str> {
let bytes = self.0.get(index)?;
if cfg!(feature = "no_unsafe") {
core::str::from_utf8(bytes).ok()
} else {
unsafe { Some(core::str::from_utf8_unchecked(bytes)) }
}
}
#[must_use]
#[cfg(not(feature = "no_unsafe"))]
pub unsafe fn get_unchecked(&self, index: usize) -> &str {
let bytes = self.0.get_unchecked(index);
core::str::from_utf8_unchecked(bytes)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.0.capacity()
}
#[inline]
#[must_use]
pub fn capacity_meta(&self) -> usize {
self.0.capacity_meta()
}
pub fn clear(&mut self) {
self.0.clear();
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.0.shrink_to_fit();
}
#[inline]
pub fn shrink_meta_to_fit(&mut self) {
self.0.shrink_meta_to_fit();
}
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.0.shrink_to(min_capacity);
}
#[inline]
pub fn shrink_meta_to(&mut self, min_capacity: usize) {
self.0.shrink_meta_to(min_capacity);
}
pub fn remove(&mut self, index: usize) {
self.0.remove(index);
}
#[inline]
#[must_use]
pub fn iter(&self) -> Iter<'_> {
Iter(self.0.iter())
}
}
impl PartialEq for FixedCompactStrings {
fn eq(&self, other: &Self) -> bool {
let len = self.len();
if len != other.len() {
return false;
}
for idx in 0..len {
if self[idx] != other[idx] {
return false;
}
}
true
}
}
impl Debug for FixedCompactStrings {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<S> Extend<S> for FixedCompactStrings
where
S: Deref<Target = str>,
{
#[inline]
fn extend<I: IntoIterator<Item = S>>(&mut self, iter: I) {
for s in iter {
self.push(s);
}
}
}
impl Index<usize> for FixedCompactStrings {
type Output = str;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.get(index).unwrap()
}
}
pub struct Iter<'a>(crate::fixed_compact_bytestrings::Iter<'a>);
impl<'a> Iter<'a> {
pub fn new(inner: &'a FixedCompactStrings) -> Self {
Self(inner.0.iter())
}
fn from_utf8_maybe_checked(bytes: &[u8]) -> Option<&str> {
if cfg!(feature = "no_unsafe") {
core::str::from_utf8(bytes).ok()
} else {
Some(unsafe { core::str::from_utf8_unchecked(bytes) })
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().and_then(Self::from_utf8_maybe_checked)
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n).and_then(Self::from_utf8_maybe_checked)
}
#[inline]
fn count(self) -> usize
where
Self: Sized,
{
self.len()
}
#[inline]
fn last(mut self) -> Option<Self::Item>
where
Self: Sized,
{
self.0.next_back().and_then(Self::from_utf8_maybe_checked)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().and_then(Self::from_utf8_maybe_checked)
}
}
impl ExactSizeIterator for Iter<'_> {
#[inline]
fn len(&self) -> usize {
self.0.len()
}
}
impl<'a> IntoIterator for &'a FixedCompactStrings {
type Item = &'a str;
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<S> FromIterator<S> for FixedCompactStrings
where
S: Deref<Target = str>,
{
fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
let iter = iter.into_iter();
let meta_capacity = match iter.size_hint() {
(a, Some(b)) if a == b => a,
_ => 0,
};
let mut out = FixedCompactStrings::with_capacity(0, meta_capacity);
for s in iter {
out.push(s);
}
out
}
}
impl<S, I> From<I> for FixedCompactStrings
where
S: Deref<Target = str>,
I: IntoIterator<Item = S>,
{
#[inline]
fn from(value: I) -> Self {
FromIterator::from_iter(value)
}
}
impl TryFrom<FixedCompactBytestrings> for FixedCompactStrings {
type Error = core::str::Utf8Error;
fn try_from(value: FixedCompactBytestrings) -> Result<Self, Self::Error> {
for bstr in &value {
let _ = core::str::from_utf8(bstr)?;
}
Ok(Self(value))
}
}
#[cfg(test)]
mod tests {
use crate::FixedCompactStrings;
#[test]
fn exact_size_iterator() {
let mut cmpstrs = FixedCompactStrings::new();
cmpstrs.push("One");
cmpstrs.push("Two");
cmpstrs.push("Three");
let mut iter = cmpstrs.iter();
assert_eq!(iter.len(), 3);
let _ = iter.next();
assert_eq!(iter.len(), 2);
let _ = iter.next();
assert_eq!(iter.len(), 1);
let _ = iter.next();
assert_eq!(iter.len(), 0);
let _ = iter.next();
assert_eq!(iter.len(), 0);
}
#[test]
fn double_ended_iterator() {
let mut cmpbytes = FixedCompactStrings::new();
cmpbytes.push("One");
cmpbytes.push("Two");
cmpbytes.push("Three");
cmpbytes.push("Four");
let mut iter = cmpbytes.iter();
assert_eq!(iter.next(), Some("One"));
assert_eq!(iter.next_back(), Some("Four"));
assert_eq!(iter.next(), Some("Two"));
assert_eq!(iter.next_back(), Some("Three"));
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
}
#[cfg(feature = "serde")]
mod serde {
use serde::{
de::{SeqAccess, Visitor},
Deserialize, Deserializer, Serialize,
};
use crate::FixedCompactStrings;
impl Serialize for FixedCompactStrings {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_seq(self)
}
}
impl<'de> Deserialize<'de> for FixedCompactStrings {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(FixedCompactStringsVisitor)
}
}
struct FixedCompactStringsVisitor;
impl<'de> Visitor<'de> for FixedCompactStringsVisitor {
type Value = FixedCompactStrings;
fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
formatter.write_str("an array of strings")
}
#[inline]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut out =
FixedCompactStrings::with_capacity(0, seq.size_hint().unwrap_or_default());
while let Some(str) = seq.next_element::<&str>()? {
out.push(str);
}
Ok(out)
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(feature = "serde", allow(unused_imports))]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub use self::serde::*;