1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
use std::collections::HashMap; use std::thread::sleep; type QueryParam<'a> = (&'a str, &'a str); type QueryParams<'a> = Vec<QueryParam<'a>>; pub mod querystring { use crate::QueryParams; pub fn stringify(query: QueryParams) -> String { query.iter().fold(String::new(), |acc, x| { acc + &escape(&x.0) + "=" + escape(&x.1).as_ref() + "&" }) } fn escape(str: &str) -> String { let mut enc = Vec::<u8>::new(); for ch in str.as_bytes() { if keep_as(*ch) { enc.push(*ch); } else { enc.push(0x25); let n1 = (*ch >> 4) & 0xf; let n2 = *ch & 0xf; enc.push(to_dec_ascii(n1)); enc.push(to_dec_ascii(n2)); } } String::from_utf8(enc).unwrap() } fn keep_as(n: u8) -> bool { return n.is_ascii_alphanumeric() || n == b'*' || n == b'-' || n == b'.' || n == b'_' || n == b'\'' || n == b'~' || n == b'!' || n == b'(' || n == b')'; } fn to_dec_ascii(n: u8) -> u8 { match n { 0 => 48, 1 => 49, 2 => 50, 3 => 51, 4 => 52, 5 => 53, 6 => 54, 7 => 55, 8 => 56, 9 => 57, 10 => b'A', 11 => b'B', 12 => b'C', 13 => b'D', 14 => b'E', 15 => b'F', _ => 127 } } } #[cfg(test)] mod tests { use super::querystring; use std::collections::HashMap; #[test] fn it_works() { let enc1 = querystring::stringify(vec![ ("params", "www. baidu. com/百度搜索"), ("encSecKey", "查询字=-)(*&^%$#@!~符串+~·!@¥%……%^%$:\"'*','-','.' and '_'"), ]); let res1: String= String::from("params=www.%20baidu.%20com%2F%E7%99%BE%E5%BA%A6%E6%90%9C%E7%B4%A2&encSecKey=%E6%9F%A5%E8%AF%A2%E5%AD%97%3D-)(*%26%5E%25%24%23%40!~%E7%AC%A6%E4%B8%B2%2B~%C2%B7%EF%BC%81%40%EF%BF%A5%25%E2%80%A6%E2%80%A6%25%5E%25%24%3A%22'*'%2C'-'%2C'.'%20and%20'_'&"); assert_eq!(enc1, res1); } }