From 05b5481fef2a0fd27c0304ad31c4877c48a3b1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Mon, 3 Feb 2020 11:51:30 -0300 Subject: [PATCH 01/14] Add thiserror --- Cargo.lock | 66 ++++++++++++++++++++++++++++++++++---- Cargo.toml | 6 ++-- tinlib/Cargo.toml | 4 ++- tinlib/src/common/error.rs | 44 ++++++++++++++++++++++++- 4 files changed, 108 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 70714c1..edf5079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,18 +3,33 @@ version = 3 [[package]] -name = "cfg-if" -version = "1.0.0" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "log" -version = "0.4.14" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ - "cfg-if", + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", ] [[package]] @@ -24,9 +39,48 @@ dependencies = [ "tinlib", ] +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinlib" version = "0.1.0" dependencies = [ + "byteorder", "log", + "thiserror", ] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml index d13cb91..2bee91f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,3 @@ [workspace] -members = [ - "tinlib", - "devkit" -] +members = ["tinlib", "devkit"] +resolver = "2" diff --git a/tinlib/Cargo.toml b/tinlib/Cargo.toml index a2128fe..d6dd436 100644 --- a/tinlib/Cargo.toml +++ b/tinlib/Cargo.toml @@ -13,4 +13,6 @@ readme = "README.md" edition = "2021" [dependencies] -log = "0.4.14" +byteorder = "^1.5" +log = "^0.4" +thiserror = "^1.0" diff --git a/tinlib/src/common/error.rs b/tinlib/src/common/error.rs index 274b7b4..22a5106 100644 --- a/tinlib/src/common/error.rs +++ b/tinlib/src/common/error.rs @@ -1,16 +1,26 @@ //! Error implementation and manipulation. +use std::io; use std::result::Result as StdResult; +use std::string::FromUtf8Error; + +use thiserror::Error; use crate::common::coord::Coord; use crate::common::size::Size; /// Internal errors. -#[derive(Debug, PartialEq)] +#[derive(Error, Debug)] pub enum Error { /// Error to reprense invalid indexes. + #[error("invalid index {index} for lenght {lenght}")] InvalidIndex { index: usize, lenght: usize }, /// Error to represent invalid coords. + #[error("invalid coord ({coord:?}) for size ({size:?})")] InvalidCoord { coord: Coord, size: Size }, + #[error("test")] + Io(#[from] io::Error), + #[error("test2")] + Utf8(#[from] FromUtf8Error), } impl Error { @@ -25,6 +35,38 @@ impl Error { } } +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + use Error::*; + + match (self, other) { + ( + InvalidIndex { + index: i1, + lenght: l1, + }, + InvalidIndex { + index: i2, + lenght: l2, + }, + ) => i1 == i2 && l1 == l2, + ( + InvalidCoord { + coord: c1, + size: s1, + }, + InvalidCoord { + coord: c2, + size: s2, + }, + ) => c1 == c2 && s1 == s2, + (Io(_), Io(_)) => true, + (Utf8(_), Utf8(_)) => true, + _ => false, + } + } +} + /// Internal result. pub type Result = StdResult; From 8f41992f125a3063a66991d9c71d7220a439e696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Mon, 3 Feb 2020 17:32:50 -0300 Subject: [PATCH 02/14] Fix error impl and tests --- Cargo.lock | 7 +++++ tinlib/Cargo.toml | 3 ++ tinlib/src/common/error.rs | 52 +++++++++-------------------------- tinlib/src/graphic/font.rs | 26 ++++++++++++------ tinlib/src/graphic/glyph.rs | 14 +++++++--- tinlib/src/graphic/palette.rs | 24 ++++++++++------ tinlib/src/machine/screen.rs | 14 +++++++--- tinlib/src/map/mod.rs | 14 +++++++--- 8 files changed, 86 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edf5079..2ddd069 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "byteorder" version = "1.5.0" @@ -74,6 +80,7 @@ dependencies = [ name = "tinlib" version = "0.1.0" dependencies = [ + "assert_matches", "byteorder", "log", "thiserror", diff --git a/tinlib/Cargo.toml b/tinlib/Cargo.toml index d6dd436..adc173b 100644 --- a/tinlib/Cargo.toml +++ b/tinlib/Cargo.toml @@ -16,3 +16,6 @@ edition = "2021" byteorder = "^1.5" log = "^0.4" thiserror = "^1.0" + +[dev-dependencies] +assert_matches = "^1.5" diff --git a/tinlib/src/common/error.rs b/tinlib/src/common/error.rs index 22a5106..747a545 100644 --- a/tinlib/src/common/error.rs +++ b/tinlib/src/common/error.rs @@ -17,10 +17,10 @@ pub enum Error { /// Error to represent invalid coords. #[error("invalid coord ({coord:?}) for size ({size:?})")] InvalidCoord { coord: Coord, size: Size }, - #[error("test")] + #[error("IO operation error")] Io(#[from] io::Error), - #[error("test2")] - Utf8(#[from] FromUtf8Error), + #[error("UFT8 conversion error")] + FromUtf8(#[from] FromUtf8Error), } impl Error { @@ -35,43 +35,13 @@ impl Error { } } -impl PartialEq for Error { - fn eq(&self, other: &Self) -> bool { - use Error::*; - - match (self, other) { - ( - InvalidIndex { - index: i1, - lenght: l1, - }, - InvalidIndex { - index: i2, - lenght: l2, - }, - ) => i1 == i2 && l1 == l2, - ( - InvalidCoord { - coord: c1, - size: s1, - }, - InvalidCoord { - coord: c2, - size: s2, - }, - ) => c1 == c2 && s1 == s2, - (Io(_), Io(_)) => true, - (Utf8(_), Utf8(_)) => true, - _ => false, - } - } -} - /// Internal result. pub type Result = StdResult; #[cfg(test)] mod test_super { + use assert_matches::assert_matches; + use super::*; #[test] @@ -80,9 +50,11 @@ mod test_super { let lenght = 1usize; let error = Error::new_invalid_index(index, lenght); - let expected = Error::InvalidIndex { index, lenght }; - assert_eq!(error, expected); + assert_matches!( + error, + Error::InvalidIndex { index: i, lenght: l } if i == index && l == lenght + ); } #[test] @@ -91,8 +63,10 @@ mod test_super { let size: Size = Size::new(1, 1); let error = Error::new_invalid_coord(coord, size); - let expected = Error::InvalidCoord { coord, size }; - assert_eq!(error, expected); + assert_matches!( + error, + Error::InvalidCoord { coord: c, size: s } if c == coord && s == size + ); } } diff --git a/tinlib/src/graphic/font.rs b/tinlib/src/graphic/font.rs index a871e5b..e14b90d 100644 --- a/tinlib/src/graphic/font.rs +++ b/tinlib/src/graphic/font.rs @@ -80,6 +80,8 @@ impl fmt::Debug for Font { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use crate::common::Coord; use crate::graphic::glyph::GlyphPixel; @@ -104,17 +106,20 @@ mod tests { let result = font.get_glyph(0); assert!(result.is_ok()); - assert_eq!(result, Ok(glyph)); + assert_eq!(result.unwrap(), glyph); } #[test] fn test_font_get_glyph_invalid_index() { let font = Font::default(); - let error = Error::new_invalid_index(256, font.lenght()); + let index = 256usize; - let result = font.get_glyph(256); + let result = font.get_glyph(index); assert!(result.is_err()); - assert_eq!(result, Err(error)); + assert_matches!( + result.unwrap_err(), + Error::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() + ); } #[test] @@ -127,21 +132,24 @@ mod tests { let result = font.set_glyph(0, new_glyph); assert!(result.is_ok()); - assert_eq!(result, Ok(())); + assert_eq!(result.unwrap(), ()); let result = font.get_glyph(0); - assert_eq!(result, Ok(new_glyph)); + assert_eq!(result.unwrap(), new_glyph); } #[test] fn test_font_set_glyph_invalid_index() { let mut font = Font::default(); let glyph = Glyph::default(); - let error = Error::new_invalid_index(256, font.lenght()); + let index = 256usize; - let result = font.set_glyph(256, glyph); + let result = font.set_glyph(index, glyph); assert!(result.is_err()); - assert_eq!(result, Err(error)); + assert_matches!( + result.unwrap_err(), + Error::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() + ); } #[test] diff --git a/tinlib/src/graphic/glyph.rs b/tinlib/src/graphic/glyph.rs index b4adc35..5fdd43d 100644 --- a/tinlib/src/graphic/glyph.rs +++ b/tinlib/src/graphic/glyph.rs @@ -130,6 +130,8 @@ impl fmt::Debug for Glyph { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::*; #[test] @@ -166,10 +168,12 @@ mod tests { let coord = Coord::new(9, 1); let glyph = Glyph::default(); - let error = Error::new_invalid_coord(coord, glyph.size()); let result = glyph.get_pixel(coord); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), error); + assert_matches!( + result.unwrap_err(), + Error::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() + ); } #[test] @@ -191,10 +195,12 @@ mod tests { let coord = Coord::new(9, 1); let mut glyph = Glyph::default(); - let error = Error::new_invalid_coord(coord, glyph.size()); let result = glyph.set_pixel(coord, GlyphPixel::Solid); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), error); + assert_matches!( + result.unwrap_err(), + Error::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() + ); } #[test] diff --git a/tinlib/src/graphic/palette.rs b/tinlib/src/graphic/palette.rs index 68d3fa5..27b07ae 100644 --- a/tinlib/src/graphic/palette.rs +++ b/tinlib/src/graphic/palette.rs @@ -80,6 +80,8 @@ impl fmt::Debug for Palette { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::*; #[test] @@ -101,17 +103,20 @@ mod tests { let result = palette.get_color(0); assert!(result.is_ok()); - assert_eq!(result, Ok(color)); + assert_eq!(result.unwrap(), color); } #[test] fn test_palette_get_color_invalid_index() { let palette = Palette::default(); - let error = Error::new_invalid_index(16, palette.lenght()); + let index = 16usize; - let result = palette.get_color(16); + let result = palette.get_color(index); assert!(result.is_err()); - assert_eq!(result, Err(error)); + assert_matches!( + result.unwrap_err(), + Error::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() + ); } #[test] @@ -121,21 +126,24 @@ mod tests { let result = palette.set_color(0, color); assert!(result.is_ok()); - assert_eq!(result, Ok(())); + assert_eq!(result.unwrap(), ()); let result = palette.get_color(0); - assert_eq!(result, Ok(color)); + assert_eq!(result.unwrap(), color); } #[test] fn test_palette_set_color_invalid_index() { let mut palette = Palette::default(); let color = Color::new(255, 255, 255); - let error = Error::new_invalid_index(16, palette.lenght()); + let index = 16usize; let result = palette.set_color(16, color); assert!(result.is_err()); - assert_eq!(result, Err(error)); + assert_matches!( + result.unwrap_err(), + Error::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() + ); } #[test] diff --git a/tinlib/src/machine/screen.rs b/tinlib/src/machine/screen.rs index 78fcd9a..5f1b7c1 100644 --- a/tinlib/src/machine/screen.rs +++ b/tinlib/src/machine/screen.rs @@ -124,6 +124,8 @@ impl fmt::Debug for Screen { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::*; #[test] @@ -160,10 +162,12 @@ mod tests { let screen = Screen::default(); let coord = Coord::new(641, 1); - let error = Error::new_invalid_coord(coord, screen.size()); let result = screen.get_pixel(coord); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), error); + assert_matches!( + result.unwrap_err(), + Error::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() + ); } #[test] @@ -187,10 +191,12 @@ mod tests { let coord = Coord::new(641, 1); let pixel = ScreenPixel::new(255, 255, 255); - let error = Error::new_invalid_coord(coord, screen.size()); let result = screen.set_pixel(coord, pixel); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), error); + assert_matches!( + result.unwrap_err(), + Error::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() + ); } #[test] diff --git a/tinlib/src/map/mod.rs b/tinlib/src/map/mod.rs index 94829d0..fc8dc50 100644 --- a/tinlib/src/map/mod.rs +++ b/tinlib/src/map/mod.rs @@ -141,6 +141,8 @@ impl<'tile> fmt::Debug for Map<'tile> { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::*; #[test] @@ -198,10 +200,12 @@ mod tests { let coord = Coord::new(321, 1); let map = Map::default(); - let error = Error::new_invalid_coord(coord, map.size()); let result = map.get_tile(coord); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), error); + assert_matches!( + result.unwrap_err(), + Error::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() + ); } #[test] @@ -233,10 +237,12 @@ mod tests { let mut map = Map::default(); let tile = Tile::new(&glyph, &color); - let error = Error::new_invalid_coord(coord, map.size()); let result = map.set_tile(coord, tile); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), error); + assert_matches!( + result.unwrap_err(), + Error::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() + ); } #[test] From 5822425a1399f09e9263b0a355adf25f00629d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Tue, 4 Feb 2020 17:01:03 -0300 Subject: [PATCH 03/14] Add InvalidChunkType error --- tinlib/src/common/error.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tinlib/src/common/error.rs b/tinlib/src/common/error.rs index 747a545..242de53 100644 --- a/tinlib/src/common/error.rs +++ b/tinlib/src/common/error.rs @@ -11,28 +11,38 @@ use crate::common::size::Size; /// Internal errors. #[derive(Error, Debug)] pub enum Error { - /// Error to reprense invalid indexes. - #[error("invalid index {index} for lenght {lenght}")] - InvalidIndex { index: usize, lenght: usize }, + /// Error to represent invalid chunk types. + #[error("invalid chunk type {0}")] + InvalidChunkType(u8), /// Error to represent invalid coords. #[error("invalid coord ({coord:?}) for size ({size:?})")] InvalidCoord { coord: Coord, size: Size }, + /// Error to reprense invalid indexes. + #[error("invalid index {index} for lenght {lenght}")] + InvalidIndex { index: usize, lenght: usize }, #[error("IO operation error")] + /// Error to wrap `io::Error`s. Io(#[from] io::Error), + /// Error ro wrap `FromUft8Error`s. #[error("UFT8 conversion error")] FromUtf8(#[from] FromUtf8Error), } impl Error { - /// Creates a `InvalidIndex` error. - pub fn new_invalid_index(index: usize, lenght: usize) -> Self { - Self::InvalidIndex { index, lenght } + /// Creates a `InvalidChunkType` error. + pub fn new_invalid_chunk_type(value: u8) -> Self { + Self::InvalidChunkType(value) } /// Creates a `InvalidCoord` error. pub fn new_invalid_coord(coord: Coord, size: Size) -> Self { Self::InvalidCoord { coord, size } } + + /// Creates a `InvalidIndex` error. + pub fn new_invalid_index(index: usize, lenght: usize) -> Self { + Self::InvalidIndex { index, lenght } + } } /// Internal result. From 26862c325e246820a62e32aeba73e8b6a5a5ec47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Tue, 4 Feb 2020 17:23:53 -0300 Subject: [PATCH 04/14] Add cartridge impl --- tinlib/src/cartridge/chunk.rs | 376 ++++++++++++++++++++++++++++++++++ tinlib/src/cartridge/mod.rs | 282 +++++++++++++++++++++++++ tinlib/src/lib.rs | 1 + 3 files changed, 659 insertions(+) create mode 100644 tinlib/src/cartridge/chunk.rs diff --git a/tinlib/src/cartridge/chunk.rs b/tinlib/src/cartridge/chunk.rs new file mode 100644 index 0000000..4d3d4af --- /dev/null +++ b/tinlib/src/cartridge/chunk.rs @@ -0,0 +1,376 @@ +//! Chunk implementation and manipulation.\ +use std::convert::TryFrom; +use std::io::{Read, Write}; +use std::result::Result as StdResult; + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + +use crate::common::{Error, Result}; + +/// The Chunk type. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ChunkType { + /// The End of all chunk data. + End = 0, + /// Cover data. + Cover, + /// Code data. + Code, + /// Dont data. + Font, + /// Palette data. + Palette, + /// Map data. + Map, +} + +impl TryFrom for ChunkType { + type Error = Error; + + fn try_from(value: u8) -> StdResult { + match value { + 0 => Ok(ChunkType::End), + 1 => Ok(ChunkType::Cover), + 2 => Ok(ChunkType::Code), + 3 => Ok(ChunkType::Font), + 4 => Ok(ChunkType::Palette), + 5 => Ok(ChunkType::Map), + _ => Err(Error::new_invalid_chunk_type(value)), + } + } +} + +/// The chunk header. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct ChunkHeader { + /// The chunk type value. + pub chunk_type: ChunkType, + /// The chunk size. + pub size: u32, +} + +impl ChunkHeader { + /// Creates a ChunkHeader from the data read from a Reader. + pub fn from_reader(reader: &mut R) -> Result { + let chunk_type = reader.read_u8()?; + let chunk_type = ChunkType::try_from(chunk_type)?; + + let size = reader.read_u32::()?; + + Ok(ChunkHeader { chunk_type, size }) + } + + // Saves the ChunkHeader data into a Writer. + pub fn save(&self, writer: &mut W) -> Result<()> { + writer.write_u8(self.chunk_type as u8)?; + writer.write_u32::(self.size)?; + + Ok(()) + } +} + +impl Default for ChunkHeader { + fn default() -> Self { + Self { + chunk_type: ChunkType::End, + size: 0, + } + } +} + +/// The data chunk. +#[derive(Debug, Clone, PartialEq)] +pub struct Chunk { + pub header: ChunkHeader, + pub data: Vec, +} + +impl Chunk { + /// Creates a Chunk from the data read from a Reader. + pub fn from_reader(reader: &mut R) -> Result { + let header = ChunkHeader::from_reader(reader)?; + + let mut data = Vec::with_capacity(header.size as usize); + for _ in 0..header.size { + data.push(reader.read_u8()?); + } + + Ok(Chunk { header, data }) + } + + // Saves the Chunk data into a Writer. + pub fn save(&self, writer: &mut W) -> Result<()> { + self.header.save(writer)?; + + for data in self.data.iter() { + writer.write_u8(*data)?; + } + + Ok(()) + } + + pub fn data_to_vec_u16(&self) -> Vec { + self.data + .chunks(2) + .map(|c| u16::from_le_bytes([c[0], c[1]])) + .collect() + } + + pub fn data_to_vec_bool(&self) -> Vec { + self.data + .iter() + .map(|c| { + (0..8) + .rev() + .map(|i| c & (1 << i) != 0) + .collect::>() + }) + .flatten() + .collect() + } +} + +impl Default for Chunk { + fn default() -> Self { + let header = ChunkHeader::default(); + let data = vec![]; + + Self { header, data } + } +} + +#[cfg(test)] +mod test { + use std::io::Cursor; + + use assert_matches::assert_matches; + + use super::*; + + #[test] + fn test_chunktype_tryfrom() { + let data = [ + (0, ChunkType::End), + (1, ChunkType::Cover), + (2, ChunkType::Code), + (3, ChunkType::Font), + (4, ChunkType::Palette), + (5, ChunkType::Map), + ]; + + for (value, expected) in data.iter() { + let result = ChunkType::try_from(*value); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), *expected); + } + } + + #[test] + fn test_chunktype_tryfrom_fail() { + let value = 255u8; + let result = ChunkType::try_from(value); + + assert!(result.is_err()); + assert_matches!( + result.unwrap_err(), + Error::InvalidChunkType(v) if v == value + ); + } + + #[test] + fn test_chunkheader_from_reader() { + let mut reader = Cursor::new(vec![5, 0, 240, 0, 0]); + let expected = ChunkHeader { + chunk_type: ChunkType::Map, + size: 61440, + }; + + let result = ChunkHeader::from_reader(&mut reader); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected); + } + + #[test] + fn test_chunkheader_from_reader_invalid_chunk_type() { + let mut reader = Cursor::new(vec![6, 0, 240, 0, 0]); + + let result = ChunkHeader::from_reader(&mut reader); + assert!(result.is_err()); + assert_matches!( + result.unwrap_err(), + Error::InvalidChunkType(v) if v == 6 + ); + } + + #[test] + fn test_chunkheader_from_reader_invalid_data() { + let mut reader = Cursor::new(vec![5, 0]); + + let result = ChunkHeader::from_reader(&mut reader); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_chunkheader_save() { + let chunk_header = ChunkHeader { + chunk_type: ChunkType::Map, + size: 61440, + }; + let expected: Vec = vec![5, 0, 240, 0, 0]; + + let mut writer = Cursor::new(vec![0u8; 5]); + let result = chunk_header.save(&mut writer); + assert!(result.is_ok()); + assert_eq!(writer.get_ref(), &expected); + } + + #[test] + fn test_chunkheader_save_error() { + let chunk_header = ChunkHeader { + chunk_type: ChunkType::Map, + size: 61440, + }; + + let mut buff = [0u8; 1]; + let mut writer = Cursor::new(&mut buff[0..]); + let result = chunk_header.save(&mut writer); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_chunkheader_default() { + let chunk_header = ChunkHeader::default(); + assert_eq!(chunk_header.chunk_type, ChunkType::End); + assert_eq!(chunk_header.size, 0); + } + + #[test] + fn test_chunk_from_reader() { + let mut reader = Cursor::new(vec![ + // header + 4, // type + 12, 0, 0, 0, // size + // data + 0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255, + ]); + let expected = Chunk { + header: ChunkHeader { + chunk_type: ChunkType::Palette, + size: 12, + }, + data: vec![0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255], + }; + + let result = Chunk::from_reader(&mut reader); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected); + } + + #[test] + fn test_header_from_reader_invalid_chunk_type() { + let mut reader = Cursor::new(vec![ + // header + 6, // type + 12, 0, 0, 0, // size + // data + 0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255, + ]); + + let result = Chunk::from_reader(&mut reader); + assert!(result.is_err()); + assert_matches!( + result.unwrap_err(), + Error::InvalidChunkType(v) if v == 6 + ); + } + + #[test] + fn test_header_from_reader_invalid_data() { + let mut reader = Cursor::new(vec![ + // header + 4, // type + 12, 0, 0, 0, // size + // data + 0, + ]); + + let result = Chunk::from_reader(&mut reader); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_chunk_save() { + let chunk = Chunk { + header: ChunkHeader { + chunk_type: ChunkType::Palette, + size: 12, + }, + data: vec![0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255], + }; + let expected: Vec = vec![ + 4, 12, 0, 0, 0, 0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255, + ]; + + let mut writer = Cursor::new(vec![0u8; 17]); + let result = chunk.save(&mut writer); + assert!(result.is_ok()); + assert_eq!(writer.get_ref(), &expected); + } + + #[test] + fn test_chunk_save_error() { + let chunk = Chunk { + header: ChunkHeader { + chunk_type: ChunkType::Palette, + size: 12, + }, + data: vec![0, 0, 0, 86, 86, 86, 172, 172, 172, 255, 255, 255], + }; + + let mut buff = [0u8; 6]; + let mut writer = Cursor::new(&mut buff[0..]); + let result = chunk.save(&mut writer); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_chunk_data_to_vec_u16() { + let mut chunk = Chunk::default(); + chunk.data = vec![0, 0, 0, 255, 255, 0, 255, 255]; + //chunk.data = vec![0, 1, 15, 128, 240, 255]; + + let expected: Vec = vec![0, 65280, 255, 65535]; + let result = chunk.data_to_vec_u16(); + assert_eq!(result, expected); + } + + #[test] + fn test_chunk_data_to_vec_bool() { + let mut chunk = Chunk::default(); + chunk.data = vec![0, 1, 15, 128, 240, 255]; + + let expected: Vec = vec![ + false, false, false, false, false, false, false, false, // 0 + false, false, false, false, false, false, false, true, // 1 + false, false, false, false, true, true, true, true, // 15 + true, false, false, false, false, false, false, false, // 128 + true, true, true, true, false, false, false, false, // 240 + true, true, true, true, true, true, true, true, // 255 + ]; + let result = chunk.data_to_vec_bool(); + assert_eq!(result, expected); + } + + #[test] + fn test_chunk_default() { + let chunk = Chunk::default(); + assert_eq!(chunk.header.chunk_type, ChunkType::End); + assert_eq!(chunk.header.size, 0); + assert_eq!(chunk.data.len(), 0); + } +} diff --git a/tinlib/src/cartridge/mod.rs b/tinlib/src/cartridge/mod.rs index e69de29..b489722 100644 --- a/tinlib/src/cartridge/mod.rs +++ b/tinlib/src/cartridge/mod.rs @@ -0,0 +1,282 @@ +//! Cartridge utilities. +mod chunk; + +use std::io::{Read, Write}; + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + +use crate::cartridge::chunk::{Chunk, ChunkHeader, ChunkType}; +use crate::common::Result; + +/// The default cartridge file version. +const DEFAULT_CART_FILE_VERSION: u8 = 1; +/// The default name size. +const DEFAULT_NAME_SIZE: u8 = 64; +/// The default description size. +const DEFAULT_DESC_SIZE: u16 = 512; +/// The default author name size. +const DEFAULT_AUTHOR_SIZE: u8 = 64; + +/// The cartridge header. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Header { + pub cart_version: u8, + pub name_size: u8, + pub desc_size: u16, + pub author_size: u8, +} + +impl Header { + /// Creates a Header from the data read from a Reader. + pub fn from_reader(reader: &mut R) -> Result
{ + let cart_version = reader.read_u8()?; // TODO validate the version + let name_size = reader.read_u8()?; + let desc_size = reader.read_u16::()?; + let author_size = reader.read_u8()?; + + Ok(Header { + cart_version, + name_size, + desc_size, + author_size, + }) + } + + /// Saves the Header data into a Writer. + pub fn save(&self, writer: &mut W) -> Result<()> { + writer.write_u8(self.cart_version)?; + writer.write_u8(self.name_size)?; + writer.write_u16::(self.desc_size)?; + writer.write_u8(self.author_size)?; + + Ok(()) + } +} + +impl Default for Header { + fn default() -> Self { + Self { + cart_version: DEFAULT_CART_FILE_VERSION, + name_size: DEFAULT_NAME_SIZE, + desc_size: DEFAULT_DESC_SIZE, + author_size: DEFAULT_AUTHOR_SIZE, + } + } +} + +/// Default game version. +const DEFAULT_VERSION: u8 = 1; + +/// The cartridge data. +#[derive(Debug, Clone, PartialEq)] +pub struct Cartridge { + pub version: u8, + pub name: String, + pub desc: String, + pub author: String, + pub cover: Vec, + pub font: Vec, + pub palette: Vec, + pub map: Vec, + code: String, +} + +impl Cartridge { + pub fn from_reader(reader: &mut R) -> Result { + let mut cart = Cartridge::default(); + let header = Header::from_reader(reader)?; + + cart.version = reader.read_u8()?; + + let mut name = vec![0u8; header.name_size as usize]; + reader.read_exact(&mut name)?; + cart.name = String::from_utf8(name)?; + + let mut desc = vec![0u8; header.desc_size as usize]; + reader.read_exact(&mut desc)?; + cart.desc = String::from_utf8(desc)?; + + let mut author = vec![0u8; header.author_size as usize]; + reader.read_exact(&mut author)?; + cart.author = String::from_utf8(author)?; + + /*loop { + let chunk = Chunk::from_reader(reader)?; + + match chunk.header.chunk_type { + ChunkType::End => { + break; + } + ChunkType::Cover => { + cart.cover = chunk.data; + } + ChunkType::Code => { + cart.code = String::from_utf8(chunk.data)?; + } + ChunkType::Font => { + cart.font = chunk.data_to_vec_bool(); + } + ChunkType::Palette => { + cart.palette = chunk.data; + } + ChunkType::Map => { + cart.map = chunk.data_to_vec_u16(); + } + } + }*/ + + Ok(cart) + } + + pub fn save(&self, writer: &mut W) -> Result<()> { + let mut header = Header::default(); + header.name_size = self.name.len() as u8; + header.desc_size = self.desc.len() as u16; + header.author_size = self.author.len() as u8; + header.save(writer)?; + + writer.write_u8(self.version)?; + writer.write_all(&self.name.as_bytes())?; + writer.write_all(&self.desc.as_bytes())?; + writer.write_all(&self.author.as_bytes())?; + + Ok(()) + } +} + +impl Default for Cartridge { + fn default() -> Self { + Self { + version: DEFAULT_VERSION, + name: "".to_string(), + desc: "".to_string(), + author: "".to_string(), + cover: vec![], + font: vec![], + palette: vec![], + map: vec![], + code: "".to_string(), + } + } +} + +#[cfg(test)] +mod test_super { + use std::io::Cursor; + + use assert_matches::assert_matches; + + use crate::common::Error; + + use super::*; + + #[test] + fn test_header_from_reader() { + let mut reader = Cursor::new(vec![5, 32, 0, 1, 32]); + let expected = Header { + cart_version: 5, + name_size: 32, + desc_size: 256, + author_size: 32, + }; + + let result = Header::from_reader(&mut reader); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected); + } + + #[test] + fn test_header_from_reader_invalid_data() { + let mut reader = Cursor::new(vec![5, 32, 0, 1]); + + let result = Header::from_reader(&mut reader); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_header_save() { + let header = Header { + cart_version: 1, + name_size: 64, + desc_size: 512, + author_size: 64, + }; + let expected: Vec = vec![1, 64, 0, 2, 64]; + + let mut writer = Cursor::new(vec![0u8; 5]); + header.save(&mut writer).unwrap(); + assert_eq!(writer.get_ref(), &expected); + } + + #[test] + fn test_header_save_error() { + let header = Header { + cart_version: 1, + name_size: 64, + desc_size: 512, + author_size: 64, + }; + + let mut buff = [0u8; 1]; + let mut writer = Cursor::new(&mut buff[0..]); + let result = header.save(&mut writer); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_header_default() { + let header = Header::default(); + assert_eq!(header.cart_version, DEFAULT_CART_FILE_VERSION); + assert_eq!(header.name_size, DEFAULT_NAME_SIZE); + assert_eq!(header.desc_size, DEFAULT_DESC_SIZE); + assert_eq!(header.author_size, DEFAULT_AUTHOR_SIZE); + } + + #[test] + fn test_cartridge_from_reader() { + let mut reader = Cursor::new(vec![ + // header + 1, // cart version + 10, // name size + 11, 0, // desc size + 2, // author size + // cart + 11, // version + 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, // name + 100, 101, 115, 99, 114, 105, 195, 167, 195, 163, 111, // desc + 109, 101, // author + ]); + let expected = Cartridge { + version: 11, + name: "thisisname".to_string(), + desc: "descrição".to_string(), + author: "me".to_string(), + cover: vec![], + font: vec![], + palette: vec![], + map: vec![], + code: "".to_string(), + }; + + //print!("{:?}", "descrição".to_string().as_bytes()); + + let cart = Cartridge::from_reader(&mut reader).unwrap(); + assert_eq!(cart, expected); + } + + #[test] + fn test_cartridge_default() { + let cart = Cartridge::default(); + assert_eq!(cart.version, DEFAULT_VERSION); + assert_eq!(cart.name, "".to_string()); + assert_eq!(cart.desc, "".to_string()); + assert_eq!(cart.author, "".to_string()); + assert_eq!(cart.cover, vec![]); + assert_eq!(cart.font, vec![]); + assert_eq!(cart.palette, vec![]); + assert_eq!(cart.map, vec![]); + assert_eq!(cart.code, "".to_string()); + } +} diff --git a/tinlib/src/lib.rs b/tinlib/src/lib.rs index 22fef06..ad7101a 100644 --- a/tinlib/src/lib.rs +++ b/tinlib/src/lib.rs @@ -1,3 +1,4 @@ +pub mod cartridge; pub mod common; pub mod graphic; pub mod machine; From 7e0f009b214ae5af2915c45f2081e04c5ff4f72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Tue, 4 Feb 2020 17:23:53 -0300 Subject: [PATCH 05/14] Add cartridge impl --- tinlib/src/cartridge/chunk.rs | 76 ++++-------- tinlib/src/cartridge/mod.rs | 210 ++++++++++++++++++++++++++++------ 2 files changed, 196 insertions(+), 90 deletions(-) diff --git a/tinlib/src/cartridge/chunk.rs b/tinlib/src/cartridge/chunk.rs index 4d3d4af..04b0332 100644 --- a/tinlib/src/cartridge/chunk.rs +++ b/tinlib/src/cartridge/chunk.rs @@ -13,15 +13,15 @@ pub enum ChunkType { /// The End of all chunk data. End = 0, /// Cover data. - Cover, + Cover = 1, /// Code data. - Code, + Code = 2, /// Dont data. - Font, + Font = 3, /// Palette data. - Palette, + Palette = 4, /// Map data. - Map, + Map = 5, } impl TryFrom for ChunkType { @@ -50,6 +50,14 @@ pub struct ChunkHeader { } impl ChunkHeader { + /// Creates a ChunkHeader with the type and data provided. + pub fn new(chunk_type: ChunkType, size: usize) -> Self { + Self { + chunk_type, + size: size as u32, + } + } + /// Creates a ChunkHeader from the data read from a Reader. pub fn from_reader(reader: &mut R) -> Result { let chunk_type = reader.read_u8()?; @@ -86,6 +94,16 @@ pub struct Chunk { } impl Chunk { + /// Creates a Chunk with the type and data provided. + pub fn new(chunk_type: ChunkType, data: Vec) -> Self { + let header = ChunkHeader::new(chunk_type, data.len()); + + Self { + header, + data: data.clone(), + } + } + /// Creates a Chunk from the data read from a Reader. pub fn from_reader(reader: &mut R) -> Result { let header = ChunkHeader::from_reader(reader)?; @@ -109,25 +127,7 @@ impl Chunk { Ok(()) } - pub fn data_to_vec_u16(&self) -> Vec { - self.data - .chunks(2) - .map(|c| u16::from_le_bytes([c[0], c[1]])) - .collect() - } - - pub fn data_to_vec_bool(&self) -> Vec { - self.data - .iter() - .map(|c| { - (0..8) - .rev() - .map(|i| c & (1 << i) != 0) - .collect::>() - }) - .flatten() - .collect() - } + // TODO Add validation methods } impl Default for Chunk { @@ -338,34 +338,6 @@ mod test { assert_matches!(result.unwrap_err(), Error::Io(_)); } - #[test] - fn test_chunk_data_to_vec_u16() { - let mut chunk = Chunk::default(); - chunk.data = vec![0, 0, 0, 255, 255, 0, 255, 255]; - //chunk.data = vec![0, 1, 15, 128, 240, 255]; - - let expected: Vec = vec![0, 65280, 255, 65535]; - let result = chunk.data_to_vec_u16(); - assert_eq!(result, expected); - } - - #[test] - fn test_chunk_data_to_vec_bool() { - let mut chunk = Chunk::default(); - chunk.data = vec![0, 1, 15, 128, 240, 255]; - - let expected: Vec = vec![ - false, false, false, false, false, false, false, false, // 0 - false, false, false, false, false, false, false, true, // 1 - false, false, false, false, true, true, true, true, // 15 - true, false, false, false, false, false, false, false, // 128 - true, true, true, true, false, false, false, false, // 240 - true, true, true, true, true, true, true, true, // 255 - ]; - let result = chunk.data_to_vec_bool(); - assert_eq!(result, expected); - } - #[test] fn test_chunk_default() { let chunk = Chunk::default(); diff --git a/tinlib/src/cartridge/mod.rs b/tinlib/src/cartridge/mod.rs index b489722..0075cd3 100644 --- a/tinlib/src/cartridge/mod.rs +++ b/tinlib/src/cartridge/mod.rs @@ -5,7 +5,7 @@ use std::io::{Read, Write}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use crate::cartridge::chunk::{Chunk, ChunkHeader, ChunkType}; +use crate::cartridge::chunk::{Chunk, ChunkType}; use crate::common::Result; /// The default cartridge file version. @@ -16,25 +16,29 @@ const DEFAULT_NAME_SIZE: u8 = 64; const DEFAULT_DESC_SIZE: u16 = 512; /// The default author name size. const DEFAULT_AUTHOR_SIZE: u8 = 64; +/// The default game version. +const DEFAULT_VERSION: u8 = 1; /// The cartridge header. #[derive(Debug, Clone, Copy, PartialEq)] -pub struct Header { +pub struct CartridgeHeader { pub cart_version: u8, pub name_size: u8, pub desc_size: u16, pub author_size: u8, } -impl Header { - /// Creates a Header from the data read from a Reader. - pub fn from_reader(reader: &mut R) -> Result
{ +// TODO save and destroy things + +impl CartridgeHeader { + /// Creates a CartridgeHeader from the data read from a Reader. + pub fn from_reader(reader: &mut R) -> Result { let cart_version = reader.read_u8()?; // TODO validate the version let name_size = reader.read_u8()?; let desc_size = reader.read_u16::()?; let author_size = reader.read_u8()?; - Ok(Header { + Ok(CartridgeHeader { cart_version, name_size, desc_size, @@ -42,7 +46,7 @@ impl Header { }) } - /// Saves the Header data into a Writer. + /// Saves the CartridgeHeader data into a Writer. pub fn save(&self, writer: &mut W) -> Result<()> { writer.write_u8(self.cart_version)?; writer.write_u8(self.name_size)?; @@ -53,7 +57,7 @@ impl Header { } } -impl Default for Header { +impl Default for CartridgeHeader { fn default() -> Self { Self { cart_version: DEFAULT_CART_FILE_VERSION, @@ -64,9 +68,6 @@ impl Default for Header { } } -/// Default game version. -const DEFAULT_VERSION: u8 = 1; - /// The cartridge data. #[derive(Debug, Clone, PartialEq)] pub struct Cartridge { @@ -75,16 +76,16 @@ pub struct Cartridge { pub desc: String, pub author: String, pub cover: Vec, - pub font: Vec, + pub font: Vec, pub palette: Vec, - pub map: Vec, - code: String, + pub map: Vec, + pub code: String, } impl Cartridge { pub fn from_reader(reader: &mut R) -> Result { let mut cart = Cartridge::default(); - let header = Header::from_reader(reader)?; + let header = CartridgeHeader::from_reader(reader)?; cart.version = reader.read_u8()?; @@ -100,7 +101,7 @@ impl Cartridge { reader.read_exact(&mut author)?; cart.author = String::from_utf8(author)?; - /*loop { + loop { let chunk = Chunk::from_reader(reader)?; match chunk.header.chunk_type { @@ -114,31 +115,47 @@ impl Cartridge { cart.code = String::from_utf8(chunk.data)?; } ChunkType::Font => { - cart.font = chunk.data_to_vec_bool(); + cart.font = chunk.data; } ChunkType::Palette => { cart.palette = chunk.data; } ChunkType::Map => { - cart.map = chunk.data_to_vec_u16(); + cart.map = chunk.data; } } - }*/ + } Ok(cart) } pub fn save(&self, writer: &mut W) -> Result<()> { - let mut header = Header::default(); + let mut header = CartridgeHeader::default(); header.name_size = self.name.len() as u8; header.desc_size = self.desc.len() as u16; header.author_size = self.author.len() as u8; header.save(writer)?; writer.write_u8(self.version)?; - writer.write_all(&self.name.as_bytes())?; - writer.write_all(&self.desc.as_bytes())?; - writer.write_all(&self.author.as_bytes())?; + writer.write_all(self.name.as_bytes())?; + writer.write_all(self.desc.as_bytes())?; + writer.write_all(self.author.as_bytes())?; + + let chunks = vec![ + (self.cover.clone(), ChunkType::Cover), + (self.font.clone(), ChunkType::Font), + (self.palette.clone(), ChunkType::Palette), + (self.map.clone(), ChunkType::Map), + (self.code.as_bytes().to_vec(), ChunkType::Code), + ]; + + for (data, chunk_type) in chunks.into_iter().filter(|(d, _)| !d.is_empty()) { + let chunk = Chunk::new(chunk_type, data); + chunk.save(writer)?; + } + + let chunk = Chunk::default(); + chunk.save(writer)?; Ok(()) } @@ -171,32 +188,32 @@ mod test_super { use super::*; #[test] - fn test_header_from_reader() { + fn test_cartridgeheader_from_reader() { let mut reader = Cursor::new(vec![5, 32, 0, 1, 32]); - let expected = Header { + let expected = CartridgeHeader { cart_version: 5, name_size: 32, desc_size: 256, author_size: 32, }; - let result = Header::from_reader(&mut reader); + let result = CartridgeHeader::from_reader(&mut reader); assert!(result.is_ok()); assert_eq!(result.unwrap(), expected); } #[test] - fn test_header_from_reader_invalid_data() { + fn test_cartridgeheader_from_reader_invalid_data() { let mut reader = Cursor::new(vec![5, 32, 0, 1]); - let result = Header::from_reader(&mut reader); + let result = CartridgeHeader::from_reader(&mut reader); assert!(result.is_err()); assert_matches!(result.unwrap_err(), Error::Io(_)); } #[test] - fn test_header_save() { - let header = Header { + fn test_cartridgeheader_save() { + let header = CartridgeHeader { cart_version: 1, name_size: 64, desc_size: 512, @@ -205,13 +222,14 @@ mod test_super { let expected: Vec = vec![1, 64, 0, 2, 64]; let mut writer = Cursor::new(vec![0u8; 5]); - header.save(&mut writer).unwrap(); + let result = header.save(&mut writer); + assert!(result.is_ok()); assert_eq!(writer.get_ref(), &expected); } #[test] - fn test_header_save_error() { - let header = Header { + fn test_cartridgeheader_save_error() { + let header = CartridgeHeader { cart_version: 1, name_size: 64, desc_size: 512, @@ -226,8 +244,8 @@ mod test_super { } #[test] - fn test_header_default() { - let header = Header::default(); + fn test_cartridgeheader_default() { + let header = CartridgeHeader::default(); assert_eq!(header.cart_version, DEFAULT_CART_FILE_VERSION); assert_eq!(header.name_size, DEFAULT_NAME_SIZE); assert_eq!(header.desc_size, DEFAULT_DESC_SIZE); @@ -247,12 +265,60 @@ mod test_super { 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, // name 100, 101, 115, 99, 114, 105, 195, 167, 195, 163, 111, // desc 109, 101, // author + // code + 2, 6, 0, 0, 0, // header + 109, 97, 105, 110, 40, 41, // data + // map + 5, 4, 0, 0, 0, // header + 0, 1, 2, 3, // data + // font + 3, 2, 0, 0, 0, // header + 1, 2, // data + // cover + 1, 4, 0, 0, 0, // header + 1, 2, 3, 4, // data + // palette + 4, 3, 0, 0, 0, // header + 0, 0, 0, // data + // end + 0, 0, 0, 0, 0, // ignored + 1, 0, 0, 0, 0, ]); let expected = Cartridge { version: 11, name: "thisisname".to_string(), desc: "descrição".to_string(), author: "me".to_string(), + cover: vec![1, 2, 3, 4], + font: vec![1, 2], + palette: vec![0, 0, 0], + map: vec![0, 1, 2, 3], + code: "main()".to_string(), + }; + + let result = Cartridge::from_reader(&mut reader); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected); + } + + #[test] + fn test_cartridge_from_reader_empty_data_and_chunks() { + let mut reader = Cursor::new(vec![ + // header + 1, // cart version + 0, // name size + 0, 0, // desc size + 0, // author size + // cart + 1, // version + // end + 0, 0, 0, 0, 0, + ]); + let expected = Cartridge { + version: 1, + name: "".to_string(), + desc: "".to_string(), + author: "".to_string(), cover: vec![], font: vec![], palette: vec![], @@ -260,10 +326,78 @@ mod test_super { code: "".to_string(), }; - //print!("{:?}", "descrição".to_string().as_bytes()); + let result = Cartridge::from_reader(&mut reader); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected); + } + + #[test] + fn test_cartridge_from_reader_missing_data() { + let mut reader = Cursor::new(vec![ + // header + 1, // cart version + 0, // name size + 0, 0, // desc size + 0, // author size + // cart + 1, // version + ]); + + let result = Cartridge::from_reader(&mut reader); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_cartridge_from_reader_missing_end_chunk() { + let mut reader = Cursor::new(vec![ + // header + 1, // cart version + 0, // name size + 0, 0, // desc size + 0, // author size + ]); + + let result = Cartridge::from_reader(&mut reader); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + + #[test] + fn test_cartridge_save() { + let cart = Cartridge { + version: 11, + name: "thisisname".to_string(), + desc: "descrição".to_string(), + author: "me".to_string(), + cover: vec![1, 2, 3, 4], + font: vec![1, 2], + palette: vec![0, 0, 0], + map: vec![0, 1, 2, 3], + code: "main()".to_string(), + }; + let expected: Vec = vec![ + 1, 10, 11, 0, 2, 11, 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, 100, 101, 115, + 99, 114, 105, 195, 167, 195, 163, 111, 109, 101, 1, 4, 0, 0, 0, 1, 2, 3, 4, 3, 2, 0, 0, + 0, 1, 2, 4, 3, 0, 0, 0, 0, 0, 0, 5, 4, 0, 0, 0, 0, 1, 2, 3, 2, 6, 0, 0, 0, 109, 97, + 105, 110, 40, 41, 0, 0, 0, 0, 0, + ]; + + let mut writer = Cursor::new(vec![0u8; 5]); + let result = cart.save(&mut writer); + assert!(result.is_ok()); + assert_eq!(writer.get_ref(), &expected); + } + + #[test] + fn test_cartridge_save_empty() { + let cart = Cartridge::default(); + let expected: Vec = vec![1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; - let cart = Cartridge::from_reader(&mut reader).unwrap(); - assert_eq!(cart, expected); + let mut writer = Cursor::new(vec![0u8; 5]); + let result = cart.save(&mut writer); + assert!(result.is_ok()); + assert_eq!(writer.get_ref(), &expected); } #[test] From 039816e7bbb6bccd570b263b3fc97da1c612d579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Fri, 7 Feb 2020 09:33:29 -0300 Subject: [PATCH 06/14] Fix clippy warnings --- tinlib/src/cartridge/chunk.rs | 4 ++-- tinlib/src/cartridge/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tinlib/src/cartridge/chunk.rs b/tinlib/src/cartridge/chunk.rs index 04b0332..01b58f4 100644 --- a/tinlib/src/cartridge/chunk.rs +++ b/tinlib/src/cartridge/chunk.rs @@ -41,7 +41,7 @@ impl TryFrom for ChunkType { } /// The chunk header. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct ChunkHeader { /// The chunk type value. pub chunk_type: ChunkType, @@ -100,7 +100,7 @@ impl Chunk { Self { header, - data: data.clone(), + data, } } diff --git a/tinlib/src/cartridge/mod.rs b/tinlib/src/cartridge/mod.rs index 0075cd3..64b1703 100644 --- a/tinlib/src/cartridge/mod.rs +++ b/tinlib/src/cartridge/mod.rs @@ -20,7 +20,7 @@ const DEFAULT_AUTHOR_SIZE: u8 = 64; const DEFAULT_VERSION: u8 = 1; /// The cartridge header. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct CartridgeHeader { pub cart_version: u8, pub name_size: u8, From ed5d50e7f378e72a440ab19022dc027af3dfc6a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Fri, 7 Feb 2020 10:03:12 -0300 Subject: [PATCH 07/14] Add missing cart test --- tinlib/src/cartridge/chunk.rs | 5 +---- tinlib/src/cartridge/mod.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tinlib/src/cartridge/chunk.rs b/tinlib/src/cartridge/chunk.rs index 01b58f4..4843668 100644 --- a/tinlib/src/cartridge/chunk.rs +++ b/tinlib/src/cartridge/chunk.rs @@ -98,10 +98,7 @@ impl Chunk { pub fn new(chunk_type: ChunkType, data: Vec) -> Self { let header = ChunkHeader::new(chunk_type, data.len()); - Self { - header, - data, - } + Self { header, data } } /// Creates a Chunk from the data read from a Reader. diff --git a/tinlib/src/cartridge/mod.rs b/tinlib/src/cartridge/mod.rs index 64b1703..5bba4dc 100644 --- a/tinlib/src/cartridge/mod.rs +++ b/tinlib/src/cartridge/mod.rs @@ -400,6 +400,17 @@ mod test_super { assert_eq!(writer.get_ref(), &expected); } + #[test] + fn test_cartridge_save_error() { + let cart = Cartridge::default(); + + let mut buff = [0u8; 1]; + let mut writer = Cursor::new(&mut buff[0..]); + let result = cart.save(&mut writer); + assert!(result.is_err()); + assert_matches!(result.unwrap_err(), Error::Io(_)); + } + #[test] fn test_cartridge_default() { let cart = Cartridge::default(); From bd3ab0eb4ea8901254e078f3635ea4c39d98d2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Fri, 7 Feb 2020 10:43:50 -0300 Subject: [PATCH 08/14] Add cartridge example --- .gitignore | 13 ------------- tinlib/examples/cartridge.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 tinlib/examples/cartridge.rs diff --git a/.gitignore b/.gitignore index 9ac1d8a..eccd7b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,2 @@ -# Generated by Cargo -# will have compiled files and executables /target/ - - -# These are backup files generated by rustfmt **/*.rs.bk - - -#Added by cargo -# -#already existing elements are commented out - -/target -#**/*.rs.bk diff --git a/tinlib/examples/cartridge.rs b/tinlib/examples/cartridge.rs new file mode 100644 index 0000000..8678831 --- /dev/null +++ b/tinlib/examples/cartridge.rs @@ -0,0 +1,36 @@ +use std::io::Cursor; + +use tinlib::cartridge::Cartridge; + +fn main() { + let mut cart = Cartridge::default(); + + // An incomplete game cart with empty fonts, map and cover. + cart.version = 17; + cart.name = "Dungeons of the Dungeon".to_string(); + cart.desc = "A cool game about dungeons inside dungeons.".to_string(); + cart.author = "Luiz de Prá".to_string(); + cart.palette = vec![ + 0x2d, 0x1b, 0x000, // dark + 0x1e, 0x60, 0x6e, // dark greenish + 0x5a, 0xb9, 0xa8, // greenish + 0xc4, 0xf0, 0xc2, // light greenish + ]; + cart.code = "def main:\n pass".to_string(); + + println!("Pre-save Cart: {:?}\n\n", &cart); + + // Saving the cart data into a cursor (file or anything that implements Write). + let mut cursor = Cursor::new(vec![]); + cart.save(&mut cursor).expect("failed to save cart"); + + println!("File data: {:?}\n\n", &cursor); + + // Loading the cart data from a cursor (file, or anything that implements Read). + cursor.set_position(0); + let new_cart = Cartridge::from_reader(&mut cursor).expect("failed to load cart"); + + println!("Post-load Cart: {:?}\n\n", &new_cart); + + println!("They has the same data? {}\n\n", cart == new_cart); +} From 1a0e54f5c8e3d10449dbef6ad9245cf1a70f5a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Sun, 9 Feb 2020 12:49:38 -0300 Subject: [PATCH 09/14] Split errors --- tinlib/src/cartridge/chunk.rs | 142 ++++++++++++++++++++++++++++++---- tinlib/src/cartridge/error.rs | 128 ++++++++++++++++++++++++++++++ tinlib/src/cartridge/mod.rs | 32 ++++---- tinlib/src/common/error.rs | 39 +++------- tinlib/src/common/mod.rs | 2 +- tinlib/src/graphic/font.rs | 10 +-- tinlib/src/graphic/glyph.rs | 12 +-- tinlib/src/graphic/palette.rs | 10 +-- tinlib/src/lib.rs | 21 +++++ tinlib/src/machine/screen.rs | 12 +-- tinlib/src/map/mod.rs | 12 +-- 11 files changed, 333 insertions(+), 87 deletions(-) create mode 100644 tinlib/src/cartridge/error.rs diff --git a/tinlib/src/cartridge/chunk.rs b/tinlib/src/cartridge/chunk.rs index 4843668..a14d74c 100644 --- a/tinlib/src/cartridge/chunk.rs +++ b/tinlib/src/cartridge/chunk.rs @@ -5,7 +5,15 @@ use std::result::Result as StdResult; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use crate::common::{Error, Result}; +use crate::cartridge::error::{CartridgeError, Result}; + +// Valid chunk sizes. +const END_CHUNK_VALID_SIZE: [usize; 1] = [0]; +const COVER_CHUNK_VALID_SIZES: [usize; 2] = [0, 245760]; +const FONT_CHUNK_VALID_SIZES: [usize; 2] = [0, 16384]; +const PALETTE_CHUNK_VALID_SIZES: [usize; 4] = [0, 4, 8, 16]; +const CODE_CHUNK_MAX_SIZE: usize = 131072; +const MAP_CHUNK_MAX_SIZE: usize = 122880; /// The Chunk type. #[derive(Debug, Copy, Clone, PartialEq)] @@ -25,7 +33,7 @@ pub enum ChunkType { } impl TryFrom for ChunkType { - type Error = Error; + type Error = CartridgeError; fn try_from(value: u8) -> StdResult { match value { @@ -35,7 +43,7 @@ impl TryFrom for ChunkType { 3 => Ok(ChunkType::Font), 4 => Ok(ChunkType::Palette), 5 => Ok(ChunkType::Map), - _ => Err(Error::new_invalid_chunk_type(value)), + _ => Err(CartridgeError::new_invalid_chunk_type(value)), } } } @@ -44,9 +52,9 @@ impl TryFrom for ChunkType { #[derive(Debug, Clone, PartialEq)] pub struct ChunkHeader { /// The chunk type value. - pub chunk_type: ChunkType, + chunk_type: ChunkType, /// The chunk size. - pub size: u32, + size: u32, } impl ChunkHeader { @@ -89,8 +97,8 @@ impl Default for ChunkHeader { /// The data chunk. #[derive(Debug, Clone, PartialEq)] pub struct Chunk { - pub header: ChunkHeader, - pub data: Vec, + header: ChunkHeader, + data: Vec, } impl Chunk { @@ -101,6 +109,14 @@ impl Chunk { Self { header, data } } + pub fn chunk_type(&self) -> ChunkType { + self.header.chunk_type + } + + pub fn data(&self) -> &Vec { + &self.data + } + /// Creates a Chunk from the data read from a Reader. pub fn from_reader(reader: &mut R) -> Result { let header = ChunkHeader::from_reader(reader)?; @@ -110,11 +126,16 @@ impl Chunk { data.push(reader.read_u8()?); } - Ok(Chunk { header, data }) + let chunk = Chunk { header, data }; + chunk.validate()?; + + Ok(chunk) } // Saves the Chunk data into a Writer. pub fn save(&self, writer: &mut W) -> Result<()> { + self.validate()?; + self.header.save(writer)?; for data in self.data.iter() { @@ -124,7 +145,96 @@ impl Chunk { Ok(()) } - // TODO Add validation methods + fn validate(&self) -> Result<()> { + if self.header.size != self.data.len() as u32 { + return Err(CartridgeError::new_mismatched_chunk_sizes( + self.header.chunk_type, + self.header.size as usize, + self.data.len(), + )); + } + + match self.chunk_type() { + ChunkType::End => self.validate_end(), + ChunkType::Cover => self.validate_cover(), + ChunkType::Code => self.validate_code(), + ChunkType::Font => self.validate_font(), + ChunkType::Palette => self.validate_palette(), + ChunkType::Map => self.validate_map(), + } + } + + fn validate_end(&self) -> Result<()> { + if END_CHUNK_VALID_SIZE.contains(&self.data.len()) { + return Err(CartridgeError::new_invalid_chunk_size( + self.header.chunk_type, + self.data.len(), + END_CHUNK_VALID_SIZE.to_vec(), + )); + } + + Ok(()) + } + + fn validate_cover(&self) -> Result<()> { + if COVER_CHUNK_VALID_SIZES.contains(&self.data.len()) { + return Err(CartridgeError::new_invalid_chunk_size( + self.header.chunk_type, + self.data.len(), + COVER_CHUNK_VALID_SIZES.to_vec(), + )); + } + + Ok(()) + } + + fn validate_code(&self) -> Result<()> { + if self.data.len() <= CODE_CHUNK_MAX_SIZE { + return Err(CartridgeError::new_invalid_chunk_max_size( + self.header.chunk_type, + self.data.len(), + CODE_CHUNK_MAX_SIZE, + )); + } + + Ok(()) + } + + fn validate_font(&self) -> Result<()> { + if FONT_CHUNK_VALID_SIZES.contains(&self.data.len()) { + return Err(CartridgeError::new_invalid_chunk_size( + self.header.chunk_type, + self.data.len(), + FONT_CHUNK_VALID_SIZES.to_vec(), + )); + } + + Ok(()) + } + + fn validate_palette(&self) -> Result<()> { + if PALETTE_CHUNK_VALID_SIZES.contains(&self.data.len()) { + return Err(CartridgeError::new_invalid_chunk_size( + self.header.chunk_type, + self.data.len(), + PALETTE_CHUNK_VALID_SIZES.to_vec(), + )); + } + + Ok(()) + } + + fn validate_map(&self) -> Result<()> { + if self.data.len() <= MAP_CHUNK_MAX_SIZE { + return Err(CartridgeError::new_invalid_chunk_max_size( + self.header.chunk_type, + self.data.len(), + MAP_CHUNK_MAX_SIZE, + )); + } + + Ok(()) + } } impl Default for Chunk { @@ -171,7 +281,7 @@ mod test { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidChunkType(v) if v == value + CartridgeError::InvalidChunkType(v) if v == value ); } @@ -196,7 +306,7 @@ mod test { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidChunkType(v) if v == 6 + CartridgeError::InvalidChunkType(v) if v == 6 ); } @@ -206,7 +316,7 @@ mod test { let result = ChunkHeader::from_reader(&mut reader); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] @@ -234,7 +344,7 @@ mod test { let mut writer = Cursor::new(&mut buff[0..]); let result = chunk_header.save(&mut writer); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] @@ -280,7 +390,7 @@ mod test { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidChunkType(v) if v == 6 + CartridgeError::InvalidChunkType(v) if v == 6 ); } @@ -296,7 +406,7 @@ mod test { let result = Chunk::from_reader(&mut reader); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] @@ -332,7 +442,7 @@ mod test { let mut writer = Cursor::new(&mut buff[0..]); let result = chunk.save(&mut writer); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] diff --git a/tinlib/src/cartridge/error.rs b/tinlib/src/cartridge/error.rs new file mode 100644 index 0000000..8041f71 --- /dev/null +++ b/tinlib/src/cartridge/error.rs @@ -0,0 +1,128 @@ +//! CartridgeError implementation and manipulation. +use std::io; +use std::result::Result as StdResult; +use std::string::FromUtf8Error; + +use thiserror::Error; + +use crate::cartridge::chunk::ChunkType; + +/// Cartridge errors. +#[derive(Error, Debug)] +pub enum CartridgeError { + /// Error to represent invalid chunk types. + #[error("invalid chunk type {0}")] + InvalidChunkType(u8), + /// Error to represent invalid chunk sizes. + #[error("invalid chunk size {1} for type {0:?}, expected: {2:?}")] + InvalidChunkSize(ChunkType, usize, Vec), + /// Error to represent invalid chunk max sizes. + #[error("invalid chunk size {1} for type {0:?}, max expected: {2}")] + InvalidChunkMaxSize(ChunkType, usize, usize), + /// Error to represent mismatched chunk sizes. + #[error("mismatched chunk header size {1} and data sizes {2} for type {0:?}")] + MismatchedChunkSizes(ChunkType, usize, usize), + /// Error to wrap an invalid conversion to UTF8. + #[error("UFT8 conversion error")] + FromUtf8(#[from] FromUtf8Error), + /// Error to wrap `io::Error`s from loading process. + #[error("IO operation error")] + Io(#[from] io::Error), +} + +impl CartridgeError { + /// Creates a `InvalidChunkType` error. + pub fn new_invalid_chunk_type(chunk_type: u8) -> Self { + Self::InvalidChunkType(chunk_type) + } + + /// Creates a `InvalidChunkSize` error. + pub fn new_invalid_chunk_size( + chunk_type: ChunkType, + value: usize, + expected: Vec, + ) -> Self { + Self::InvalidChunkSize(chunk_type, value, expected) + } + + /// Creates a `InvalidChunkMaxSize` error. + pub fn new_invalid_chunk_max_size( + chunk_type: ChunkType, + value: usize, + expected: usize, + ) -> Self { + Self::InvalidChunkMaxSize(chunk_type, value, expected) + } + + /// Creates a `MismatchedChunkSizes` error. + pub fn new_mismatched_chunk_sizes( + chunk_type: ChunkType, + header_size: usize, + data_size: usize, + ) -> Self { + Self::MismatchedChunkSizes(chunk_type, header_size, data_size) + } +} + +pub type Result = StdResult; + +#[cfg(test)] +mod test_super { + use assert_matches::assert_matches; + + use super::*; + + #[test] + fn test_cartridgeerror_new_invalid_chunk_type() { + let chunk_type = 99u8; + + let error = CartridgeError::new_invalid_chunk_type(chunk_type); + + assert_matches!( + error, + CartridgeError::InvalidChunkType(ct) if ct == chunk_type + ); + } + + #[test] + fn test_cartridgeerror_new_invalid_chunk_size() { + let chunk_type = ChunkType::End; + let value = 1usize; + let expected = vec![0usize]; + + let error = CartridgeError::new_invalid_chunk_size(chunk_type, value, expected.clone()); + + assert_matches!( + error, + CartridgeError::InvalidChunkSize(ct, v, e) if ct == chunk_type && v == value && e == expected + ); + } + + #[test] + fn test_cartridgeerror_new_invalid_chunk_max_size() { + let chunk_type = ChunkType::Code; + let value = 140000usize; + let expected = 131072usize; + + let error = CartridgeError::new_invalid_chunk_max_size(chunk_type, value, expected); + + assert_matches!( + error, + CartridgeError::InvalidChunkMaxSize(ct, v, e) if ct == chunk_type && v == value && e == expected + ); + } + + #[test] + fn test_cartridgeerror_new_mismatched_chunk_sizes() { + let chunk_type = ChunkType::Code; + let header_size = 10usize; + let data_size = 15usize; + + let error = CartridgeError::new_mismatched_chunk_sizes(chunk_type, header_size, data_size); + + assert_matches!( + error, + CartridgeError::MismatchedChunkSizes(ct, h, d) if ct == chunk_type && h == header_size && d == data_size + ); + } +} diff --git a/tinlib/src/cartridge/mod.rs b/tinlib/src/cartridge/mod.rs index 5bba4dc..4f2cade 100644 --- a/tinlib/src/cartridge/mod.rs +++ b/tinlib/src/cartridge/mod.rs @@ -1,12 +1,14 @@ //! Cartridge utilities. mod chunk; +mod error; + +pub use crate::cartridge::error::{CartridgeError, Result}; use std::io::{Read, Write}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crate::cartridge::chunk::{Chunk, ChunkType}; -use crate::common::Result; /// The default cartridge file version. const DEFAULT_CART_FILE_VERSION: u8 = 1; @@ -21,15 +23,13 @@ const DEFAULT_VERSION: u8 = 1; /// The cartridge header. #[derive(Debug, Clone, PartialEq)] -pub struct CartridgeHeader { +struct CartridgeHeader { pub cart_version: u8, pub name_size: u8, pub desc_size: u16, pub author_size: u8, } -// TODO save and destroy things - impl CartridgeHeader { /// Creates a CartridgeHeader from the data read from a Reader. pub fn from_reader(reader: &mut R) -> Result { @@ -104,24 +104,24 @@ impl Cartridge { loop { let chunk = Chunk::from_reader(reader)?; - match chunk.header.chunk_type { + match chunk.chunk_type() { ChunkType::End => { break; } ChunkType::Cover => { - cart.cover = chunk.data; + cart.cover = chunk.data().clone(); } ChunkType::Code => { - cart.code = String::from_utf8(chunk.data)?; + cart.code = String::from_utf8(chunk.data().clone())?; } ChunkType::Font => { - cart.font = chunk.data; + cart.font = chunk.data().clone(); } ChunkType::Palette => { - cart.palette = chunk.data; + cart.palette = chunk.data().clone(); } ChunkType::Map => { - cart.map = chunk.data; + cart.map = chunk.data().clone(); } } } @@ -183,8 +183,6 @@ mod test_super { use assert_matches::assert_matches; - use crate::common::Error; - use super::*; #[test] @@ -208,7 +206,7 @@ mod test_super { let result = CartridgeHeader::from_reader(&mut reader); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] @@ -240,7 +238,7 @@ mod test_super { let mut writer = Cursor::new(&mut buff[0..]); let result = header.save(&mut writer); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] @@ -345,7 +343,7 @@ mod test_super { let result = Cartridge::from_reader(&mut reader); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] @@ -360,7 +358,7 @@ mod test_super { let result = Cartridge::from_reader(&mut reader); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] @@ -408,7 +406,7 @@ mod test_super { let mut writer = Cursor::new(&mut buff[0..]); let result = cart.save(&mut writer); assert!(result.is_err()); - assert_matches!(result.unwrap_err(), Error::Io(_)); + assert_matches!(result.unwrap_err(), CartridgeError::Io(_)); } #[test] diff --git a/tinlib/src/common/error.rs b/tinlib/src/common/error.rs index 242de53..77c5e35 100644 --- a/tinlib/src/common/error.rs +++ b/tinlib/src/common/error.rs @@ -1,39 +1,23 @@ -//! Error implementation and manipulation. -use std::io; +//! CommonError implementation and manipulation. use std::result::Result as StdResult; -use std::string::FromUtf8Error; use thiserror::Error; use crate::common::coord::Coord; use crate::common::size::Size; -/// Internal errors. +/// Common errors. #[derive(Error, Debug)] -pub enum Error { - /// Error to represent invalid chunk types. - #[error("invalid chunk type {0}")] - InvalidChunkType(u8), +pub enum CommonError { /// Error to represent invalid coords. #[error("invalid coord ({coord:?}) for size ({size:?})")] InvalidCoord { coord: Coord, size: Size }, /// Error to reprense invalid indexes. #[error("invalid index {index} for lenght {lenght}")] InvalidIndex { index: usize, lenght: usize }, - #[error("IO operation error")] - /// Error to wrap `io::Error`s. - Io(#[from] io::Error), - /// Error ro wrap `FromUft8Error`s. - #[error("UFT8 conversion error")] - FromUtf8(#[from] FromUtf8Error), } -impl Error { - /// Creates a `InvalidChunkType` error. - pub fn new_invalid_chunk_type(value: u8) -> Self { - Self::InvalidChunkType(value) - } - +impl CommonError { /// Creates a `InvalidCoord` error. pub fn new_invalid_coord(coord: Coord, size: Size) -> Self { Self::InvalidCoord { coord, size } @@ -45,8 +29,7 @@ impl Error { } } -/// Internal result. -pub type Result = StdResult; +pub type Result = StdResult; #[cfg(test)] mod test_super { @@ -55,28 +38,28 @@ mod test_super { use super::*; #[test] - fn test_error_new_invalid_index() { + fn test_commonerror_new_invalid_index() { let index = 2usize; let lenght = 1usize; - let error = Error::new_invalid_index(index, lenght); + let error = CommonError::new_invalid_index(index, lenght); assert_matches!( error, - Error::InvalidIndex { index: i, lenght: l } if i == index && l == lenght + CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == lenght ); } #[test] - fn test_error_new_invalid_coord() { + fn test_commonerror_new_invalid_coord() { let coord = Coord::new(2, 2); let size: Size = Size::new(1, 1); - let error = Error::new_invalid_coord(coord, size); + let error = CommonError::new_invalid_coord(coord, size); assert_matches!( error, - Error::InvalidCoord { coord: c, size: s } if c == coord && s == size + CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == size ); } } diff --git a/tinlib/src/common/mod.rs b/tinlib/src/common/mod.rs index f407ef8..cf80ac2 100644 --- a/tinlib/src/common/mod.rs +++ b/tinlib/src/common/mod.rs @@ -4,5 +4,5 @@ mod error; mod size; pub use crate::common::coord::{Coord, CoordEnumerate, CoordEnumerateMut, CoordIter}; -pub use crate::common::error::{Error, Result}; +pub use crate::common::error::{CommonError, Result}; pub use crate::common::size::Size; diff --git a/tinlib/src/graphic/font.rs b/tinlib/src/graphic/font.rs index e14b90d..68a982a 100644 --- a/tinlib/src/graphic/font.rs +++ b/tinlib/src/graphic/font.rs @@ -2,7 +2,7 @@ use std::fmt; use std::slice; -use crate::common::{Error, Result}; +use crate::common::{CommonError, Result}; use crate::graphic::glyph::Glyph; /// Number of Glyphs in a Font. @@ -29,7 +29,7 @@ impl Font { /// Returns a glyph. pub fn get_glyph(&self, index: usize) -> Result { if !self.is_index_valid(index) { - return Err(Error::new_invalid_index(index, self.lenght())); + return Err(CommonError::new_invalid_index(index, self.lenght())); } Ok(self.glyphs[index]) @@ -38,7 +38,7 @@ impl Font { /// Sets a glyph. pub fn set_glyph(&mut self, index: usize, glyph: Glyph) -> Result<()> { if !self.is_index_valid(index) { - return Err(Error::new_invalid_index(index, self.lenght())); + return Err(CommonError::new_invalid_index(index, self.lenght())); } self.glyphs[index] = glyph; @@ -118,7 +118,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() + CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() ); } @@ -148,7 +148,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() + CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == font.lenght() ); } diff --git a/tinlib/src/graphic/glyph.rs b/tinlib/src/graphic/glyph.rs index 5fdd43d..77bf2f2 100644 --- a/tinlib/src/graphic/glyph.rs +++ b/tinlib/src/graphic/glyph.rs @@ -2,7 +2,9 @@ use std::fmt; use std::slice; -use crate::common::{Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Error, Result, Size}; +use crate::common::{ + CommonError, Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Result, Size, +}; /// The Glyph width. pub const GLYPH_WIDTH: usize = 8; @@ -52,7 +54,7 @@ impl Glyph { /// Returns a pixel. pub fn get_pixel(&self, coord: Coord) -> Result { if !self.is_coord_valid(coord) { - return Err(Error::new_invalid_coord(coord, self.size())); + return Err(CommonError::new_invalid_coord(coord, self.size())); } let index = self.get_index(coord); @@ -62,7 +64,7 @@ impl Glyph { /// Sets a pixel. pub fn set_pixel(&mut self, coord: Coord, value: GlyphPixel) -> Result<()> { if !self.is_coord_valid(coord) { - return Err(Error::new_invalid_coord(coord, self.size())); + return Err(CommonError::new_invalid_coord(coord, self.size())); } let index = self.get_index(coord); @@ -172,7 +174,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() + CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() ); } @@ -199,7 +201,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() + CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == glyph.size() ); } diff --git a/tinlib/src/graphic/palette.rs b/tinlib/src/graphic/palette.rs index 27b07ae..ac0b23f 100644 --- a/tinlib/src/graphic/palette.rs +++ b/tinlib/src/graphic/palette.rs @@ -2,7 +2,7 @@ use std::fmt; use std::slice; -use crate::common::{Error, Result}; +use crate::common::{CommonError, Result}; use crate::graphic::color::Color; /// Number of colors in a Palette. @@ -29,7 +29,7 @@ impl Palette { /// Returns a color. pub fn get_color(&self, index: usize) -> Result { if !self.is_index_valid(index) { - return Err(Error::new_invalid_index(index, self.lenght())); + return Err(CommonError::new_invalid_index(index, self.lenght())); } Ok(self.colors[index]) @@ -38,7 +38,7 @@ impl Palette { /// Sets a color. pub fn set_color(&mut self, index: usize, color: Color) -> Result<()> { if !self.is_index_valid(index) { - return Err(Error::new_invalid_index(index, self.lenght())); + return Err(CommonError::new_invalid_index(index, self.lenght())); } self.colors[index] = color; @@ -115,7 +115,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() + CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() ); } @@ -142,7 +142,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() + CommonError::InvalidIndex { index: i, lenght: l } if i == index && l == palette.lenght() ); } diff --git a/tinlib/src/lib.rs b/tinlib/src/lib.rs index ad7101a..734998b 100644 --- a/tinlib/src/lib.rs +++ b/tinlib/src/lib.rs @@ -1,5 +1,26 @@ +use std::result::Result as StdResult; + pub mod cartridge; pub mod common; pub mod graphic; pub mod machine; pub mod map; + +use thiserror::Error; + +use crate::cartridge::CartridgeError; +use crate::common::CommonError; + +/// Internal errors. +#[derive(Error, Debug)] +pub enum Error { + /// Error to wrap internal Cartridge errors. + #[error(transparent)] + Cartridge(#[from] CartridgeError), + /// Error to wrap internal Common errors. + #[error(transparent)] + Common(#[from] CommonError), +} + +/// Internal result. +pub type Result = StdResult; diff --git a/tinlib/src/machine/screen.rs b/tinlib/src/machine/screen.rs index 5f1b7c1..42f5f1a 100644 --- a/tinlib/src/machine/screen.rs +++ b/tinlib/src/machine/screen.rs @@ -2,7 +2,9 @@ use std::fmt; use std::slice; -use crate::common::{Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Error, Result, Size}; +use crate::common::{ + CommonError, Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Result, Size, +}; use crate::graphic::Color; /// Screen width in pixels. @@ -45,7 +47,7 @@ impl Screen { /// Returns a pixel. pub fn get_pixel(&self, coord: Coord) -> Result { if !self.is_coord_valid(coord) { - return Err(Error::new_invalid_coord(coord, self.size())); + return Err(CommonError::new_invalid_coord(coord, self.size())); } let index = self.get_index(coord); @@ -55,7 +57,7 @@ impl Screen { /// Sets a pixels. pub fn set_pixel(&mut self, coord: Coord, pixel: ScreenPixel) -> Result<()> { if !self.is_coord_valid(coord) { - return Err(Error::new_invalid_coord(coord, self.size())); + return Err(CommonError::new_invalid_coord(coord, self.size())); } let index = self.get_index(coord); @@ -166,7 +168,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() + CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() ); } @@ -195,7 +197,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() + CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == screen.size() ); } diff --git a/tinlib/src/map/mod.rs b/tinlib/src/map/mod.rs index fc8dc50..d7b07ae 100644 --- a/tinlib/src/map/mod.rs +++ b/tinlib/src/map/mod.rs @@ -2,7 +2,9 @@ use std::fmt; use std::slice; -use crate::common::{Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Error, Result, Size}; +use crate::common::{ + CommonError, Coord, CoordEnumerate, CoordEnumerateMut, CoordIter, Result, Size, +}; use crate::graphic::{Color, Glyph}; /// Map width in Glyphs. @@ -69,7 +71,7 @@ impl<'tile> Map<'tile> { /// Returns a tile. pub fn get_tile(&self, coord: Coord) -> Result>> { if !self.is_coord_valid(coord) { - return Err(Error::new_invalid_coord(coord, self.size())); + return Err(CommonError::new_invalid_coord(coord, self.size())); } let index = self.get_index(coord); @@ -79,7 +81,7 @@ impl<'tile> Map<'tile> { /// Sets a tile. pub fn set_tile(&mut self, coord: Coord, value: Tile<'tile>) -> Result<()> { if !self.is_coord_valid(coord) { - return Err(Error::new_invalid_coord(coord, self.size())); + return Err(CommonError::new_invalid_coord(coord, self.size())); } let index = self.get_index(coord); @@ -204,7 +206,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() + CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() ); } @@ -241,7 +243,7 @@ mod tests { assert!(result.is_err()); assert_matches!( result.unwrap_err(), - Error::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() + CommonError::InvalidCoord { coord: c, size: s } if c == coord && s == map.size() ); } From 8ef0e84f0a3c2c2ec99684d7db572a156d186ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Wed, 12 Aug 2020 15:29:21 -0300 Subject: [PATCH 10/14] Pushing files to not lose them --- tinlib/src/cartridge/chunk.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tinlib/src/cartridge/chunk.rs b/tinlib/src/cartridge/chunk.rs index a14d74c..23d3e0e 100644 --- a/tinlib/src/cartridge/chunk.rs +++ b/tinlib/src/cartridge/chunk.rs @@ -8,10 +8,11 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crate::cartridge::error::{CartridgeError, Result}; // Valid chunk sizes. +// TODO Use machine constants to give meaning to these guys. const END_CHUNK_VALID_SIZE: [usize; 1] = [0]; const COVER_CHUNK_VALID_SIZES: [usize; 2] = [0, 245760]; const FONT_CHUNK_VALID_SIZES: [usize; 2] = [0, 16384]; -const PALETTE_CHUNK_VALID_SIZES: [usize; 4] = [0, 4, 8, 16]; +const PALETTE_CHUNK_VALID_SIZES: [usize; 4] = [0, 12, 24, 48]; const CODE_CHUNK_MAX_SIZE: usize = 131072; const MAP_CHUNK_MAX_SIZE: usize = 122880; @@ -165,7 +166,7 @@ impl Chunk { } fn validate_end(&self) -> Result<()> { - if END_CHUNK_VALID_SIZE.contains(&self.data.len()) { + if !END_CHUNK_VALID_SIZE.contains(&self.data.len()) { return Err(CartridgeError::new_invalid_chunk_size( self.header.chunk_type, self.data.len(), @@ -177,7 +178,7 @@ impl Chunk { } fn validate_cover(&self) -> Result<()> { - if COVER_CHUNK_VALID_SIZES.contains(&self.data.len()) { + if !COVER_CHUNK_VALID_SIZES.contains(&self.data.len()) { return Err(CartridgeError::new_invalid_chunk_size( self.header.chunk_type, self.data.len(), @@ -189,7 +190,7 @@ impl Chunk { } fn validate_code(&self) -> Result<()> { - if self.data.len() <= CODE_CHUNK_MAX_SIZE { + if self.data.len() > CODE_CHUNK_MAX_SIZE { return Err(CartridgeError::new_invalid_chunk_max_size( self.header.chunk_type, self.data.len(), @@ -201,7 +202,7 @@ impl Chunk { } fn validate_font(&self) -> Result<()> { - if FONT_CHUNK_VALID_SIZES.contains(&self.data.len()) { + if !FONT_CHUNK_VALID_SIZES.contains(&self.data.len()) { return Err(CartridgeError::new_invalid_chunk_size( self.header.chunk_type, self.data.len(), @@ -213,7 +214,7 @@ impl Chunk { } fn validate_palette(&self) -> Result<()> { - if PALETTE_CHUNK_VALID_SIZES.contains(&self.data.len()) { + if !PALETTE_CHUNK_VALID_SIZES.contains(&self.data.len()) { return Err(CartridgeError::new_invalid_chunk_size( self.header.chunk_type, self.data.len(), @@ -225,7 +226,7 @@ impl Chunk { } fn validate_map(&self) -> Result<()> { - if self.data.len() <= MAP_CHUNK_MAX_SIZE { + if self.data.len() > MAP_CHUNK_MAX_SIZE { return Err(CartridgeError::new_invalid_chunk_max_size( self.header.chunk_type, self.data.len(), @@ -372,6 +373,7 @@ mod test { }; let result = Chunk::from_reader(&mut reader); + println!("{:?}", result); assert!(result.is_ok()); assert_eq!(result.unwrap(), expected); } From 891c54e76239912bd140872e4c3f1df7f169e2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Sat, 13 Jan 2024 14:49:21 -0300 Subject: [PATCH 11/14] chore: refactor ci config --- .github/workflows/ci.yml | 120 +++++++++++++++++++-------------------- .gitignore | 10 +++- devkit/Cargo.toml | 1 + tinlib/Cargo.toml | 1 + 4 files changed, 70 insertions(+), 62 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00ac620..799dfb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,108 +1,106 @@ -on: [push, pull_request] - name: Continuous Integration +on: + push: + branches: + - main + pull_request: + jobs: - check: - name: Check + fmt: + name: Fmt runs-on: ubuntu-latest - strategy: - matrix: - rust: - - stable - - beta - - nightly + steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - toolchain: ${{ matrix.rust }} - override: true + toolchain: stable + components: rustfmt - - name: Run cargo check - uses: actions-rs/cargo@v1 - with: - command: check + - name: Restore cache + uses: Swatinem/rust-cache@v1 - test: - name: Test Suite + - name: Run fmt + run: cargo fmt --all --check + + clippy: + name: Clippy runs-on: ubuntu-latest - strategy: - matrix: - rust: - - stable - - beta - - nightly + steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - toolchain: ${{ matrix.rust }} - override: true + toolchain: stable + components: clippy - - name: Run cargo test - uses: actions-rs/cargo@v1 - with: - command: test + - name: Restore cache + uses: Swatinem/rust-cache@v1 + + - name: Run clippy + run: cargo clippy --all-targets --all-features --locked -- -D warnings + + check: + name: Check - fmt: - name: Rustfmt - runs-on: ubuntu-latest strategy: + fail-fast: false matrix: rust: - stable - beta - nightly + os: + - ubuntu-latest + - macos-latest + - windows-latest + + runs-on: ${{ matrix.os }} + steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.rust }} - override: true - - name: Install rustfmt - run: rustup component add rustfmt + - name: Run check + run: cargo check --all-targets --locked - - name: Run cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + test: + name: Test - clippy: - name: Clippy - runs-on: ubuntu-latest strategy: + fail-fast: false matrix: rust: - stable - beta - nightly + os: + - ubuntu-latest + - macos-latest + - windows-latest + + runs-on: ${{ matrix.os }} + steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.rust }} - override: true - - name: Install clippy - run: rustup component add clippy - - - name: Run cargo clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -- -D warnings + - name: Run test + run: cargo test --all-targets --locked diff --git a/.gitignore b/.gitignore index eccd7b4..73fab07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,10 @@ -/target/ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt **/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/devkit/Cargo.toml b/devkit/Cargo.toml index 64cd427..689c3e4 100644 --- a/devkit/Cargo.toml +++ b/devkit/Cargo.toml @@ -11,6 +11,7 @@ categories = ["games", "game-development", "game-engines"] license = "MIT" readme = "README.md" edition = "2021" +rust-version = "1.60.0" [dependencies] tinlib = { version = "0.1.0", path = "../tinlib" } diff --git a/tinlib/Cargo.toml b/tinlib/Cargo.toml index adc173b..22287db 100644 --- a/tinlib/Cargo.toml +++ b/tinlib/Cargo.toml @@ -11,6 +11,7 @@ categories = ["games", "game-development", "game-engines"] license = "MIT" readme = "README.md" edition = "2021" +rust-version = "1.60.0" [dependencies] byteorder = "^1.5" From d9dbf20b0cc2f3a87a2c083366ca0dc6574bb8a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Sat, 13 Jan 2024 15:55:18 -0300 Subject: [PATCH 12/14] chore(tools): add git-cliff and update pre-commit --- .pre-commit-config.yaml | 45 ++++++++++++++++++---------- CHANGELOG.md | 10 +++++++ cliff.toml | 66 +++++++++++++++++++++++++++++++++++++++++ devkit/README.md | 2 +- tinlib/README.md | 2 +- 5 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 cliff.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f29c64a..ef34ef1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,33 @@ -fail_fast: true - repos: - - repo: local + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-executables-have-shebangs + - id: check-json + - id: check-shebang-scripts-are-executable + - id: check-merge-conflict + - id: check-symlinks + - id: check-toml + - id: check-yaml + - id: detect-private-key + - id: end-of-file-fixer + - id: forbid-submodules + - id: mixed-line-ending + - id: mixed-line-ending + - id: no-commit-to-branch + - id: trailing-whitespace + + - repo: https://github.com/compilerla/conventional-pre-commit + rev: v3.1.0 hooks: - - id: fmt - name: fmt - language: system - files: '[.]rs$' - entry: cargo fmt - pass_filenames: false + - id: conventional-pre-commit + stages: [commit-msg] - - id: clippy - name: clippy - language: system - files: '[.]rs$' - entry: cargo clippy - pass_filenames: false + - repo: https://github.com/doublify/pre-commit-rust + rev: v1.0 + hooks: + - id: fmt + - id: cargo-check + args: ['--all-targets', '--locked'] + - id: clippy + args: ['--all-targets', '--all-features', '--locked', '--', '-D', 'warnings'] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..88e0ac3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +--- +## [unreleased] + +### Miscellaneous Chores + +- refactor ci config - ([891c54e](https://github.com/cocogitto/cocogitto/commit/891c54e76239912bd140872e4c3f1df7f169e2e2)) - Luiz F. A. de Prá + + diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..77d4353 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,66 @@ +[changelog] +header = """ +# Changelog\n +""" +body = """ +--- +{% if version %}\ + {% if previous.version %}\ + ## [{{ version | trim_start_matches(pat="v") }}]($REPO/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} + {% else %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} + {% endif %}\ +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits + | filter(attribute="scope") + | sort(attribute="scope") %} + - **({{commit.scope}})**{% if commit.breaking %} [**breaking**]{% endif %} \ + {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} + {%- endfor -%} + {% raw %}\n{% endraw %}\ + {%- for commit in commits %} + {%- if commit.scope -%} + {% else -%} + - {% if commit.breaking %} [**breaking**]{% endif %}\ + {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} + {% endif -%} + {% endfor -%} +{% endfor %}\n +""" +footer = """ + +""" +trim = true +postprocessors = [ + { pattern = '\$REPO', replace = "https://github.com/cocogitto/cocogitto" }, # replace repository URL +] + +[git] +conventional_commits = true +filter_unconventional = false +split_commits = false +commit_preprocessors = [] +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^doc", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Refactoring" }, + { message = "^style", group = "Style" }, + { message = "^revert", group = "Revert" }, + { message = "^test", group = "Tests" }, + { message = "^chore\\(version\\):", skip = true }, + { message = "^chore", group = "Miscellaneous Chores" }, + { body = ".*security", group = "Security" }, +] +protect_breaking_commits = false +filter_commits = false +tag_pattern = "v[0-9].*" +skip_tags = "v0.1.0-beta.1" +ignore_tags = "" +topo_order = false +sort_commits = "oldest" diff --git a/devkit/README.md b/devkit/README.md index effe45d..a6181d7 100644 --- a/devkit/README.md +++ b/devkit/README.md @@ -1,3 +1,3 @@ # SN-50 DevKit -The SN-50 Fantasy Computer with DevKit tools. \ No newline at end of file +The SN-50 Fantasy Computer with DevKit tools. diff --git a/tinlib/README.md b/tinlib/README.md index 50029f1..b278d5c 100644 --- a/tinlib/README.md +++ b/tinlib/README.md @@ -1,3 +1,3 @@ # tinlib -Components for SN-50 Fantasy Computer implementations. \ No newline at end of file +Components for SN-50 Fantasy Computer implementations. From cd89806a53c636354afd73d23f05d90c621115bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Sat, 13 Jan 2024 16:27:18 -0300 Subject: [PATCH 13/14] chore(tools): add makefile and fix clippy issues --- .editorconfig | 14 ++++++++++---- Makefile | 36 +++++++++++++++++++++++++++++++++++ tinlib/examples/cartridge.rs | 29 ++++++++++++++-------------- tinlib/src/cartridge/mod.rs | 10 ++++++---- tinlib/src/graphic/font.rs | 1 - tinlib/src/graphic/glyph.rs | 1 - tinlib/src/graphic/palette.rs | 1 - tinlib/src/machine/screen.rs | 1 - tinlib/src/map/mod.rs | 1 - 9 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 Makefile diff --git a/.editorconfig b/.editorconfig index 9ba8519..d289d29 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,12 +5,18 @@ end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -indent_style = space -indent_size = 4 + +[*.rs] +max_line_length = 120 [*.md] trim_trailing_whitespace = false +indent_style = space +indent_size = 2 -[*.{yml,yaml}] +[*.{json,yml,yaml}] +indent_style = space indent_size = 2 -trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..24513f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +.PHONY: all install pre-commit check check-fmt check-clippy fix fix-fmt fix-clippy test test-cov build + +all: install fix test build + +install: + cargo install git-cliff --locked + cargo install cargo-tarpaulin --locked + pre-commit install + +pre-commit: + pre-commit run --all --verbose + +check: check-fmt check-clippy + +check-fmt: + cargo fmt --all --check + +check-clippy: + cargo clippy --all-targets --all-features --locked -- -D warnings + +fix: fix-fmt fix-clippy + +fix-fmt: + cargo fmt --all + +fix-clippy: + cargo clippy --all-targets --all-features --fix --locked + +test: + cargo test --all-targets --locked + +test-cov: + cargo tarpaulin --all-targets --locked + +build: + cargo build --all-targets --locked diff --git a/tinlib/examples/cartridge.rs b/tinlib/examples/cartridge.rs index 8678831..3e38f45 100644 --- a/tinlib/examples/cartridge.rs +++ b/tinlib/examples/cartridge.rs @@ -3,20 +3,21 @@ use std::io::Cursor; use tinlib::cartridge::Cartridge; fn main() { - let mut cart = Cartridge::default(); - - // An incomplete game cart with empty fonts, map and cover. - cart.version = 17; - cart.name = "Dungeons of the Dungeon".to_string(); - cart.desc = "A cool game about dungeons inside dungeons.".to_string(); - cart.author = "Luiz de Prá".to_string(); - cart.palette = vec![ - 0x2d, 0x1b, 0x000, // dark - 0x1e, 0x60, 0x6e, // dark greenish - 0x5a, 0xb9, 0xa8, // greenish - 0xc4, 0xf0, 0xc2, // light greenish - ]; - cart.code = "def main:\n pass".to_string(); + let cart = Cartridge { + // An incomplete game cart with empty fonts, map and cover. + version: 17, + name: "Dungeons of the Dungeon".to_string(), + desc: "A cool game about dungeons inside dungeons.".to_string(), + author: "Luiz de Prá".to_string(), + palette: vec![ + 0x2d, 0x1b, 0x000, // dark + 0x1e, 0x60, 0x6e, // dark greenish + 0x5a, 0xb9, 0xa8, // greenish + 0xc4, 0xf0, 0xc2, // light greenish + ], + code: "def main:\n pass".to_string(), + ..Default::default() + }; println!("Pre-save Cart: {:?}\n\n", &cart); diff --git a/tinlib/src/cartridge/mod.rs b/tinlib/src/cartridge/mod.rs index 4f2cade..9ae95dc 100644 --- a/tinlib/src/cartridge/mod.rs +++ b/tinlib/src/cartridge/mod.rs @@ -130,10 +130,12 @@ impl Cartridge { } pub fn save(&self, writer: &mut W) -> Result<()> { - let mut header = CartridgeHeader::default(); - header.name_size = self.name.len() as u8; - header.desc_size = self.desc.len() as u16; - header.author_size = self.author.len() as u8; + let header = CartridgeHeader { + name_size: self.name.len() as u8, + desc_size: self.desc.len() as u16, + author_size: self.author.len() as u8, + ..Default::default() + }; header.save(writer)?; writer.write_u8(self.version)?; diff --git a/tinlib/src/graphic/font.rs b/tinlib/src/graphic/font.rs index 68a982a..2d6eff5 100644 --- a/tinlib/src/graphic/font.rs +++ b/tinlib/src/graphic/font.rs @@ -132,7 +132,6 @@ mod tests { let result = font.set_glyph(0, new_glyph); assert!(result.is_ok()); - assert_eq!(result.unwrap(), ()); let result = font.get_glyph(0); assert_eq!(result.unwrap(), new_glyph); diff --git a/tinlib/src/graphic/glyph.rs b/tinlib/src/graphic/glyph.rs index 77bf2f2..00a1b40 100644 --- a/tinlib/src/graphic/glyph.rs +++ b/tinlib/src/graphic/glyph.rs @@ -185,7 +185,6 @@ mod tests { let result = glyph.set_pixel(coord, GlyphPixel::Solid); assert!(result.is_ok()); - assert_eq!(result.unwrap(), ()); let result = glyph.get_pixel(coord); assert!(result.is_ok()); diff --git a/tinlib/src/graphic/palette.rs b/tinlib/src/graphic/palette.rs index ac0b23f..a0f2bce 100644 --- a/tinlib/src/graphic/palette.rs +++ b/tinlib/src/graphic/palette.rs @@ -126,7 +126,6 @@ mod tests { let result = palette.set_color(0, color); assert!(result.is_ok()); - assert_eq!(result.unwrap(), ()); let result = palette.get_color(0); assert_eq!(result.unwrap(), color); diff --git a/tinlib/src/machine/screen.rs b/tinlib/src/machine/screen.rs index 42f5f1a..7265bba 100644 --- a/tinlib/src/machine/screen.rs +++ b/tinlib/src/machine/screen.rs @@ -180,7 +180,6 @@ mod tests { let result = screen.set_pixel(coord, pixel); assert!(result.is_ok()); - assert_eq!(result.unwrap(), ()); let result = screen.get_pixel(coord); assert!(result.is_ok()); diff --git a/tinlib/src/map/mod.rs b/tinlib/src/map/mod.rs index d7b07ae..f6b4a03 100644 --- a/tinlib/src/map/mod.rs +++ b/tinlib/src/map/mod.rs @@ -221,7 +221,6 @@ mod tests { let result = map.set_tile(coord, tile); assert!(result.is_ok()); - assert_eq!(result.unwrap(), ()); let result = map.get_tile(coord); assert!(result.is_ok()); From 4dee9b0e2db040d7b561e407b03408331e5661db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20F=2E=20A=2E=20de=20Pr=C3=A1?= Date: Sun, 14 Jan 2024 14:45:09 -0300 Subject: [PATCH 14/14] tests: fix cartridge tests --- tinlib/src/cartridge/mod.rs | 122 ++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/tinlib/src/cartridge/mod.rs b/tinlib/src/cartridge/mod.rs index 9ae95dc..eb30a43 100644 --- a/tinlib/src/cartridge/mod.rs +++ b/tinlib/src/cartridge/mod.rs @@ -145,10 +145,10 @@ impl Cartridge { let chunks = vec![ (self.cover.clone(), ChunkType::Cover), + (self.code.as_bytes().to_vec(), ChunkType::Code), (self.font.clone(), ChunkType::Font), (self.palette.clone(), ChunkType::Palette), (self.map.clone(), ChunkType::Map), - (self.code.as_bytes().to_vec(), ChunkType::Code), ]; for (data, chunk_type) in chunks.into_iter().filter(|(d, _)| !d.is_empty()) { @@ -254,45 +254,66 @@ mod test_super { #[test] fn test_cartridge_from_reader() { - let mut reader = Cursor::new(vec![ - // header + let mut data = vec![ + // cart header 1, // cart version 10, // name size 11, 0, // desc size 2, // author size - // cart - 11, // version + ]; + + // cart data + data.extend_from_slice(&[ + 11, // cart version 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, // name 100, 101, 115, 99, 114, 105, 195, 167, 195, 163, 111, // desc 109, 101, // author - // code + ]); + + // code chunk + data.extend_from_slice(&[ 2, 6, 0, 0, 0, // header 109, 97, 105, 110, 40, 41, // data - // map - 5, 4, 0, 0, 0, // header - 0, 1, 2, 3, // data - // font - 3, 2, 0, 0, 0, // header - 1, 2, // data - // cover - 1, 4, 0, 0, 0, // header - 1, 2, 3, 4, // data - // palette - 4, 3, 0, 0, 0, // header - 0, 0, 0, // data - // end + ]); + + // map chunk + data.extend_from_slice(&[ + 5, 0, 0, 0, 0, // header + ]); + + // font chunk + data.extend_from_slice(&[ + 3, 0, 64, 0, 0, // header + ]); + data.extend_from_slice(&[0; 16384]); + + // cover chunk + data.extend_from_slice(&[ + 1, 0, 0, 0, 0, // header + ]); + + // palette chunk + data.extend_from_slice(&[ + 4, 12, 0, 0, 0, // header + 0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90, // data + ]); + + // end chunk + data.extend_from_slice(&[ 0, 0, 0, 0, 0, // ignored - 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, // junk data ]); + + let mut reader = Cursor::new(data); let expected = Cartridge { version: 11, name: "thisisname".to_string(), desc: "descrição".to_string(), author: "me".to_string(), - cover: vec![1, 2, 3, 4], - font: vec![1, 2], - palette: vec![0, 0, 0], - map: vec![0, 1, 2, 3], + cover: vec![], + font: vec![0; 16384], + palette: vec![0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90], + map: vec![], code: "main()".to_string(), }; @@ -370,20 +391,53 @@ mod test_super { name: "thisisname".to_string(), desc: "descrição".to_string(), author: "me".to_string(), - cover: vec![1, 2, 3, 4], - font: vec![1, 2], - palette: vec![0, 0, 0], - map: vec![0, 1, 2, 3], + cover: vec![], + font: vec![0; 16384], + palette: vec![0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90], + map: vec![], code: "main()".to_string(), }; - let expected: Vec = vec![ - 1, 10, 11, 0, 2, 11, 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, 100, 101, 115, - 99, 114, 105, 195, 167, 195, 163, 111, 109, 101, 1, 4, 0, 0, 0, 1, 2, 3, 4, 3, 2, 0, 0, - 0, 1, 2, 4, 3, 0, 0, 0, 0, 0, 0, 5, 4, 0, 0, 0, 0, 1, 2, 3, 2, 6, 0, 0, 0, 109, 97, - 105, 110, 40, 41, 0, 0, 0, 0, 0, + + let mut expected = vec![ + // cart header + 1, // cart version + 10, // name size + 11, 0, // desc size + 2, // author size ]; - let mut writer = Cursor::new(vec![0u8; 5]); + // cart data + expected.extend_from_slice(&[ + 11, // cart version + 116, 104, 105, 115, 105, 115, 110, 97, 109, 101, // name + 100, 101, 115, 99, 114, 105, 195, 167, 195, 163, 111, // desc + 109, 101, // author + ]); + + // code chunk + expected.extend_from_slice(&[ + 2, 6, 0, 0, 0, // header + 109, 97, 105, 110, 40, 41, // data + ]); + + // font chunk + expected.extend_from_slice(&[ + 3, 0, 64, 0, 0, // header + ]); + expected.extend_from_slice(&[0; 16384]); + + // palette chunk + expected.extend_from_slice(&[ + 4, 12, 0, 0, 0, // header + 0, 0, 0, 255, 255, 255, 180, 180, 180, 90, 90, 90, // data + ]); + + // end chunk + expected.extend_from_slice(&[ + 0, 0, 0, 0, 0, // ignored + ]); + + let mut writer = Cursor::new(vec![0u8; expected.len()]); let result = cart.save(&mut writer); assert!(result.is_ok()); assert_eq!(writer.get_ref(), &expected);