use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
pub const MAX_CHAR: u32 = 0x3FFFF;
#[derive(Clone, PartialEq, Eq, Hash, Default)]
pub struct Zstring {
chars: Vec<u32>,
}
impl Zstring {
#[inline]
pub const fn new() -> Zstring {
Zstring { chars: Vec::new() }
}
#[inline]
pub fn len(&self) -> usize {
self.chars.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.chars.is_empty()
}
#[inline]
pub fn char_at(&self, i: usize) -> u32 {
self.chars[i]
}
#[inline]
pub fn as_slice(&self) -> &[u32] {
&self.chars
}
#[inline]
pub fn from_code_points(chars: Vec<u32>) -> Zstring {
Zstring { chars }
}
pub fn to_rust_string(&self) -> String {
self.chars
.iter()
.filter_map(|&c| char::from_u32(c))
.collect()
}
}
impl From<&str> for Zstring {
fn from(s: &str) -> Zstring {
Zstring {
chars: s.chars().map(|c| c as u32).collect(),
}
}
}
impl fmt::Display for Zstring {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &c in &self.chars {
match char::from_u32(c) {
Some(ch) => f.write_str(ch.encode_utf8(&mut [0u8; 4]))?,
None => write!(f, "\\u{{{c:x}}}")?,
}
}
Ok(())
}
}
impl fmt::Debug for Zstring {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.to_rust_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn roundtrip_and_indexing() {
let z = Zstring::from("abc");
assert_eq!(z.len(), 3);
assert_eq!(z.char_at(0), b'a' as u32);
assert_eq!(z.to_rust_string(), "abc");
assert_eq!(z.to_string(), "abc");
assert!(Zstring::new().is_empty());
}
#[test]
fn eq_and_unicode() {
assert_eq!(Zstring::from("x"), Zstring::from("x"));
assert_ne!(Zstring::from("x"), Zstring::from("y"));
let z = Zstring::from("héllo");
assert_eq!(z.to_rust_string(), "héllo");
}
}