use std::io::Write;
use crate::error::{MakcuError, Result};
pub const MOVE_RANGE: i32 = 32767;
pub const WHEEL_RANGE: i32 = 127;
pub struct CommandBuf {
buf: [u8; 64],
len: usize,
}
impl CommandBuf {
fn new() -> Self {
Self {
buf: [0u8; 64],
len: 0,
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.buf[..self.len]
}
}
pub fn build_move(x: i32, y: i32) -> Result<CommandBuf> {
check_move_range(x, "x")?;
check_move_range(y, "y")?;
build_cmd(|buf| write!(buf, "km.move({},{})\r\n", x, y))
}
pub fn build_silent_move(x: i32, y: i32) -> Result<CommandBuf> {
check_move_range(x, "x")?;
check_move_range(y, "y")?;
build_cmd(|buf| write!(buf, "km.silent({},{})\r\n", x, y))
}
pub fn build_wheel(delta: i32) -> Result<CommandBuf> {
if !(-WHEEL_RANGE..=WHEEL_RANGE).contains(&delta) {
return Err(MakcuError::OutOfRange {
value: delta as i64,
min: -WHEEL_RANGE as i64,
max: WHEEL_RANGE as i64,
});
}
build_cmd(|buf| write!(buf, "km.wheel({})\r\n", delta))
}
fn check_move_range(v: i32, _axis: &str) -> Result<()> {
if !(-MOVE_RANGE..=MOVE_RANGE).contains(&v) {
return Err(MakcuError::OutOfRange {
value: v as i64,
min: -MOVE_RANGE as i64,
max: MOVE_RANGE as i64,
});
}
Ok(())
}
fn build_cmd(f: impl FnOnce(&mut &mut [u8]) -> std::io::Result<()>) -> Result<CommandBuf> {
let mut cmd = CommandBuf::new();
let mut buf: &mut [u8] = &mut cmd.buf[..];
let _ = f(&mut buf);
cmd.len = fmt_len(&cmd.buf);
if cmd.len == 0 {
return Err(MakcuError::Protocol("command too long for buffer".into()));
}
Ok(cmd)
}
pub fn build_serial_set(value: &str) -> Result<CommandBuf> {
if value.len() > 45 {
return Err(MakcuError::Protocol("serial value too long".into()));
}
build_cmd(|buf| write!(buf, "km.serial('{}')\r\n", value))
}
fn fmt_len(buf: &[u8; 64]) -> usize {
buf.iter()
.position(|&b| b == b'\n')
.map(|p| p + 1)
.unwrap_or(0)
}