#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Color {
W,
B,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct EntityBuilder {
list: Vec<Entity>,
current: Option<Entity>,
}
pub fn entity<T>(f: T) -> Entity
where T: Fn(&mut EntityBuilder) -> &mut EntityBuilder {
let mut b = EntityBuilder::new();
f(&mut b);
b.build()
}
impl EntityBuilder {
pub fn new() -> EntityBuilder {
EntityBuilder::default()
}
pub fn i(&mut self, i: u32) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Int(i));
self
}
pub fn f(&mut self, f: f32) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Float(f));
self
}
pub fn s(&mut self, s: &str) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::String(s.to_string()));
self
}
pub fn v_pass(&mut self) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Vertex((0, 0)));
self
}
pub fn v(&mut self, v: (i32, i32)) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Vertex(v));
self
}
pub fn w(&mut self) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Color(Color::W));
self
}
pub fn b(&mut self) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Color(Color::B));
self
}
pub fn bool(&mut self, b: bool) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Boolean(b));
self
}
pub fn color(&mut self, b: bool) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Color(if b { Color::W } else { Color::B }));
self
}
pub fn mv_w(&mut self, v: (i32, i32)) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Move((Color::W, v)));
self
}
pub fn mv_b(&mut self, v: (i32, i32)) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Move((Color::B, v)));
self
}
pub fn mv(&mut self, color: bool, v: (i32, i32)) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::Move((if color { Color::W } else { Color::B }, v)));
self
}
pub fn list(&mut self) -> &mut Self {
if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
self.current = Some(Entity::List(self.list.clone()));
self.list = Vec::new();
self
}
pub fn build(&self) -> Entity {
self.current.clone().expect("Did not setup any entitiy in EntityBuilder!")
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Entity {
Int(u32),
Float(f32),
String(String),
Vertex((i32, i32)),
Color(Color),
Move((Color, (i32, i32))),
Boolean(bool),
List(Vec<Entity>),
}
fn gen_move_char(i: u32) -> char {
let c = if i <= 8 {
('A' as u32) + (i - 1)
} else {
('A' as u32) + i
};
if let Some(c) = std::char::from_u32(c) {
c
} else {
'Z'
}
}
impl std::fmt::Display for Entity {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Entity::Int(i) => write!(f, "{}", i),
Entity::Float(n) => write!(f, "{}", n),
Entity::String(s) => write!(f, "{}", s),
Entity::Vertex((h, v)) => {
let mut s = String::from("");
if *h <= 0 || *v <= 0 {
s += &"pass".to_string();
} else {
s += &format!("{}", gen_move_char(*h as u32));
s += &format!("{}", v);
}
write!(f, "{}", s)
},
Entity::Color(Color::W) => write!(f, "w"),
Entity::Color(Color::B) => write!(f, "b"),
Entity::Move((Color::W, (h, v))) => {
let mut s = String::from("");
if *h <= 0 || *v <= 0 {
s += &"w pass".to_string();
} else {
s += &format!("w {}", gen_move_char(*h as u32));
s += &format!("{}", v);
}
write!(f, "{}", s)
},
Entity::Move((Color::B, (h, v))) => {
let mut s = String::from("");
if *h <= 0 || *v <= 0 {
s += &"b pass".to_string();
} else {
s += &format!("b {}", gen_move_char(*h as u32));
s += &format!("{}", v);
}
write!(f, "{}", s)
},
Entity::Boolean(true) => write!(f, "true"),
Entity::Boolean(false) => write!(f, "false"),
Entity::List(vec) => {
let mut s = String::from("");
if vec.is_empty() { return write!(f, ""); }
let sep = if let Entity::List(_) = vec[0] {
"\n"
} else {
" "
};
for (i, e) in vec.iter().enumerate() {
if i > 0 { s += sep; }
s += &e.to_string();
}
write!(f, "{}", s)
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct EntityParser {
buffer: String,
entities: Vec<Entity>,
parse_error: bool,
}
impl std::iter::Iterator for EntityParser {
type Item = String;
fn next(&mut self) -> Option<String> {
self.buffer = self.buffer.chars().skip_while(|c| *c == ' ' || *c == '\n').collect();
let mut s = String::from("");
let mut skip_count = 0;
for c in self.buffer.chars() {
skip_count += 1;
if c == ' ' || c == '\n' { break; }
s.push(c);
}
self.buffer = self.buffer.chars().skip(skip_count).collect();
if s.is_empty() { None } else { Some(s) }
}
}
impl EntityParser {
pub fn new(s: &str) -> Self {
EntityParser {
buffer: String::from(s),
entities: Vec::new(),
parse_error: false,
}
}
pub fn result(&self) -> Option<Vec<Entity>> {
if self.parse_error { return None; }
Some(self.entities.clone())
}
pub fn is_eof(&self) -> bool { self.buffer.is_empty() }
pub fn had_parse_error(&self) -> bool { self.parse_error }
pub fn s(&mut self) -> &mut Self {
let s = self.next().unwrap_or_else(|| String::from(""));
if s.is_empty() { self.parse_error = true; return self; }
self.entities.push(Entity::String(s));
self
}
pub fn i(&mut self) -> &mut Self {
let s = self.next().unwrap_or_else(|| String::from(""));
if let Ok(i) = s.parse::<u32>() {
self.entities.push(Entity::Int(i));
} else {
self.parse_error = true;
}
self
}
pub fn f(&mut self) -> &mut Self {
let s = self.next().unwrap_or_else(|| String::from(""));
if let Ok(f) = s.parse::<f32>() {
self.entities.push(Entity::Float(f));
} else {
self.parse_error = true;
}
self
}
pub fn color(&mut self) -> &mut Self {
let s = self.next().unwrap_or_else(|| String::from(""));
let s = s.to_lowercase();
if s == "w" || s == "white" { self.entities.push(Entity::Color(Color::W)); return self; }
if s == "b" || s == "black" { self.entities.push(Entity::Color(Color::B)); return self; }
self.parse_error = true;
self
}
pub fn vertex(&mut self) -> &mut Self {
let s = self.next().unwrap_or_else(|| String::from(""));
let s = s.to_uppercase();
if s == "PASS" { self.entities.push(Entity::Vertex((0, 0))); return self; }
if s.len() < 2 || s.len() > 3 {
self.parse_error = true;
return self;
}
let h = s.chars().nth(0).unwrap();
if !h.is_ascii_alphabetic() {
self.parse_error = true;
return self;
}
let h = h as u32;
let mut h = (h - ('A' as u32)) + 1;
if h > 8 { h -= 1; }
let v : String = s.chars().skip(1).collect();
if let Ok(v) = i32::from_str_radix(&v, 10) {
self.entities.push(Entity::Vertex((h as i32, v)));
} else {
self.parse_error = true;
}
self
}
pub fn mv(&mut self) -> &mut Self {
self.color();
if self.parse_error { return self; }
self.vertex();
if self.parse_error { self.entities.pop(); }
let m = self.entities.pop().unwrap();
let c = self.entities.pop().unwrap();
if let Entity::Vertex((h, v)) = m {
if let Entity::Color(c) = c {
self.entities.push(Entity::Move((c, (h, v))));
return self;
}
}
self.parse_error = true;
self
}
pub fn bool(&mut self) -> &mut Self {
let s = self.next().unwrap_or_else(|| String::from(""));
let s = s.to_uppercase();
if s == "TRUE" { self.entities.push(Entity::Boolean(true)); return self; }
if s == "FALSE" { self.entities.push(Entity::Boolean(false)); return self; }
self.parse_error = true;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Command {
id: Option<u32>,
name: String,
args: Option<Entity>,
}
impl Command {
pub fn new(name: &str) -> Command {
Command {
name: String::from(name),
id: None,
args: None,
}
}
pub fn new_with_args<T>(name: &str, args: T) -> Command
where T: Fn(&mut EntityBuilder) -> &mut EntityBuilder {
let mut cmd = Self::new(name);
cmd.args(args);
cmd
}
pub fn set_id(&mut self, id: u32) {
self.id = Some(id);
}
pub fn args<T>(&mut self, f: T)
where T: Fn(&mut EntityBuilder) -> &mut EntityBuilder {
let mut b = EntityBuilder::new();
f(&mut b);
self.set_args(&b.build());
}
pub fn set_args(&mut self, args: &Entity) {
self.args = Some(args.clone());
}
pub fn to_string(&self) -> String {
let mut out = String::from("");
if self.id.is_some() {
out += &format!("{}", self.id.unwrap());
out += " ";
}
out += &self.name;
if self.args.is_some() {
out += " ";
out += &self.args.as_ref().unwrap().to_string();
}
out += "\n";
out
}
#[allow(dead_code)]
pub fn to_bytes(&self) -> Vec<u8> {
Vec::from(self.to_string().as_bytes())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Response {
Error((Option<u32>, String)),
Result((Option<u32>, String)),
}
#[derive(Debug)]
pub enum ResponseParseError {
NoInput,
BadEntityInput,
BadResponse
}
impl Response {
pub fn text(&self) -> String {
match self {
Response::Error((_, t)) => t.clone(),
Response::Result((_, t)) => t.clone(),
}
}
pub fn id_0(&self) -> u32 {
match self {
Response::Error((None, _)) => 0,
Response::Result((None, _)) => 0,
Response::Error((Some(id), _)) => *id,
Response::Result((Some(id), _)) => *id,
}
}
pub fn entities<T>(&self, parse_fn: T) -> Result<Vec<Entity>, ResponseParseError>
where T: Fn(&mut EntityParser) -> &mut EntityParser {
let response = match self {
Response::Result((_, res)) => res.to_string(),
Response::Error((_, res)) => res.to_string(),
};
let mut ep = EntityParser::new(&response);
parse_fn(&mut ep);
if ep.had_parse_error() {
return Err(ResponseParseError::BadEntityInput);
}
Ok(ep.result().unwrap())
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ResponseParser {
buffer: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResponseError {
IncompleteResponse,
BadResponse(String),
}
fn refine_input(s: String) -> String {
let mut ret : String =
s.chars()
.filter(|c| *c != '\r')
.map(|c| if c == '\x09' { ' ' } else { c })
.skip_while(|c| *c == '\n' || *c == ' ' || *c == '\x09')
.collect();
loop {
let comment_pos = ret.find('#');
if comment_pos.is_some() {
let end_comment_pos = (&ret[comment_pos.unwrap()..]).find('\n');
if end_comment_pos.is_some() {
ret = String::from(&ret[..comment_pos.unwrap()])
+ &ret[comment_pos.unwrap() + end_comment_pos.unwrap() + 1..];
} else {
break;
}
} else {
break;
}
}
ret
}
impl ResponseParser {
pub fn new() -> ResponseParser {
ResponseParser::default()
}
pub fn feed(&mut self, s: &str) {
self.buffer += s;
}
#[allow(unused_assignments, clippy::collapsible_if)]
pub fn get_response(&mut self) -> Result<Response, ResponseError> {
self.buffer = refine_input(self.buffer.to_string());
if self.buffer.is_empty() { return Err(ResponseError::IncompleteResponse); }
let is_error = self.buffer.chars().nth(0).unwrap() != '=';
let mut id_str = String::from("");
let mut response = String::from("");
let mut read_id =
!( self.buffer.len() > 1
&& self.buffer.chars().nth(1).unwrap() == ' ');
let mut found_start = false;
let mut found_end = false;
let mut last_was_newline = false;
let mut skip_count = 1;
for c in self.buffer.chars().skip(1) {
skip_count += 1;
if read_id {
match c {
c if c.is_ascii_digit() => {
id_str.push(c);
},
' ' => {
found_start = true;
read_id = false;
},
_ => { return Err(ResponseError::BadResponse(self.buffer.to_string())); }
}
} else if !found_start {
if c == ' ' {
found_start = true;
} else {
return Err(ResponseError::BadResponse(self.buffer.to_string()));
}
} else {
if c == '\n' {
if last_was_newline {
found_end = true;
break;
} else {
last_was_newline = true;
}
} else {
if last_was_newline {
response.push('\n');
}
last_was_newline = false;
response.push(c);
}
}
}
if found_end {
self.buffer = self.buffer.chars().skip(skip_count).collect();
} else {
return Err(ResponseError::IncompleteResponse);
}
let id = if !id_str.is_empty() {
if let Ok(cn) = u32::from_str_radix(&id_str, 10) {
Some(cn)
} else {
None
}
} else {
None
};
if is_error {
Ok(Response::Error((id, response)))
} else {
Ok(Response::Result((id, response)))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_printing() {
assert_eq!(Entity::Int(10).to_string(), "10");
assert_eq!(Entity::Float(10.12).to_string(), "10.12");
assert_eq!(Entity::String(String::from("Test")).to_string(), "Test");
assert_eq!(Entity::Vertex((-1, -1)).to_string(), "pass");
assert_eq!(Entity::Vertex((1, 1)).to_string(), "A1");
assert_eq!(Entity::Vertex((19, 19)).to_string(), "T19");
assert_eq!(Entity::Vertex((8, 19)).to_string(), "H19");
assert_eq!(Entity::Vertex((9, 19)).to_string(), "J19");
assert_eq!(Entity::Color(Color::W).to_string(), "w");
assert_eq!(Entity::Color(Color::B).to_string(), "b");
assert_eq!(Entity::Move((Color::B, (0, 0))).to_string(), "b pass");
assert_eq!(Entity::Move((Color::W, (0, 0))).to_string(), "w pass");
assert_eq!(Entity::Move((Color::B, (8, 1))).to_string(), "b H1");
assert_eq!(Entity::Move((Color::W, (9, 1))).to_string(), "w J1");
assert_eq!(Entity::Move((Color::B, (19, 1))).to_string(), "b T1");
assert_eq!(Entity::Move((Color::W, (19, 19))).to_string(), "w T19");
assert_eq!(Entity::Boolean(true).to_string(), "true");
assert_eq!(Entity::Boolean(false).to_string(), "false");
assert_eq!(Entity::List(vec![Entity::Int(1), Entity::Int(2)]).to_string(),
"1 2");
assert_eq!(Entity::List(vec![
Entity::List(vec![Entity::Int(1), Entity::Int(2)]),
Entity::List(vec![Entity::Int(3), Entity::Int(4)])]).to_string(),
"1 2\n3 4");
}
#[test]
fn check_entity_builder() {
assert_eq!(entity(|eb| eb.i(10)).to_string(), "10");
assert_eq!(entity(|eb| eb.f(10.12)).to_string(), "10.12");
assert_eq!(entity(|eb| eb.s("ok")).to_string(), "ok");
assert_eq!(entity(|eb| eb.v_pass()).to_string(), "pass");
assert_eq!(entity(|eb| eb.v((19, 19))).to_string(), "T19");
assert_eq!(entity(|eb| eb.bool(false)).to_string(), "false");
assert_eq!(entity(|eb| eb.w()).to_string(), "w");
assert_eq!(entity(|eb| eb.b()).to_string(), "b");
assert_eq!(entity(|eb| eb.color(true)).to_string(), "w");
assert_eq!(entity(|eb| eb.color(false)).to_string(), "b");
assert_eq!(entity(|eb| eb.mv_w((8, 8))).to_string(), "w H8");
assert_eq!(entity(|eb| eb.mv_b((8, 8))).to_string(), "b H8");
assert_eq!(entity(|eb| eb.mv(true, (8, 8))).to_string(),"w H8");
assert_eq!(entity(|eb| eb.mv_w((8, 8)).mv_b((9, 9)).list()).to_string(),
"w H8 b J9");
}
#[test]
fn check_entity_parser() {
let mut ep = EntityParser::new("10 10.2 ok WHite t19 false");
ep.i().f().s().mv().bool();
let res = ep.result().unwrap();
assert_eq!(res[0].to_string(), "10");
assert_eq!(res[1].to_string(), "10.2");
assert_eq!(res[2].to_string(), "ok");
assert_eq!(res[3].to_string(), "w T19");
assert_eq!(res[4].to_string(), "false");
}
#[test]
fn check_eof() {
let mut ep = EntityParser::new("t19 b10 a1 d2");
while !ep.is_eof() {
ep.vertex();
}
let res = ep.result().unwrap();
assert_eq!(res[3].to_string(), "D2");
}
#[test]
fn check_build_command() {
let mut c = Command::new("list_commands");
c.args(|eb| eb.i(10).f(10.20).s("OK").list());
assert_eq!(c.to_string(), "list_commands 10 10.2 OK\n");
assert_eq!(
Command::new_with_args("boardsize", |eb| eb.i(9)).to_string(),
"boardsize 9\n");
}
#[test]
fn check_setid_command() {
let mut c = Command::new("list_commands");
c.set_id(12);
assert_eq!(c.to_string(), "12 list_commands\n");
}
fn must_parse(s: &str) -> Response {
let mut rp = ResponseParser::new();
rp.feed(s);
let s = rp.get_response().unwrap();
s
}
#[test]
fn check_parser() {
{
let mut rp = ResponseParser::new();
rp.feed("= ok\n\n");
let s = rp.get_response().unwrap();
assert_eq!(format!("{:?}", s), "Result((None, \"ok\"))");
}
{
let mut rp = ResponseParser::new();
rp.feed("= ok\n\n");
rp.feed("= \n\n");
assert_eq!(rp.get_response().unwrap().text(), "ok");
}
{
let mut rp = ResponseParser::new();
rp.feed("= ok\n");
assert!(rp.get_response().is_err());
}
let res = must_parse("= ok\nfoobar\n\n");
assert_eq!(res.text(), "ok\nfoobar");
assert_eq!(format!("{:?}", must_parse("=10 ok\n\n")),
"Result((Some(10), \"ok\"))");
assert_eq!(format!("{:?}", must_parse("#\n=10 ok\n\n")),
"Result((Some(10), \"ok\"))");
assert_eq!(format!("{:?}", must_parse("= ok\n\n")),
"Result((None, \"ok\"))");
assert_eq!(format!("{:?}", must_parse("= \n\n")),
"Result((None, \"\"))");
assert_eq!(format!("{:?}", must_parse("= \na\nb\nc\n\n")),
"Result((None, \"\\na\\nb\\nc\"))");
assert_eq!(format!("{:?}", must_parse("= foo # all ok\n\n\n")),
"Result((None, \"foo \"))");
assert_eq!(format!("{:?}", must_parse("= \na\nb fooo #fewiofw jfw\nc\n\n")),
"Result((None, \"\\na\\nb fooo c\"))");
}
}