use std::fmt;
use std::borrow::Cow;
#[allow(unused_imports)] use std::fmt::Display;
impl<T: AsRef<str>> HeadTail for T {}
pub const DOT: &str = "...";
pub trait HeadTail: AsRef<str> {
fn head(&self, head: usize) -> Head<'_> {
let s = self.as_ref();
#[allow(clippy::string_slice)]
if let Some((index, _)) = s.char_indices().nth(head) {
Head { string: &s[..index], cut: true }
} else {
Head { string: s, cut: false }
}
}
fn head_dot(&self, head: usize) -> HeadDot<'_> {
let s = self.as_ref();
#[allow(clippy::string_slice)]
if let Some((index, _)) = s.char_indices().nth(head) {
let mut string = String::with_capacity(s.len() + 3);
string += &s[..index];
string += DOT;
HeadDot { cow: Cow::Owned(string) }
} else {
HeadDot { cow: Cow::Borrowed(s) }
}
}
fn tail(&self, tail: usize) -> Tail<'_> {
let s = self.as_ref();
let end = s.chars().count();
if tail >= end {
return Tail { string: s, cut: false };
}
#[allow(clippy::string_slice)]
if let Some((index, _)) = s.char_indices().nth(end - tail) {
Tail { string: &s[index..], cut: true }
} else {
Tail { string: s, cut: false }
}
}
fn tail_dot(&self, tail: usize) -> TailDot<'_> {
let s = self.as_ref();
let end = s.chars().count();
if tail >= end {
return TailDot { cow: Cow::Borrowed(s) }
}
#[allow(clippy::string_slice)]
if let Some((index, _)) = s.char_indices().nth(end - tail) {
let mut string = String::with_capacity(end + 3);
string += DOT;
string += &s[index..];
TailDot { cow: Cow::Owned(string) }
} else {
TailDot { cow: Cow::Borrowed(s) }
}
}
fn head_tail(&self, head: usize, tail: usize) -> HeadTailStr<'_> {
let s = self.as_ref();
let end = s.chars().count();
if head + tail >= end {
return HeadTailStr { head: s, tail: None }
}
let head = s.char_indices().nth(head);
let tail = s.char_indices().nth(end - tail);
#[allow(clippy::string_slice)]
if let (Some((head, _)), Some((tail, _))) = (head, tail) {
HeadTailStr { head: &s[..head], tail: Some(&s[tail..]) }
} else {
HeadTailStr { head: s, tail: None }
}
}
fn head_tail_dot(&self, head: usize, tail: usize) -> HeadTailDot<'_> {
let s = self.as_ref();
let end = s.chars().count();
if head + tail >= end {
return HeadTailDot { head: s, tail: None }
}
let head = s.char_indices().nth(head);
let tail = s.char_indices().nth(end - tail);
#[allow(clippy::string_slice)]
if let (Some((head, _)), Some((tail, _))) = (head, tail) {
HeadTailDot { head: &s[..head], tail: Some(&s[tail..]) }
} else {
HeadTailDot { head: s, tail: None }
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
pub struct Head<'a> {
string: &'a str,
cut: bool,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
pub struct Tail<'a> {
string: &'a str,
cut: bool,
}
macro_rules! impl_string {
($($name:ident),*) => {
$(
impl<'a> $name<'a> {
#[inline]
#[must_use]
pub const fn as_str(&self) -> &str {
self.string
}
#[inline]
#[must_use]
pub const fn into_parts(self) -> (&'a str, bool) {
(self.string, self.cut)
}
#[inline]
#[must_use]
pub const fn cut(&self) -> bool {
self.cut
}
}
impl PartialEq<str> for $name<'_> {
fn eq(&self, other: &str) -> bool {
self.string == other
}
}
impl PartialEq<&str> for $name<'_> {
fn eq(&self, other: &&str) -> bool {
self.string == *other
}
}
impl AsRef<str> for $name<'_> {
fn as_ref(&self) -> &str {
self.string
}
}
impl std::ops::Deref for $name<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.string
}
}
impl fmt::Display for $name<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.string)
}
}
)*
};
}
impl_string!(Head, Tail);
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
pub struct HeadDot<'a> {
cow: Cow<'a, str>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
pub struct TailDot<'a> {
cow: Cow<'a, str>,
}
macro_rules! impl_cow {
($($name:ident),*) => {
$(
impl PartialEq<str> for $name<'_> {
fn eq(&self, other: &str) -> bool {
self.cow == other
}
}
impl PartialEq<&str> for $name<'_> {
fn eq(&self, other: &&str) -> bool {
self.cow == *other
}
}
impl fmt::Display for $name<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.cow)
}
}
impl AsRef<str> for $name<'_> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl std::ops::Deref for $name<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> $name<'a> {
#[inline]
#[must_use]
pub fn as_str(&self) -> &str {
match self.cow {
Cow::Borrowed(s) => s,
Cow::Owned(ref s) => s,
}
}
#[inline]
#[must_use]
pub fn into_cow(self) -> Cow<'a, str> {
self.cow
}
#[inline]
#[must_use]
pub const fn cut(&self) -> bool {
match self.cow {
Cow::Borrowed(_) => false,
Cow::Owned(_) => true,
}
}
}
)*
};
}
impl_cow!(HeadDot, TailDot);
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
pub struct HeadTailStr<'a> {
head: &'a str,
tail: Option<&'a str>,
}
impl fmt::Display for HeadTailStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.tail {
Some(t) => write!(f, "{}{t}", self.head),
None => write!(f, "{}", self.head),
}
}
}
impl HeadTailStr<'_> {
#[inline]
fn str_cmp(&self, other: &str) -> bool {
match self.tail {
Some(t) => {
let head_bytes = self.head.as_bytes();
let tail_bytes = t.as_bytes();
let str_bytes = other.as_bytes();
let head_len = head_bytes.len();
let tail_len = tail_bytes.len();
let total_len = head_len + tail_len;
total_len == str_bytes.len() &&
head_bytes == &str_bytes[..head_len] &&
tail_bytes == &str_bytes[tail_len..]
},
None => self.head == other,
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
pub struct HeadTailDot<'a> {
head: &'a str,
tail: Option<&'a str>,
}
impl fmt::Display for HeadTailDot<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.tail {
Some(t) => write!(f, "{}{DOT}{t}", self.head),
None => write!(f, "{}", self.head),
}
}
}
impl HeadTailDot<'_> {
#[inline]
fn str_cmp(&self, other: &str) -> bool {
match self.tail {
Some(t) => {
let head_bytes = self.head.as_bytes();
let tail_bytes = t.as_bytes();
let str_bytes = other.as_bytes();
let head_len = head_bytes.len();
let tail_len = tail_bytes.len();
let total_len = head_len + tail_len;
(total_len + 3) == str_bytes.len() &&
head_bytes == &str_bytes[..head_len] &&
tail_bytes == &str_bytes[tail_len + 3..]
},
None => self.head == other,
}
}
}
macro_rules! impl_head_tail {
($($name:ident),*) => {
$(
impl PartialEq<str> for $name<'_> {
fn eq(&self, other: &str) -> bool {
self.str_cmp(other)
}
}
impl PartialEq<&str> for $name<'_> {
fn eq(&self, other: &&str) -> bool {
self.str_cmp(other)
}
}
impl<'a> $name<'a> {
#[inline]
#[must_use]
pub const fn cut(&self) -> bool {
self.tail.is_some()
}
#[inline]
#[must_use]
pub const fn head(&self) -> &str {
self.head
}
#[inline]
#[must_use]
pub const fn tail(&self) -> &str {
match self.tail {
Some(t) => t,
None => self.head,
}
}
#[inline]
#[must_use]
pub const fn into_parts(self) -> (&'a str, Option<&'a str>) {
(self.head, self.tail)
}
}
)*
};
}
impl_head_tail!(HeadTailStr, HeadTailDot);