aquatic/aquatic_ws_protocol/src/serde_helpers.rs
2020-08-12 05:14:27 +02:00

170 lines
No EOL
4.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use serde::{Serializer, Deserializer, de::Visitor};
use super::{AnnounceAction, ScrapeAction};
pub struct AnnounceActionVisitor;
impl<'de> Visitor<'de> for AnnounceActionVisitor {
type Value = AnnounceAction;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("string with value 'announce'")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where E: ::serde::de::Error, {
if v == "announce" {
Ok(AnnounceAction)
} else {
Err(E::custom("value is not 'announce'"))
}
}
}
pub struct ScrapeActionVisitor;
impl<'de> Visitor<'de> for ScrapeActionVisitor {
type Value = ScrapeAction;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("string with value 'scrape'")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where E: ::serde::de::Error, {
if v == "scrape" {
Ok(ScrapeAction)
} else {
Err(E::custom("value is not 'scrape'"))
}
}
}
pub fn serialize_20_bytes<S>(
data: &[u8; 20],
serializer: S
) -> Result<S::Ok, S::Error> where S: Serializer {
let text: String = data.iter().map(|byte| char::from(*byte)).collect();
serializer.serialize_str(&text)
}
struct TwentyByteVisitor;
impl<'de> Visitor<'de> for TwentyByteVisitor {
type Value = [u8; 20];
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("string consisting of 20 bytes")
}
#[inline]
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: ::serde::de::Error,
{
// Value is encoded in nodejs reference client something as follows:
// ```
// var infoHash = 'abcd..'; // 40 hexadecimals
// Buffer.from(infoHash, 'hex').toString('binary');
// ```
// As I understand it:
// - the code above produces a UTF16 string of 20 chars, each having
// only the "low byte" set (e.g., numeric value ranges from 0-255)
// - serde_json decodes this to string of 20 chars (tested), each in
// the aforementioned range (tested), so the bytes can be extracted
// by casting each char to u8.
let mut arr = [0u8; 20];
let mut char_iter = value.chars();
for a in arr.iter_mut(){
if let Some(c) = char_iter.next(){
if c as u32 > 255 {
return Err(E::custom(format!(
"character not in single byte range: {:#?}",
c
)));
}
*a = c as u8;
} else {
return Err(E::custom(format!("not 20 bytes: {:#?}", value)));
}
}
Ok(arr)
}
}
#[inline]
pub fn deserialize_20_bytes<'de, D>(
deserializer: D
) -> Result<[u8; 20], D::Error>
where D: Deserializer<'de>
{
deserializer.deserialize_any(TwentyByteVisitor)
}
#[cfg(test)]
mod tests {
use quickcheck_macros::quickcheck;
use crate::InfoHash;
fn info_hash_from_bytes(bytes: &[u8]) -> InfoHash {
let mut arr = [0u8; 20];
assert!(bytes.len() == 20);
arr.copy_from_slice(&bytes[..]);
InfoHash(arr)
}
#[test]
fn test_deserialize_20_bytes(){
let mut input = r#""aaaabbbbccccddddeeee""#.to_string();
let expected = info_hash_from_bytes(b"aaaabbbbccccddddeeee");
let observed: InfoHash = ::simd_json::serde::from_str(&mut input).unwrap();
assert_eq!(observed, expected);
let mut input = r#""aaaabbbbccccddddeee""#.to_string();
let res_info_hash: Result<InfoHash, _> = ::simd_json::serde::from_str(&mut input);
assert!(res_info_hash.is_err());
let mut input = r#""aaaabbbbccccddddeee𝕊""#.to_string();
let res_info_hash: Result<InfoHash, _> = ::simd_json::serde::from_str(&mut input);
assert!(res_info_hash.is_err());
}
#[test]
fn test_serde_20_bytes(){
let info_hash = info_hash_from_bytes(b"aaaabbbbccccddddeeee");
let mut out = ::simd_json::serde::to_string(&info_hash).unwrap();
let info_hash_2 = ::simd_json::serde::from_str(&mut out).unwrap();
assert_eq!(info_hash, info_hash_2);
}
#[quickcheck]
fn quickcheck_serde_20_bytes(info_hash: InfoHash) -> bool {
let mut out = ::simd_json::serde::to_string(&info_hash).unwrap();
let info_hash_2 = ::simd_json::serde::from_str(&mut out).unwrap();
info_hash == info_hash_2
}
}