extern crate alloc;
use alloc::{vec::Vec, string::String};
use core::{fmt, ops::{Deref, Index}};
use core::str::FromStr;
use crate::VDChar;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VDStringError {
InvalidChar(char),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VDString {
chars: Vec<VDChar>,
cache: String,
}
impl VDString {
pub fn new(chars: Vec<VDChar>) -> Self {
let cache = chars.iter().map(|c| c.as_char()).collect();
Self { chars, cache }
}
pub fn as_vdchars(&self) -> &[VDChar] {
&self.chars
}
}
impl Deref for VDString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.cache
}
}
impl fmt::Display for VDString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.cache)
}
}
impl Index<usize> for VDString {
type Output = VDChar;
fn index(&self, index: usize) -> &Self::Output {
&self.chars[index]
}
}
impl<'a> IntoIterator for &'a VDString {
type Item = VDChar;
type IntoIter = core::iter::Copied<core::slice::Iter<'a, VDChar>>;
fn into_iter(self) -> Self::IntoIter {
self.chars.iter().copied()
}
}
impl FromStr for VDString {
type Err = VDStringError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.chars()
.map(|c| VDChar::new(c).ok_or(VDStringError::InvalidChar(c)))
.collect::<Result<Vec<_>, _>>()
.map(VDString::new)
}
}
impl TryFrom<&str> for VDString {
type Error = VDStringError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
VDString::from_str(s)
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::{string::ToString, vec, vec::Vec};
use super::*;
fn vd(c: char) -> VDChar {
VDChar::new(c).unwrap()
}
#[test]
fn construct_from_vec() {
let chars = vec![vd('A'), vd('B'), vd('2')];
let s = VDString::new(chars.clone());
assert_eq!(s.as_vdchars(), &chars[..]);
assert_eq!(&*s, "AB2");
}
#[test]
fn parse_valid_string() {
let s: VDString = "M29W".parse().unwrap();
assert_eq!(&*s, "M29W");
assert_eq!(s.len(), 4);
assert_eq!(s[0], vd('M'));
assert_eq!(s[3].as_char(), 'W');
}
#[test]
fn parse_invalid_string() {
let err = "HELLO!".parse::<VDString>();
assert!(err.is_err());
let err2 = "O0I1".parse::<VDString>();
assert!(err2.is_err());
}
#[test]
fn index_returns_correct_char() {
let s: VDString = "5K7".parse().unwrap();
assert_eq!(s[0], vd('5'));
assert_eq!(s[1].as_char(), 'K');
assert_eq!(s[2].to_string(), "7");
}
#[test]
fn iterates_over_chars() {
let s: VDString = "X2Z".parse().unwrap();
let collected: Vec<char> = s.into_iter().map(|c| c.as_char()).collect();
assert_eq!(collected, vec!['X', '2', 'Z']);
}
#[test]
fn from_str_and_try_from_match() {
let a = "Q4V";
let parsed = a.parse::<VDString>().unwrap();
let tried = VDString::try_from(a).unwrap();
assert_eq!(parsed, tried);
}
}