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);
107 i.next();
109 i
110 }
111 }
112}
113
114fn _insert<'s, I>(
115 i: I,
116 k: &'s str,
117 v: &'s str,
118) -> (impl Iterator<Item = (&'s str, &'s str)>, Option<&'s str>)
119where
120 I: Iterator<Item = (&'s str, &'s str)> + Clone,
121{
122 let mut iter = i.clone();
123 let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v);
124
125 let current = i.filter(move |x| x.0 != k);
126 let new = Some((k, v)).into_iter();
127 (current.chain(new), item)
128}
129
130pub fn insert<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) {
132 let (iter, item) = _insert(iter(s), k, v);
133 (from_iter(iter), item)
134}
135
136pub fn insert_sort<'s>(s: &'s str, k: &'s str, v: &'s str) -> (String, Option<&'s str>) {
138 let (iter, item) = _insert(iter(s), k, v);
139 (from_iter(sort(iter)), item)
140}
141
142pub fn remove<'s>(s: &'s str, k: &str) -> (String, Option<&'s str>) {
144 let mut iter = iter(s);
145 let item = iter.find(|(key, _)| *key == k).map(|(_, v)| v);
146 let iter = iter.filter(|x| x.0 != k);
147 (concat(iter), item)
148}
149
150pub fn is_ordered(s: &str) -> bool {
152 let mut prev = None;
153 for (k, _) in iter(s) {
154 match prev.take() {
155 Some(p) if k < p => return false,
156 _ => prev = Some(k),
157 }
158 }
159 true
160}
161
162fn concat<'s, I>(iter: I) -> String
163where
164 I: Iterator<Item = (&'s str, &'s str)>,
165{
166 let mut into = String::new();
167 concat_into(iter, &mut into);
168 into
169}
170
171fn concat_into<'s, I>(iter: I, into: &mut String)
172where
173 I: Iterator<Item = (&'s str, &'s str)>,
174{
175 let mut first = true;
176 for (k, v) in iter.filter(|(k, _)| !k.is_empty()) {
177 if !first {
178 into.push(LIST_SEPARATOR);
179 }
180 into.push_str(k);
181 if !v.is_empty() {
182 into.push(FIELD_SEPARATOR);
183 into.push_str(v);
184 }
185 first = false;
186 }
187}
188
189#[cfg(feature = "test")]
190#[doc(hidden)]
191pub fn rand(into: &mut String) {
192 use rand::{
193 distributions::{Alphanumeric, DistString},
194 Rng,
195 };
196
197 const MIN: usize = 2;
198 const MAX: usize = 8;
199
200 let mut rng = rand::thread_rng();
201
202 let num = rng.gen_range(MIN..MAX);
203 for i in 0..num {
204 if i != 0 {
205 into.push(LIST_SEPARATOR);
206 }
207 let len = rng.gen_range(MIN..MAX);
208 let key = Alphanumeric.sample_string(&mut rng, len);
209 into.push_str(key.as_str());
210
211 into.push(FIELD_SEPARATOR);
212
213 let len = rng.gen_range(MIN..MAX);
214 let value = Alphanumeric.sample_string(&mut rng, len);
215 into.push_str(value.as_str());
216 }
217}
218
219#[derive(Clone, PartialEq, Eq, Hash, Default)]
253pub struct Parameters<'s>(Cow<'s, str>);
254
255impl<'s> Parameters<'s> {
256 pub const fn empty() -> Self {
258 Self(Cow::Borrowed(""))
259 }
260
261 pub fn is_empty(&self) -> bool {
263 self.0.is_empty()
264 }
265
266 pub fn as_str(&'s self) -> &'s str {
268 &self.0
269 }
270
271 pub fn contains_key<K>(&self, k: K) -> bool
273 where
274 K: Borrow<str>,
275 {
276 super::parameters::get(self.as_str(), k.borrow()).is_some()
277 }
278
279 pub fn get<K>(&'s self, k: K) -> Option<&'s str>
281 where
282 K: Borrow<str>,
283 {
284 super::parameters::get(self.as_str(), k.borrow())
285 }
286
287 pub fn values<K>(&'s self, k: K) -> impl DoubleEndedIterator<Item = &'s str>
289 where
290 K: Borrow<str>,
291 {
292 super::parameters::values(self.as_str(), k.borrow())
293 }
294
295 pub fn iter(&'s self) -> impl DoubleEndedIterator<Item = (&'s str, &'s str)> + Clone {
297 super::parameters::iter(self.as_str())
298 }
299
300 pub fn insert<K, V>(&mut self, k: K, v: V) -> Option<String>
304 where
305 K: Borrow<str>,
306 V: Borrow<str>,
307 {
308 let (inner, item) = super::parameters::insert(self.as_str(), k.borrow(), v.borrow());
309 let item = item.map(|i| i.to_string());
310 self.0 = Cow::Owned(inner);
311 item
312 }
313
314 pub fn remove<K>(&mut self, k: K) -> Option<String>
316 where
317 K: Borrow<str>,
318 {
319 let (inner, item) = super::parameters::remove(self.as_str(), k.borrow());
320 let item = item.map(|i| i.to_string());
321 self.0 = Cow::Owned(inner);
322 item
323 }
324
325 pub fn extend(&mut self, other: &Parameters) {
327 self.extend_from_iter(other.iter());
328 }
329
330 pub fn extend_from_iter<'e, I, K, V>(&mut self, iter: I)
332 where
333 I: Iterator<Item = (&'e K, &'e V)> + Clone,
334 K: Borrow<str> + 'e + ?Sized,
335 V: Borrow<str> + 'e + ?Sized,
336 {
337 let inner = super::parameters::from_iter(super::parameters::join(
338 self.iter(),
339 iter.map(|(k, v)| (k.borrow(), v.borrow())),
340 ));
341 self.0 = Cow::Owned(inner);
342 }
343
344 pub fn into_owned(self) -> Parameters<'static> {
346 Parameters(Cow::Owned(self.0.into_owned()))
347 }
348
349 pub fn is_ordered(&self) -> bool {
351 super::parameters::is_ordered(self.as_str())
352 }
353}
354
355impl<'s> From<&'s str> for Parameters<'s> {
356 fn from(mut value: &'s str) -> Self {
357 value = value.trim_end_matches(|c| {
358 c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR
359 });
360 Self(Cow::Borrowed(value))
361 }
362}
363
364impl From<String> for Parameters<'_> {
365 fn from(mut value: String) -> Self {
366 let s = value.trim_end_matches(|c| {
367 c == LIST_SEPARATOR || c == FIELD_SEPARATOR || c == VALUE_SEPARATOR
368 });
369 value.truncate(s.len());
370 Self(Cow::Owned(value))
371 }
372}
373
374impl<'s> From<Cow<'s, str>> for Parameters<'s> {
375 fn from(value: Cow<'s, str>) -> Self {
376 match value {
377 Cow::Borrowed(s) => Parameters::from(s),
378 Cow::Owned(s) => Parameters::from(s),
379 }
380 }
381}
382
383impl<'a> From<Parameters<'a>> for Cow<'_, Parameters<'a>> {
384 fn from(props: Parameters<'a>) -> Self {
385 Cow::Owned(props)
386 }
387}
388
389impl<'a> From<&'a Parameters<'a>> for Cow<'a, Parameters<'a>> {
390 fn from(props: &'a Parameters<'a>) -> Self {
391 Cow::Borrowed(props)
392 }
393}
394
395impl<'s, K, V> FromIterator<(&'s K, &'s V)> for Parameters<'_>
396where
397 K: Borrow<str> + 's + ?Sized,
398 V: Borrow<str> + 's + ?Sized,
399{
400 fn from_iter<T: IntoIterator<Item = (&'s K, &'s V)>>(iter: T) -> Self {
401 let iter = iter.into_iter();
402 let inner = super::parameters::from_iter(iter.map(|(k, v)| (k.borrow(), v.borrow())));
403 Self(Cow::Owned(inner))
404 }
405}
406
407impl<'s, K, V> FromIterator<&'s (K, V)> for Parameters<'_>
408where
409 K: Borrow<str> + 's,
410 V: Borrow<str> + 's,
411{
412 fn from_iter<T: IntoIterator<Item = &'s (K, V)>>(iter: T) -> Self {
413 Self::from_iter(iter.into_iter().map(|(k, v)| (k.borrow(), v.borrow())))
414 }
415}
416
417impl<'s, K, V> From<&'s [(K, V)]> for Parameters<'_>
418where
419 K: Borrow<str> + 's,
420 V: Borrow<str> + 's,
421{
422 fn from(value: &'s [(K, V)]) -> Self {
423 Self::from_iter(value.iter())
424 }
425}
426
427#[cfg(feature = "std")]
428impl<K, V> From<HashMap<K, V>> for Parameters<'_>
429where
430 K: Borrow<str>,
431 V: Borrow<str>,
432{
433 fn from(map: HashMap<K, V>) -> Self {
434 Self::from_iter(map.iter())
435 }
436}
437
438#[cfg(feature = "std")]
439impl<'s> From<&'s Parameters<'s>> for HashMap<&'s str, &'s str> {
440 fn from(props: &'s Parameters<'s>) -> Self {
441 HashMap::from_iter(props.iter())
442 }
443}
444
445#[cfg(feature = "std")]
446impl From<&Parameters<'_>> for HashMap<String, String> {
447 fn from(props: &Parameters<'_>) -> Self {
448 HashMap::from_iter(props.iter().map(|(k, v)| (k.to_string(), v.to_string())))
449 }
450}
451
452#[cfg(feature = "std")]
453impl<'s> From<&'s Parameters<'s>> for HashMap<Cow<'s, str>, Cow<'s, str>> {
454 fn from(props: &'s Parameters<'s>) -> Self {
455 HashMap::from_iter(props.iter().map(|(k, v)| (Cow::from(k), Cow::from(v))))
456 }
457}
458
459#[cfg(feature = "std")]
460impl From<Parameters<'_>> for HashMap<String, String> {
461 fn from(props: Parameters) -> Self {
462 HashMap::from(&props)
463 }
464}
465
466impl fmt::Display for Parameters<'_> {
467 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468 write!(f, "{}", self.0)
469 }
470}
471
472impl fmt::Debug for Parameters<'_> {
473 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
474 write!(f, "{self}")
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 #[test]
483 fn test_parameters() {
484 assert!(Parameters::from("").0.is_empty());
485
486 assert_eq!(Parameters::from("p1"), Parameters::from(&[("p1", "")][..]));
487
488 assert_eq!(
489 Parameters::from("p1=v1"),
490 Parameters::from(&[("p1", "v1")][..])
491 );
492
493 assert_eq!(
494 Parameters::from("p1=v1;p2=v2;"),
495 Parameters::from(&[("p1", "v1"), ("p2", "v2")][..])
496 );
497
498 assert_eq!(
499 Parameters::from("p1=v1;p2=v2;|="),
500 Parameters::from(&[("p1", "v1"), ("p2", "v2")][..])
501 );
502
503 assert_eq!(
504 Parameters::from("p1=v1;p2;p3=v3"),
505 Parameters::from(&[("p1", "v1"), ("p2", ""), ("p3", "v3")][..])
506 );
507
508 assert_eq!(
509 Parameters::from("p1=v 1;p 2=v2"),
510 Parameters::from(&[("p1", "v 1"), ("p 2", "v2")][..])
511 );
512
513 assert_eq!(
514 Parameters::from("p1=x=y;p2=a==b"),
515 Parameters::from(&[("p1", "x=y"), ("p2", "a==b")][..])
516 );
517
518 let mut hm: HashMap<String, String> = HashMap::new();
519 hm.insert("p1".to_string(), "v1".to_string());
520 assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
521
522 let mut hm: HashMap<&str, &str> = HashMap::new();
523 hm.insert("p1", "v1");
524 assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
525
526 let mut hm: HashMap<Cow<str>, Cow<str>> = HashMap::new();
527 hm.insert(Cow::from("p1"), Cow::from("v1"));
528 assert_eq!(Parameters::from(hm), Parameters::from("p1=v1"));
529 }
530
531 #[test]
532 fn values_iterator_for_non_existing_key_is_empty() {
533 let params = Parameters::from("p1=1");
534
535 assert_eq!(params.values("p2").next(), None);
536 }
537}