xapi_rs/data/
ci_string.rs1use crate::data::Fingerprint;
7use core::fmt;
8use serde::{
9 de::{self, Visitor},
10 Deserialize, Deserializer, Serialize, Serializer,
11};
12use std::{
13 cmp::Ordering,
14 hash::{Hash, Hasher},
15 ops::Deref,
16};
17use unicase::UniCase;
18
19#[derive(Clone, Debug, Deserialize, Eq, Serialize)]
23pub struct CIString(
24 #[serde(serialize_with = "unicase_ser", deserialize_with = "unicase_des")] UniCase<String>,
25);
26
27impl CIString {
28 pub(crate) fn from<S: AsRef<str>>(s: S) -> Self {
30 CIString(UniCase::from(s.as_ref()))
31 }
32
33 pub(crate) fn is_empty(&self) -> bool {
35 self.0.is_empty()
36 }
37}
38
39impl PartialEq for CIString {
40 fn eq(&self, other: &Self) -> bool {
41 self.0 == other.0
42 }
43}
44
45impl PartialEq<String> for CIString {
46 fn eq(&self, other: &String) -> bool {
47 self.0.as_str() == other
48 }
49}
50
51impl PartialEq<str> for CIString {
52 fn eq(&self, other: &str) -> bool {
53 self.0.as_str() == other
54 }
55}
56
57impl Hash for CIString {
58 fn hash<H: Hasher>(&self, state: &mut H) {
59 self.0.hash(state);
60 }
61}
62
63impl Ord for CIString {
64 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
65 self.0.cmp(&other.0)
66 }
67}
68
69impl PartialOrd for CIString {
70 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
71 Some(self.cmp(other))
72 }
73}
74
75impl PartialOrd<str> for CIString {
76 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
77 self.0.as_str().partial_cmp(other)
78 }
79}
80
81impl fmt::Display for CIString {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "{}", self.0)
84 }
85}
86
87impl Deref for CIString {
88 type Target = str;
89
90 fn deref(&self) -> &Self::Target {
91 self.0.as_str()
92 }
93}
94
95impl Fingerprint for CIString {
96 fn fingerprint<H: Hasher>(&self, state: &mut H) {
97 self.0.as_str().to_lowercase().hash(state);
98 }
99}
100
101struct UnicaseVisitor;
103
104impl Visitor<'_> for UnicaseVisitor {
105 type Value = UniCase<String>;
106
107 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
108 formatter.write_str("a case-insensitive string")
109 }
110
111 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
112 where
113 E: de::Error,
114 {
115 Ok(UniCase::new(value.to_owned()))
116 }
117}
118
119fn unicase_ser<S>(this: &UniCase<String>, ser: S) -> Result<S::Ok, S::Error>
121where
122 S: Serializer,
123{
124 ser.serialize_str(this.as_str())
125}
126
127fn unicase_des<'de, D>(des: D) -> Result<UniCase<String>, D::Error>
129where
130 D: Deserializer<'de>,
131{
132 des.deserialize_str(UnicaseVisitor)
133}