static BASE16_HEXA_DECIMAL: u8 = 16; static BASE16_HEXA_DECIMAL_POSSIBLE_VALUE_PER_DIGIT: u8 = 15; static BASE16_HEXA_DECIMAL_DIGIT_BITS: u8 = 4; pub trait EnCodable { fn encode(&self) -> String; } pub trait DeCodable { fn decode(&self) -> Result<Vec<u8>, ()>; } impl EnCodable for [u8] { fn encode(self: &[u8]) -> String { let mut result = String::with_capacity(self.len()); let result_vec = unsafe { result.as_mut_vec() }; self.iter().for_each(|byte| { if !matches!(byte, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'-' | b'_' | b'.' | b'~') { result_vec.push(b'%'); result_vec.push(hex_to_digit(byte >> BASE16_HEXA_DECIMAL_DIGIT_BITS)); result_vec.push(hex_to_digit( byte & BASE16_HEXA_DECIMAL_POSSIBLE_VALUE_PER_DIGIT, )); } else { result_vec.push(*byte) } }); result } } impl DeCodable for &str { fn decode(&self) -> Result<Vec<u8>, ()> { let mut first = true; let mut result = Vec::with_capacity(self.len()); for i in self.split('%') { if first { first = false; result.extend_from_slice(i.as_bytes()); continue; } let Ok(char) = u8::from_str_radix(i[0..2].as_ref(), BASE16_HEXA_DECIMAL.into()) else { return Err(()); }; result.push(char); result.extend_from_slice(i[2..].as_bytes()); } Ok(result) } } fn hex_to_digit(digit: u8) -> u8 { match digit { 0..=9 => b'0' + digit, 10..=255 => b'A' + digit - 10, } } #[cfg(test)] mod test { use crate::utils::urlencoded::endecode::DeCodable; use crate::utils::urlencoded::endecode::EnCodable; #[test] fn urlencoded_test() { assert_eq!( "Darius%20is%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire", b"Darius is the biggest genius/Genie/Human extraordin\xC3\xA4ire".encode() ); } #[test] fn urldecoded_test() { assert_eq!( "Darius is the biggest genius/Genie/Human extraordinäire", String::from_utf8( "Darius%20is%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire" .decode() .unwrap() ) .unwrap() ); assert_eq!( Err(()), "Darius%2iis%20the%20biggest%20genius%2FGenie%2FHuman%20extraordin%C3%A4ire".decode() ); } }