#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
use std::convert::TryFrom;
use std::error;
use std::fmt;
use std::hash;
use std::iter;
use std::ops;
use std::str;
use std::str::FromStr;
#[derive(Clone, Copy)]
pub struct Str<const SIZE: usize>([u8; SIZE], usize);
impl<const SIZE: usize> Str<SIZE> {
pub const EMPTY: Str<SIZE> = Str::new_const("");
pub fn new<S: AsRef<str>>(string: S) -> Result<Self, ErrorOverflow> {
let mut copstr = Self::default();
copstr.replace(string)?;
Ok(copstr)
}
pub fn new_trunc<S: AsRef<str>>(string: S) -> Self {
let mut copstr = Self::default();
copstr.replace_trunc(string);
copstr
}
pub const fn new_const(string: &str) -> Self {
Self::new_const_u8(string.as_bytes())
}
pub fn new_const_trunc(string: &str) -> Self {
Self::new_const_trunc_u8(string.as_bytes())
}
pub const fn new_const_u8(bytes: &[u8]) -> Self {
assert!(bytes.len() <= SIZE);
Self::new_const_trunc_u8(bytes)
}
pub const fn new_const_trunc_u8(bytes: &[u8]) -> Self {
let len = if SIZE < bytes.len() {
SIZE
} else {
bytes.len()
};
let mut copstr = Str::<SIZE>([0; SIZE], len);
let mut i = 0;
while i < len {
copstr.0[i] = bytes[i];
i += 1;
}
copstr
}
pub fn capacity(&self) -> usize {
SIZE
}
pub fn byte_len(&self) -> usize {
self.1
}
pub fn push(&mut self, ch: char) -> Result<(), ErrorOverflow> {
let mut buffer = [0; 4];
let result = ch.encode_utf8(&mut buffer).as_bytes();
if result.len() > self.capacity() - self.byte_len() {
Err(ErrorOverflow {})
} else {
let fromlen = self.0.split_at_mut(self.1).1;
let dest = fromlen.split_at_mut(result.len()).0;
dest.copy_from_slice(result);
self.1 += result.len();
Ok(())
}
}
pub fn replace<S: AsRef<str>>(&mut self, string: S) -> Result<(), ErrorOverflow> {
let s = string.as_ref();
let bytes = s.as_bytes();
let byteslen = bytes.len();
if byteslen > self.capacity() {
Err(ErrorOverflow {})
} else {
let dest = self.0.split_at_mut(byteslen).0;
dest.copy_from_slice(bytes);
self.1 = byteslen;
Ok(())
}
}
pub fn replace_trunc<S: AsRef<str>>(&mut self, string: S) {
let s = string.as_ref();
self.1 = 0;
for ch in s.chars() {
if let Err(ErrorOverflow {}) = self.push(ch) {
return;
}
}
}
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.0[0..self.1]) }
}
}
impl<const SIZE: usize> Default for Str<SIZE> {
fn default() -> Self {
Self::EMPTY
}
}
impl<const SIZE: usize> TryFrom<&str> for Str<SIZE> {
type Error = ErrorOverflow;
fn try_from(string: &str) -> Result<Self, Self::Error> {
Self::new(string)
}
}
impl<const SIZE: usize> TryFrom<&[u8]> for Str<SIZE> {
type Error = Error;
fn try_from(arr: &[u8]) -> Result<Self, Self::Error> {
let s = str::from_utf8(arr)?;
let s = Self::new(s)?;
Ok(s)
}
}
impl<const SIZE: usize> FromStr for Str<SIZE> {
type Err = ErrorOverflow;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Str::<SIZE>::try_from(s)
}
}
impl<const SIZE: usize> AsRef<str> for Str<SIZE> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<const SIZE: usize> AsRef<[u8]> for Str<SIZE> {
fn as_ref(&self) -> &[u8] {
&self.0[0..self.byte_len()]
}
}
impl<const SIZE: usize> fmt::Display for Str<SIZE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<const SIZE: usize> fmt::Debug for Str<SIZE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<const SIZE: usize> ops::Deref for Str<SIZE> {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl<const SIZE: usize> PartialEq for Str<SIZE> {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl<const SIZE: usize> Eq for Str<SIZE> {}
impl<const SIZE: usize> hash::Hash for Str<SIZE> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl<const SIZE: usize> PartialOrd for Str<SIZE> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<const SIZE: usize> Ord for Str<SIZE> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl<const SIZE: usize> iter::FromIterator<char> for Str<SIZE> {
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
let mut s = Str::default();
for ch in iter {
if let Err(ErrorOverflow {}) = s.push(ch) {
break;
}
}
s
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ErrorOverflow;
impl error::Error for ErrorOverflow {}
impl fmt::Display for ErrorOverflow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "overflow")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
Overflow(ErrorOverflow),
Utf8(str::Utf8Error),
}
impl error::Error for Error {}
impl From<str::Utf8Error> for Error {
fn from(err: str::Utf8Error) -> Self {
Error::Utf8(err)
}
}
impl From<ErrorOverflow> for Error {
fn from(e: ErrorOverflow) -> Self {
Error::Overflow(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Overflow(ref e) => write!(f, "{}", e),
Error::Utf8(ref e) => write!(f, "{}", e),
}
}
}