use byte_slice::ByteStream;
use std::fmt;
pub enum DecodeError {
Byte(u8),
HexSequence(u8)
}
impl DecodeError {
fn format(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match *self {
DecodeError::Byte(x) => {
write!(
formatter,
"<DecodeError::Byte: {}>",
x
)
},
DecodeError::HexSequence(x) => {
write!(
formatter,
"<DecodeError::HexSequence: {}>",
x
)
}
}
}
}
impl fmt::Debug for DecodeError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.format(formatter)
}
}
impl fmt::Display for DecodeError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.format(formatter)
}
}
pub fn decode(encoded: &[u8]) -> Result<String, DecodeError> {
macro_rules! submit {
($string:expr, $slice:expr) => (unsafe {
$string.as_mut_vec().extend_from_slice($slice);
});
}
let mut context = ByteStream::new(encoded);
let mut string = String::new();
loop {
bs_mark!(context);
collect_visible_7bit!(
context,
context.byte == b'+'
|| context.byte == b'%',
{
if context.mark_index < context.stream_index {
submit!(string, bs_slice!(context));
}
return Ok(string);
}
);
if bs_slice_length!(context) > 1 {
submit!(string, bs_slice_ignore!(context));
}
if context.byte == b'+' {
submit!(string, b" ");
} else if context.byte == b'%' {
if bs_has_bytes!(context, 2) {
submit!(string, &[
collect_hex8!(context, DecodeError::HexSequence)
]);
} else {
if bs_has_bytes!(context, 1) {
bs_next!(context);
}
return Err(DecodeError::HexSequence(context.byte));
}
} else {
return Err(DecodeError::Byte(context.byte));
}
}
}