chunk type implemented (also first commit)
This commit is contained in:
commit
823480491a
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "pngtoto"
|
||||
version = "0.1.0"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "pngtoto"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
0
src/args.rs
Normal file
0
src/args.rs
Normal file
0
src/chunk.rs
Normal file
0
src/chunk.rs
Normal file
256
src/chunk_type.rs
Normal file
256
src/chunk_type.rs
Normal file
@ -0,0 +1,256 @@
|
||||
use core::str;
|
||||
use std::{error::Error, fmt::{write, Display}, str::FromStr, u8};
|
||||
|
||||
fn byte_to_bits(byte: u8) -> [u8; 8] {
|
||||
let mut bits = [0u8; 8];
|
||||
|
||||
for i in 0..=7 {
|
||||
let shifted_byte = byte >> i;
|
||||
let cur_bit = shifted_byte & 1;
|
||||
bits[7 - i] = cur_bit;
|
||||
}
|
||||
|
||||
bits
|
||||
}
|
||||
|
||||
/// ChunkType is defined as a 4 byte string with the 5th
|
||||
/// bit of every byte having a special meaning, thus being
|
||||
/// the "property bit". I have named the bytes with their
|
||||
/// property bit name.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct ChunkType {
|
||||
ancillary_byte: u8,
|
||||
private_byte: u8,
|
||||
reserved_byte: u8,
|
||||
safe_to_copy_byte: u8,
|
||||
}
|
||||
|
||||
impl ChunkType {
|
||||
|
||||
fn bytes(&self) -> [u8; 4] {
|
||||
let bytes: [u8; 4] = [
|
||||
self.ancillary_byte,
|
||||
self.private_byte,
|
||||
self.reserved_byte,
|
||||
self.safe_to_copy_byte
|
||||
];
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
let mut x = self.bytes().len();
|
||||
|
||||
loop {
|
||||
x -= 1;
|
||||
|
||||
if x == 2 {
|
||||
if self.bytes()[x].is_ascii_lowercase() {
|
||||
break false;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if !self.bytes()[x].is_ascii_alphabetic() {
|
||||
break false;
|
||||
} else if x == 0 {
|
||||
break true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fn is_critical(&self) -> bool {
|
||||
self.ancillary_byte.is_ascii_uppercase()
|
||||
}
|
||||
|
||||
fn is_public(&self) -> bool {
|
||||
self.private_byte.is_ascii_uppercase()
|
||||
|
||||
}
|
||||
|
||||
fn is_reserved_bit_valid(&self) -> bool {
|
||||
self.reserved_byte.is_ascii_uppercase()
|
||||
|
||||
}
|
||||
|
||||
fn is_safe_to_copy(&self) -> bool {
|
||||
self.safe_to_copy_byte.is_ascii_lowercase()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl TryFrom<[u8; 4]> for ChunkType {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
|
||||
|
||||
let chunk_type = ChunkType {
|
||||
ancillary_byte: value[0],
|
||||
private_byte: value[1],
|
||||
reserved_byte: value[2],
|
||||
safe_to_copy_byte: value[3],
|
||||
};
|
||||
|
||||
let mut x = chunk_type.bytes().len();
|
||||
|
||||
loop {
|
||||
x -= 1;
|
||||
|
||||
if !chunk_type.bytes()[x].is_ascii_alphabetic() {
|
||||
break Err("The chunk type can't contain non-alphabetic bytes");
|
||||
} else if x == 0 {
|
||||
break Ok(chunk_type);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ChunkType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let buf = self.bytes();
|
||||
|
||||
let s = match str::from_utf8(&buf) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("Invalid utf8 sequence: {}", e),
|
||||
};
|
||||
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl FromStr for ChunkType {
|
||||
type Err = Box<dyn std::error::Error>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
let chunk_type = ChunkType {
|
||||
ancillary_byte: bytes[0],
|
||||
private_byte: bytes[1],
|
||||
reserved_byte: bytes[2],
|
||||
safe_to_copy_byte: bytes[3],
|
||||
};
|
||||
|
||||
let mut x = chunk_type.bytes().len();
|
||||
|
||||
loop {
|
||||
x -= 1;
|
||||
|
||||
if !chunk_type.bytes()[x].is_ascii_alphabetic() {
|
||||
break Err("The chunk type can't contain non-alphabetic bytes".into());
|
||||
} else if x == 0 {
|
||||
break Ok(chunk_type);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// unit tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_from_bytes() {
|
||||
let expected = [82, 117, 83, 116];
|
||||
let actual = ChunkType::try_from([82, 117, 83, 116]).unwrap();
|
||||
|
||||
assert_eq!(expected, actual.bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_from_str() {
|
||||
let expected = ChunkType::try_from([82, 117, 83, 116]).unwrap();
|
||||
let actual = ChunkType::from_str("RuSt").unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_critical() {
|
||||
let chunk = ChunkType::from_str("RuSt").unwrap();
|
||||
assert!(chunk.is_critical());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_not_critical() {
|
||||
let chunk = ChunkType::from_str("ruSt").unwrap();
|
||||
assert!(!chunk.is_critical());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_public() {
|
||||
let chunk = ChunkType::from_str("RUSt").unwrap();
|
||||
assert!(chunk.is_public());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_not_public() {
|
||||
let chunk = ChunkType::from_str("RuSt").unwrap();
|
||||
assert!(!chunk.is_public());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_reserved_bit_valid() {
|
||||
let chunk = ChunkType::from_str("RuSt").unwrap();
|
||||
assert!(chunk.is_reserved_bit_valid());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_reserved_bit_invalid() {
|
||||
let chunk = ChunkType::from_str("Rust").unwrap();
|
||||
assert!(!chunk.is_reserved_bit_valid());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_safe_to_copy() {
|
||||
let chunk = ChunkType::from_str("RuSt").unwrap();
|
||||
assert!(chunk.is_safe_to_copy());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_is_unsafe_to_copy() {
|
||||
let chunk = ChunkType::from_str("RuST").unwrap();
|
||||
assert!(!chunk.is_safe_to_copy());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_valid_chunk_is_valid() {
|
||||
let chunk = ChunkType::from_str("RuSt").unwrap();
|
||||
assert!(chunk.is_valid());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_invalid_chunk_is_valid() {
|
||||
let chunk = ChunkType::from_str("Rust").unwrap();
|
||||
assert!(!chunk.is_valid());
|
||||
|
||||
let chunk = ChunkType::from_str("Ru1t");
|
||||
assert!(chunk.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_string() {
|
||||
let chunk = ChunkType::from_str("RuSt").unwrap();
|
||||
assert_eq!(&chunk.to_string(), "RuSt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_chunk_type_trait_impls() {
|
||||
let chunk_type_1: ChunkType = TryFrom::try_from([82, 117, 83, 116]).unwrap();
|
||||
let chunk_type_2: ChunkType = FromStr::from_str("RuSt").unwrap();
|
||||
let _chunk_string = format!("{}", chunk_type_1);
|
||||
let _are_chunks_equal = chunk_type_1 == chunk_type_2;
|
||||
}
|
||||
}
|
0
src/commands.rs
Normal file
0
src/commands.rs
Normal file
12
src/main.rs
Normal file
12
src/main.rs
Normal file
@ -0,0 +1,12 @@
|
||||
mod args;
|
||||
mod chunk;
|
||||
mod chunk_type;
|
||||
mod commands;
|
||||
mod png;
|
||||
|
||||
pub type Error = Box<dyn std::error::Error>;
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
todo!()
|
||||
}
|
0
src/png.rs
Normal file
0
src/png.rs
Normal file
Loading…
Reference in New Issue
Block a user