#![allow(clippy::must_use_candidate)]
use std::fmt::{Debug, Write};
use std::ops::{Deref, DerefMut};
use std::str;
use dizzy::DstNewtype;
#[derive(PartialEq, DstNewtype)]
#[dizzy(invariant = str::is_ascii)]
#[dizzy(constructor = pub const from_str, getter = pub const as_str)]
#[dizzy(derive(Debug, Deref, IntoBoxed, CloneBoxed))]
#[repr(transparent)]
pub struct AsciiStr(str);
impl AsciiStr {
#[inline]
pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
str::from_utf8(bytes).ok().and_then(Self::from_str)
}
#[inline]
pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
unsafe { Self::from_str_unchecked(str::from_utf8_unchecked(bytes)) }
}
#[inline]
pub const unsafe fn from_str_unchecked(src: &str) -> &Self {
unsafe { std::mem::transmute(src) }
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[must_use]
pub struct AsciiArray<const CAP: usize> {
array: [u8; CAP],
len: usize,
}
impl<const CAP: usize> Deref for AsciiArray<CAP> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.array[0..self.len]
}
}
impl<const CAP: usize> AsRef<AsciiStr> for AsciiArray<CAP> {
#[inline]
fn as_ref(&self) -> &AsciiStr {
self.as_ascii_str()
}
}
impl<const CAP: usize> AsRef<str> for AsciiArray<CAP> {
#[inline]
fn as_ref(&self) -> &str {
self.as_ascii_str().as_str()
}
}
impl<const CAP: usize> std::fmt::Display for AsciiArray<CAP> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl<const CAP: usize> std::fmt::Debug for AsciiArray<CAP> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char('"')?;
for ch in self.escape_ascii() {
f.write_char(ch as char)?;
}
f.write_char('"')
}
}
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("Cannot store Non-Ascii data in an a AsciiArray")]
pub struct NonAscii;
impl<const N: usize> TryFrom<[u8; N]> for AsciiArray<N> {
type Error = NonAscii;
#[inline]
fn try_from(array: [u8; N]) -> Result<Self, Self::Error> {
Self::from_array(array, N)
}
}
impl<const CAP: usize> Default for AsciiArray<CAP> {
fn default() -> Self {
Self::new()
}
}
impl<const CAP: usize> AsciiArray<CAP> {
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub const fn new() -> Self {
Self {
array: [0u8; CAP],
len: 0,
}
}
#[inline]
pub const fn new_filled(byte: u8) -> Self {
let mut x = Self::new();
x.fill(byte);
x
}
#[inline]
pub const fn from_array(array: [u8; CAP], len: usize) -> Result<Self, NonAscii> {
assert!(len <= CAP);
if array.is_ascii() {
Ok(Self { array, len })
} else {
Err(NonAscii)
}
}
pub const fn get(&self, idx: usize) -> u8 {
assert!(idx < self.len);
self.array[idx]
}
#[inline]
pub const fn fill(&mut self, ch: u8) {
assert!(ch.is_ascii());
let mut i = 0;
while i < self.array.len() {
self.array[i] = ch;
i += 1;
}
}
#[inline]
pub const fn push(&mut self, ch: u8) {
assert!(ch.is_ascii());
assert!(self.len < CAP);
self.array[self.len] = ch;
self.len += 1;
}
#[inline]
pub const fn pop(&mut self) -> Option<u8> {
if self.len == 0 {
None
} else {
self.len -= 1;
Some(self.array[self.len])
}
}
pub fn rtrim(&mut self, needle: impl AsRef<[u8]>) {
let needle = needle.as_ref();
if needle.is_empty() {
return;
}
while self.array[..self.len].ends_with(needle) {
self.len = self.len.saturating_sub(needle.len());
}
}
pub fn copy_from_str(s: &str) -> Option<Self> {
if s.len() > CAP {
return None;
}
let ascii = AsciiStr::from_str(s)?.as_bytes();
let mut array = [0u8; CAP];
array[..ascii.len()].copy_from_slice(ascii);
Some(Self {
array,
len: ascii.len(),
})
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if !bytes.is_ascii() || bytes.len() > CAP {
return None;
}
let mut array = [0u8; CAP];
array[0..bytes.len()].copy_from_slice(bytes);
Some(Self {
array,
len: bytes.len(),
})
}
#[inline]
pub fn as_str(&self) -> &str {
self.as_ascii_str().as_str()
}
#[inline]
pub fn as_ascii_str(&self) -> &AsciiStr {
self.array
.is_ascii()
.then(|| unsafe { self.as_ascii_str_unchecked() })
.expect("`AsciiArray` contains only ASCII characters")
}
#[inline]
pub unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr {
unsafe { AsciiStr::from_bytes_unchecked(&self.array[0..self.len]) }
}
}
pub struct ArrayList<T, const C: usize> {
array: [T; C],
len: usize,
}
impl<T: Debug, const C: usize> Debug for ArrayList<T, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ArrayList")
.field("array", &&self.array[0..self.len])
.field("len", &self.len)
.finish()
}
}
impl<T: Default + Clone + Copy, const C: usize> Default for ArrayList<T, C> {
fn default() -> Self {
Self {
array: [T::default(); C],
len: 0,
}
}
}
impl<T: Default + Clone + Copy, const C: usize> ArrayList<T, C> {
#[inline]
pub fn new_with_length(len: usize) -> Self {
assert!(len <= C);
Self {
array: [T::default(); C],
len,
}
}
}
impl<T: Default + Clone, const C: usize> ArrayList<T, C> {
pub fn extend_from_slice(&mut self, slice: &[T]) {
if slice.is_empty() {
return;
}
assert!(slice.len() + self.len <= C);
self.array[self.len..(self.len + slice.len())].clone_from_slice(slice);
self.len += slice.len();
}
}
impl<T, const C: usize> AsRef<[T]> for ArrayList<T, C> {
#[inline]
fn as_ref(&self) -> &[T] {
&self.array[0..self.len]
}
}
impl<T, const C: usize> AsMut<[T]> for ArrayList<T, C> {
fn as_mut(&mut self) -> &mut [T] {
&mut self.array[0..self.len]
}
}
impl<T, const C: usize> Deref for ArrayList<T, C> {
type Target = [T];
#[inline]
fn deref(&self) -> &Self::Target {
&self.array[0..self.len]
}
}
impl<T, const C: usize> DerefMut for ArrayList<T, C> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.array[0..self.len]
}
}
impl<T, const C: usize> ArrayList<T, C> {
#[inline]
pub fn push(&mut self, item: T) {
assert!(self.len < C);
self.array[self.len] = item;
self.len += 1;
}
#[inline]
pub const fn set_len(&mut self, new_len: usize) {
assert!(new_len <= C);
self.len = new_len;
}
}