#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use core::{
borrow::Borrow,
cmp, fmt,
hash::{Hash, Hasher},
ops::{Add, AddAssign, Deref},
str,
str::FromStr,
};
use alloc::{borrow::Cow, boxed::Box, string::String};
#[cfg(feature = "std")]
use std::ffi::OsStr;
mod repr;
use repr::Repr;
mod errors;
pub use errors::*;
mod traits;
pub use traits::ToLeanString;
mod features;
#[repr(transparent)]
pub struct LeanString(Repr);
const _: () = {
assert!(size_of::<LeanString>() == size_of::<[usize; 2]>());
assert!(size_of::<Option<LeanString>>() == size_of::<[usize; 2]>());
assert!(align_of::<LeanString>() == align_of::<usize>());
assert!(align_of::<Option<LeanString>>() == align_of::<usize>());
};
impl LeanString {
#[inline]
pub const fn new() -> Self {
LeanString(Repr::new())
}
#[inline]
pub const fn from_static_str(text: &'static str) -> Self {
match Repr::from_static_str(text) {
Ok(repr) => LeanString(repr),
Err(_) => panic!("text is too long"),
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
LeanString::try_with_capacity(capacity).unwrap_with_msg()
}
#[inline]
pub fn try_with_capacity(capacity: usize) -> Result<Self, ReserveError> {
Repr::with_capacity(capacity).map(LeanString)
}
#[inline]
pub fn from_utf8(buf: &[u8]) -> Result<Self, str::Utf8Error> {
let str = str::from_utf8(buf)?;
Ok(LeanString::from(str))
}
#[inline]
pub fn from_utf8_lossy(buf: &[u8]) -> Self {
let mut ret = LeanString::with_capacity(buf.len());
for chunk in buf.utf8_chunks() {
ret.push_str(chunk.valid());
if !chunk.invalid().is_empty() {
ret.push(char::REPLACEMENT_CHARACTER);
}
}
ret
}
#[inline]
pub unsafe fn from_utf8_unchecked(buf: &[u8]) -> Self {
let str = unsafe { str::from_utf8_unchecked(buf) };
LeanString::from(str)
}
#[inline]
pub fn from_utf16(buf: &[u16]) -> Result<Self, FromUtf16Error> {
let mut ret = LeanString::with_capacity(buf.len());
for c in char::decode_utf16(buf.iter().copied()) {
match c {
Ok(c) => ret.push(c),
Err(_) => return Err(FromUtf16Error),
}
}
Ok(ret)
}
#[inline]
pub fn from_utf16_lossy(buf: &[u16]) -> Self {
char::decode_utf16(buf.iter().copied())
.map(|c| c.unwrap_or(char::REPLACEMENT_CHARACTER))
.collect()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn capacity(&self) -> usize {
self.0.capacity()
}
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.try_reserve(additional).unwrap_with_msg()
}
#[inline]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), ReserveError> {
self.0.reserve(additional)
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.try_shrink_to_fit().unwrap_with_msg()
}
#[inline]
pub fn try_shrink_to_fit(&mut self) -> Result<(), ReserveError> {
self.0.shrink_to(0)
}
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.try_shrink_to(min_capacity).unwrap_with_msg()
}
#[inline]
pub fn try_shrink_to(&mut self, min_capacity: usize) -> Result<(), ReserveError> {
self.0.shrink_to(min_capacity)
}
#[inline]
pub fn push(&mut self, ch: char) {
self.try_push(ch).unwrap_with_msg()
}
#[inline]
pub fn try_push(&mut self, ch: char) -> Result<(), ReserveError> {
self.0.push_str(ch.encode_utf8(&mut [0; 4]))
}
#[inline]
pub fn pop(&mut self) -> Option<char> {
self.try_pop().unwrap_with_msg()
}
#[inline]
pub fn try_pop(&mut self) -> Result<Option<char>, ReserveError> {
self.0.pop()
}
#[inline]
pub fn push_str(&mut self, string: &str) {
self.try_push_str(string).unwrap_with_msg()
}
#[inline]
pub fn try_push_str(&mut self, string: &str) -> Result<(), ReserveError> {
self.0.push_str(string)
}
#[inline]
pub fn remove(&mut self, idx: usize) -> char {
self.try_remove(idx).unwrap_with_msg()
}
#[inline]
pub fn try_remove(&mut self, idx: usize) -> Result<char, ReserveError> {
self.0.remove(idx)
}
#[inline]
pub fn retain(&mut self, predicate: impl FnMut(char) -> bool) {
self.try_retain(predicate).unwrap_with_msg()
}
#[inline]
pub fn try_retain(&mut self, predicate: impl FnMut(char) -> bool) -> Result<(), ReserveError> {
self.0.retain(predicate)
}
#[inline]
pub fn insert(&mut self, idx: usize, ch: char) {
self.try_insert(idx, ch).unwrap_with_msg()
}
#[inline]
pub fn try_insert(&mut self, idx: usize, ch: char) -> Result<(), ReserveError> {
self.0.insert_str(idx, ch.encode_utf8(&mut [0; 4]))
}
#[inline]
pub fn insert_str(&mut self, idx: usize, string: &str) {
self.try_insert_str(idx, string).unwrap_with_msg()
}
#[inline]
pub fn try_insert_str(&mut self, idx: usize, string: &str) -> Result<(), ReserveError> {
self.0.insert_str(idx, string)
}
#[inline]
pub fn truncate(&mut self, new_len: usize) {
self.try_truncate(new_len).unwrap_with_msg()
}
#[inline]
pub fn try_truncate(&mut self, new_len: usize) -> Result<(), ReserveError> {
self.0.truncate(new_len)
}
#[inline]
pub fn clear(&mut self) {
if self.0.is_unique() {
unsafe { self.0.set_len(0) }
} else {
self.0.replace_inner(Repr::new());
}
}
#[inline]
pub fn is_heap_allocated(&self) -> bool {
self.0.is_heap_buffer()
}
}
impl Clone for LeanString {
#[inline]
fn clone(&self) -> Self {
LeanString(self.0.make_shallow_clone())
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.0.replace_inner(source.0.make_shallow_clone());
}
}
impl Drop for LeanString {
fn drop(&mut self) {
self.0.replace_inner(Repr::new());
}
}
unsafe impl Send for LeanString {}
unsafe impl Sync for LeanString {}
impl Default for LeanString {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Deref for LeanString {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl fmt::Debug for LeanString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl fmt::Display for LeanString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl AsRef<str> for LeanString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[cfg(feature = "std")]
impl AsRef<OsStr> for LeanString {
#[inline]
fn as_ref(&self) -> &OsStr {
OsStr::new(self.as_str())
}
}
impl AsRef<[u8]> for LeanString {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Borrow<str> for LeanString {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Eq for LeanString {}
impl PartialEq for LeanString {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_str().eq(other.as_str())
}
}
impl PartialEq<str> for LeanString {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str().eq(other)
}
}
impl PartialEq<LeanString> for str {
#[inline]
fn eq(&self, other: &LeanString) -> bool {
self.eq(other.as_str())
}
}
impl PartialEq<&str> for LeanString {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str().eq(*other)
}
}
impl PartialEq<LeanString> for &str {
#[inline]
fn eq(&self, other: &LeanString) -> bool {
(*self).eq(other.as_str())
}
}
impl PartialEq<String> for LeanString {
#[inline]
fn eq(&self, other: &String) -> bool {
self.as_str().eq(other.as_str())
}
}
impl PartialEq<LeanString> for String {
#[inline]
fn eq(&self, other: &LeanString) -> bool {
self.as_str().eq(other.as_str())
}
}
impl PartialEq<Cow<'_, str>> for LeanString {
#[inline]
fn eq(&self, other: &Cow<'_, str>) -> bool {
self.as_str().eq(other.as_ref())
}
}
impl PartialEq<LeanString> for Cow<'_, str> {
#[inline]
fn eq(&self, other: &LeanString) -> bool {
self.as_ref().eq(other.as_str())
}
}
impl Ord for LeanString {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl PartialOrd for LeanString {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Hash for LeanString {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}
impl From<char> for LeanString {
#[inline]
#[track_caller]
fn from(value: char) -> Self {
LeanString(Repr::from_char(value))
}
}
impl From<&str> for LeanString {
#[inline]
#[track_caller]
fn from(value: &str) -> Self {
LeanString(Repr::from_str(value).unwrap_with_msg())
}
}
impl From<String> for LeanString {
#[inline]
#[track_caller]
fn from(value: String) -> Self {
LeanString(Repr::from_str(&value).unwrap_with_msg())
}
}
impl From<&String> for LeanString {
#[inline]
#[track_caller]
fn from(value: &String) -> Self {
LeanString(Repr::from_str(value).unwrap_with_msg())
}
}
impl From<Cow<'_, str>> for LeanString {
fn from(cow: Cow<str>) -> Self {
match cow {
Cow::Borrowed(s) => s.into(),
Cow::Owned(s) => s.into(),
}
}
}
impl From<Box<str>> for LeanString {
#[inline]
#[track_caller]
fn from(value: Box<str>) -> Self {
LeanString(Repr::from_str(&value).unwrap_with_msg())
}
}
impl From<&LeanString> for LeanString {
#[inline]
fn from(value: &LeanString) -> Self {
value.clone()
}
}
impl From<LeanString> for String {
#[inline]
fn from(value: LeanString) -> Self {
value.as_str().into()
}
}
impl From<&LeanString> for String {
#[inline]
fn from(value: &LeanString) -> Self {
value.as_str().into()
}
}
impl FromStr for LeanString {
type Err = ReserveError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Repr::from_str(s).map(Self)
}
}
impl FromIterator<char> for LeanString {
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
let iter = iter.into_iter();
let (lower_bound, _) = iter.size_hint();
let mut repr = match Repr::with_capacity(lower_bound) {
Ok(buf) => buf,
Err(_) => Repr::new(), };
for ch in iter {
repr.push_str(ch.encode_utf8(&mut [0; 4])).unwrap_with_msg();
}
LeanString(repr)
}
}
impl<'a> FromIterator<&'a char> for LeanString {
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
iter.into_iter().copied().collect()
}
}
impl<'a> FromIterator<&'a str> for LeanString {
fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
let mut buf = LeanString::new();
buf.extend(iter);
buf
}
}
impl FromIterator<Box<str>> for LeanString {
fn from_iter<I: IntoIterator<Item = Box<str>>>(iter: I) -> Self {
let mut buf = LeanString::new();
buf.extend(iter);
buf
}
}
impl<'a> FromIterator<Cow<'a, str>> for LeanString {
fn from_iter<I: IntoIterator<Item = Cow<'a, str>>>(iter: I) -> Self {
let mut buf = LeanString::new();
buf.extend(iter);
buf
}
}
impl FromIterator<String> for LeanString {
fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
let mut buf = LeanString::new();
buf.extend(iter);
buf
}
}
impl FromIterator<LeanString> for LeanString {
fn from_iter<T: IntoIterator<Item = LeanString>>(iter: T) -> Self {
let mut buf = LeanString::new();
buf.extend(iter);
buf
}
}
impl Extend<char> for LeanString {
fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
let iter = iter.into_iter();
let (lower_bound, _) = iter.size_hint();
let _ = self.try_reserve(lower_bound);
for ch in iter {
self.push(ch);
}
}
}
impl<'a> Extend<&'a char> for LeanString {
fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
self.extend(iter.into_iter().copied());
}
}
impl<'a> Extend<&'a str> for LeanString {
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
iter.into_iter().for_each(|s| self.push_str(s));
}
}
impl Extend<Box<str>> for LeanString {
fn extend<T: IntoIterator<Item = Box<str>>>(&mut self, iter: T) {
iter.into_iter().for_each(move |s| self.push_str(&s));
}
}
impl<'a> Extend<Cow<'a, str>> for LeanString {
fn extend<T: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: T) {
iter.into_iter().for_each(move |s| self.push_str(&s));
}
}
impl Extend<String> for LeanString {
fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
iter.into_iter().for_each(move |s| self.push_str(&s));
}
}
impl Extend<LeanString> for LeanString {
fn extend<T: IntoIterator<Item = LeanString>>(&mut self, iter: T) {
for s in iter {
self.push_str(&s);
}
}
}
impl Extend<LeanString> for String {
fn extend<T: IntoIterator<Item = LeanString>>(&mut self, iter: T) {
for s in iter {
self.push_str(&s);
}
}
}
impl fmt::Write for LeanString {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.push_str(s);
Ok(())
}
}
impl Add<&str> for LeanString {
type Output = Self;
#[inline]
fn add(mut self, rhs: &str) -> Self::Output {
self.push_str(rhs);
self
}
}
impl AddAssign<&str> for LeanString {
#[inline]
fn add_assign(&mut self, rhs: &str) {
self.push_str(rhs);
}
}
trait UnwrapWithMsg {
type T;
fn unwrap_with_msg(self) -> Self::T;
}
impl<T, E: fmt::Display> UnwrapWithMsg for Result<T, E> {
type T = T;
#[inline(always)]
#[track_caller]
fn unwrap_with_msg(self) -> T {
#[inline(never)]
#[cold]
#[track_caller]
fn do_panic_with_msg<E: fmt::Display>(error: E) -> ! {
panic!("{error}")
}
match self {
Ok(value) => value,
Err(err) => do_panic_with_msg(err),
}
}
}