1use alloc::{
24 borrow::Cow,
25 string::{String, ToString},
26 vec::Vec,
27};
28use core::{borrow::Borrow, fmt};
29#[cfg(feature = "std")]
30use std::collections::HashMap;
31
32pub(super) const LIST_SEPARATOR: char = ';';
33pub(super) const FIELD_SEPARATOR: char = '=';
34pub(super) const VALUE_SEPARATOR: char = '|';
35
36fn split_once(s: &str, c: char) -> (&str, &str) {
37 match s.find(c) {
38 Some(index) => {
39 let (l, r) = s.split_at(index);
40 (l, &r[1..])
41 }
42 None => (s, ""),
43 }
44}
45
46pub fn iter(s: &str) -> impl DoubleEndedIterator<Item = (&str, &str)> + Clone {
48 s.split(LIST_SEPARATOR)
49 .filter(|p| !p.is_empty())
50 .map(|p| split_once(p, FIELD_SEPARATOR))
51}
52
53pub fn sort<'s, I>(iter: I) -> impl Iterator<Item = (&'s str, &'s str)>
55where
56 I: Iterator<Item = (&'s str, &'s str)>,
57{
58 let mut from = iter.collect::<Vec<(&str, &str)>>();
59 from.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
60 from.into_iter()
61}
62
63pub fn join<'s, C, N>(current: C, new: N) -> impl Iterator<Item = (&'s str, &'s str)> + Clone
65where
66 C: Iterator<Item = (&'s str, &'s str)> + Clone,
67 N: Iterator<Item = (&'s str, &'s str)> + Clone + 's,
68{
69 let n = new.clone();
70 let current = current
71 .clone()
72 .filter(move |(kc, _)| !n.clone().any(|(kn, _)| kn == *kc));
73 current.chain(new)
74}
75
76#[allow(clippy::should_implement_trait)]
78pub fn from_iter<'s, I>(iter: I) -> String
79where
80 I: Iterator<Item = (&'s str, &'s str)>,
81{
82 let mut into = String::new();
83 from_iter_into(iter, &mut into);
84 into
85}
86
87pub fn from_iter_into<'s, I>(iter: I, into: &mut String)
89where
90 I: Iterator<Item = (&'s str, &'s str)>,
91{
92 concat_into(iter, into);
93}
94
95pub fn get<'s>(s: &'s str, k: &str) -> Option<&'s str> {
97 iter(s).find(|(key, _)| *key == k).map(|(_, value)| value)
98}
99
100pub fn values<'s>(s: &'s str, k: &str) -> impl DoubleEndedIterator<Item = &'s str> {
102 match get(s, k) {
103 Some(v) => v.split(VALUE_SEPARATOR),
104 None => {
105 let mut i = "".split(VALUE_SEPARATOR);
106 i.next();
107 i
108 }
109 }
110}
111
112fn _insert<'s, I>(
113 i: I,
114 k: &'s str,
115 v: &'s str,
116) -> (impl Iterator<Item = (&'s str, &'s str)>, Option<&'s str>)
117where
118 I: Iterator<Item = (&'s str, &'s str)> + Clone,
119{
120 let mut iter = i.clone();
121 let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v);
122
123 let current = i.filter(move |x| x.0 != k);
124 let new = Some((k, v)).into_iter();
125 (current.chain(new), item)
126}
127
128pub fn insert<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) {
130 let (iter, item) = _insert(iter(s), k, v);
131 (from_iter(iter), item)
132}
133
134pub fn insert_sort<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) {
136 let (iter, item) = _insert(iter(s), k, v);
137 (from_iter(sort(iter)), item)
138}
139
140pub fn remove<'s>(s: &'s str, k: &str) -> (String, Option<&'s str>) {
142 let mut iter = iter(s);
143 let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v);
144 let iter = iter.filter(|x| x.0 != k);
145 (concat(iter), item)
146}
147
148pub fn is_ordered(s: &str) -> bool {
150 let mut prev = None;
151 for (k, _) in iter(s) {
152 match prev.take() {
153 Some(p) if k < p => return false,
154 _ => prev = Some(k),
155 }
156 }
157 true
158}
159
160fn concat<'s, I>(iter: I) -> String
161where
162 I: Iterator<Item = (&'s str, &'s str)>,
163{
164 let mut into = String::new();
165 concat_into(iter, &mut into);
166 into
167}
168
169fn concat_into<'s, I>(iter: I, into: &mut String)
170where
171 I: Iterator<Item = (&'s str, &'s str)>,
172{
173 let mut first = true;
174 for (k, v) in iter.filter(|(k, _)| !k.is_empty()) {
175 if !first {
176 into.push(LIST_SEPARATOR);
177 }
178 into.push_str(k);
179 if !v.is_empty() {
180 into.push(FIELD_SEPARATOR);
181 into.push_str(v);
182 }
183 first = false;
184 }
185}
186
187#[cfg(feature = "test")]
188#[doc(hidden)]
189pub fn rand(into: &mut String) {
190 use rand::{
191 distributions::{Alphanumeric, DistString},
192 Rng,
193 };
194
195 const MIN: usize = 2;
196 const MAX: usize = 8;
197
198 let mut rng = rand::thread_rng();
199
200 let num = rng.gen_range(MIN..MAX);
201 for i in 0..num {
202 if i != 0 {
203 into.push(LIST_SEPARATOR);
204 }
205 let len = rng.gen_range(MIN..MAX);
206 let key = Alphanumeric.sample_string(&mut rng, len);
207 into.push_str(key.as_str());
208
209 into.push(FIELD_SEPARATOR);
210
211 let len = rng.gen_range(MIN..MAX);
212 let value = Alphanumeric.sample_string(&mut rng, len);
213 into.push_str(value.as_str());
214 }
215}
216
217#[derive(Clone, PartialEq, Eq, Hash, Default)]
249pub struct Parameters<'s>(Cow<'s, str>);
250
251impl<'s> Parameters<'s> {
252 pub const fn empty() -> Self {
254 Self(Cow::Borrowed(""))
255 }
256
257 pub fn is_empty(&self) -> bool {
259 self.0.is_empty()
260 }
261
262 pub fn as_str(&'s self) -> &'s str {
264 &self.0
265 }
266
267 pub fn contains_key<K>(&self, k: K) -> bool
269 where
270 K: Borrow<str>,
271 {
272 super::parameters::get(self.as_str(), k.borrow()).is_some()
273 }
274
275 pub fn get<K>(&'s self, k: K) -> Option<&'s str>
277 where
278 K: Borrow<str>,
279 {
280 super::parameters::get(self.as_str(), k.borrow())
281 }
282
283 pub fn values<K>(&'s self, k: K) -> impl DoubleEndedIterator<Item = &'s str>
285 where
286 K: Borrow<str>,
287 {
288 super::parameters::values(self.as_str(), k.borrow())
289 }
290
291 pub fn iter(&'s self) -> impl DoubleEndedIterator<Item = (&'s str, &'s str)> + Clone {
293 super::parameters::iter(self.as_str())
294 }
295
296 pub fn insert<K, V>(&mut self, k: K, v: V) -> Option<String>
300 where
301 K: Borrow<str>,
302 V: Borrow<str>,
303 {
304 let (inner, item) = super::parameters::insert(self.as_str(), k.borrow(), v.borrow());
305 let item = item.map(|i| i.to_string());
306 self.0 = Cow::Owned(inner);
307 item
308 }
309
310 pub fn remove<K>(&mut self, k: K) -> Option<String>
312 where
313 K: Borrow<str>,
314 {
315 let (inner, item) = super::parameters::remove(self.as_str(), k.borrow());
316 let item = item.map(|i| i.to_string());
317 self.0 = Cow::Owned(inner);
318 item
319 }
320
321 pub fn extend(&mut self, other: &Parameters) {
323 self.extend_from_iter(other.iter());
324 }
325
326 pub fn extend_from_iter<'e, I, K, V>(&mut self, iter: I)
328 where
329 I: Iterator<Item = (&'e K, &'e V)> + Clone,
330 K: Borrow<str> + 'e + ?Sized,
331 V: Borrow<str> + 'e + ?Sized,
332 {
333 let inner = super::parameters::from_iter(super::parameters::join(
334 self.iter(),
335 iter.map(|(k, v)| (k.borrow(), v.borrow())),
336 ));
337 self.0 = Cow::Owned(inner);
338 }
339
340 pub fn into_owned(self) -> Parameters<'static> {
342 Parameters(Cow::Owned(self.0.into_owned()))
343 }
344
345 pub fn is_ordered(&self) -> bool {
347 super::parameters::is_ordered(self.as_str())
348 }
349}
350
351impl<'s> From<&'s str> for Parameters<'s> {
352 fn from(mut value: &'s str) -> Self {
353 value = value.trim_end_matches(|c| {
354 c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR
355 });
356 Self(Cow::Borrowed(value))
357 }
358}
359
360impl From<String> for Parameters<'_> {
361 fn from(mut value: String) -> Self {
362 let s = value.trim_end_matches(|c| {
363 c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR
364 });
365 value.truncate(s.len());
366 Self(Cow::Owned(value))
367 }
368}
369
370impl<'s> From<Cow<'s, str>> for Parameters<'s> {
371 fn from(value: Cow<'s, str>) -> Self {
372 match value {
373 Cow::Borrowed(s) => Parameters::from(s),
374 Cow::Owned(s) => Parameters::from(s),
375 }
376 }
377}
378
379impl<'a> From<Parameters<'a>> for Cow<'_, Parameters<'a>> {
380 fn from(props: Parameters<'a>) -> Self {
381 Cow::Owned(props)
382 }
383}
384
385impl<'a> From<&'a Parameters<'a>> for Cow<'a, Parameters<'a>> {
386 fn from(props: &'a Parameters<'a>) -> Self {
387 Cow::Borrowed(props)
388 }
389}
390
391impl<'s, K, V> FromIterator<(&'s K, &'s V)> for Parameters<'_>
392where
393 K: Borrow<str> + 's + ?Sized,
394 V: Borrow<str> + 's + ?Sized,
395{
396 fn from_iter<T: IntoIterator<Item = (&'s K, &'s V)>>(iter: T) -> Self {
397 let iter = iter.into_iter();
398 let inner = super::parameters::from_iter(iter.map(|(k, v)| (k.borrow(), v.borrow())));
399 Self(Cow::Owned(inner))
400 }
401}
402
403impl<'s, K, V> FromIterator<&'s (K, V)> for Parameters<'_>
404where
405 K: Borrow<str> + 's,
406 V: Borrow<str> + 's,
407{
408 fn from_iter<T: IntoIterator<Item = &'s (K, V)>>(iter: T) -> Self {
409 Self::from_iter(iter.into_iter().map(|(k, v)| (k.borrow(), v.borrow())))
410 }
411}
412
413impl<'s, K, V> From<&'s [(K, V)]> for Parameters<'_>
414where
415 K: Borrow<str> + 's,
416 V: Borrow<str> + 's,
417{
418 fn from(value: &'s [(K, V)]) -> Self {
419 Self::from_iter(value.iter())
420 }
421}
422
423#[cfg(feature = "std")]
424impl<K, V> From<HashMap<K, V>> for Parameters<'_>
425where
426 K: Borrow<str>,
427 V: Borrow<str>,
428{
429 fn from(map: HashMap<K, V>) -> Self {
430 Self::from_iter(map.iter())
431 }
432}
433
434#[cfg(feature = "std")]
435impl<'s> From<&'s Parameters<'s>> for HashMap<&'s str, &'s str> {
436 fn from(props: &'s Parameters<'s>) -> Self {
437 HashMap::from_iter(props.iter())
438 }
439}
440
441#[cfg(feature = "std")]
442impl From<&Parameters<'_>> for HashMap<String, String> {
443 fn from(props: &Parameters<'_>) -> Self {
444 HashMap::from_iter(props.iter().map(|(k, v)| (k.to_string(), v.to_string())))
445 }
446}
447
448#[cfg(feature = "std")]
449impl<'s> From<&'s Parameters<'s>> for HashMap<Cow<'s, str>, Cow<'s, str>> {
450 fn from(props: &'s Parameters<'s>) -> Self {
451 HashMap::from_iter(props.iter().map(|(k, v)| (Cow::from(k), Cow::from(v))))
452 }
453}
454
455#[cfg(feature = "std")]
456impl From<Parameters<'_>> for HashMap<String, String> {
457 fn from(props: Parameters) -> Self {
458 HashMap::from(&props)
459 }
460}
461
462impl fmt::Display for Parameters<'_> {
463 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464 write!(f, "{}", self.0)
465 }
466}
467
468impl fmt::Debug for Parameters<'_> {
469 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
470 write!(f, "{self}")
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477
478 #[test]
479 fn test_parameters() {
480 assert!(Parameters::from("").0.is_empty());
481
482 assert_eq!(Parameters::from("p1"), Parameters::from(&[("p1", "")][..]));
483
484 assert_eq!(
485 Parameters::from("p1=v1"),
486 Parameters::from(&[("p1", "v1")][..])
487 );
488
489 assert_eq!(
490 Parameters::from("p1=v1;p2=v2;"),
491 Parameters::from(&[("p1", "v1"), ("p2", "v2")][..])
492 );
493
494 assert_eq!(
495 Parameters::from("p1=v1;p2=v2;|="),
496 Parameters::from(&[("p1", "v1"), ("p2", "v2")][..])
497 );
498
499 assert_eq!(
500 Parameters::from("p1=v1;p2;p3=v3"),
501 Parameters::from(&[("p1", "v1"), ("p2", ""), ("p3", "v3")][..])
502 );
503
504 assert_eq!(
505 Parameters::from("p1=v 1;p 2=v2"),
506 Parameters::from(&[("p1", "v 1"), ("p 2", "v2")][..])
507 );
508
509 assert_eq!(
510 Parameters::from("p1=x=y;p2=a==b"),
511 Parameters::from(&[("p1", "x=y"), ("p2", "a==b")][..])
512 );
513
514 let mut hm: HashMap<String, String> = HashMap::new();
515 hm.insert("p1".to_string(), "v1".to_string());
516 assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
517
518 let mut hm: HashMap<&str, &str> = HashMap::new();
519 hm.insert("p1", "v1");
520 assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
521
522 let mut hm: HashMap<Cow<str>, Cow<str>> = HashMap::new();
523 hm.insert(Cow::from("p1"), Cow::from("v1"));
524 assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
525 }
526}