#![doc(html_root_url = "https://docs.rs/toad-string/0.0.0")]
#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
#![allow(clippy::unused_unit)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(missing_copy_implementations)]
#![cfg_attr(not(test), deny(unsafe_code))]
#![cfg_attr(not(test), warn(unreachable_pub))]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc as std_alloc;
use core::fmt::{Display, Write};
use core::ops::{Deref, DerefMut};
use tinyvec::ArrayVec;
use toad_array::AppendCopy;
use toad_len::Len;
use toad_writable::Writable;
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Default)]
pub struct FromUtf8Error;
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Default)]
pub struct FromUtf16Error;
#[macro_export]
macro_rules! format {
($cap:literal, $($arg:tt)*) => {
$crate::String::<$cap>::fmt(format_args!($($arg)*))
};
}
#[derive(Debug, Copy, Clone, Default)]
pub struct String<const N: usize>(Writable<ArrayVec<[u8; N]>>);
impl<const N: usize> String<N> {
pub fn new() -> Self {
Default::default()
}
pub fn as_str(&self) -> &str {
self.as_ref()
}
pub fn as_mut_str(&mut self) -> &mut str {
self.as_mut()
}
pub fn resize<const M: usize>(&mut self) -> String<M> {
let mut bytes = self.0.unwrap();
bytes.truncate(M);
String(Writable::from(self.as_writable().drain(..).collect::<ArrayVec<[u8; M]>>()))
}
pub fn as_bytes(&self) -> &[u8] {
self.as_ref()
}
pub fn as_writable(&mut self) -> &mut Writable<ArrayVec<[u8; N]>> {
&mut self.0
}
pub fn fmt(args: core::fmt::Arguments) -> Self {
let mut s = Self::new();
s.write_fmt(args).ok();
s
}
pub fn capacity(&self) -> usize {
N
}
pub fn clear(&mut self) {
self.0.clear()
}
#[inline]
pub fn from_utf8(bytes: &[u8]) -> Result<Self, FromUtf8Error> {
match core::str::from_utf8(bytes) {
| Ok(s) => Ok(Self::from(s)),
| Err(_) => Err(FromUtf8Error),
}
}
pub fn from_utf16(v: &[u16]) -> Result<Self, FromUtf16Error> {
let mut ret = String::new();
for c in char::decode_utf16(v.iter().cloned()) {
if let Ok(c) = c {
ret.push(c);
} else {
return Err(FromUtf16Error);
}
}
Ok(ret)
}
#[inline]
pub fn insert_str(&mut self, idx: usize, string: &str) {
assert!(self.is_char_boundary(idx));
for (i, b) in string.bytes().enumerate() {
self.0.insert(idx + i, b);
}
}
#[inline]
pub fn insert(&mut self, idx: usize, ch: char) {
assert!(self.is_char_boundary(idx));
let mut bits = [0; 4];
let bits = ch.encode_utf8(&mut bits).as_bytes();
for (i, b) in bits.iter().enumerate() {
self.0.insert(idx + i, *b);
}
}
pub fn push(&mut self, ch: char) {
match ch.len_utf8() {
| 1 => self.0.push(ch as u8),
| _ => self.0
.extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()),
}
}
#[inline]
pub fn push_str(&mut self, string: &str) {
self.0.append_copy(string.as_bytes())
}
}
impl<const N: usize> Len for String<N> {
const CAPACITY: Option<usize> = Some(N);
fn len(&self) -> usize {
self.0.len()
}
fn is_full(&self) -> bool {
self.0.is_full()
}
}
impl<const N: usize> Display for String<N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<const N: usize> PartialEq for String<N> {
fn eq(&self, other: &Self) -> bool {
self.0.as_str() == other.0.as_str()
}
}
impl<const N: usize> Eq for String<N> {}
impl<const N: usize> core::fmt::Write for String<N> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.0.write_str(s)
}
}
impl<'a, const N: usize> From<&'a str> for String<N> {
fn from(s: &'a str) -> Self {
let mut arr = Writable::default();
ArrayVec::extend_from_slice(&mut arr, s.as_bytes());
Self(arr)
}
}
impl<const N: usize> Deref for String<N> {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl<const N: usize> DerefMut for String<N> {
fn deref_mut(&mut self) -> &mut str {
self.as_mut()
}
}
impl<const N: usize> AsRef<str> for String<N> {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
impl<const N: usize> AsMut<str> for String<N> {
fn as_mut(&mut self) -> &mut str {
core::str::from_utf8_mut(self.0.as_mut_slice()).unwrap()
}
}
impl<const N: usize> AsRef<[u8]> for String<N> {
fn as_ref(&self) -> &[u8] {
self.0.as_str().as_bytes()
}
}
impl<const N: usize> PartialEq<&str> for String<N> {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl<const N: usize> PartialEq<str> for String<N> {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl<const N: usize> PartialEq<String<N>> for &str {
fn eq(&self, other: &String<N>) -> bool {
*self == other.as_str()
}
}
impl<const N: usize> PartialEq<&String<N>> for &str {
fn eq(&self, other: &&String<N>) -> bool {
*self == other.as_str()
}
}