about summary refs log tree commit diff
path: root/corgi/src/util.rs
diff options
context:
space:
mode:
Diffstat (limited to 'corgi/src/util.rs')
-rw-r--r--corgi/src/util.rs66
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)
+}