use std::{collections::{HashMap, BTreeMap}};
#[derive(Debug)]
pub struct UrlEncoded {
values: HashMap<String, String>,
}
impl UrlEncoded {
pub fn from_string(d: String) -> UrlEncoded {
UrlEncoded {
values: d
.split('&')
.filter_map(|x| {
let x_spl = x.split('=').collect::<Vec<&str>>();
Some((
x_spl.first()?.to_string().decode_url(),
x_spl.get(1)?.to_string(),
))
})
.collect(),
}
}
pub fn get<S: AsRef<str>, T: FromUrlEncoded>(&self, key: S) -> Option<T> {
T::from_url_encoded(&self.values.get(key.as_ref())?.clone().decode_url())
}
pub fn get_dict<S: AsRef<str>>(&self, key: S) -> Option<UrlEncoded> {
let matched_values = self
.values
.iter()
.filter(|x| {
let sw = format!("{}[", key.as_ref());
x.0.starts_with(&sw)
})
.map(|(k, v)| {
let mut new_k = k.clone();
new_k.replace_range(0..key.as_ref().len() + 1, "");
new_k = new_k.replacen(']', "", 1);
format!("{}={}", new_k, v)
})
.collect::<Vec<String>>()
.join("&");
Some(UrlEncoded::from_string(matched_values))
}
pub fn get_vec_dict<S: AsRef<str>>(&self, key: S) -> Option<Vec<UrlEncoded>> {
let matched_values = self
.values
.iter()
.filter(|x| {
let sw = format!("{}[", key.as_ref());
x.0.starts_with(&sw)
})
.map(|(k, v)| {
let mut new_k = k.clone();
new_k.replace_range(0..key.as_ref().len() + 1, "");
(new_k, v.to_owned())
})
.collect::<Vec<(String, String)>>();
let mut values = BTreeMap::<i32, String>::new();
for v in matched_values {
let split = v.0.replacen('[', "", 1);
let new_key_data = split.split(']').collect::<Vec<&str>>();
let array_num: i32 = new_key_data[0].parse().unwrap();
let i_value = format!("{}={}", new_key_data[1], v.1);
if let Some(x) = values.get_mut(&array_num) { x.push('&'); x.push_str(&i_value) }
else { values.insert(array_num, i_value); }
}
Some(values.into_values().map(UrlEncoded::from_string).collect::<Vec<_>>())
}
pub fn get_vec<S: AsRef<str>, T: FromUrlEncoded>(&self, key: S) -> Option<Vec<T>> {
let matched_values = self
.values
.iter()
.filter(|x| {
let sw = format!("{}[", key.as_ref());
x.0.starts_with(&sw)
})
.map(|(k, v)| {
let mut new_k = k.clone();
new_k.replace_range(0..key.as_ref().len() + 1, "");
(new_k, v.to_owned())
})
.collect::<Vec<(String, String)>>();
let mut values = BTreeMap::<i32, String>::new();
for v in matched_values {
let split = v.0.replacen('[', "", 1);
let new_key_data = split.split(']').collect::<Vec<&str>>();
let array_num: i32 = new_key_data[0].parse().unwrap();
values.insert(array_num, v.1);
}
Some(values.into_values().filter_map(|x| T::from_url_encoded(&x.decode_url())).collect::<Vec<_>>())
}
}
pub trait FromUrlEncoded {
fn from_url_encoded(data: &str) -> Option<Self>
where
Self: Sized;
}
impl FromUrlEncoded for String {
fn from_url_encoded(data: &str) -> Option<Self> {
Some(data.to_string())
}
}
impl FromUrlEncoded for i32 {
fn from_url_encoded(data: &str) -> Option<Self> {
data.parse().ok()
}
}
impl FromUrlEncoded for f64 {
fn from_url_encoded(data: &str) -> Option<Self> {
data.parse().ok()
}
}
trait UrlEncodedParse {
fn decode_url(self) -> String;
}
impl UrlEncodedParse for String {
fn decode_url(self) -> String {
self.replace('+', " ")
.replace("%20", " ")
.replace("%21", "!")
.replace("%22", "\"")
.replace("%23", "#")
.replace("%24", "$")
.replace("%26", "&")
.replace("%27", "'")
.replace("%28", "(")
.replace("%29", ")")
.replace("%2A", "*")
.replace("%2B", "+")
.replace("%2C", ",")
.replace("%25", "%")
.replace("%5B", "[")
.replace("%5D", "]")
}
}