use core::str::Utf8Error;
use bytes::Buf;
use crate::{
CompactString,
Repr,
};
impl CompactString {
/// Converts a buffer of bytes to a [`CompactString`]
///
/// # Examples
/// ### Basic usage
/// ```
/// # use compact_str::CompactString;
/// # use std::collections::VecDeque;
///
/// // `bytes::Buf` is implemented for `VecDeque<u8>`
/// let mut sparkle_heart = VecDeque::from(vec![240, 159, 146, 150]);
/// // We know these bytes are valid, so we can `.unwrap()` or `.expect(...)` here
/// let compact_str = CompactString::from_utf8_buf(&mut sparkle_heart).expect("valid utf-8");
///
/// assert_eq!(compact_str, "💖");
/// ```
///
/// ### With invalid/non-UTF8 bytes
/// ```
/// # use compact_str::CompactString;
/// # use std::io;
///
/// // `bytes::Buf` is implemented for `std::io::Cursor<&[u8]>`
/// let mut invalid = io::Cursor::new(&[0, 159]);
///
/// // The provided buffer is invalid, so trying to create a `CompactString` will fail
/// assert!(CompactString::from_utf8_buf(&mut invalid).is_err());
/// ```
#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
pub fn from_utf8_buf<B: Buf>(buf: &mut B) -> Result<Self, Utf8Error> {
Repr::from_utf8_buf(buf).map(CompactString)
}
/// Converts a buffer of bytes to a [`CompactString`], without checking that the provided buffer
/// is valid UTF-8.
///
/// # Safety
/// This function is unsafe because it does not check that the provided bytes are valid UTF-8.
/// If this constraint is violated, it may cause memory safety issues with futures uses of the
/// `CompactString`, as the rest of the library assumes that `CompactString`s are valid UTF-8
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// # use std::io;
///
/// let word = "hello world";
/// // `bytes::Buf` is implemented for `std::io::Cursor<&[u8]>`
/// let mut buffer = io::Cursor::new(word.as_bytes());
/// let compact_str = unsafe { CompactString::from_utf8_buf_unchecked(&mut buffer) };
///
/// assert_eq!(compact_str, word);
/// ```
#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
pub unsafe fn from_utf8_buf_unchecked<B: Buf>(buf: &mut B) -> Self {
let repr = Repr::from_utf8_buf_unchecked(buf);
CompactString(repr)
}
}
#[cfg(test)]
mod test {
use std::io::Cursor;
use proptest::prelude::*;
use test_strategy::proptest;
use crate::tests::{
rand_bytes,
rand_unicode,
};
use crate::CompactString;
const MAX_SIZE: usize = core::mem::size_of::<String>();
#[proptest]
#[cfg_attr(miri, ignore)]
fn proptest_buffers_roundtrip(#[strategy(rand_unicode())] word: String) {
let mut buf = Cursor::new(word.as_bytes());
let compact = CompactString::from_utf8_buf(&mut buf).unwrap();
proptest::prop_assert_eq!(&word, &compact);
}
#[proptest]
#[cfg_attr(miri, ignore)]
fn proptest_allocated_properly(#[strategy(rand_unicode())] word: String) {
let mut buf = Cursor::new(word.as_bytes());
let compact = CompactString::from_utf8_buf(&mut buf).unwrap();
if word.len() <= MAX_SIZE {
proptest::prop_assert!(!compact.is_heap_allocated())
} else {
proptest::prop_assert!(compact.is_heap_allocated())
}
}
#[proptest]
#[cfg_attr(miri, ignore)]
fn proptest_only_accept_valid_utf8(#[strategy(rand_bytes())] bytes: Vec<u8>) {
let mut buf = Cursor::new(bytes.as_slice());
let compact_result = CompactString::from_utf8_buf(&mut buf);
let str_result = core::str::from_utf8(bytes.as_slice());
match (compact_result, str_result) {
(Ok(c), Ok(s)) => prop_assert_eq!(c, s),
(Err(c_err), Err(s_err)) => prop_assert_eq!(c_err, s_err),
_ => panic!("CompactString and core::str read UTF-8 differently?"),
}
}
}