diff --git a/server/texture-sync-server/Cargo.toml b/server/texture-sync-server/Cargo.toml index 6259ae4..a0f5ebd 100644 --- a/server/texture-sync-server/Cargo.toml +++ b/server/texture-sync-server/Cargo.toml @@ -6,6 +6,6 @@ edition = "2018" [dependencies] image = "0.21.1" -serde = "1.0.90" +serde = { version = "1.0.90", features = ["derive"] } serde_json = "1.0.39" lovecraft = "0.2.0" \ No newline at end of file diff --git a/server/texture-sync-server/src/model/mod.rs b/server/texture-sync-server/src/model/mod.rs index 133fd04..4737e34 100644 --- a/server/texture-sync-server/src/model/mod.rs +++ b/server/texture-sync-server/src/model/mod.rs @@ -3,8 +3,13 @@ #![allow(unused_variables)] #![allow(dead_code)] -pub struct Sha256(pub [u8; 64]); +mod sha256; +pub use sha256::Sha256; +mod texture_format; +pub use texture_format::TextureFormat; + +#[derive(Eq, PartialEq, Clone, Serialize, Deserialize, Debug)] pub struct Texture { pub id: String, pub name: String, @@ -13,8 +18,3 @@ pub struct Texture { pub resolution: (usize, usize), pub texture_hash: Sha256, } - -pub enum TextureFormat { - PNG, - JPEG, -} \ No newline at end of file diff --git a/server/texture-sync-server/src/model/sha256.rs b/server/texture-sync-server/src/model/sha256.rs new file mode 100644 index 0000000..7397836 --- /dev/null +++ b/server/texture-sync-server/src/model/sha256.rs @@ -0,0 +1,88 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] +pub struct Sha256(#[serde(serialize_with = "as_hex", deserialize_with = "from_hex")] [u8; 32]); + +fn as_hex(hash: &[u8; 32], serializer: S) -> Result +where + S: Serializer, +{ + use std::fmt::*; + + let mut hex_string = String::new(); + for digit in hash { + write!(hex_string, "{:02X}", digit).unwrap(); + } + + serializer.serialize_str(&hex_string) +} + +fn from_hex<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> +where + D: Deserializer<'de>, +{ + use serde::de::Error; + + fn hex2bytes(s: &str) -> Option<[u8; 32]> { + if s.len() != 32 * 2 { + return None; // String has wrong length + } + + let mut out = [0u8; 32]; + for (i, byte) in out.iter_mut().enumerate() { + let string_index = i * 2; + *byte = u8::from_str_radix(&s[string_index..=string_index + 1], 16).ok()?; + } + + return Some(out); + } + + String::deserialize(deserializer).and_then(|string| { + hex2bytes(&string).ok_or_else(|| Error::custom("Invalid HEX String!".to_string())) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + const HASH_BYTES: [u8; 32] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, + ]; + + const HASH_STRING: &'static str = + r#""000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F""#; + + #[test] + fn serialize() { + let hash = serde_json::to_string_pretty(&Sha256(HASH_BYTES)).unwrap(); + assert_eq!(&hash, HASH_STRING); + } + + #[test] + fn deserialize() { + let hash: Sha256 = serde_json::from_str(HASH_STRING).unwrap(); + assert_eq!(hash, Sha256(HASH_BYTES)) + } + + #[test] + fn fail_deserialize() { + // too short. + assert_eq!( + serde_json::from_str::<'_, Sha256>(r#""0102013""#).is_err(), + true + ); + // wrong radix + assert_eq!( + serde_json::from_str::<'_, Sha256>(r#""Hallo Welt""#).is_err(), + true + ); + // too long. + assert_eq!( + serde_json::from_str::<'_, Sha256>( r#""000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F""#).is_err(), + true + ); + } +} diff --git a/server/texture-sync-server/src/model/texture_format.rs b/server/texture-sync-server/src/model/texture_format.rs new file mode 100644 index 0000000..98d342a --- /dev/null +++ b/server/texture-sync-server/src/model/texture_format.rs @@ -0,0 +1,50 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] +pub enum TextureFormat { + #[serde(rename = "png")] + PNG, + #[serde(rename = "jpeg")] + JPEG, +} + +#[cfg(test)] +mod tests { + // Lol, I thought we would need custom code, like for Sha256, but it works out of the box :D + // Anyhow, left the Test in. + + use super::*; + use serde_json; + + #[test] + fn serialize() { + let format = serde_json::to_string_pretty(&TextureFormat::PNG).unwrap(); + assert_eq!(&format, r#""png""#); + + let format = serde_json::to_string_pretty(&TextureFormat::JPEG).unwrap(); + assert_eq!(&format, r#""jpeg""#); + } + + #[test] + fn deserialize() { + let format: TextureFormat = serde_json::from_str(r#""png""#).unwrap(); + assert_eq!(format, TextureFormat::PNG); + + let format: TextureFormat = serde_json::from_str(r#""jpeg""#).unwrap(); + assert_eq!(format, TextureFormat::JPEG); + } + + #[test] + fn fail_deserialize() { + assert_eq!( + serde_json::from_str::<'_, TextureFormat>(r#""notafmt""#).is_err(), + true + ); + + // Format must be lowercase! + assert_eq!( + serde_json::from_str::<'_, TextureFormat>(r#""PNG""#).is_err(), + true + ); + } +}