#![warn(missing_docs)]
use ahash::AHashMap;
use std::fmt::Display;
use crate::url::{decode, encode};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormData {
pub(crate) inner: AHashMap<String, String>,
}
impl FormData {
pub fn new() -> Self {
FormData {
inner: AHashMap::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
FormData {
inner: AHashMap::with_capacity(capacity),
}
}
pub fn insert<K, V>(&mut self, key: K, value: V) -> Option<String>
where
K: Into<String>,
V: Into<String>,
{
self.inner.insert(key.into(), value.into())
}
pub fn get(&self, key: &str) -> Option<&str> {
self.inner.get(key).map(|s| s.as_str())
}
pub fn get_or<'a>(&'a self, key: &str, default: &'a str) -> &'a str {
self.get(key).unwrap_or(default)
}
pub fn get_mut(&mut self, key: &str) -> Option<&mut String> {
self.inner.get_mut(key)
}
pub fn as_map(&self) -> &AHashMap<String, String> {
&self.inner
}
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.inner.keys().map(|s| s.as_str())
}
pub fn values(&self) -> impl Iterator<Item = &str> {
self.inner.values().map(|s| s.as_str())
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn byte_len(&self) -> usize {
self.to_query_string().len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn remove(&mut self, key: &str) -> Option<String> {
self.inner.remove(key)
}
pub fn contains_key(&self, key: &str) -> bool {
self.inner.contains_key(key)
}
pub fn from_map(map: AHashMap<String, String>) -> Self {
Self { inner: map }
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.inner.iter().map(|(k, v)| (k.as_str(), v.as_str()))
}
pub fn clear(&mut self) {
self.inner.clear();
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&str, &str) -> bool,
{
self.inner.retain(|k, v| f(k, v));
}
pub fn extend<I, K, V>(&mut self, iter: I)
where
I: IntoIterator<Item = (K, V)>,
K: Into<String>,
V: Into<String>,
{
self.inner
.extend(iter.into_iter().map(|(k, v)| (k.into(), v.into())));
}
pub fn append<K, V>(&mut self, key: K, value: V)
where
K: Into<String> + AsRef<str>,
V: Into<String>,
{
let key_str = key.as_ref();
let value_string = value.into();
match self.inner.get_mut(key_str) {
Some(existing) => {
existing.push(',');
existing.push_str(&value_string);
}
None => {
self.inner.insert(key.into(), value_string);
}
}
}
pub fn to_query_string(&self) -> String {
self.inner
.iter()
.map(|(k, v)| format!("{}={}", encode(k), encode(v)))
.collect::<Vec<_>>()
.join("&")
}
pub fn from_query_string(query: &str) -> Result<Self, String> {
let mut form_data = FormData::new();
if query.is_empty() {
return Ok(form_data);
}
if query.contains(", ") && !query.contains("&") {
return Self::from_comma_separated(query);
}
for pair in query.split('&') {
if let Some((key, value)) = pair.split_once('=') {
let decoded_key =
decode(key).map_err(|e| format!("Failed to decode key '{}': {}", key, e))?;
let decoded_value = decode(value)
.map_err(|e| format!("Failed to decode value '{}': {}", value, e))?;
form_data.insert(decoded_key.into_owned(), decoded_value.into_owned());
} else {
let decoded_key =
decode(pair).map_err(|e| format!("Failed to decode key '{}': {}", pair, e))?;
form_data.insert(decoded_key.into_owned(), String::new());
}
}
Ok(form_data)
}
pub fn from_comma_separated(query: &str) -> Result<Self, String> {
let mut form_data = FormData::new();
if query.is_empty() {
return Ok(form_data);
}
let separator = if query.contains(", ") { ", " } else { "&" };
for pair in query.split(separator) {
let pair = pair.trim();
if let Some((key, value)) = pair.split_once('=') {
let decoded_key = decode(key.trim())
.map_err(|e| format!("Failed to decode key '{}': {}", key, e))?;
let decoded_value = decode(value.trim())
.map_err(|e| format!("Failed to decode value '{}': {}", value, e))?;
form_data.insert(decoded_key.into_owned(), decoded_value.into_owned());
}
}
Ok(form_data)
}
}
impl Default for FormData {
fn default() -> Self {
Self::new()
}
}
impl Display for FormData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let form_data_strings: Vec<String> = self
.inner
.iter()
.map(|(name, value)| format!("{}={}", name, value))
.collect();
write!(f, "{}", form_data_strings.join(", "))
}
}
impl From<AHashMap<String, String>> for FormData {
fn from(map: AHashMap<String, String>) -> Self {
Self::from_map(map)
}
}
impl From<FormData> for AHashMap<String, String> {
fn from(form_data: FormData) -> Self {
form_data.inner
}
}
impl<K, V> FromIterator<(K, V)> for FormData
where
K: Into<String>,
V: Into<String>,
{
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let mut form_data = FormData::new();
form_data.extend(iter);
form_data
}
}
impl<K, V> Extend<(K, V)> for FormData
where
K: Into<String>,
V: Into<String>,
{
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
FormData::extend(self, iter);
}
}
impl std::ops::Index<&str> for FormData {
type Output = str;
fn index(&self, key: &str) -> &Self::Output {
self.get(key)
.unwrap_or_else(|| panic!("FormData parameter '{}' not found", key))
}
}
impl IntoIterator for FormData {
type Item = (String, String);
type IntoIter = std::collections::hash_map::IntoIter<String, String>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a> IntoIterator for &'a FormData {
type Item = (&'a String, &'a String);
type IntoIter = std::collections::hash_map::Iter<'a, String, String>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter()
}
}
impl<'a> IntoIterator for &'a mut FormData {
type Item = (&'a String, &'a mut String);
type IntoIter = std::collections::hash_map::IterMut<'a, String, String>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter_mut()
}
}