1use caseless::default_case_fold_str;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4
5const KEY_DELIMITER: &str = ":";
6
7#[derive(Clone)]
10pub struct ConfigKey(String);
11
12impl ConfigKey {
13 fn new() -> Self {
14 Self(String::new())
15 }
16
17 pub fn empty() -> Self {
19 Self::new()
20 }
21
22 pub fn is_empty(&self) -> bool {
23 self.0.is_empty()
24 }
25
26 pub fn len(&self) -> usize {
28 self.0.len()
29 }
30
31 pub fn separator() -> &'static str {
33 KEY_DELIMITER
34 }
35
36 pub fn section_key(&self) -> Self {
52 if self == "" {
53 ConfigKey::new()
54 } else {
55 match self.0.rfind(KEY_DELIMITER) {
56 None => self.clone(),
57 Some(i) => Self(self.0[(i + 1)..].to_owned()),
58 }
59 }
60 }
61
62 pub fn parent(&self) -> Self {
78 if self == "" {
79 ConfigKey::new()
80 } else {
81 match self.0.rfind(KEY_DELIMITER) {
82 None => ConfigKey::new(),
83 Some(i) => Self(self.0[..i].to_owned()),
84 }
85 }
86 }
87
88 pub fn combine<S2: Into<ConfigKey>>(&self, key: S2) -> Self {
105 let key = key.into();
106 if self == "" {
107 key
108 } else {
109 let mut s = String::with_capacity(self.len() + key.len() + KEY_DELIMITER.len());
110 s.push_str(self.as_ref());
111 s.push_str(KEY_DELIMITER);
112 s.push_str(key.as_ref());
113 Self(s)
114 }
115 }
116
117 pub fn join<S: Into<ConfigKey>, I: IntoIterator<Item = S>>(parts: I) -> Self {
133 let mut s = String::with_capacity(50);
135 let mut first = true;
136 for part in parts.into_iter().filter_map(|part| {
137 let part = part.into();
138 if part.as_ref() == "" {
139 None
140 } else {
141 Some(part)
142 }
143 }) {
144 if first {
145 first = false;
146 } else {
147 s.push_str(KEY_DELIMITER);
148 }
149 s.push_str(part.as_ref());
150 }
151
152 Self(s)
153 }
154
155 pub fn unsafe_from<S: Into<String>>(value: S) -> Self {
171 Self(value.into())
172 }
173}
174
175impl fmt::Display for ConfigKey {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177 fmt::Display::fmt(&self.0, f)
178 }
179}
180
181impl fmt::Debug for ConfigKey {
182 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183 fmt::Debug::fmt(&self.0, f)
184 }
185}
186
187impl AsRef<str> for ConfigKey {
188 fn as_ref(&self) -> &str {
189 &self.0
190 }
191}
192
193impl Hash for ConfigKey {
194 #[inline]
195 fn hash<H: Hasher>(&self, hasher: &mut H) {
196 self.0.hash(hasher)
197 }
198}
199
200impl PartialEq for ConfigKey {
201 #[inline]
202 fn eq(&self, other: &Self) -> bool {
203 self.0.eq(&other.0)
204 }
205}
206
207impl PartialEq<String> for ConfigKey {
208 fn eq(&self, other: &String) -> bool {
209 self.eq(&ConfigKey::from(other))
210 }
211}
212
213impl PartialEq<str> for ConfigKey {
214 fn eq(&self, other: &str) -> bool {
215 self.eq(&ConfigKey::from(other))
216 }
217}
218
219impl PartialEq<&String> for ConfigKey {
220 fn eq(&self, other: &&String) -> bool {
221 self.eq(&ConfigKey::from(*other))
222 }
223}
224
225impl PartialEq<&str> for ConfigKey {
226 fn eq(&self, other: &&str) -> bool {
227 self.eq(&ConfigKey::from(*other))
228 }
229}
230
231impl Eq for ConfigKey {}
232
233impl From<&str> for ConfigKey {
234 fn from(value: &str) -> Self {
235 ConfigKey(default_case_fold_str(value))
236 }
237}
238
239impl From<&String> for ConfigKey {
240 #[inline]
241 fn from(value: &String) -> Self {
242 let s: &str = &value;
243 ConfigKey::from(s)
244 }
245}
246
247impl From<String> for ConfigKey {
248 #[inline]
249 fn from(value: String) -> Self {
250 let s: &str = &value;
251 ConfigKey::from(s)
252 }
253}
254
255impl From<&ConfigKey> for ConfigKey {
256 #[inline]
257 fn from(value: &ConfigKey) -> Self {
258 value.clone()
259 }
260}
261
262impl Into<String> for ConfigKey {
263 fn into(self) -> String {
264 self.0
265 }
266}