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