use linear_map::LinearMap;
use std::{rc::Rc, str::FromStr};
use thiserror::Error;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ParamsMap(pub LinearMap<String, String>);
impl ParamsMap {
pub fn new() -> Self {
Self(LinearMap::new())
}
pub fn with_capacity(capacity: usize) -> Self {
Self(LinearMap::with_capacity(capacity))
}
pub fn insert(&mut self, key: String, value: String) -> Option<String> {
self.0.insert(key, value)
}
pub fn get(&self, key: &str) -> Option<&String> {
self.0.get(key)
}
pub fn remove(&mut self, key: &str) -> Option<String> {
self.0.remove(key)
}
#[cfg(any(feature = "csr", feature = "hydrate", feature = "ssr"))]
pub fn to_query_string(&self) -> String {
use crate::history::url::escape;
let mut buf = String::from("?");
for (k, v) in &self.0 {
buf.push_str(&escape(k));
buf.push('=');
buf.push_str(&escape(v));
buf.push('&');
}
buf
}
}
impl Default for ParamsMap {
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, $val); )*
$crate::ParamsMap(map)
});
}
pub trait Params
where
Self: Sized,
{
fn from_map(map: &ParamsMap) -> Result<Self, ParamsError>;
}
impl Params for () {
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 + '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) => {
eprintln!("{}", e);
Err(ParamsError::Params(Rc::new(e)))
}
},
}
}
}
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(Rc::new(e)))
}
}
#[derive(Error, Debug, Clone)]
pub enum ParamsError {
#[error("could not find parameter {0}")]
MissingParam(String),
#[error("failed to deserialize parameters")]
Params(Rc<dyn std::error::Error>),
}
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,
}
}
}