diff options
Diffstat (limited to 'corgi/src/util.rs')
-rw-r--r-- | corgi/src/util.rs | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/corgi/src/util.rs b/corgi/src/util.rs new file mode 100644 index 0000000..727c8c7 --- /dev/null +++ b/corgi/src/util.rs @@ -0,0 +1,66 @@ +use std::str::FromStr; + +use hyper::header::HeaderValue; + +use crate::RuntimeError; + +// Ripped and modified from gennyble/mavourings query.rs +/// Decode a URL encoded string, optionally treating a plus, '+', as a space. If +/// the final string is not UTF8, RuntimeError::MalformedRequest is returned +pub fn url_decode(urlencoded: &str, plus_as_space: bool) -> Result<String, RuntimeError> { + let mut uncoded: Vec<u8> = Vec::with_capacity(urlencoded.len()); + + let mut chars = urlencoded.chars().peekable(); + loop { + let mut utf8_bytes = [0; 4]; + match chars.next() { + Some('+') => match plus_as_space { + true => uncoded.push(b' '), + false => uncoded.push(b'+'), + }, + Some('%') => match chars.peek() { + Some(c) if c.is_ascii_hexdigit() => { + let upper = chars.next().unwrap(); + + if let Some(lower) = chars.peek() { + if lower.is_ascii_hexdigit() { + let upper = upper.to_digit(16).unwrap(); + let lower = chars.next().unwrap().to_digit(16).unwrap(); + + uncoded.push(upper as u8 * 16 + lower as u8); + continue; + } + } + + uncoded.push(b'%'); + uncoded.extend_from_slice(upper.encode_utf8(&mut utf8_bytes).as_bytes()); + } + _ => { + uncoded.push(b'%'); + } + }, + Some(c) => { + uncoded.extend_from_slice(c.encode_utf8(&mut utf8_bytes).as_bytes()); + } + None => { + uncoded.shrink_to_fit(); + return String::from_utf8(uncoded).map_err(|_| RuntimeError::MalformedRequest); + } + } + } +} + +pub fn parse_from_header<T: FromStr>(maybe_hval: Option<&HeaderValue>) -> Option<T> { + maybe_hval + .map(|h| h.to_str().ok()) + .flatten() + .map(|s| s.parse().ok()) + .flatten() +} + +pub fn owned_header(maybe_hval: Option<&HeaderValue>) -> Option<String> { + maybe_hval + .map(|h| h.to_str().ok()) + .flatten() + .map(<_>::to_owned) +} |