use core::{
cmp::{Eq, Ord, PartialEq, PartialOrd},
convert::TryFrom,
fmt,
hash::Hash,
ops::{self, Deref, DerefMut, Index, IndexMut},
str,
};
use self::builder::StringBuilder;
use crate::error::Error;
pub mod builder;
#[cfg(feature = "rkyv-derive")]
pub mod rkyv;
#[cfg(feature = "serde-derive")]
pub mod serde;
#[derive(Copy, Clone, Eq, PartialOrd, Ord, Hash)]
pub struct String<const L: usize>(pub(crate) [u8; L]);
impl<const L: usize> String<L> {
pub const fn empty() -> Self {
Self([b' '; L])
}
pub fn try_from_str_padded(s: impl AsRef<str>) -> Result<Self, Error<L>> {
Self::try_from_bytes_padded(s.as_ref().as_bytes())
}
pub fn try_from_bytes_padded(bytes: impl AsRef<[u8]>) -> Result<Self, Error<L>> {
let bytes = bytes.as_ref();
if bytes.len() > L {
return Err(Error(bytes.len()));
}
let mut builder = Self::builder();
unsafe {
builder.push_bytes_unchecked(bytes);
}
Ok(builder.build())
}
pub const fn builder() -> StringBuilder<L> {
StringBuilder::empty()
}
pub fn as_str(&self) -> &str {
&self
}
pub fn as_bytes(&self) -> &[u8; L] {
&self.0
}
pub fn as_slice(&self) -> &[u8] {
self.as_str().as_bytes()
}
pub fn into_bytes(self) -> [u8; L] {
self.0
}
pub fn is_empty(&self) -> bool {
self.0.iter().all(|&x| x == b' ')
}
pub fn starts_empty(&self) -> bool {
self.0[0] == b' '
}
}
impl<const L: usize> Default for String<L> {
fn default() -> Self {
Self::empty()
}
}
impl<const L: usize> fmt::Debug for String<L> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<const L: usize> Deref for String<L> {
type Target = str;
fn deref(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.0) }
}
}
impl<const L: usize> DerefMut for String<L> {
#[inline]
fn deref_mut(&mut self) -> &mut str {
unsafe { str::from_utf8_unchecked_mut(&mut self.0) }
}
}
impl<const L: usize> From<[u8; L]> for String<L> {
fn from(buf: [u8; L]) -> Self {
String(buf)
}
}
impl<const L: usize> TryFrom<&str> for String<L> {
type Error = crate::error::Error<L>;
fn try_from(s: &str) -> Result<Self, Self::Error> {
if s.len() != L {
return Err(s.len().into());
}
let mut res = Self::empty();
res.0.copy_from_slice(s.as_bytes());
Ok(res)
}
}
impl<const L: usize> PartialEq for String<L> {
#[inline]
fn eq(&self, other: &String<L>) -> bool {
PartialEq::eq(&self[..], &other[..])
}
#[inline]
fn ne(&self, other: &String<L>) -> bool {
PartialEq::ne(&self[..], &other[..])
}
}
impl<const L: usize> PartialEq<&str> for String<L> {
#[inline]
fn eq(&self, other: &&str) -> bool {
PartialEq::eq(&self[..], &other[..])
}
#[inline]
fn ne(&self, other: &&str) -> bool {
PartialEq::ne(&self[..], &other[..])
}
}
impl<const L: usize> PartialEq<String<L>> for &str {
#[inline]
fn eq(&self, other: &String<L>) -> bool {
PartialEq::eq(&self[..], &other[..])
}
#[inline]
fn ne(&self, other: &String<L>) -> bool {
PartialEq::ne(&self[..], &other[..])
}
}
impl<const L: usize> PartialEq<std::string::String> for String<L> {
#[inline]
fn eq(&self, other: &std::string::String) -> bool {
PartialEq::eq(&self[..], &other[..])
}
#[inline]
fn ne(&self, other: &std::string::String) -> bool {
PartialEq::ne(&self[..], &other[..])
}
}
impl<const L: usize> PartialEq<String<L>> for std::string::String {
#[inline]
fn eq(&self, other: &String<L>) -> bool {
PartialEq::eq(&self[..], &other[..])
}
#[inline]
fn ne(&self, other: &String<L>) -> bool {
PartialEq::ne(&self[..], &other[..])
}
}
impl<const L: usize> ops::Index<ops::Range<usize>> for String<L> {
type Output = str;
#[inline]
fn index(&self, index: ops::Range<usize>) -> &str {
&self[..][index]
}
}
impl<const L: usize> ops::Index<ops::RangeTo<usize>> for String<L> {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeTo<usize>) -> &str {
&self[..][index]
}
}
impl<const L: usize> ops::Index<ops::RangeFrom<usize>> for String<L> {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &str {
&self[..][index]
}
}
impl<const L: usize> ops::Index<ops::RangeFull> for String<L> {
type Output = str;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &str {
unsafe { str::from_utf8_unchecked(&self.0) }
}
}
impl<const L: usize> ops::Index<ops::RangeInclusive<usize>> for String<L> {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeInclusive<usize>) -> &str {
Index::index(&**self, index)
}
}
impl<const L: usize> ops::Index<ops::RangeToInclusive<usize>> for String<L> {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeToInclusive<usize>) -> &str {
Index::index(&**self, index)
}
}
impl<const L: usize> ops::IndexMut<ops::Range<usize>> for String<L> {
#[inline]
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
&mut self[..][index]
}
}
impl<const L: usize> ops::IndexMut<ops::RangeTo<usize>> for String<L> {
#[inline]
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
&mut self[..][index]
}
}
impl<const L: usize> ops::IndexMut<ops::RangeFrom<usize>> for String<L> {
#[inline]
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
&mut self[..][index]
}
}
impl<const L: usize> ops::IndexMut<ops::RangeFull> for String<L> {
#[inline]
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
unsafe { str::from_utf8_unchecked_mut(&mut self.0) }
}
}
impl<const L: usize> ops::IndexMut<ops::RangeInclusive<usize>> for String<L> {
#[inline]
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut str {
IndexMut::index_mut(&mut **self, index)
}
}
impl<const L: usize> ops::IndexMut<ops::RangeToInclusive<usize>> for String<L> {
#[inline]
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut str {
IndexMut::index_mut(&mut **self, index)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deref() {
let s_ = "abcde";
let s = String::<5>::try_from(s_).unwrap();
assert_eq!(s_, s);
}
#[test]
fn slice() {
let s = String::<3>::try_from("abc").unwrap();
assert_eq!(&s[..2], "ab");
}
#[test]
fn eq_impls() {
let s_ = "abcde";
let s = String::<5>::try_from(s_).unwrap();
assert_eq!(s_, s);
let s_ = s_.to_owned();
assert_eq!(s_, s);
}
}