use crate::data::Data;
use crate::error::*;
use std::borrow::{Borrow, BorrowMut, Cow};
use std::cmp::Ordering;
use std::convert::{AsMut, AsRef, Infallible};
use std::ffi::OsStr;
use std::fmt::{Debug, Display, Error as FmtError, Formatter, Write};
use std::hash::{Hash, Hasher};
use std::iter::{Extend, FromIterator};
use std::net::ToSocketAddrs;
use std::ops::{
Add, AddAssign, Bound, Deref, DerefMut, Index, IndexMut, Range, RangeBounds, RangeFrom,
RangeFull, RangeInclusive, RangeTo,
};
use std::path::Path;
use std::rc::Rc;
use std::str::FromStr;
use std::string::{String, ToString};
use std::sync::Arc;
pub type Threadsafe = Arc<String>;
pub type Local = Rc<String>;
#[derive(Clone)]
pub struct ImString<S: Data<String>> {
string: S,
offset: Range<usize>,
}
fn slice_ptr_range(slice: &[u8]) -> Range<*const u8> {
let start = slice.as_ptr();
let end = unsafe { start.add(slice.len()) };
start..end
}
fn try_slice_offset(current: &[u8], candidate: &[u8]) -> Option<Range<usize>> {
let current_slice = slice_ptr_range(current);
let candidate_slice = slice_ptr_range(candidate);
let contains_start = current_slice.start <= candidate_slice.start;
let contains_end = current_slice.end >= candidate_slice.end;
if !contains_start || !contains_end {
return None;
}
let offset_start = unsafe { candidate_slice.start.offset_from(current_slice.start) } as usize;
let offset_end = unsafe { candidate_slice.end.offset_from(current_slice.start) } as usize;
Some(offset_start..offset_end)
}
impl<S: Data<String>> ImString<S> {
pub fn as_bytes(&self) -> &[u8] {
&self.string.get().as_bytes()[self.offset.clone()]
}
pub fn capacity(&self) -> usize {
self.string.get().capacity()
}
pub fn from_std_string(string: String) -> Self {
ImString {
offset: 0..string.as_bytes().len(),
string: S::new(string),
}
}
pub fn clear(&mut self) {
unsafe {
self.try_modify_unchecked(|string| string.clear());
}
self.offset = 0..0;
}
pub fn as_mut_str(&mut self) -> &mut str {
if self.string.get_mut().is_none() {
let string = self.as_str().to_string();
self.offset = 0..string.len();
self.string = S::new(string);
}
let string = self.string.get_mut().unwrap();
return &mut string[self.offset.clone()];
}
unsafe fn try_modify_unchecked<F: FnOnce(&mut String)>(&mut self, f: F) -> bool {
if let Some(string) = self.string.get_mut() {
f(string);
true
} else {
false
}
}
pub fn with_capacity(capacity: usize) -> Self {
ImString::from_std_string(String::with_capacity(capacity))
}
pub fn len(&self) -> usize {
self.offset.len()
}
pub fn into_std_string(mut self) -> String {
if self.offset.start == 0 {
if let Some(string) = self.string.get_mut() {
string.truncate(self.offset.end);
return std::mem::take(string);
}
}
self.as_str().to_string()
}
pub fn new() -> Self {
ImString::from_std_string(String::new())
}
pub fn as_str(&self) -> &str {
let slice = &self.string.get().as_bytes()[self.offset.start..self.offset.end];
unsafe { std::str::from_utf8_unchecked(slice) }
}
pub fn from_utf16(string: &[u16]) -> Result<Self, FromUtf16Error> {
Ok(ImString::from_std_string(String::from_utf16(string)?))
}
pub fn from_utf16_lossy(string: &[u16]) -> Self {
ImString::from_std_string(String::from_utf16_lossy(string))
}
pub fn from_utf8(vec: Vec<u8>) -> Result<Self, FromUtf8Error> {
Ok(ImString::from_std_string(String::from_utf8(vec)?))
}
pub fn from_utf8_lossy(bytes: &[u8]) -> Self {
let string = String::from_utf8_lossy(bytes).into_owned();
ImString::from_std_string(string)
}
pub unsafe fn from_utf8_unchecked(vec: Vec<u8>) -> Self {
ImString::from_std_string(String::from_utf8_unchecked(vec))
}
pub fn into_bytes(self) -> Vec<u8> {
self.into_std_string().into_bytes()
}
unsafe fn unchecked_append<F: FnOnce(String) -> String>(&mut self, f: F) {
match self.string.get_mut() {
Some(mut string_ref) if self.offset.start == 0 => {
let mut string: String = std::mem::take(&mut string_ref);
string.truncate(self.offset.end);
*string_ref = f(string);
}
_ => {
self.string = S::new(f(self.as_str().to_string()));
self.offset.start = 0;
}
}
self.offset.end = self.string.get().as_bytes().len();
}
pub fn insert(&mut self, index: usize, c: char) {
unsafe {
self.unchecked_append(|mut string| {
string.insert(index, c);
string
});
}
}
pub fn insert_str(&mut self, index: usize, s: &str) {
unsafe {
self.unchecked_append(|mut string| {
string.insert_str(index, s);
string
});
}
}
pub fn truncate(&mut self, length: usize) {
let length = self.offset.start + length;
if let Some(string) = self.string.get_mut() {
string.truncate(length);
}
self.offset.end = self.offset.end.min(length);
}
pub fn pop(&mut self) -> Option<char> {
let last_char = self.as_str().chars().rev().next()?;
self.offset.end -= last_char.len_utf8();
Some(last_char)
}
pub fn push(&mut self, c: char) {
unsafe {
self.unchecked_append(|mut string| {
string.push(c);
string
});
}
}
pub fn push_str(&mut self, slice: &str) {
unsafe {
self.unchecked_append(|mut string| {
string.push_str(slice);
string
});
}
}
pub fn is_empty(&self) -> bool {
self.offset.is_empty()
}
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
self.try_slice(range).unwrap()
}
pub fn try_slice(&self, range: impl RangeBounds<usize>) -> Result<Self, SliceError> {
let start = match range.start_bound() {
Bound::Included(value) => *value,
Bound::Excluded(value) => *value + 1,
Bound::Unbounded => 0,
};
if start > self.offset.len() {
return Err(SliceError::StartOutOfBounds);
}
let end = match range.end_bound() {
Bound::Included(value) => *value - 1,
Bound::Excluded(value) => *value,
Bound::Unbounded => self.offset.len(),
};
if end < start {
return Err(SliceError::EndBeforeStart);
}
if end > self.offset.len() {
return Err(SliceError::EndOutOfBounds);
}
if !self.as_str().is_char_boundary(start) {
return Err(SliceError::StartNotAligned);
}
if !self.as_str().is_char_boundary(end) {
return Err(SliceError::EndNotAligned);
}
let slice = unsafe { self.slice_unchecked(range) };
Ok(slice)
}
pub unsafe fn slice_unchecked(&self, range: impl RangeBounds<usize>) -> Self {
let start = match range.start_bound() {
Bound::Included(value) => *value,
Bound::Excluded(value) => *value + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(value) => *value - 1,
Bound::Excluded(value) => *value,
Bound::Unbounded => self.offset.len(),
};
let offset = self.offset.start + start..self.offset.start + end;
ImString {
string: self.string.clone(),
offset,
}
}
pub fn try_str_ref(&self, string: &str) -> Option<Self> {
self.try_slice_ref(string.as_bytes())
}
pub fn str_ref(&self, string: &str) -> Self {
self.try_str_ref(string)
.unwrap_or_else(|| Self::from(string))
}
pub fn try_slice_ref(&self, slice: &[u8]) -> Option<Self> {
try_slice_offset(self.string.get().as_bytes(), slice).map(|range| ImString {
offset: range,
..self.clone()
})
}
pub fn slice_ref(&self, slice: &[u8]) -> Self {
self.try_slice_ref(slice).unwrap()
}
pub fn try_split_off(&mut self, position: usize) -> Option<Self> {
if position > self.offset.end {
return None;
}
if !self.as_str().is_char_boundary(position) {
return None;
}
let new = ImString {
offset: position..self.offset.end,
..self.clone()
};
self.offset.end = position;
Some(new)
}
pub fn split_off(&mut self, position: usize) -> Self {
self.try_split_off(position).unwrap()
}
pub fn raw_string(&self) -> S {
self.string.clone()
}
pub fn raw_offset(&self) -> Range<usize> {
self.offset.clone()
}
pub fn lines(&self) -> Lines<'_, S> {
ImStringIterator::new(self.string.clone(), self.as_str().lines())
}
pub fn chars(&self) -> Chars<S> {
Chars {
string: self.clone(),
}
}
pub fn char_indices(&self) -> CharIndices<S> {
CharIndices {
offset: 0,
string: self.clone(),
}
}
pub fn trim(&self) -> Self {
self.str_ref(self.as_str().trim())
}
pub fn trim_start(&self) -> Self {
self.str_ref(self.as_str().trim_start())
}
pub fn trim_end(&self) -> Self {
self.str_ref(self.as_str().trim_end())
}
}
impl<S: Data<String>> Default for ImString<S> {
fn default() -> Self {
ImString::new()
}
}
impl<S: Data<String>> From<&str> for ImString<S> {
fn from(string: &str) -> Self {
ImString::from_std_string(string.to_string())
}
}
impl<S: Data<String>> From<char> for ImString<S> {
fn from(c: char) -> Self {
String::from(c).into()
}
}
impl<S: Data<String>> From<String> for ImString<S> {
fn from(string: String) -> Self {
ImString::from_std_string(string)
}
}
impl<'a, S: Data<String>> From<Cow<'a, str>> for ImString<S> {
fn from(string: Cow<'a, str>) -> Self {
ImString::from(string.into_owned())
}
}
impl<S: Data<String>> From<ImString<S>> for String {
fn from(string: ImString<S>) -> Self {
string.into_std_string()
}
}
impl<S: Data<String>> PartialEq<str> for ImString<S> {
fn eq(&self, other: &str) -> bool {
self.as_str().eq(other)
}
}
impl<'a, S: Data<String>> PartialEq<&'a str> for ImString<S> {
fn eq(&self, other: &&'a str) -> bool {
self.as_str().eq(*other)
}
}
impl<S: Data<String>> PartialEq<String> for ImString<S> {
fn eq(&self, other: &String) -> bool {
self.as_str().eq(other.as_str())
}
}
impl<S: Data<String>, O: Data<String>> PartialEq<ImString<O>> for ImString<S> {
fn eq(&self, other: &ImString<O>) -> bool {
self.as_str().eq(other.as_str())
}
}
impl<S: Data<String>> Eq for ImString<S> {}
impl<S: Data<String>> PartialOrd<ImString<S>> for ImString<S> {
fn partial_cmp(&self, other: &ImString<S>) -> Option<Ordering> {
self.as_str().partial_cmp(other.as_str())
}
}
impl<S: Data<String>> Ord for ImString<S> {
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl<S: Data<String>> Debug for ImString<S> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
Debug::fmt(self.as_str(), f)
}
}
impl<S: Data<String>> Display for ImString<S> {
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), FmtError> {
Display::fmt(self.as_str(), formatter)
}
}
impl<S: Data<String>> FromStr for ImString<S> {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(ImString::from(s))
}
}
impl<S: Data<String>> Hash for ImString<S> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.as_str().hash(hasher)
}
}
impl<S: Data<String>> Write for ImString<S> {
fn write_str(&mut self, string: &str) -> Result<(), FmtError> {
self.push_str(string);
Ok(())
}
fn write_char(&mut self, c: char) -> Result<(), FmtError> {
self.push(c);
Ok(())
}
}
impl<S: Data<String>> Index<Range<usize>> for ImString<S> {
type Output = str;
fn index(&self, index: Range<usize>) -> &str {
&self.as_str()[index]
}
}
impl<S: Data<String>> Index<RangeFrom<usize>> for ImString<S> {
type Output = str;
fn index(&self, index: RangeFrom<usize>) -> &str {
&self.as_str()[index]
}
}
impl<S: Data<String>> Index<RangeFull> for ImString<S> {
type Output = str;
fn index(&self, index: RangeFull) -> &str {
&self.as_str()[index]
}
}
impl<S: Data<String>> Index<RangeInclusive<usize>> for ImString<S> {
type Output = str;
fn index(&self, index: RangeInclusive<usize>) -> &str {
&self.as_str()[index]
}
}
impl<S: Data<String>> Index<RangeTo<usize>> for ImString<S> {
type Output = str;
fn index(&self, index: RangeTo<usize>) -> &str {
&self.as_str()[index]
}
}
impl<S: Data<String>> IndexMut<Range<usize>> for ImString<S> {
fn index_mut(&mut self, index: Range<usize>) -> &mut str {
&mut self.as_mut_str()[index]
}
}
impl<S: Data<String>> IndexMut<RangeFrom<usize>> for ImString<S> {
fn index_mut(&mut self, index: RangeFrom<usize>) -> &mut str {
&mut self.as_mut_str()[index]
}
}
impl<S: Data<String>> IndexMut<RangeFull> for ImString<S> {
fn index_mut(&mut self, index: RangeFull) -> &mut str {
&mut self.as_mut_str()[index]
}
}
impl<S: Data<String>> IndexMut<RangeInclusive<usize>> for ImString<S> {
fn index_mut(&mut self, index: RangeInclusive<usize>) -> &mut str {
&mut self.as_mut_str()[index]
}
}
impl<S: Data<String>> IndexMut<RangeTo<usize>> for ImString<S> {
fn index_mut(&mut self, index: RangeTo<usize>) -> &mut str {
&mut self.as_mut_str()[index]
}
}
pub type Lines<'a, S> = ImStringIterator<'a, S, std::str::Lines<'a>>;
pub struct ImStringIterator<'a, S: Data<String>, I: Iterator<Item = &'a str>> {
string: S,
iterator: I,
}
impl<'a, S: Data<String>, I: Iterator<Item = &'a str>> Iterator for ImStringIterator<'a, S, I> {
type Item = ImString<S>;
fn next(&mut self) -> Option<Self::Item> {
match self.iterator.next() {
Some(slice) => {
let offset =
try_slice_offset(self.string.get().as_bytes(), slice.as_bytes()).unwrap();
Some(ImString {
string: self.string.clone(),
offset,
})
}
None => None,
}
}
}
impl<'a, S: Data<String>, I: Iterator<Item = &'a str>> ImStringIterator<'a, S, I> {
fn new(string: S, iterator: I) -> Self {
ImStringIterator { string, iterator }
}
}
#[derive(Clone, Debug)]
pub struct CharIndices<S: Data<String>> {
offset: usize,
string: ImString<S>,
}
impl<S: Data<String>> Iterator for CharIndices<S> {
type Item = (usize, char);
fn next(&mut self) -> Option<Self::Item> {
match self.string.as_str().chars().next() {
Some(c) => {
let len = c.len_utf8();
self.string = self.string.slice(len..);
let offset = self.offset;
self.offset += len;
Some((offset, c))
}
None => None,
}
}
}
#[derive(Clone, Debug)]
pub struct Chars<S: Data<String>> {
string: ImString<S>,
}
impl<S: Data<String>> Iterator for Chars<S> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
match self.string.as_str().chars().next() {
Some(c) => {
self.string = self.string.slice(c.len_utf8()..);
Some(c)
}
None => None,
}
}
}
impl<S: Data<String>> Deref for ImString<S> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl<S: Data<String>> DerefMut for ImString<S> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_str()
}
}
impl<S: Data<String>> Borrow<str> for ImString<S> {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<S: Data<String>> BorrowMut<str> for ImString<S> {
fn borrow_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<S: Data<String>> AsRef<str> for ImString<S> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<S: Data<String>> AsRef<Path> for ImString<S> {
fn as_ref(&self) -> &Path {
self.as_str().as_ref()
}
}
impl<S: Data<String>> AsRef<OsStr> for ImString<S> {
fn as_ref(&self) -> &OsStr {
self.as_str().as_ref()
}
}
impl<S: Data<String>> AsRef<[u8]> for ImString<S> {
fn as_ref(&self) -> &[u8] {
self.as_str().as_ref()
}
}
impl<S: Data<String>> AsMut<str> for ImString<S> {
fn as_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl<S: Data<String>> ToSocketAddrs for ImString<S> {
type Iter = <String as ToSocketAddrs>::Iter;
fn to_socket_addrs(&self) -> std::io::Result<<String as ToSocketAddrs>::Iter> {
self.as_str().to_socket_addrs()
}
}
impl<S: Data<String>> Add<&str> for ImString<S> {
type Output = ImString<S>;
fn add(mut self, string: &str) -> Self::Output {
self.push_str(string);
self
}
}
impl<S: Data<String>> AddAssign<&str> for ImString<S> {
fn add_assign(&mut self, string: &str) {
self.push_str(string);
}
}
impl<S: Data<String>> Extend<char> for ImString<S> {
fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
unsafe {
self.unchecked_append(|mut string| {
string.extend(iter);
string
});
}
}
}
impl<'a, S: Data<String>> Extend<&'a char> for ImString<S> {
fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
unsafe {
self.unchecked_append(|mut string| {
string.extend(iter);
string
});
}
}
}
impl<'a, S: Data<String>> Extend<&'a str> for ImString<S> {
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
unsafe {
self.unchecked_append(|mut string| {
string.extend(iter);
string
});
}
}
}
impl<S: Data<String>> FromIterator<char> for ImString<S> {
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
let mut string = ImString::new();
string.extend(iter);
string
}
}
impl<'a, S: Data<String>> FromIterator<&'a char> for ImString<S> {
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
let mut string = ImString::new();
string.extend(iter);
string
}
}
impl<'a, S: Data<String>> FromIterator<&'a str> for ImString<S> {
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
let mut string = ImString::new();
string.extend(iter);
string
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::Cloned;
fn test_strings<S: Data<String>>() -> Vec<ImString<S>> {
let long = ImString::from("long string here");
let world = ImString::from("world");
let some = ImString::from("some");
let multiline = ImString::from("some\nmulti\nline\nstring\nthat\nis\nlong");
let large: ImString<S> = (0..100).map(|_| "hello\n").collect();
vec![
ImString::new(),
ImString::default(),
ImString::from(""),
ImString::from("a"),
ImString::from("ü"),
ImString::from("hello"),
ImString::from("0.0.0.0:800"),
ImString::from("localhost:1234"),
ImString::from("0.0.0.0:1234"),
large.slice(0..6),
large.slice(6..12),
large.slice(..),
long.clone(),
long.slice(4..10),
long.slice(0..4),
long.slice(4..4),
long.slice(5..),
long.slice(..),
world.clone(),
world.clone(),
some.slice(4..),
some,
multiline.slice(5..15),
multiline,
ImString::from("\u{e4}\u{fc}\u{f6}\u{f8}\u{3a9}"),
ImString::from("\u{1f600}\u{1f603}\u{1f604}"),
ImString::from("o\u{308}u\u{308}a\u{308}"),
]
}
macro_rules! tests {
() => {};
(#[test] fn $name:ident <S: Data<String>>() $body:tt $($rest:tt)*) => {
#[test]
fn $name() {
fn $name <S: Data<String>>() $body
$name::<Threadsafe>();
$name::<Local>();
$name::<Cloned<String>>();
$name::<Box<String>>();
}
tests!{$($rest)*}
};
(#[test] fn $name:ident <S: Data<String>>($string:ident: ImString<S>) $body:tt $($rest:tt)*) => {
#[test]
fn $name() {
fn $name <S: Data<String>>() {
fn $name <S: Data<String>>($string: ImString<S>) $body
for string in test_strings::<S>().into_iter() {
$name(string);
}
}
$name::<Threadsafe>();
$name::<Local>();
$name::<Cloned<String>>();
$name::<Box<String>>();
}
tests!{$($rest)*}
}
}
tests! {
#[test]
fn test_new<S: Data<String>>() {
let string: ImString<S> = ImString::new();
assert_eq!(string.string.get().len(), 0);
assert_eq!(string.offset, 0..0);
}
#[test]
fn test_default<S: Data<String>>() {
let string: ImString<S> = ImString::new();
assert_eq!(string.string.get().len(), 0);
assert_eq!(string.offset, 0..0);
}
#[test]
fn test_with_capacity<S: Data<String>>() {
for capacity in [10, 100, 256] {
let string: ImString<S> = ImString::with_capacity(capacity);
assert!(string.capacity() >= capacity);
assert_eq!(string.string.get().len(), 0);
assert_eq!(string.offset, 0..0);
}
}
#[test]
fn test_offset<S: Data<String>>(string: ImString<S>) {
assert!(string.offset.start <= string.string.get().len());
assert!(string.offset.start <= string.offset.end);
assert!(string.offset.end <= string.string.get().len());
}
#[test]
fn test_as_str<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.as_str(), &string.string.get()[string.offset.clone()]);
assert_eq!(string.as_str().len(), string.len());
}
#[test]
fn test_as_mut_str<S: Data<String>>(string: ImString<S>) {
let string_uppercase = string.as_str().to_ascii_uppercase();
let mut string = string;
let string_slice = string.as_mut_str();
string_slice.make_ascii_uppercase();
assert_eq!(string_uppercase, &*string_slice);
drop(string_slice);
assert_eq!(string, string_uppercase);
}
#[test]
fn test_as_bytes<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.as_bytes(), &string.string.get().as_bytes()[string.offset.clone()]);
assert_eq!(string.as_bytes().len(), string.len());
}
#[test]
fn test_len<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.len(), string.offset.len());
assert_eq!(string.len(), string.as_str().len());
assert_eq!(string.len(), string.as_bytes().len());
}
#[test]
fn test_clear<S: Data<String>>(string: ImString<S>) {
let mut string = string;
string.clear();
assert_eq!(string.as_str(), "");
assert_eq!(string.len(), 0);
}
#[test]
fn test_debug<S: Data<String>>(string: ImString<S>) {
let debug_string = format!("{string:?}");
let debug_str = format!("{:?}", string.as_str());
assert_eq!(debug_string, debug_str);
}
#[test]
fn test_deref<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.deref(), string.as_str());
}
#[test]
fn test_clone<S: Data<String>>(string: ImString<S>) {
assert_eq!(string, string.clone());
}
#[test]
fn test_display<S: Data<String>>(string: ImString<S>) {
let display_string = format!("{string}");
let display_str = format!("{}", string.as_str());
assert_eq!(display_string, display_str);
}
#[test]
fn test_insert_start<S: Data<String>>(string: ImString<S>) {
let mut string = string;
let length = string.len();
string.insert(0, 'h');
assert_eq!(string.len(), length + 1);
assert_eq!(string.chars().nth(0), Some('h'));
}
#[test]
fn test_insert_one<S: Data<String>>(string: ImString<S>) {
if !string.is_empty() && string.is_char_boundary(1) {
let mut string = string;
let length = string.len();
string.insert(1, 'h');
assert_eq!(string.len(), length + 1);
assert_eq!(string.chars().nth(1), Some('h'));
}
}
#[test]
fn test_insert_end<S: Data<String>>(string: ImString<S>) {
let mut string = string;
let length = string.len();
string.insert(length, 'h');
assert_eq!(string.len(), length + 1);
}
#[test]
fn test_insert_str_start<S: Data<String>>(string: ImString<S>) {
let original = string.as_str().to_string();
let mut string = string;
let inserted = "hello";
string.insert_str(0, inserted);
assert_eq!(string.len(), original.len() + inserted.len());
assert_eq!(&string[0..inserted.len()], inserted);
assert_eq!(&string[inserted.len()..], original);
}
#[test]
fn test_insert_str_end<S: Data<String>>(string: ImString<S>) {
let original = string.as_str().to_string();
let mut string = string;
let inserted = "hello";
string.insert_str(string.len(), inserted);
assert_eq!(string.len(), original.len() + inserted.len());
assert_eq!(&string[0..original.len()], original);
assert_eq!(&string[original.len()..], inserted);
}
#[test]
fn test_is_empty<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.is_empty(), string.len() == 0);
assert_eq!(string.is_empty(), string.as_str().is_empty());
}
#[test]
fn test_push<S: Data<String>>(string: ImString<S>) {
let mut string = string;
let mut std_string = string.as_str().to_string();
let c = 'c';
std_string.push(c);
string.push(c);
assert_eq!(string, std_string);
}
#[test]
fn test_push_str<S: Data<String>>(string: ImString<S>) {
let mut string = string;
let mut std_string = string.as_str().to_string();
let s = "string";
std_string.push_str(s);
string.push_str(s);
assert_eq!(string, std_string);
}
#[test]
fn test_pop<S: Data<String>>(string: ImString<S>) {
let mut characters: Vec<char> = string.chars().collect();
let mut string = string;
loop {
let c1 = characters.pop();
let c2 = string.pop();
assert_eq!(c1, c2);
if c1.is_none() {
break;
}
}
}
#[test]
fn test_index_range_full<S: Data<String>>(string: ImString<S>) {
assert_eq!(&string[..], &string.as_str()[..]);
}
#[test]
fn test_index_range_from<S: Data<String>>(string: ImString<S>) {
for i in (0..string.len()).filter(|i| string.is_char_boundary(*i)) {
assert_eq!(&string[i..], &string.as_str()[i..]);
}
}
#[test]
fn test_index_range_to<S: Data<String>>(string: ImString<S>) {
for i in (0..string.len()).filter(|i| string.is_char_boundary(*i)) {
assert_eq!(&string[..i], &string.as_str()[..i]);
}
}
#[test]
fn test_index_range_exclusive<S: Data<String>>(string: ImString<S>) {
for start in (0..string.len()).filter(|i| string.is_char_boundary(*i)) {
for end in (start..string.len()).filter(|i| string.is_char_boundary(*i)) {
assert_eq!(&string[start..end], &string.as_str()[start..end]);
}
}
}
#[test]
fn test_index_range_inclusive<S: Data<String>>(string: ImString<S>) {
if !string.is_empty() {
for start in (0..string.len()-1).filter(|i| string.is_char_boundary(*i)) {
for end in (start..string.len()-1).filter(|i| string.is_char_boundary(*i + 1)) {
assert_eq!(&string[start..=end], &string.as_str()[start..=end]);
}
}
}
}
#[test]
fn test_into_bytes<S: Data<String>>(string: ImString<S>) {
let std_bytes = string.as_str().to_string().into_bytes();
let bytes = string.into_bytes();
assert_eq!(bytes, std_bytes);
}
#[test]
fn test_slice_all<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.slice(..), string);
}
#[test]
fn test_slice_start<S: Data<String>>(string: ImString<S>) {
for end in 0..string.len() {
if string.is_char_boundary(end) {
assert_eq!(string.slice(..end), string.as_str()[..end]);
}
}
}
#[test]
fn test_slice_end<S: Data<String>>(string: ImString<S>) {
for start in 0..string.len() {
if string.is_char_boundary(start) {
assert_eq!(string.slice(start..), string.as_str()[start..]);
}
}
}
#[test]
fn test_slice_middle<S: Data<String>>(string: ImString<S>) {
for start in 0..string.len() {
if string.is_char_boundary(start) {
for end in start..string.len() {
if string.is_char_boundary(end) {
assert_eq!(string.slice(start..end), string.as_str()[start..end]);
}
}
}
}
}
#[test]
fn test_try_slice_all<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.try_slice(..).unwrap(), string);
}
#[test]
fn test_try_slice_start<S: Data<String>>(string: ImString<S>) {
for end in 0..string.len() {
if string.is_char_boundary(end) {
assert_eq!(string.try_slice(..end).unwrap(), string.as_str()[..end]);
} else {
assert_eq!(string.try_slice(..end), Err(SliceError::EndNotAligned));
}
}
assert_eq!(string.try_slice(..string.len()+1), Err(SliceError::EndOutOfBounds));
}
#[test]
fn test_try_slice_end<S: Data<String>>(string: ImString<S>) {
for start in 0..string.len() {
if string.is_char_boundary(start) {
assert_eq!(string.try_slice(start..).unwrap(), string.as_str()[start..]);
} else {
assert_eq!(string.try_slice(start..), Err(SliceError::StartNotAligned));
}
}
assert_eq!(string.try_slice(string.len()+1..), Err(SliceError::StartOutOfBounds));
}
#[test]
fn test_write<S: Data<String>>() {
let mut string: ImString<S> = ImString::new();
string.write_str("Hello").unwrap();
string.write_char(',').unwrap();
string.write_char(' ').unwrap();
string.write_str("World").unwrap();
string.write_char('!').unwrap();
assert_eq!(string, "Hello, World!");
}
#[test]
fn test_add_assign<S: Data<String>>(string: ImString<S>) {
let mut std_string = string.as_str().to_string();
let mut string = string;
string += "hello";
std_string += "hello";
assert_eq!(string, std_string);
}
#[test]
fn test_add<S: Data<String>>(string: ImString<S>) {
let std_string = string.as_str().to_string();
let std_string = std_string + "hello";
let string = string + "hello";
assert_eq!(string, std_string);
}
#[test]
fn test_to_socket_addrs<S: Data<String>>(string: ImString<S>) {
#[cfg(not(miri))]
{
let addrs = string.to_socket_addrs().map(|s| s.collect::<Vec<_>>());
let str_addrs = string.as_str().to_socket_addrs().map(|s| s.collect::<Vec<_>>());
match addrs {
Ok(addrs) => assert_eq!(addrs, str_addrs.unwrap()),
Err(_err) => assert!(str_addrs.is_err()),
}
}
}
#[test]
fn test_from_iterator_char<S: Data<String>>() {
let input = ['h', 'e', 'l', 'l', 'o'];
let string: ImString<S> = input.into_iter().collect();
assert_eq!(string, "hello");
}
#[test]
fn test_from_iterator_char_ref<S: Data<String>>() {
let input = ['h', 'e', 'l', 'l', 'o'];
let string: ImString<S> = input.iter().collect();
assert_eq!(string, "hello");
}
#[test]
fn test_from_iterator_str<S: Data<String>>() {
let input = ["hello", "world", "!"];
let string: ImString<S> = input.into_iter().collect();
assert_eq!(string, "helloworld!");
}
#[test]
fn test_extend_char<S: Data<String>>() {
let input = ['h', 'e', 'l', 'l', 'o'];
let mut string: ImString<S> = ImString::new();
string.extend(input.into_iter());
assert_eq!(string, "hello");
}
#[test]
fn test_extend_char_ref<S: Data<String>>() {
let input = ['h', 'e', 'l', 'l', 'o'];
let mut string: ImString<S> = ImString::new();
string.extend(input.into_iter());
assert_eq!(string, "hello");
}
#[test]
fn test_extend_str<S: Data<String>>() {
let input = ["hello", "world", "!"];
let mut string: ImString<S> = ImString::new();
string.extend(input.into_iter());
assert_eq!(string, "helloworld!");
}
#[test]
fn test_from_utf8_lossy<S: Data<String>>() {
let string: ImString<S> = ImString::from_utf8_lossy(b"hello");
assert_eq!(string, "hello");
}
#[test]
fn test_from_utf8_unchecked<S: Data<String>>() {
let string: ImString<S> = unsafe {
ImString::from_utf8_unchecked(b"hello".to_vec())
};
assert_eq!(string, "hello");
}
#[test]
fn test_borrow<S: Data<String>>(string: ImString<S>) {
let s: &str = string.borrow();
assert_eq!(s, string.as_str());
}
#[test]
fn test_as_ref_str<S: Data<String>>(string: ImString<S>) {
let s: &str = string.as_ref();
assert_eq!(s, string.as_str());
}
#[test]
fn test_as_ref_bytes<S: Data<String>>(string: ImString<S>) {
let s: &[u8] = string.as_ref();
assert_eq!(s, string.as_bytes());
}
#[test]
fn test_as_ref_path<S: Data<String>>(string: ImString<S>) {
let s: &Path = string.as_ref();
assert_eq!(s, string.as_str().as_ref() as &Path);
}
#[test]
fn test_as_ref_os_str<S: Data<String>>(string: ImString<S>) {
let s: &OsStr = string.as_ref();
assert_eq!(s, string.as_str().as_ref() as &OsStr);
}
#[test]
fn test_deref_mut<S: Data<String>>(string: ImString<S>) {
let mut string = string;
let data = string.as_str().to_string();
let mutable: &mut str = string.deref_mut();
assert_eq!(&*mutable, &data);
}
#[test]
fn test_as_mut<S: Data<String>>(string: ImString<S>) {
let mut string = string;
let data = string.as_str().to_string();
let mutable: &mut str = string.as_mut();
assert_eq!(&*mutable, &data);
}
#[test]
fn test_borrow_mut<S: Data<String>>(string: ImString<S>) {
let mut string = string;
let data = string.as_str().to_string();
let mutable: &mut str = string.borrow_mut();
assert_eq!(&*mutable, &data);
}
#[test]
fn test_partial_eq<S: Data<String>>(string: ImString<S>) {
assert_eq!(string, string.as_str());
assert_eq!(string, string.to_string());
assert_eq!(string, string);
}
#[test]
fn test_partial_ord<S: Data<String>>(string: ImString<S>) {
let other = ImString::from("test");
assert_eq!(string.as_str().partial_cmp(other.as_str()), string.partial_cmp(&other));
assert_eq!(other.as_str().partial_cmp(string.as_str()), other.partial_cmp(&string));
}
#[test]
fn test_ord<S: Data<String>>(string: ImString<S>) {
let other = ImString::from("test");
assert_eq!(string.as_str().cmp(other.as_str()), string.cmp(&other));
assert_eq!(other.as_str().cmp(string.as_str()), other.cmp(&string));
}
#[test]
fn test_from<S: Data<String>>(string: ImString<S>) {
let std_string: String = string.clone().into();
assert_eq!(string, std_string);
}
#[test]
fn test_raw_offset<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.offset, string.raw_offset());
}
#[test]
fn test_raw_string<S: Data<String>>(string: ImString<S>) {
assert_eq!(string.string.get(), string.raw_string().get());
}
#[test]
fn into_std_string<S: Data<String>>(string: ImString<S>) {
let std_clone = string.as_str().to_string();
let std_string = string.into_std_string();
assert_eq!(std_clone, std_string);
}
#[test]
fn test_truncate<S: Data<String>>(string: ImString<S>) {
let mut clone = string.as_str().to_string();
let mut string = string;
for length in (0..string.len()).rev() {
if string.is_char_boundary(length) {
string.truncate(length);
clone.truncate(length);
assert_eq!(string, clone);
}
}
}
#[test]
fn test_str_ref<S: Data<String>>(string: ImString<S>) {
assert_eq!(string, string.str_ref(string.as_str()));
}
#[test]
fn test_try_str_ref<S: Data<String>>(string: ImString<S>) {
assert_eq!(string, string.try_str_ref(string.as_str()).unwrap());
assert_eq!(string.try_str_ref("test"), None);
}
#[test]
fn test_slice_ref<S: Data<String>>(string: ImString<S>) {
assert_eq!(string, string.slice_ref(string.as_bytes()));
}
#[test]
fn test_try_slice_ref<S: Data<String>>(string: ImString<S>) {
assert_eq!(string, string.try_slice_ref(string.as_bytes()).unwrap());
assert_eq!(string.try_slice_ref(b"test"), None);
}
}
}