use linear_map::LinearMap;
use serde::{Deserialize, Serialize};
use std::{str::FromStr, sync::Arc};
use thiserror::Error;
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[repr(transparent)]
pub struct ParamsMap(pub LinearMap<String, String>);
impl ParamsMap {
#[inline(always)]
pub fn new() -> Self {
Self(LinearMap::new())
}
#[inline(always)]
pub fn with_capacity(capacity: usize) -> Self {
Self(LinearMap::with_capacity(capacity))
}
#[inline(always)]
pub fn insert(&mut self, key: String, value: String) -> Option<String> {
self.0.insert(key, value)
}
#[inline(always)]
pub fn get(&self, key: &str) -> Option<&String> {
self.0.get(key)
}
#[inline(always)]
pub fn remove(&mut self, key: &str) -> Option<String> {
self.0.remove(key)
}
pub fn to_query_string(&self) -> String {
use crate::history::url::escape;
let mut buf = String::new();
if !self.0.is_empty() {
buf.push('?');
for (k, v) in &self.0 {
buf.push_str(&escape(k));
buf.push('=');
buf.push_str(&escape(v));
buf.push('&');
}
if buf.len() > 1 {
buf.pop();
}
}
buf
}
}
impl Default for ParamsMap {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
#[macro_export]
macro_rules! params_map {
($($key:expr => $val:expr),* ,) => (
$crate::ParamsMap!($($key => $val),*)
);
($($key:expr => $val:expr),*) => ({
let start_capacity = common_macros::const_expr_count!($($key);*);
#[allow(unused_mut)]
let mut map = linear_map::LinearMap::with_capacity(start_capacity);
$( map.insert($key.to_string(), $val.to_string()); )*
$crate::ParamsMap(map)
});
}
pub trait Params
where
Self: Sized,
{
fn from_map(map: &ParamsMap) -> Result<Self, ParamsError>;
}
impl Params for () {
#[inline(always)]
fn from_map(_map: &ParamsMap) -> Result<Self, ParamsError> {
Ok(())
}
}
pub trait IntoParam
where
Self: Sized,
{
fn into_param(value: Option<&str>, name: &str)
-> Result<Self, ParamsError>;
}
impl<T> IntoParam for Option<T>
where
T: FromStr,
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
fn into_param(
value: Option<&str>,
_name: &str,
) -> Result<Self, ParamsError> {
match value {
None => Ok(None),
Some(value) => match T::from_str(value) {
Ok(value) => Ok(Some(value)),
Err(e) => Err(ParamsError::Params(Arc::new(e))),
},
}
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "nightly")] {
auto trait NotOption {}
impl<T> !NotOption for Option<T> {}
impl<T> IntoParam for T
where
T: FromStr + NotOption,
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
fn into_param(value: Option<&str>, name: &str) -> Result<Self, ParamsError> {
let value = value.ok_or_else(|| ParamsError::MissingParam(name.to_string()))?;
Self::from_str(value).map_err(|e| ParamsError::Params(Arc::new(e)))
}
}
}
}
#[derive(Error, Debug, Clone)]
pub enum ParamsError {
#[error("could not find parameter {0}")]
MissingParam(String),
#[error("failed to deserialize parameters")]
Params(Arc<dyn std::error::Error + Send + Sync>),
}
impl PartialEq for ParamsError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::MissingParam(l0), Self::MissingParam(r0)) => l0 == r0,
(Self::Params(_), Self::Params(_)) => false,
_ => false,
}
}
}