use std::collections::HashMap;
use std::convert::From;
use std::fmt;
use regex::Regex;
use crate::instructions::{EBBInstruction, EBBResponse};
use crate::plotter::Device;
use crate::runtime::Config;
lazy_static! {
static ref VERSION_RE: Regex =
Regex::new(r"^EBBv13_and_above EB Firmware Version (\d+)\.(\d+)(?:\.(\d+))?$").unwrap();
}
#[derive(Debug, PartialEq, Eq)]
pub struct Response {
msg: String,
}
impl Response {
pub fn new(msg: String) -> Self {
Response { msg }
}
pub fn noop() -> Self {
Response {
msg: String::from("This command was a no-op"),
}
}
pub fn fail(msg: Option<String>) -> Self {
let default = String::from("This command failed");
let message = msg.unwrap_or(default);
Self::new(message)
}
}
impl fmt::Display for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
#[derive(Debug, Serialize)]
pub enum Error {
InvalidCommand,
ParsePlotterResponse(String),
PlotterExecution(String),
}
pub type Result = std::result::Result<Vec<EBBResponse>, Error>;
type ValidationResult = std::result::Result<(), Error>;
pub trait Command: fmt::Debug {
fn run(&self, dev: Box<dyn Device>) -> Result;
fn validate(&self, _ctx: Config) -> ValidationResult {
Ok(())
}
}
pub trait FromParams {
type Item;
fn from_params(params: &[String]) -> Vec<Self::Item>;
}
pub trait IntoResponse {
type CmdResponse;
fn into_response(self, response: String) -> Self::CmdResponse;
}
#[derive(Debug, PartialEq)]
pub struct TogglePen {}
impl Command for TogglePen {
fn run(&self, mut dev: Box<dyn Device>) -> Result {
let cmd = EBBInstruction::TogglePen;
let bytes = cmd.as_bytes();
match dev.execute(&bytes, cmd.responses()) {
Err(e) => Err(Error::PlotterExecution(e.to_string())),
Ok(res) => match cmd.parse_response(res) {
Ok(resp) => Ok(vec![resp]),
Err(e) => Err(Error::ParsePlotterResponse(e.to_string())),
},
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, FromParams, Clone, Copy)]
pub struct GoTo {
x: i32,
y: i32,
duration: Option<i32>,
}
impl From<&HashMap<String, String>> for GoTo {
fn from(hm: &HashMap<String, String>) -> GoTo {
let x: i32 = hm.get("x_steps").unwrap().parse().expect("Failed to parse");
let y: i32 = hm.get("y_steps").unwrap().parse().expect("Failed to parse");
GoTo {
x,
y,
duration: None,
}
}
}
impl fmt::Display for GoTo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Go to: X{}, Y{}", self.x, self.y)
}
}
impl Command for GoTo {
fn run(&self, mut dev: Box<dyn Device>) -> Result {
let cmd = EBBInstruction::MixedGeometryMove {
x: self.x,
y: self.y,
duration: None,
};
let bytes = cmd.as_bytes();
match dev.execute(&bytes, cmd.responses()) {
Ok(resp) => match cmd.parse_response(resp) {
Ok(ebb_resp) => Ok(vec![ebb_resp]),
Err(e) => Err(Error::ParsePlotterResponse(e)),
},
Err(e) => Err(Error::PlotterExecution(e.to_string())),
}
}
}
#[derive(Debug, FromParams, Serialize, Deserialize)]
pub struct Home {}
#[derive(PartialEq, Debug)]
pub struct Version {}
impl Command for Version {
fn run(&self, mut dev: Box<dyn Device>) -> Result {
let cmd = EBBInstruction::Version;
let bytes = cmd.as_bytes();
match dev.execute(&bytes, cmd.responses()) {
Err(e) => Err(Error::PlotterExecution(e.to_string())),
Ok(res) => match cmd.parse_response(res) {
Ok(resp) => Ok(vec![resp]),
Err(e) => Err(Error::ParsePlotterResponse(e.to_string())),
},
}
}
}