feat: WOWA, ik workie :3
This commit is contained in:
parent
ccf226f350
commit
9350e6bc63
6 changed files with 718 additions and 247 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -39,6 +39,23 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi_radix10"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6970a22a33d6a8f862aac371bac48505a1bfaa230ecb268c7b86fa4ac6e7121"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
|
|
@ -88,7 +105,10 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
name = "flurry"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"atoi_radix10",
|
||||
"bytes",
|
||||
"hex",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
]
|
||||
|
|
@ -111,6 +131,12 @@ version = "0.3.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.83"
|
||||
atoi_radix10 = "0.0.1"
|
||||
bytes = "1.6.0"
|
||||
hex = "0.4.3"
|
||||
tokio = { version = "1.38", features = ["full"] }
|
||||
tokio-test = "*"
|
||||
|
||||
|
|
|
|||
234
src/binary_protocol.rs
Normal file
234
src/binary_protocol.rs
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
use std::io::{self, Error, ErrorKind};
|
||||
|
||||
use atoi_radix10::parse_from_str;
|
||||
use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::{
|
||||
increment_counter, Canvas, Color, Command, Coordinate, Parser, Protocol, Responder, Response,
|
||||
MEEHHEH,
|
||||
};
|
||||
|
||||
const SIZE_BIN: u8 = 115;
|
||||
const HELP_BIN: u8 = 104;
|
||||
const LOCK: u8 = 0;
|
||||
const GET_PX_BIN: u8 = 32;
|
||||
const SET_PX_RGB_BIN: u8 = 128;
|
||||
const SET_PX_RGBA_BIN: u8 = 129;
|
||||
const SET_PX_W_BIN: u8 = 130;
|
||||
|
||||
const SET_PX_RGB_BIN_LENGTH: usize = 8;
|
||||
pub struct BinaryParser {}
|
||||
|
||||
impl BinaryParser {
|
||||
pub fn new() -> BinaryParser {
|
||||
return BinaryParser {};
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead + AsyncBufReadExt + std::marker::Unpin> Parser<R> for BinaryParser {
|
||||
async fn parse(&self, reader: &mut R) -> io::Result<Command> {
|
||||
let fst = reader.read_u8().await;
|
||||
match fst {
|
||||
Ok(i) => match i {
|
||||
HELP_BIN => return Ok(Command::Help),
|
||||
SIZE_BIN => {
|
||||
let canvas = reader.read_u8().await?;
|
||||
return Ok(Command::Size(canvas));
|
||||
}
|
||||
GET_PX_BIN => {
|
||||
let canvas = reader.read_u8().await?;
|
||||
let x = reader.read_u16_le().await?;
|
||||
let y = reader.read_u16_le().await?;
|
||||
return Ok(Command::GetPixel(canvas, x, y));
|
||||
}
|
||||
SET_PX_W_BIN => {
|
||||
let canvas = reader.read_u8().await?;
|
||||
let x = reader.read_u16_le().await?;
|
||||
let y = reader.read_u16_le().await?;
|
||||
let w = reader.read_u8().await?;
|
||||
return Ok(Command::SetPixel(canvas, x, y, Color::W8(w)));
|
||||
}
|
||||
SET_PX_RGB_BIN => {
|
||||
let canvas = reader.read_u8().await?;
|
||||
let x = reader.read_u16_le().await?;
|
||||
let y = reader.read_u16_le().await?;
|
||||
let r = reader.read_u8().await?;
|
||||
let g = reader.read_u8().await?;
|
||||
let b = reader.read_u8().await?;
|
||||
return Ok(Command::SetPixel(canvas, x, y, Color::RGB24(r, g, b)));
|
||||
}
|
||||
SET_PX_RGBA_BIN => {
|
||||
let canvas = reader.read_u8().await?;
|
||||
let x = reader.read_u16_le().await?;
|
||||
let y = reader.read_u16_le().await?;
|
||||
let r = reader.read_u8().await?;
|
||||
let g = reader.read_u8().await?;
|
||||
let b = reader.read_u8().await?;
|
||||
let a = reader.read_u8().await?;
|
||||
return Ok(Command::SetPixel(canvas, x, y, Color::RGBA32(r, g, b, a)));
|
||||
}
|
||||
_ => {
|
||||
eprintln!("received illegal command: {}", i);
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MEEHHEH for BinaryParser {
|
||||
fn change_canvas(&mut self, _canvas: Canvas) -> io::Result<()> {
|
||||
return Err(Error::from(ErrorKind::Unsupported));
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: AsyncWriteExt + std::marker::Unpin> Responder<W> for BinaryParser {
|
||||
async fn unparse(&self, response: Response, writer: &mut W) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::grid::FlutGrid;
|
||||
use tokio::io::BufReader;
|
||||
use tokio_test::assert_ok;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bin_help_parse() {
|
||||
let parser = BinaryParser::new();
|
||||
let reader = tokio_test::io::Builder::new().read(&[HELP_BIN]).build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::Help)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bin_size_parse() {
|
||||
let parser = BinaryParser::new();
|
||||
let reader = tokio_test::io::Builder::new().read(&[SIZE_BIN, 3]).build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::Size(3))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bin_px_set_w_parse() {
|
||||
let parser = BinaryParser::new();
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[SET_PX_W_BIN, 0x01, 0x69, 0x42, 0x42, 0x69, 0x82])
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(
|
||||
thingy.unwrap(),
|
||||
Command::SetPixel(1, 0x4269, 0x6942, Color::W8(0x82))
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bin_px_set_rgb_parse() {
|
||||
let parser = BinaryParser::new();
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[
|
||||
SET_PX_RGB_BIN,
|
||||
0x01,
|
||||
0x69,
|
||||
0x42,
|
||||
0x42,
|
||||
0x69,
|
||||
0x82,
|
||||
0x00,
|
||||
0xff,
|
||||
])
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(
|
||||
thingy.unwrap(),
|
||||
Command::SetPixel(1, 0x4269, 0x6942, Color::RGB24(0x82, 0x00, 0xff))
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bin_px_set_rgba_parse() {
|
||||
let parser = BinaryParser::new();
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[
|
||||
SET_PX_RGBA_BIN,
|
||||
0x01,
|
||||
0x69,
|
||||
0x42,
|
||||
0x42,
|
||||
0x69,
|
||||
0x82,
|
||||
0x00,
|
||||
0xff,
|
||||
0xa0,
|
||||
])
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(
|
||||
thingy.unwrap(),
|
||||
Command::SetPixel(1, 0x4269, 0x6942, Color::RGBA32(0x82, 0x00, 0xff, 0xa0))
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bin_px_get_parse() {
|
||||
let parser = BinaryParser::new();
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[GET_PX_BIN, 0x03, 0x69, 0x42, 0x42, 0x69])
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::GetPixel(3, 0x4269, 0x6942))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bin_parse_multiple() {
|
||||
let parser = BinaryParser::new();
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[
|
||||
SET_PX_RGB_BIN,
|
||||
0x01,
|
||||
0x69,
|
||||
0x42,
|
||||
0x42,
|
||||
0x69,
|
||||
0x82,
|
||||
0x00,
|
||||
0xff,
|
||||
])
|
||||
.read(&[
|
||||
SET_PX_RGBA_BIN,
|
||||
0x01,
|
||||
0x69,
|
||||
0x42,
|
||||
0x42,
|
||||
0x69,
|
||||
0x82,
|
||||
0x00,
|
||||
0xff,
|
||||
0xa0,
|
||||
])
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
let thingy2 = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(
|
||||
thingy.unwrap(),
|
||||
Command::SetPixel(1, 0x4269, 0x6942, Color::RGB24(0x82, 0x00, 0xff))
|
||||
);
|
||||
assert_eq!(
|
||||
thingy2.unwrap(),
|
||||
Command::SetPixel(1, 0x4269, 0x6942, Color::RGBA32(0x82, 0x00, 0xff, 0xa0))
|
||||
);
|
||||
}
|
||||
}
|
||||
26
src/grid.rs
26
src/grid.rs
|
|
@ -1,5 +1,7 @@
|
|||
use std::cell::SyncUnsafeCell;
|
||||
|
||||
use crate::{Coordinate, Response};
|
||||
|
||||
pub trait Grid<I, V> {
|
||||
fn get(&self, x: I, y: I) -> Option<&V>;
|
||||
fn get_unchecked(&self, x: I, y: I) -> &V;
|
||||
|
|
@ -24,10 +26,14 @@ impl<T: Clone> FlutGrid<T> {
|
|||
cells: vec.into(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> (usize, usize) {
|
||||
return (self.size_x, self.size_y);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FlutGrid<T> {
|
||||
fn index(&self, x: u16, y: u16) -> Option<usize> {
|
||||
fn index(&self, x: Coordinate, y: Coordinate) -> Option<usize> {
|
||||
let x = x as usize;
|
||||
let y = y as usize;
|
||||
if x >= self.size_x || y >= self.size_y {
|
||||
|
|
@ -37,22 +43,22 @@ impl<T> FlutGrid<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Grid<u16, T> for FlutGrid<T> {
|
||||
fn get(&self, x: u16, y: u16) -> Option<&T> {
|
||||
impl<T> Grid<Coordinate, T> for FlutGrid<T> {
|
||||
fn get(&self, x: Coordinate, y: Coordinate) -> Option<&T> {
|
||||
match self.index(x, y) {
|
||||
None => None,
|
||||
Some(idx) => Some(unsafe { &(*self.cells.get())[idx] }),
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&self, x: u16, y: u16, value: T) {
|
||||
fn set(&self, x: Coordinate, y: Coordinate, value: T) {
|
||||
match self.index(x, y) {
|
||||
None => (),
|
||||
Some(idx) => unsafe { (*self.cells.get())[idx] = value },
|
||||
}
|
||||
}
|
||||
|
||||
fn get_unchecked(&self, x: u16, y: u16) -> &T {
|
||||
fn get_unchecked(&self, x: Coordinate, y: Coordinate) -> &T {
|
||||
let idx = y as usize * self.size_x + x as usize;
|
||||
return unsafe { &(*self.cells.get())[idx] };
|
||||
}
|
||||
|
|
@ -81,7 +87,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_grid_set() {
|
||||
let mut grid = FlutGrid::init(3, 3, 0);
|
||||
let grid = FlutGrid::init(3, 3, 0);
|
||||
grid.set(1, 1, 255);
|
||||
grid.set(2, 1, 256);
|
||||
assert_eq!(grid.cells.into_inner(), vec![0, 0, 0, 0, 255, 256, 0, 0, 0])
|
||||
|
|
@ -89,7 +95,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_grid_set_out_of_range() {
|
||||
let mut grid = FlutGrid::init(3, 3, 0);
|
||||
let grid = FlutGrid::init(3, 3, 0);
|
||||
grid.set(1, 1, 255);
|
||||
grid.set(3, 1, 256);
|
||||
assert_eq!(grid.cells.into_inner(), vec![0, 0, 0, 0, 255, 0, 0, 0, 0])
|
||||
|
|
@ -97,14 +103,14 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_grid_get() {
|
||||
let mut grid = FlutGrid::init(3, 3, 0);
|
||||
let grid = FlutGrid::init(3, 3, 0);
|
||||
grid.set(1, 2, 222);
|
||||
assert_eq!(grid.get(1, 2), Some(&222));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_grid_get_out_of_range() {
|
||||
let mut grid = FlutGrid::init(3, 3, 0);
|
||||
let grid = FlutGrid::init(3, 3, 0);
|
||||
grid.set(3, 1, 256);
|
||||
assert_eq!(grid.get(3, 1), None);
|
||||
assert_eq!(grid.get(1, 2), Some(&0));
|
||||
|
|
@ -117,7 +123,7 @@ mod tests {
|
|||
|
||||
#[bench]
|
||||
fn bench_set(b: &mut Bencher) {
|
||||
let mut grid = FlutGrid::init(800, 600, 0 as u32);
|
||||
let grid = FlutGrid::init(800, 600, 0 as u32);
|
||||
b.iter(|| {
|
||||
let x = test::black_box(293);
|
||||
let y = test::black_box(222);
|
||||
|
|
|
|||
442
src/main.rs
442
src/main.rs
|
|
@ -1,7 +1,9 @@
|
|||
#![feature(test)]
|
||||
#![feature(sync_unsafe_cell)]
|
||||
|
||||
mod binary_protocol;
|
||||
mod grid;
|
||||
mod text_protocol;
|
||||
|
||||
use std::{
|
||||
cell::SyncUnsafeCell,
|
||||
|
|
@ -11,38 +13,50 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use binary_protocol::BinaryParser;
|
||||
use grid::{FlutGrid, Grid};
|
||||
use text_protocol::TextParser;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt, BufReader, BufWriter},
|
||||
io::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter},
|
||||
net::TcpListener,
|
||||
};
|
||||
|
||||
extern crate test;
|
||||
|
||||
const HELP_TEXT: u8 = 72;
|
||||
const SIZE_TEXT: u8 = 83;
|
||||
const PX_TEXT: u8 = 80;
|
||||
const SIZE_BIN: u8 = 115;
|
||||
const HELP_BIN: u8 = 104;
|
||||
const LOCK: u8 = 0;
|
||||
const GET_PX_BIN: u8 = 32;
|
||||
const SET_PX_RGB_BIN: u8 = 128;
|
||||
const SET_PX_RGBA_BIN: u8 = 129;
|
||||
const SET_PX_W_BIN: u8 = 130;
|
||||
|
||||
const SET_PX_RGB_BIN_LENGTH: usize = 8;
|
||||
const HELP_TEXT: &[u8] = b"Flurry is a pixelflut implementation, this means you can use commands to get and set pixels in the canvas
|
||||
SIZE returns the size of the canvas
|
||||
PX {x} {y} returns the color of the pixel at {x}, {y}
|
||||
If you include a color in hex format you set a pixel instead
|
||||
PX {x} {y} {RGB} sets the color of the pixel at {x}, {y} to the rgb value
|
||||
PX {x} {y} {RGBA} blends the pixel at {x}, {y} with the rgb value weighted by the a
|
||||
PX {x} {y} {W} sets the color of the pixel at {x}, {y} to the grayscale value
|
||||
";
|
||||
const GRID_LENGTH: usize = 1;
|
||||
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
fn set_pixel_rgba(grids: &[grid::FlutGrid<u32>], canvas: u8, x: u16, y: u16, rgb: u32) {
|
||||
type Canvas = u8;
|
||||
type Coordinate = u16;
|
||||
|
||||
fn set_pixel_rgba(
|
||||
grids: &[grid::FlutGrid<u32>],
|
||||
canvas: Canvas,
|
||||
x: Coordinate,
|
||||
y: Coordinate,
|
||||
rgb: u32,
|
||||
) {
|
||||
match grids.get(canvas as usize) {
|
||||
Some(grid) => grid.set(x, y, rgb),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pixel(grids: &[grid::FlutGrid<u32>], canvas: u8, x: u16, y: u16) -> Option<&u32> {
|
||||
fn get_pixel(
|
||||
grids: &[grid::FlutGrid<u32>],
|
||||
canvas: Canvas,
|
||||
x: Coordinate,
|
||||
y: Coordinate,
|
||||
) -> Option<&u32> {
|
||||
match grids.get(canvas as usize) {
|
||||
Some(grid) => return grid.get(x, y),
|
||||
None => return None,
|
||||
|
|
@ -53,175 +67,52 @@ fn increment_counter() {
|
|||
COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
async fn process_lock<
|
||||
R: AsyncReadExt + std::marker::Unpin,
|
||||
W: AsyncWriteExt + std::marker::Unpin,
|
||||
>(
|
||||
reader: &mut R,
|
||||
_writer: &mut W,
|
||||
grids: &[grid::FlutGrid<u32>; GRID_LENGTH],
|
||||
) -> io::Result<()> {
|
||||
let amount = reader.read_u16_le().await?;
|
||||
let command = reader.read_u8().await?;
|
||||
let lockmask = reader.read_u16().await?;
|
||||
let mut buf = vec![0; lockmask.count_ones() as usize];
|
||||
let statics = reader.read_exact(&mut buf).await?;
|
||||
|
||||
match command {
|
||||
GET_PX_BIN => todo!("GET pixel lock"),
|
||||
SET_PX_RGB_BIN => {
|
||||
let per = SET_PX_RGB_BIN_LENGTH - statics;
|
||||
let mut j = 0;
|
||||
let mut a;
|
||||
let static_buf: Vec<Option<u8>> = (0..SET_PX_RGB_BIN_LENGTH)
|
||||
.map(|i| match lockmask >> (15 - i) & 1 {
|
||||
1 => {
|
||||
let b = Some(buf[j]);
|
||||
|
||||
j += 1;
|
||||
return b;
|
||||
}
|
||||
0 => None,
|
||||
k => panic!("WTF, how is {} not 0 or 1", k),
|
||||
})
|
||||
.collect();
|
||||
let mut mod_buf: Vec<u8> = vec![0; per];
|
||||
for _ in 0..amount {
|
||||
a = 0;
|
||||
let _ = reader.read_exact(&mut mod_buf).await?;
|
||||
let aa = static_buf
|
||||
.iter()
|
||||
.map(|x| *match x {
|
||||
Some(val) => val,
|
||||
None => {
|
||||
let b = mod_buf[a];
|
||||
a += 1;
|
||||
return b;
|
||||
}
|
||||
})
|
||||
.map(|z| z)
|
||||
.collect::<Vec<_>>();
|
||||
match grids.get(aa[0] as usize) {
|
||||
Some(grid) => {
|
||||
grid.set(
|
||||
u16::from_le_bytes([aa[1], aa[2]]),
|
||||
u16::from_le_bytes([aa[3], aa[4]]),
|
||||
u32::from_be_bytes([aa[5], aa[6], aa[7], 0xff]),
|
||||
);
|
||||
increment_counter()
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
SET_PX_RGBA_BIN => todo!("Set rgba lock"),
|
||||
SET_PX_W_BIN => todo!("set w lock"),
|
||||
_ => {
|
||||
eprintln!("not a cmd");
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Color {
|
||||
RGB24(u8, u8, u8),
|
||||
RGBA32(u8, u8, u8, u8),
|
||||
W8(u8),
|
||||
}
|
||||
|
||||
async fn process_msg<
|
||||
R: AsyncReadExt + std::marker::Unpin,
|
||||
W: AsyncWriteExt + std::marker::Unpin,
|
||||
>(
|
||||
reader: &mut R,
|
||||
writer: &mut W,
|
||||
grids: &[grid::FlutGrid<u32>; GRID_LENGTH],
|
||||
) -> io::Result<()> {
|
||||
let fst = reader.read_u8().await;
|
||||
match fst {
|
||||
Ok(i) => match i {
|
||||
HELP_TEXT => todo!("HELP command check and message"),
|
||||
SIZE_TEXT => todo!("SIZE command check and message"),
|
||||
PX_TEXT => todo!("PX command handling"),
|
||||
HELP_BIN => todo!("HELP command message"),
|
||||
SIZE_BIN => todo!("SIZE command check and message"),
|
||||
LOCK => process_lock(reader, writer, grids).await,
|
||||
GET_PX_BIN => {
|
||||
let canvas = reader.read_u8().await?;
|
||||
let x = reader.read_u16_le().await?;
|
||||
let y = reader.read_u16_le().await?;
|
||||
match get_pixel(grids, canvas, x, y) {
|
||||
None => (),
|
||||
Some(color) => {
|
||||
let towrite = &once(GET_PX_BIN)
|
||||
.chain(once(canvas))
|
||||
.chain(x.to_le_bytes())
|
||||
.chain(y.to_le_bytes())
|
||||
.chain(color.to_be_bytes().into_iter().take(3))
|
||||
.collect::<Vec<_>>();
|
||||
writer.write_all(towrite).await?;
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
SET_PX_RGB_BIN => set_px_rgb_bin(reader, grids).await,
|
||||
SET_PX_RGBA_BIN => todo!("SET rgba"),
|
||||
SET_PX_W_BIN => todo!("SET w"),
|
||||
_ => {
|
||||
eprintln!("received illegal command: {}", i);
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Protocol {
|
||||
Text,
|
||||
Binary,
|
||||
}
|
||||
|
||||
async fn rgb_bin_read<R: AsyncReadExt + std::marker::Unpin>(
|
||||
reader: &mut R,
|
||||
) -> io::Result<(u8, u16, u16, u8, u8, u8)> {
|
||||
let canvas = reader.read_u8().await?;
|
||||
let x = reader.read_u16_le().await?;
|
||||
let y = reader.read_u16_le().await?;
|
||||
let r = reader.read_u8().await?;
|
||||
let g = reader.read_u8().await?;
|
||||
let b = reader.read_u8().await?;
|
||||
return Ok((canvas, x, y, r, g, b));
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Command {
|
||||
Help,
|
||||
Size(Canvas),
|
||||
GetPixel(Canvas, Coordinate, Coordinate),
|
||||
SetPixel(Canvas, Coordinate, Coordinate, Color),
|
||||
ChangeCanvas(Canvas),
|
||||
ChangeProtocol(Protocol),
|
||||
}
|
||||
|
||||
async fn set_px_rgb_bin<R: AsyncReadExt + std::marker::Unpin>(
|
||||
reader: &mut R,
|
||||
grids: &[grid::FlutGrid<u32>; GRID_LENGTH],
|
||||
) -> io::Result<()> {
|
||||
let (canvas, x, y, r, g, b) = rgb_bin_read(reader).await?;
|
||||
let rgb = u32::from_be_bytes([r, g, b, 0xff]);
|
||||
set_pixel_rgba(grids, canvas, x, y, rgb);
|
||||
increment_counter();
|
||||
return Ok(());
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Response {
|
||||
Help,
|
||||
Size(Coordinate, Coordinate),
|
||||
GetPixel(Coordinate, Coordinate, [u8; 3]),
|
||||
}
|
||||
|
||||
async fn process_socket<W, R>(
|
||||
reader: R,
|
||||
writer: W,
|
||||
grids: &[grid::FlutGrid<u32>; GRID_LENGTH],
|
||||
) -> io::Result<()>
|
||||
trait Parser<R>
|
||||
where
|
||||
R: std::marker::Unpin + tokio::io::AsyncBufRead,
|
||||
{
|
||||
async fn parse(&self, reader: &mut R) -> io::Result<Command>;
|
||||
}
|
||||
|
||||
trait MEEHHEH {
|
||||
fn change_canvas(&mut self, canvas: Canvas) -> io::Result<()>;
|
||||
}
|
||||
|
||||
trait Responder<W>
|
||||
where
|
||||
W: AsyncWriteExt + std::marker::Unpin,
|
||||
R: AsyncReadExt + std::marker::Unpin,
|
||||
{
|
||||
let mut reader = BufReader::new(reader);
|
||||
let mut writer = BufWriter::new(writer);
|
||||
loop {
|
||||
match process_msg(&mut reader, &mut writer, grids).await {
|
||||
Ok(()) => {}
|
||||
Err(err) if err.kind() == ErrorKind::UnexpectedEof => {
|
||||
let _ = writer.flush().await;
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("error with kind {}", e.kind());
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn unparse(&self, response: Response, writer: &mut W) -> io::Result<()>;
|
||||
}
|
||||
|
||||
async fn listen_handle() {
|
||||
|
|
@ -233,6 +124,144 @@ async fn listen_handle() {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! build_parser_type_enum {
|
||||
($($name:ident: $t:ty,)*) => {
|
||||
|
||||
enum ParserTypes {
|
||||
$($name($t),)*
|
||||
}
|
||||
|
||||
macro_rules! match_parser {
|
||||
($pident:ident: $parser:expr => $f:expr) => (
|
||||
match &mut $parser {
|
||||
$(
|
||||
ParserTypes::$name($pident) => $f,
|
||||
)*
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
build_parser_type_enum! {
|
||||
TextParser: TextParser,
|
||||
BinaryParser: BinaryParser,
|
||||
}
|
||||
|
||||
struct FlutClient<R, W>
|
||||
where
|
||||
R: AsyncReadExt + std::marker::Unpin,
|
||||
W: AsyncWriteExt + std::marker::Unpin,
|
||||
{
|
||||
reader: BufReader<R>,
|
||||
writer: BufWriter<W>,
|
||||
grids: Arc<[FlutGrid<u32>]>,
|
||||
parser: ParserTypes,
|
||||
}
|
||||
|
||||
impl<R, W> FlutClient<R, W>
|
||||
where
|
||||
R: AsyncReadExt + std::marker::Unpin,
|
||||
W: AsyncWriteExt + std::marker::Unpin,
|
||||
{
|
||||
async fn help_command(&mut self) -> io::Result<()> {
|
||||
println!("HELP wanted");
|
||||
match_parser!(parser: self.parser => parser.unparse(Response::Help, &mut self.writer).await?);
|
||||
|
||||
self.writer.flush().await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
async fn size_command(&mut self, canvas: Canvas) -> io::Result<()> {
|
||||
let (x, y) = self.grids[canvas as usize].get_size();
|
||||
match_parser!(parser: self.parser => parser.unparse(Response::Size(x as Coordinate, y as Coordinate), &mut self.writer).await?);
|
||||
|
||||
self.writer.flush().await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
async fn get_pixel_command(
|
||||
&mut self,
|
||||
canvas: Canvas,
|
||||
x: Coordinate,
|
||||
y: Coordinate,
|
||||
) -> io::Result<()> {
|
||||
let color = match get_pixel(&self.grids, canvas, x, y) {
|
||||
None => return Err(Error::from(ErrorKind::InvalidInput)),
|
||||
Some(color) => color.to_be_bytes(),
|
||||
};
|
||||
match_parser!(parser: self.parser => parser.unparse(Response::GetPixel(x,y,[color[0], color[1], color[2]]), &mut self.writer).await);
|
||||
|
||||
self.writer.flush().await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
async fn set_pixel_command(
|
||||
&mut self,
|
||||
canvas: Canvas,
|
||||
x: Coordinate,
|
||||
y: Coordinate,
|
||||
color: Color,
|
||||
) -> io::Result<()> {
|
||||
let c: u32 = match color {
|
||||
Color::RGB24(r, g, b) => u32::from_be_bytes([r, g, b, 0xff]),
|
||||
Color::RGBA32(r, g, b, a) => u32::from_be_bytes([r, g, b, a]),
|
||||
Color::W8(w) => u32::from_be_bytes([w, w, w, 0xff]),
|
||||
};
|
||||
println!("setting pixel {},{} to {}", x, y, c);
|
||||
set_pixel_rgba(self.grids.as_ref(), canvas, x, y, c);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
async fn change_canvas_command(&mut self, canvas: Canvas) -> io::Result<()> {
|
||||
match_parser!(parser: self.parser => parser.change_canvas(canvas))
|
||||
}
|
||||
|
||||
async fn change_protocol(&mut self, protocol: Protocol) -> io::Result<()> {
|
||||
match protocol {
|
||||
Protocol::Text => self.parser = ParserTypes::TextParser(TextParser::new(0)),
|
||||
Protocol::Binary => self.parser = ParserTypes::BinaryParser(BinaryParser::new()),
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn new(reader: R, writer: W, grids: Arc<[grid::FlutGrid<u32>]>) -> FlutClient<R, W> {
|
||||
return FlutClient {
|
||||
reader: BufReader::new(reader),
|
||||
writer: BufWriter::new(writer),
|
||||
grids,
|
||||
parser: ParserTypes::TextParser(TextParser::new(0)),
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn process_socket(&mut self) -> io::Result<()> {
|
||||
loop {
|
||||
let parsed = match &self.parser {
|
||||
ParserTypes::TextParser(parser) => parser.parse(&mut self.reader).await,
|
||||
ParserTypes::BinaryParser(parser) => parser.parse(&mut self.reader).await,
|
||||
};
|
||||
|
||||
match parsed {
|
||||
Ok(Command::Help) => self.help_command().await?,
|
||||
Ok(Command::Size(canvas)) => self.size_command(canvas).await?,
|
||||
Ok(Command::GetPixel(canvas, x, y)) => self.get_pixel_command(canvas, x, y).await?,
|
||||
Ok(Command::SetPixel(canvas, x, y, color)) => {
|
||||
self.set_pixel_command(canvas, x, y, color).await?
|
||||
}
|
||||
Ok(Command::ChangeCanvas(canvas)) => self.change_canvas_command(canvas).await?,
|
||||
Ok(Command::ChangeProtocol(protocol)) => self.change_protocol(protocol).await?,
|
||||
|
||||
Err(err) if err.kind() == ErrorKind::UnexpectedEof => {
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
println!("created grids");
|
||||
|
|
@ -242,9 +271,6 @@ async fn main() -> io::Result<()> {
|
|||
let flut_listener = TcpListener::bind("0.0.0.0:7791").await?;
|
||||
println!("bound flut listener");
|
||||
|
||||
let web_listener = TcpListener::bind("0.0.0.0:7792").await?;
|
||||
|
||||
println!("bound web listener");
|
||||
let _ = tokio::spawn(listen_handle());
|
||||
|
||||
loop {
|
||||
|
|
@ -252,7 +278,9 @@ async fn main() -> io::Result<()> {
|
|||
let grids = grids.clone();
|
||||
let _ = tokio::spawn(async move {
|
||||
let (reader, writer) = socket.split();
|
||||
match process_socket(reader, writer, &grids).await {
|
||||
let mut connection = FlutClient::new(reader, writer, grids);
|
||||
let resp = connection.process_socket().await;
|
||||
match resp {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
|
|
@ -265,64 +293,4 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::grid::FlutGrid;
|
||||
use tokio_test::assert_ok;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_set_rgb_bin() {
|
||||
let mut grids = [FlutGrid::init(800, 600, 0xFF00FF)];
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[SET_PX_RGB_BIN, 0, 16, 0, 32, 0, 0, 0, 0])
|
||||
.read(&[SET_PX_RGB_BIN, 0, 16, 0, 33, 0, 2, 3, 5])
|
||||
.build();
|
||||
let writer = tokio_test::io::Builder::new().build();
|
||||
assert_ok!(process_socket(reader, writer, &mut grids).await);
|
||||
assert_eq!(grids[0].get(16, 32), Some(&0x000000ff));
|
||||
assert_eq!(grids[0].get(16, 33), Some(&0x020305ff));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_set_rgb_lock() {
|
||||
let mut grids = [FlutGrid::init(800, 600, 0xFF00FF)];
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[
|
||||
LOCK,
|
||||
3,
|
||||
0,
|
||||
SET_PX_RGB_BIN,
|
||||
0b1011_1110,
|
||||
0b0000_0000,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
])
|
||||
.read(&[100, 4])
|
||||
.read(&[101, 5])
|
||||
.read(&[102, 6])
|
||||
.build();
|
||||
let writer = tokio_test::io::Builder::new().build();
|
||||
assert_ok!(process_socket(reader, writer, &mut grids).await);
|
||||
|
||||
assert_eq!(grids[0].get(100, 0), Some(&0x020304ff));
|
||||
assert_eq!(grids[0].get(101, 0), Some(&0x020305ff));
|
||||
assert_eq!(grids[0].get(102, 0), Some(&0x020306ff));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_rgb_bin() {
|
||||
let mut grids = [FlutGrid::init(800, 600, 0xFF00F0FF)];
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(&[GET_PX_BIN, 0, 15, 0, 21, 0])
|
||||
.read(&[GET_PX_BIN, 0, 16, 0, 21, 0])
|
||||
.read(&[GET_PX_BIN, 0, 17, 0, 21, 0])
|
||||
.build();
|
||||
let writer = tokio_test::io::Builder::new()
|
||||
.write(&[GET_PX_BIN, 0, 15, 0, 21, 0, 0xff, 0x00, 0xf0])
|
||||
.write(&[GET_PX_BIN, 0, 16, 0, 21, 0, 0xff, 0x00, 0xf0])
|
||||
.write(&[GET_PX_BIN, 0, 17, 0, 21, 0, 0xff, 0x00, 0xf0])
|
||||
.build();
|
||||
assert_ok!(process_socket(reader, writer, &mut grids).await);
|
||||
assert_eq!(grids[0].get(15, 21), Some(&0xff00f0ff));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
234
src/text_protocol.rs
Normal file
234
src/text_protocol.rs
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
use std::io::{self, Error, ErrorKind};
|
||||
|
||||
use atoi_radix10::parse_from_str;
|
||||
use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::{
|
||||
Canvas, Color, Command, Coordinate, Parser, Protocol, Responder, Response, GRID_LENGTH,
|
||||
HELP_TEXT, MEEHHEH,
|
||||
};
|
||||
|
||||
pub struct TextParser {
|
||||
canvas: Canvas,
|
||||
}
|
||||
|
||||
fn parse_coordinate(string: &str) -> io::Result<Coordinate> {
|
||||
match parse_from_str(string) {
|
||||
Ok(coord) => return Ok(coord),
|
||||
Err(_) => return Err(Error::from(ErrorKind::InvalidInput)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_color(color: &str) -> io::Result<Color> {
|
||||
if let Ok(bytes) = hex::decode(color) {
|
||||
match bytes.len() {
|
||||
1 => return Ok(Color::W8(bytes[0])),
|
||||
3 => return Ok(Color::RGB24(bytes[0], bytes[1], bytes[2])),
|
||||
4 => return Ok(Color::RGBA32(bytes[0], bytes[1], bytes[2], bytes[3])),
|
||||
_ => return Err(Error::from(ErrorKind::InvalidInput)),
|
||||
}
|
||||
}
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
|
||||
impl TextParser {
|
||||
pub fn new(canvas: Canvas) -> TextParser {
|
||||
return TextParser { canvas };
|
||||
}
|
||||
|
||||
fn parse_pixel(&self, line: &str) -> io::Result<Command> {
|
||||
let mut split = line.trim().split(' ');
|
||||
|
||||
let _command = split.next().ok_or(Error::from(ErrorKind::InvalidInput))?;
|
||||
let x_coordinate = split.next().ok_or(Error::from(ErrorKind::InvalidInput))?;
|
||||
let y_coordinate = split.next().ok_or(Error::from(ErrorKind::InvalidInput))?;
|
||||
if let (Ok(horizontal), Ok(vertical)) = (x_coordinate.parse(), y_coordinate.parse()) {
|
||||
match split.next() {
|
||||
None => return Ok(Command::GetPixel(self.canvas, horizontal, vertical)),
|
||||
Some(color) => match parse_color(color) {
|
||||
Ok(color) => {
|
||||
return Ok(Command::SetPixel(self.canvas, horizontal, vertical, color))
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
}
|
||||
fn parse_canvas(&self, line: &str) -> io::Result<Command> {
|
||||
let mut split = line.trim().split(' ');
|
||||
|
||||
let _command = split.next().ok_or(Error::from(ErrorKind::InvalidInput))?;
|
||||
let canvas = split.next().ok_or(Error::from(ErrorKind::InvalidInput))?;
|
||||
println!("{:?}", canvas);
|
||||
if let Ok(canvas) = canvas.parse() {
|
||||
return Ok(Command::ChangeCanvas(canvas));
|
||||
} else {
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
}
|
||||
fn parse_protocol(&self, line: &str) -> io::Result<Command> {
|
||||
let mut split = line.trim().split(' ');
|
||||
|
||||
let _command = split.next().ok_or(Error::from(ErrorKind::InvalidInput))?;
|
||||
let protocol = split.next().ok_or(Error::from(ErrorKind::InvalidInput))?;
|
||||
match protocol {
|
||||
"binary" => return Ok(Command::ChangeProtocol(Protocol::Binary)),
|
||||
"text" => return Ok(Command::ChangeProtocol(Protocol::Text)),
|
||||
_ => return Err(Error::from(ErrorKind::InvalidInput)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead + AsyncBufReadExt + std::marker::Unpin> Parser<R> for TextParser {
|
||||
async fn parse(&self, reader: &mut R) -> io::Result<Command> {
|
||||
let mut line = "".to_string();
|
||||
if let Ok(_) = reader.read_line(&mut line).await {
|
||||
println!("{:?}", line);
|
||||
if line.starts_with("HELP") {
|
||||
return Ok(Command::Help);
|
||||
} else if line.starts_with("SIZE") {
|
||||
return Ok(Command::Size(self.canvas));
|
||||
} else if line.starts_with("PX ") {
|
||||
return self.parse_pixel(&line);
|
||||
} else if line.starts_with("CANVAS ") {
|
||||
return self.parse_canvas(&line);
|
||||
} else if line.starts_with("PROTOCOL ") {
|
||||
return self.parse_protocol(&line);
|
||||
}
|
||||
}
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
}
|
||||
|
||||
impl MEEHHEH for TextParser {
|
||||
fn change_canvas(&mut self, canvas: Canvas) -> io::Result<()> {
|
||||
if (canvas as usize) < GRID_LENGTH {
|
||||
self.canvas = canvas;
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(Error::from(ErrorKind::InvalidInput));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: AsyncWriteExt + std::marker::Unpin> Responder<W> for TextParser {
|
||||
async fn unparse(&self, response: Response, writer: &mut W) -> io::Result<()> {
|
||||
match response {
|
||||
Response::Help => writer.write_all(HELP_TEXT).await,
|
||||
Response::Size(x, y) => {
|
||||
writer
|
||||
.write_all(format!("SIZE {} {}\n", x, y).as_bytes())
|
||||
.await
|
||||
}
|
||||
Response::GetPixel(x, y, color) => {
|
||||
writer
|
||||
.write_all(format!("PX {} {} {}\n", x, y, hex::encode_upper(color)).as_bytes())
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::grid::FlutGrid;
|
||||
use tokio::io::BufReader;
|
||||
use tokio_test::assert_ok;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_help_parse() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new().read(b"HELP\n").build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::Help)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_size_parse() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new().read(b"SIZE\n").build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::Size(0))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_canvas_parse() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new().read(b"CANVAS 12\n").build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::ChangeCanvas(12))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_px_set_w_parse() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(b"PX 28283 29991 81\n")
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(
|
||||
thingy.unwrap(),
|
||||
Command::SetPixel(0, 28283, 29991, Color::W8(0x81))
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_px_set_rgb_parse() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(b"PX 28283 29991 8800ff\n")
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(
|
||||
thingy.unwrap(),
|
||||
Command::SetPixel(0, 28283, 29991, Color::RGB24(0x88, 0x00, 0xff))
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_px_set_rgba_parse() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(b"PX 28283 29991 8800ff28\n")
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(
|
||||
thingy.unwrap(),
|
||||
Command::SetPixel(0, 28283, 29991, Color::RGBA32(0x88, 0x00, 0xff, 0x28))
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_px_get_parse() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(b"PX 28283 29991\n")
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::GetPixel(0, 28283, 29991))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn parse_multiple() {
|
||||
let parser = TextParser::new(0);
|
||||
let reader = tokio_test::io::Builder::new()
|
||||
.read(b"CANVAS 12\n")
|
||||
.read(b"SIZE\n")
|
||||
.build();
|
||||
let mut bufreader = BufReader::new(reader);
|
||||
let thingy = parser.parse(&mut bufreader).await;
|
||||
let thingy2 = parser.parse(&mut bufreader).await;
|
||||
assert_eq!(thingy.unwrap(), Command::ChangeCanvas(12));
|
||||
assert_eq!(thingy2.unwrap(), Command::Size(0));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue