use std::fmt;
use std::str::FromStr;
use std::collections::HashMap;
use std::io::{BufReader,Read,BufWriter,Write};
use std::fs::File;
use std::io::{Error, ErrorKind};
use crate::primio::*;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct Cab {
name: String,
color: String,
}
impl fmt::Display for Cab {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<Cab \"{}\" \"{}\">",self.name,self.color)
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum CabParseError {
StartSyntaxError,
NameMissing,
ColorMissing,
MissingBracket,
ExtraCharacters,
}
impl fmt::Display for CabParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CabParseError::StartSyntaxError =>
write!(f,"Missing '<Cab '"),
CabParseError::NameMissing =>
write!(f,"Missing name"),
CabParseError::ColorMissing =>
write!(f,"Missing color"),
CabParseError::MissingBracket =>
write!(f,"Missing '>'"),
CabParseError::ExtraCharacters =>
write!(f,"Extra Characters"),
}
}
}
impl FromStr for Cab {
type Err = CabParseError;
fn from_str(string: &str) -> Result<Self, Self::Err> {
let (result,pos) = Cab::ParseCab(string)?;
if pos == string.len() {
Ok(result)
} else {
Err(CabParseError::ExtraCharacters)
}
}
}
impl Cab {
pub fn new(name_: String, color_: String) -> Self {
Self { name: name_.clone(), color: color_.clone() }
}
pub fn Name(&self) -> String {self.name.clone()}
pub fn Color(&self) -> String {self.color.clone()}
pub fn ParseCab(string: &str) -> Result<(Self, usize), CabParseError> {
let mut pos: usize;
let name: String;
let color: String;
match string.match_indices("<Cab \"").next() {
None => return Err(CabParseError::StartSyntaxError),
Some((n, m)) => pos = n + m.len(),
};
match string[pos..].match_indices('"').next() {
None => return Err(CabParseError::NameMissing),
Some((n, m)) => {
name = String::from(&string[pos..n+pos]);
pos += n + m.len();
},
};
match string[pos..].match_indices('"').next() {
None => return Err(CabParseError::ColorMissing),
Some((n, m)) => pos += n + m.len(),
};
match string[pos..].match_indices('"').next() {
None => return Err(CabParseError::ColorMissing),
Some((n, m)) => {
color = String::from(&string[pos..n+pos]);
pos += n + m.len();
},
};
match string[pos..].match_indices('>').next() {
None => return Err(CabParseError::MissingBracket),
Some((n, m)) => pos += n + m.len(),
};
Ok((Cab::new(name,color),pos))
}
pub fn Write(&self,f: &mut BufWriter<File>) -> std::io::Result<()> {
write!(f,"<Cab \"{}\" \"{}\">",self.name,self.color)
}
pub fn Read(inp: &mut BufReader<File>) -> std::io::Result<Option<Self>> {
let mut ch: char;
let mut byte: [u8; 1] = [0; 1];
loop {
let status = inp.read(&mut byte)?;
if status == 0 {return Ok(None);}
ch = byte[0] as char;
match ch {
' '|'\t'|'\n' => (),
_ => {break;},
}
}
for c in "<Cab".chars() {
if c != ch {return Ok(None);}
let status = inp.read(&mut byte)?;
if status == 0 {return Ok(None);}
ch = byte[0] as char;
}
let name: String = match ReadQuotedString(inp)? {
None => {return Ok(None);},
Some(s) => s,
};
let color: String = match ReadQuotedString(inp)? {
None => {return Ok(None);},
Some(s) => s,
};
loop {
let status = inp.read(&mut byte)?;
if status == 0 {return Ok(None);}
ch = byte[0] as char;
match ch {
'>' => {return Ok(Some(Self::new(name,color)));},
' '|'\t'|'\n' => {continue;},
_ => {return Err(Error::new(ErrorKind::Other,"Syntax error: missing '>'"));},
};
}
}
}
pub type CabNameMap = HashMap<String, Cab>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn Cab_new () {
let temp = Cab::new(String::from("Cab A"),String::from("red"));
assert_eq!(temp,Cab {name: String::from("Cab A"), color: String::from("red")});
}
#[test]
fn Cab_Name () {
let temp = Cab::new(String::from("Cab A"),String::from("red"));
assert_eq!(temp.Name(),String::from("Cab A"));
}
#[test]
fn Cab_Color () {
let temp = Cab::new(String::from("Cab A"),String::from("red"));
assert_eq!(temp.Color(),String::from("red"));
}
#[test]
fn Cab_Display () {
let temp = Cab::new(String::from("Cab A"),String::from("red"));
let output = format!("{}",temp);
assert_eq!(output,String::from("<Cab \"Cab A\" \"red\">"));
}
#[test]
fn Cab_from_str () {
let temp = Cab::new(String::from("Cab A"),String::from("red"));
let output = format!("{}",temp);
let other = Cab::from_str(&output).expect("Parse error");
assert_eq!(temp,other);
}
}