use crate::{ascii, cp437::CP437Char, cp437::CP437Error};
use serde::{de, Deserialize, Serialize};
use std::{fmt, slice::Iter, str::FromStr};
#[derive(Debug, Default, Clone, PartialEq)]
pub struct CP437String {
bytes: Vec<u8>,
chars: Vec<CP437Char>,
}
impl CP437String {
pub fn new() -> CP437String {
CP437String::default()
}
pub fn with_capacity(capacity: usize) -> CP437String {
CP437String {
bytes: Vec::with_capacity(capacity),
chars: Vec::with_capacity(capacity),
}
}
pub fn as_slice(&self) -> &[u8] {
&self.bytes
}
pub fn len(&self) -> usize {
self.chars.len()
}
pub fn is_empty(&self) -> bool {
self.chars.is_empty()
}
fn strip_trailing_value(&self, value: u8) -> CP437String {
let mut cp437 = self.clone();
while let Some(last) = cp437.last() {
if last.byte == value {
cp437.pop();
} else {
break;
}
}
cp437
}
pub fn strip_trailing_spaces(&self) -> CP437String {
self.strip_trailing_value(ascii::SPACE)
}
pub fn strip_trailing_nulls(&self) -> CP437String {
self.strip_trailing_value(ascii::NULL)
}
fn pad_with_value(&self, length: usize, value: u8) -> CP437String {
let mut cp437 = self.clone();
while cp437.len() < length {
cp437.push_byte(value);
}
cp437
}
pub fn pad_with_spaces(&self, length: usize) -> CP437String {
self.pad_with_value(length, 0x20)
}
pub fn pad_with_nulls(&self, length: usize) -> CP437String {
self.pad_with_value(length, 0x0)
}
pub fn push_byte(&mut self, byte: u8) {
let ch = CP437Char::from(byte);
self.bytes.push(byte);
self.chars.push(ch);
}
fn push(&mut self, ch: CP437Char) {
self.bytes.push(ch.byte);
self.chars.push(ch);
}
fn pop(&mut self) -> Option<CP437Char> {
if let Some(ch) = self.chars.pop() {
self.bytes.pop();
Some(ch)
} else {
None
}
}
fn last(&self) -> Option<&CP437Char> {
self.chars.last()
}
fn chars(&self) -> Iter<CP437Char> {
self.chars.iter()
}
}
impl From<Vec<u8>> for CP437String {
fn from(bytes: Vec<u8>) -> Self {
let mut cp437 = CP437String::with_capacity(bytes.len());
for byte in bytes {
cp437.push_byte(byte);
}
cp437
}
}
impl FromStr for CP437String {
type Err = CP437Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut cp437_string = CP437String::with_capacity(s.len());
for ch in s.chars() {
match CP437Char::try_from(ch) {
Ok(ch) => cp437_string.push(ch),
Err(err) => return Err(err),
}
}
Ok(cp437_string)
}
}
impl TryFrom<&str> for CP437String {
type Error = CP437Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
CP437String::from_str(s)
}
}
impl From<&[u8]> for CP437String {
fn from(bytes: &[u8]) -> Self {
CP437String::from(bytes.to_vec())
}
}
impl From<&CP437String> for String {
fn from(cp_437: &CP437String) -> Self {
cp_437.chars().map(|cp_char| cp_char.ch).collect::<String>()
}
}
impl fmt::Display for CP437String {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{string}", string = String::from(self))
}
}
impl Serialize for CP437String {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_str(&self.to_string())
}
}
struct StringVisitor;
impl<'de> de::Visitor<'de> for StringVisitor {
type Value = CP437String;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("expecting a string")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match CP437String::from_str(value) {
Ok(cp437) => Ok(cp437),
Err(err) => Err(E::custom(err)),
}
}
}
impl<'de> Deserialize<'de> for CP437String {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(StringVisitor)
}
}
#[cfg(test)]
mod test {
use crate::CP437String;
use serde_json::to_string;
#[test]
fn test() {
let a = CP437String::try_from("☺☻♥♦♣♠").expect("cp437 string");
let bytes: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
let b = CP437String::from(bytes.as_slice());
assert_eq!(a, b);
assert_eq!(to_string(&a).unwrap(), "\"☺☻♥♦♣♠\"");
}
}