api_response/
utils.rs

1use std::{
2    collections::HashMap,
3    error::Error,
4    fmt::{Debug, Display},
5    ops::{Deref, DerefMut},
6};
7
8use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeMap};
9
10#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
11#[derive(Debug, Default)]
12pub(crate) struct OrderedHashMap<K, V>(pub(crate) HashMap<K, V>);
13
14impl<K, V> Serialize for OrderedHashMap<K, V>
15where
16    K: Serialize + Ord,
17    V: Serialize,
18{
19    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
20    where
21        S: Serializer,
22    {
23        let mut sorted_entries: Vec<_> = self.0.iter().collect();
24        sorted_entries.sort_by_key(|&(k, _)| k);
25
26        let mut map = serializer.serialize_map(Some(sorted_entries.len()))?;
27        for (k, v) in sorted_entries {
28            map.serialize_entry(k, v)?;
29        }
30        map.end()
31    }
32}
33
34impl<'de, K, V> Deserialize<'de> for OrderedHashMap<K, V>
35where
36    K: Deserialize<'de> + Eq + std::hash::Hash,
37    V: Deserialize<'de>,
38{
39    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
40    where
41        D: Deserializer<'de>,
42    {
43        let map = HashMap::deserialize(deserializer)?;
44        Ok(OrderedHashMap(map))
45    }
46}
47
48impl<K, V> Deref for OrderedHashMap<K, V> {
49    type Target = HashMap<K, V>;
50
51    fn deref(&self) -> &Self::Target {
52        &self.0
53    }
54}
55
56impl<K, V> DerefMut for OrderedHashMap<K, V> {
57    fn deref_mut(&mut self) -> &mut Self::Target {
58        &mut self.0
59    }
60}
61
62#[derive(Clone, PartialEq, Eq, Debug, Hash)]
63#[non_exhaustive]
64pub enum MaybeString {
65    String(String),
66    Str(&'static str),
67    OptionString(Option<String>),
68    OptionStr(Option<&'static str>),
69    UnitTuple,
70}
71impl From<()> for MaybeString {
72    fn from(_: ()) -> Self {
73        MaybeString::UnitTuple
74    }
75}
76impl From<String> for MaybeString {
77    fn from(value: String) -> Self {
78        MaybeString::String(value)
79    }
80}
81impl From<&'static str> for MaybeString {
82    fn from(value: &'static str) -> Self {
83        MaybeString::Str(value)
84    }
85}
86impl From<Option<String>> for MaybeString {
87    fn from(value: Option<String>) -> Self {
88        MaybeString::OptionString(value)
89    }
90}
91impl From<Option<&'static str>> for MaybeString {
92    fn from(value: Option<&'static str>) -> Self {
93        MaybeString::OptionStr(value)
94    }
95}
96impl MaybeString {
97    pub fn option_string(self) -> Option<String> {
98        match self {
99            MaybeString::String(v) => Some(v),
100            MaybeString::Str(v) => Some(v.to_owned()),
101            MaybeString::OptionString(v) => v,
102            MaybeString::OptionStr(v) => v.map(ToOwned::to_owned),
103            MaybeString::UnitTuple => None,
104        }
105    }
106    /// # Panics
107    /// If the string is none, trigger a panic using the `msg` parameter.
108    pub fn expect(self, msg: &str) -> String {
109        match self {
110            MaybeString::String(v) => v,
111            MaybeString::Str(v) => v.to_owned(),
112            MaybeString::OptionString(v) => v.expect(msg),
113            MaybeString::OptionStr(v) => v.expect(msg).to_owned(),
114            MaybeString::UnitTuple => panic!("{msg}"),
115        }
116    }
117    pub fn unwrap_or(self, default: impl Into<String>) -> String {
118        match self {
119            MaybeString::String(v) => v,
120            MaybeString::Str(v) => v.to_owned(),
121            MaybeString::OptionString(v) => v.unwrap_or_else(|| default.into()),
122            MaybeString::OptionStr(v) => v.map_or_else(|| default.into(), ToOwned::to_owned),
123            MaybeString::UnitTuple => default.into(),
124        }
125    }
126    pub fn unwrap_or_else(self, f: impl FnOnce() -> String) -> String {
127        match self {
128            MaybeString::String(v) => v,
129            MaybeString::Str(v) => v.to_owned(),
130            MaybeString::OptionString(v) => v.unwrap_or_else(f),
131            MaybeString::OptionStr(v) => v.map_or_else(f, ToOwned::to_owned),
132            MaybeString::UnitTuple => f(),
133        }
134    }
135    pub fn unwrap_or_default(self) -> String {
136        match self {
137            MaybeString::String(v) => v,
138            MaybeString::Str(v) => v.to_owned(),
139            MaybeString::OptionString(v) => v.unwrap_or_default(),
140            MaybeString::OptionStr(v) => v.map_or_else(Default::default, ToOwned::to_owned),
141            MaybeString::UnitTuple => String::default(),
142        }
143    }
144}
145impl From<MaybeString> for Option<String> {
146    fn from(value: MaybeString) -> Self {
147        value.option_string()
148    }
149}
150
151#[derive(Debug)]
152#[non_exhaustive]
153pub struct ErrWrapper<E: Display>(pub E);
154
155impl<E: Debug + Display> Display for ErrWrapper<E> {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        Display::fmt(&self.0, f)
158    }
159}
160
161impl<E: Debug + Display> Error for ErrWrapper<E> {}
162
163pub trait IntoError: Debug + Display + Sized {
164    fn into_error(self) -> ErrWrapper<Self> {
165        ErrWrapper(self)
166    }
167}
168
169impl<E: Debug + Display + Sized> IntoError for E {}
170
171#[cfg(test)]
172mod tests {
173    use std::{collections::HashMap, error::Error};
174
175    use super::{IntoError, OrderedHashMap};
176
177    #[test]
178    fn ordered_hash_map() {
179        const S: &str = r#"{"k1":"v1","k2":"v2"}"#;
180        let mut map = HashMap::new();
181        map.insert("k2", "v2");
182        map.insert("k1", "v1");
183        let map1 = OrderedHashMap(map);
184        assert_eq!(S, serde_json::to_string(&map1).unwrap());
185        let map2: OrderedHashMap<&str, &str> = serde_json::from_str(S).unwrap();
186        assert_eq!(S, serde_json::to_string(&map2).unwrap());
187    }
188
189    #[test]
190    fn error_wrapper() {
191        let e: super::ErrWrapper<u32> = 1.into_error();
192        let d: &dyn Error = &e;
193        assert_eq!("1", d.to_string())
194    }
195}