use std::{
borrow::Cow,
cell::{Cell, RefCell},
collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
rc::Rc,
sync::{Arc, Mutex, RwLock},
};
pub trait Describer {
fn describe() -> String;
}
macro_rules! describer_primitive {
($($t:ty),+) => (
$(impl Describer for $t {
fn describe() -> String {
stringify!($t).to_string()
}
})*
)
}
describer_primitive!(
i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize
);
describer_primitive!(f32, f64);
describer_primitive!(bool, char);
impl Describer for String {
fn describe() -> String {
"String".to_string()
}
}
impl Describer for str {
fn describe() -> String {
"&str".to_string()
}
}
impl<T: Describer> Describer for Option<T> {
fn describe() -> String {
format!("{}?", T::describe())
}
}
impl<T: Describer, E: Describer> Describer for Result<T, E> {
fn describe() -> String {
format!("!{{{}, {}}}", T::describe(), E::describe())
}
}
impl<T: Describer> Describer for Vec<T> {
fn describe() -> String {
format!("[{}]", T::describe())
}
}
impl<T: Describer> Describer for [T] {
fn describe() -> String {
format!("[{}]", T::describe())
}
}
impl<T: Describer> Describer for VecDeque<T> {
fn describe() -> String {
format!("[{}]", T::describe())
}
}
impl<T: Describer> Describer for LinkedList<T> {
fn describe() -> String {
format!("[{}]", T::describe())
}
}
impl<T: Describer> Describer for BinaryHeap<T> {
fn describe() -> String {
format!("^[{}]", T::describe())
}
}
impl<T: Describer, S: ::std::hash::BuildHasher> Describer for HashSet<T, S> {
fn describe() -> String {
format!("#{{{}}}", T::describe())
}
}
#[cfg(feature = "indexmap")]
impl<T: Describer, S: ::std::hash::BuildHasher> Describer for indexmap::IndexSet<T, S> {
fn describe() -> String {
format!("#{{{}}}", T::describe())
}
}
#[cfg(feature = "dashmap")]
impl<T: Describer, S: ::std::hash::BuildHasher> Describer for dashmap::DashSet<T, S> {
fn describe() -> String {
format!("#{{{}}}", T::describe())
}
}
impl<T: Describer> Describer for BTreeSet<T> {
fn describe() -> String {
format!("#{{{}}}", T::describe())
}
}
impl<T: Describer, V: Describer, S: ::std::hash::BuildHasher> Describer for HashMap<T, V, S> {
fn describe() -> String {
format!("{{{}: {}}}", T::describe(), V::describe())
}
}
#[cfg(feature = "indexmap")]
impl<T: Describer, V: Describer, S: ::std::hash::BuildHasher> Describer
for indexmap::IndexMap<T, V, S>
{
fn describe() -> String {
format!("{{{}: {}}}", T::describe(), V::describe())
}
}
#[cfg(feature = "dashmap")]
impl<T: Describer, V: Describer, S: ::std::hash::BuildHasher> Describer
for dashmap::DashMap<T, V, S>
{
fn describe() -> String {
format!("{{{}: {}}}", T::describe(), V::describe())
}
}
impl<T: Describer, V: Describer> Describer for BTreeMap<T, V> {
fn describe() -> String {
format!("{{{}: {}}}", T::describe(), V::describe())
}
}
impl<T: Describer> Describer for RwLock<T> {
fn describe() -> String {
T::describe()
}
}
impl<T: Describer> Describer for Mutex<T> {
fn describe() -> String {
T::describe()
}
}
impl<T: Describer> Describer for Box<T> {
fn describe() -> String {
format!("*{}", T::describe())
}
}
impl<T: Describer> Describer for Arc<T> {
fn describe() -> String {
format!("arc {}", T::describe())
}
}
impl<T: Describer> Describer for Rc<T> {
fn describe() -> String {
format!("rc {}", T::describe())
}
}
impl<T: Describer> Describer for Cell<T> {
fn describe() -> String {
format!("&{}", T::describe())
}
}
impl<T: Describer> Describer for RefCell<T> {
fn describe() -> String {
format!("&{}", T::describe())
}
}
impl<T: Describer + Clone> Describer for Cow<'_, T> {
fn describe() -> String {
format!("~{}", T::describe())
}
}
macro_rules! describer_tuple {
() => {
impl Describer for () {
fn describe() -> String {
"()".to_string()
}
}
};
($($T:ident),+) => {
impl<$($T: Describer),+> Describer for ($($T,)+) {
fn describe() -> String {
format!(
"({})",
vec![$($T::describe()),+].join(", ")
)
}
}
};
}
describer_tuple!();
describer_tuple!(T0);
describer_tuple!(T0, T1);
describer_tuple!(T0, T1, T2);
describer_tuple!(T0, T1, T2, T3);
describer_tuple!(T0, T1, T2, T3, T4);
describer_tuple!(T0, T1, T2, T3, T4, T5);
describer_tuple!(T0, T1, T2, T3, T4, T5, T6);
describer_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
describer_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
describer_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
describer_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
describer_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl<T: Describer> Describer for RangeToInclusive<T> {
fn describe() -> String {
format!("]-∞..{}]", T::describe())
}
}
impl<T: Describer> Describer for RangeTo<T> {
fn describe() -> String {
format!("]-∞..{}[", T::describe())
}
}
impl<T: Describer> Describer for RangeInclusive<T> {
fn describe() -> String {
format!("[{}]", T::describe())
}
}
impl Describer for RangeFull {
fn describe() -> String {
"]∞[".to_string()
}
}
impl<T: Describer> Describer for RangeFrom<T> {
fn describe() -> String {
format!("[{}..+∞[", T::describe())
}
}
impl<T: Describer> Describer for Range<T> {
fn describe() -> String {
format!("[{}[", T::describe())
}
}
#[cfg(feature = "chrono")]
impl<T: chrono::TimeZone> Describer for chrono::DateTime<T> {
fn describe() -> String {
"#DateTime".to_string()
}
}
#[cfg(feature = "chrono")]
impl Describer for chrono::NaiveDate {
fn describe() -> String {
"#NaiveDate".to_string()
}
}
#[cfg(feature = "chrono")]
impl Describer for chrono::NaiveDateTime {
fn describe() -> String {
"#NaiveDateTime".to_string()
}
}
#[cfg(feature = "chrono")]
impl Describer for chrono::NaiveTime {
fn describe() -> String {
"#NaiveTime".to_string()
}
}
#[cfg(feature = "uuid")]
impl Describer for uuid::Uuid {
fn describe() -> String {
"#Uuid".to_string()
}
}
#[cfg(feature = "regex")]
impl Describer for regex::Regex {
fn describe() -> String {
"#Regex".to_string()
}
}
#[cfg(feature = "http")]
impl Describer for http::HeaderMap {
fn describe() -> String {
"#Http/HeaderMap".to_string()
}
}
#[cfg(feature = "http")]
impl Describer for http::Extensions {
fn describe() -> String {
"#Http/Extensions".to_string()
}
}
#[cfg(feature = "http")]
impl Describer for http::Method {
fn describe() -> String {
"#Http/Method".to_string()
}
}
#[cfg(feature = "http")]
impl<T> Describer for http::Request<T> {
fn describe() -> String {
"#Http/Request".to_string()
}
}
#[cfg(feature = "http")]
impl<T> Describer for http::Response<T> {
fn describe() -> String {
"#Http/Response".to_string()
}
}
#[cfg(feature = "http")]
impl Describer for http::Uri {
fn describe() -> String {
"#Http/Uri".to_string()
}
}
#[cfg(feature = "http")]
impl Describer for http::StatusCode {
fn describe() -> String {
"#Http/StatusCode".to_string()
}
}
#[cfg(feature = "http")]
impl Describer for http::Version {
fn describe() -> String {
"#Http/Version".to_string()
}
}
#[cfg(feature = "tokio")]
impl<T: Describer> Describer for tokio::sync::Mutex<T> {
fn describe() -> String {
format!("#tokio/{}", T::describe())
}
}
#[cfg(feature = "tokio")]
impl<T: Describer> Describer for tokio::sync::RwLock<T> {
fn describe() -> String {
format!("#tokio/{}", T::describe())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn primitives() {
assert_eq!(i8::describe(), "i8");
assert_eq!(i16::describe(), "i16");
assert_eq!(i32::describe(), "i32");
assert_eq!(i64::describe(), "i64");
assert_eq!(i128::describe(), "i128");
assert_eq!(isize::describe(), "isize");
assert_eq!(u8::describe(), "u8");
assert_eq!(u16::describe(), "u16");
assert_eq!(u32::describe(), "u32");
assert_eq!(u64::describe(), "u64");
assert_eq!(u128::describe(), "u128");
assert_eq!(usize::describe(), "usize");
assert_eq!(f32::describe(), "f32");
assert_eq!(f64::describe(), "f64");
assert_eq!(char::describe(), "char");
assert_eq!(bool::describe(), "bool");
assert_eq!(String::describe(), "String");
assert_eq!(str::describe(), "&str");
}
#[test]
fn optional() {
assert_eq!(Option::<u8>::describe(), "u8?");
}
#[test]
fn result() {
assert_eq!(Result::<u8, bool>::describe(), "!{u8, bool}");
}
#[test]
fn collections() {
assert_eq!(HashMap::<u8, bool>::describe(), "{u8: bool}");
assert_eq!(BTreeMap::<u8, bool>::describe(), "{u8: bool}");
assert_eq!(Vec::<u8>::describe(), "[u8]");
assert_eq!(VecDeque::<u8>::describe(), "[u8]");
assert_eq!(LinkedList::<u8>::describe(), "[u8]");
assert_eq!(BinaryHeap::<u8>::describe(), "^[u8]");
assert_eq!(HashSet::<u8>::describe(), "#{u8}");
assert_eq!(BTreeSet::<u8>::describe(), "#{u8}");
}
#[test]
fn pointers() {
assert_eq!(Box::<u8>::describe(), "*u8");
assert_eq!(Arc::<u8>::describe(), "arc u8");
assert_eq!(Rc::<u8>::describe(), "rc u8");
assert_eq!(Cell::<u8>::describe(), "&u8");
assert_eq!(RefCell::<u8>::describe(), "&u8");
assert_eq!(Cow::<u8>::describe(), "~u8");
}
#[test]
fn range() {
assert_eq!(RangeToInclusive::<i32>::describe(), "]-∞..i32]");
assert_eq!(RangeTo::<i32>::describe(), "]-∞..i32[");
assert_eq!(RangeInclusive::<i32>::describe(), "[i32]");
assert_eq!(RangeFull::describe(), "]∞[");
assert_eq!(RangeFrom::<i32>::describe(), "[i32..+∞[");
assert_eq!(Range::<i32>::describe(), "[i32[");
}
}