reliq 0.1.0

A portable library of primitive-like constructs.
Documentation
use super::*;

pub type Result<T> = ::core::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    Overflow,
    IllegalByteSequence,
    Empty
}

#[derive(Clone)]
pub struct Utf8<const A: usize> {
    buf: array::Array<A, u8>,
    len: usize
}

impl<const A: usize> Utf8<A> {
    #[inline]
    pub const fn zero() -> Self {
        Self {
            buf: array::Array {
                buf: unsafe {
                    core::mem::MaybeUninit::uninit().assume_init()
                },
                len: 0
            },
            len: 0
        }
    }

    #[inline]
    pub const fn encode(bytes: &[u8]) -> Self {
        let mut ret: Self = Self::zero();
        let mut key: usize = 0;
        while key < bytes.len() && ret.buf.len < A {
            let byte = bytes[key];
            ret.buf.buf[key].write(byte);
            ret.buf.len += 1;
            if byte & 0b1100_0000 != 0b1000_0000 {
                ret.len += 1
            }
            key += 1;
        }
        ret
    }

    #[inline] 
    pub fn push_str(&mut self, s: &str) -> Result<()> {
        let old_buf_len: usize = self.buf.len();
        let old_len: usize = self.len;
        for c in s.chars() {
            if let Err(e) = self.push(c) {
                self.buf.len = old_buf_len;
                self.len = old_len;
                return Err(e)
            }
        }
        Ok(())
    }

    #[inline]
    pub const fn push(&mut self, c: char) -> Result<()> {
        let code: u32 = c as u32;
        let arr: &mut array::Array<A, u8> = &mut self.buf;
        let req: usize = if code <= 0x7f {
            1
        } else if code <= 0x7ff {
            2
        } else if code <= 0xffff {
            3
        } else {
            4
        };
        if arr.len() + req > A {
            return Err(Error::Overflow)
        }
        match req {
            1 => {
                let _ = arr.push(code as u8);
            },
            2 => {
                let _ = arr.push((0b1100_0000 | ((code >> 6) & 0x1F)) as u8);
                let _ = arr.push((0b1000_0000 | (code & 0x3F)) as u8);
            },
            3 => {
                let _ = arr.push((0b1110_0000 | ((code >> 12) & 0x0F)) as u8);
                let _ = arr.push((0b1000_0000 | ((code >> 6) & 0x3F)) as u8);
                let _ = arr.push((0b1000_0000 | (code & 0x3F)) as u8);
            },
            4 => {
                let _ = arr.push((0b1111_0000 | ((code >> 18) & 0x07)) as u8);
                let _ = arr.push((0b1000_0000 | ((code >> 12) & 0x3F)) as u8);
                let _ = arr.push((0b1000_0000 | ((code >> 6) & 0x3F)) as u8);
                let _ = arr.push((0b1000_0000 | (code & 0x3F)) as u8);
            },
            _ => unreachable!()
        }
        self.len += 1;
        Ok(())
    }

    #[inline]
    pub const fn pop(&mut self) -> Result<()> {
        let buf: &[u8] = self.buf.as_slice();
        let key: usize = self.buf.len();
        if key == 0 {
            return Err(Error::Empty)
        }
        let mut back: usize = 1;
        while back <= 4 {
            if key < back {
                break
            }
            let b: u8 = buf[key - back];
            if back == 1 && b & 0b1000_0000 == 0 {
                self.buf.len -= 1;
                self.len -= 1;
                return Ok(())
            } else if back == 2 && b & 0b1110_0000 == 0b1100_0000 {
                self.buf.len -= 2;
                self.len -= 1;
                return Ok(())
            } else if back == 3 && b & 0b1111_0000 == 0b1110_0000 {
                self.buf.len -= 3;
                self.len -= 1;
                return Ok(())
            } else if back == 4 && b & 0b1111_1000 == 0b1111_0000 {
                self.buf.len -= 4;
                self.len -= 1;
                return Ok(())
            }
            back += 1;
        }
        Err(Error::IllegalByteSequence)
    }

    pub fn cast<const B: usize>(self) -> Result<Utf8<B>> {
        let a: Utf8<A> = self;
        let a: &str = a.as_str();
        let mut b: Utf8<B> = Utf8::zero();
        b.push_str(a)?;
        Ok(b)
    }

    pub fn as_str(&self) -> &str {
        let ret = self.buf.as_slice();
        ::core::str::from_utf8(ret)
            .ok()
            .ok_or(Error::IllegalByteSequence)
            .unwrap()
    }
}

impl<const T: usize> TryFrom<&str> for Utf8<T> {
    type Error = Error;

    fn try_from(value: &str) -> ::core::result::Result<Self, Self::Error> {
        let mut s: Self = Self::zero();
        s.push_str(value)?;
        Ok(s)
    }
}

impl<const T: usize> Eq for Utf8<T> {}
impl<const T: usize> PartialEq for Utf8<T> {
    fn eq(&self, other: &Self) -> bool {
        self.len == other.len && self.buf == other.buf
    }
}

impl<const A: usize> Ord for Utf8<A> {
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        let x: &str = self.as_str();
        let y: &str = other.as_str();
        x.cmp(y)
    }
}

impl<const T: usize> PartialOrd for Utf8<T> {
    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
        let x: &str = self.as_str();
        let y: &str = other.as_str();
        let ret: ::core::cmp::Ordering = x.cmp(y);
        Some(ret)
    }
}

impl<const A: usize> ::core::fmt::Debug for Utf8<A> {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        f
            .debug_struct("Utf8")
            .field("len", &self.len)
            .field("buf_len", &self.buf.len)
            .field("as_str", &self.as_str())
            .finish()
    }
}

impl<const T: usize> ::core::fmt::Display for Utf8<T> {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        let s: &str = self.as_str();
        write!(f, "{}", s)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn try_it() {
        let s: Utf8<64> = "Hello World".try_into().unwrap();
        let expected: Utf8<64> = "Hello World".try_into().unwrap();
        assert_eq!(s, expected);
    }
}