1use std::{borrow::Cow, convert::Infallible, fmt, marker::PhantomData, ops::Deref, str::FromStr};
2
3#[cfg(feature = "serde")]
4use serde::Serialize;
5
6use crate::spec::{AttributeError, Raw, Specification};
7
8#[derive(Debug, PartialEq, Eq, Clone)]
22#[cfg_attr(feature = "serde", derive(Serialize), serde(bound = ""))]
23pub struct Attribute<'a, Spec: Specification = Raw> {
24 pub name: Name<'a, Spec>,
26 #[cfg_attr(feature = "serde", serde(rename = "values"))]
28 pub value: Value<'a, Spec>,
29}
30
31impl<'a, Spec: Specification> Attribute<'a, Spec> {
32 #[must_use]
34 pub fn new<N, V>(name: N, value: V) -> Self
35 where
36 N: Into<Name<'a, Spec>>,
37 V: Into<Value<'a, Spec>>,
38 {
39 Self {
40 name: name.into(),
41 value: value.into(),
42 }
43 }
44
45 pub fn validate<TargetSpec: Specification>(&self) -> Result<(), AttributeError> {
50 let candidate = Attribute {
51 name: self.name.clone().into_specification(),
52 value: self.value.clone().into_specification(),
53 };
54 TargetSpec::validate_attribute(&candidate)
55 }
56
57 pub fn into_spec<TargetSpec: Specification>(
62 self,
63 ) -> Result<Attribute<'a, TargetSpec>, AttributeError> {
64 let candidate = Attribute {
65 name: self.name.into_specification(),
66 value: self.value.into_specification(),
67 };
68 TargetSpec::validate_attribute(&candidate)?;
69 Ok(candidate)
70 }
71
72 #[must_use]
74 pub fn into_owned(self) -> Attribute<'static, Spec> {
75 Attribute {
76 name: self.name.into_owned(),
77 value: self.value.into_owned(),
78 }
79 }
80}
81
82#[cfg(test)]
83impl<'a> Attribute<'a, Raw> {
84 pub(crate) fn unchecked_single<V>(name: &'a str, value: V) -> Self
86 where
87 V: Into<Option<&'a str>>,
88 {
89 let name = Name::new(name);
90 let value = match value.into() {
91 Some(value) => Value::new_single(value),
92 None => Value::new_single(String::new()),
93 };
94 Self { name, value }
95 }
96
97 pub(crate) fn unchecked_multi<I, V>(name: &'a str, values: I) -> Self
99 where
100 I: IntoIterator<Item = V>,
101 V: Into<Option<&'a str>>,
102 {
103 let name = Name::new(name);
104 let collected: Vec<String> = values
105 .into_iter()
106 .map(|value| match value.into() {
107 Some(value) => value.to_string(),
108 None => String::new(),
109 })
110 .collect();
111 let value = Value::new_multi(collected);
112 Self { name, value }
113 }
114}
115
116impl<Spec: Specification> fmt::Display for Attribute<'_, Spec> {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 let values = self.value.values();
119
120 let first_value = values.first().expect("must contain at least one value");
121 match first_value {
122 Some(value) => {
123 writeln!(f, "{:16}{}", format!("{}:", self.name), value)?;
124 }
125 None => writeln!(f, "{}:", self.name)?,
126 }
127
128 let remaining_values = &values[1..];
129 for value in remaining_values {
130 match value {
131 Some(value) => {
132 writeln!(f, "{:16}{}", " ", value)?;
133 }
134 None => {
135 writeln!(f, " ")?;
136 }
137 }
138 }
139
140 Ok(())
141 }
142}
143
144#[derive(Debug, PartialEq, Eq, Clone)]
146#[cfg_attr(feature = "serde", derive(Serialize), serde(transparent))]
147pub struct Name<'a, Spec: Specification = Raw> {
148 inner: Cow<'a, str>,
149 #[cfg_attr(feature = "serde", serde(skip))]
150 _spec: PhantomData<Spec>,
151}
152
153impl Name<'static, Raw> {
154 pub fn new<S>(name: S) -> Self
156 where
157 S: Into<String>,
158 {
159 Self {
160 inner: Cow::Owned(name.into()),
161 _spec: PhantomData,
162 }
163 }
164}
165
166impl<'a> Name<'a, Raw> {
167 pub(crate) fn from_parsed(name: &'a str) -> Self {
169 Self {
170 inner: Cow::Borrowed(name),
171 _spec: PhantomData,
172 }
173 }
174}
175
176impl<'a, Spec: Specification> Name<'a, Spec> {
177 fn into_specification<TargetSpec: Specification>(self) -> Name<'a, TargetSpec> {
179 Name {
180 inner: self.inner,
181 _spec: PhantomData,
182 }
183 }
184
185 pub(crate) fn into_raw(self) -> Name<'a> {
187 self.into_specification::<Raw>()
188 }
189
190 #[must_use]
191 pub fn into_owned(self) -> Name<'static, Spec> {
193 Name {
194 inner: Cow::Owned(self.inner.into_owned()),
195 _spec: PhantomData,
196 }
197 }
198}
199
200impl FromStr for Name<'static, Raw> {
201 type Err = Infallible;
202
203 fn from_str(name: &str) -> Result<Self, Self::Err> {
205 Ok(Self::new(name))
206 }
207}
208
209impl From<&str> for Name<'static, Raw> {
210 fn from(name: &str) -> Self {
211 Self::new(name)
212 }
213}
214
215impl From<String> for Name<'static, Raw> {
216 fn from(name: String) -> Self {
217 Self::new(name)
218 }
219}
220
221impl<Spec: Specification> Deref for Name<'_, Spec> {
222 type Target = str;
223
224 fn deref(&self) -> &Self::Target {
225 self.inner.as_ref()
226 }
227}
228
229impl<Spec: Specification> PartialEq<&str> for Name<'_, Spec> {
230 fn eq(&self, other: &&str) -> bool {
231 self.inner == *other
232 }
233}
234
235impl<Spec: Specification> fmt::Display for Name<'_, Spec> {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 write!(f, "{}", self.inner)
238 }
239}
240
241#[derive(Debug, PartialEq, Eq, Clone)]
245#[cfg_attr(
246 feature = "serde",
247 derive(Serialize),
248 serde(into = "Vec<Option<String>>")
249)]
250pub enum Value<'a, Spec: Specification = Raw> {
251 SingleLine {
265 inner: Option<Cow<'a, str>>,
267 #[cfg_attr(feature = "serde", serde(skip))]
269 _spec: PhantomData<Spec>,
270 },
271 MultiLine {
287 inner: Vec<Option<Cow<'a, str>>>,
289 #[cfg_attr(feature = "serde", serde(skip))]
291 _spec: PhantomData<Spec>,
292 },
293}
294
295impl<'a, Spec: Specification> Value<'a, Spec> {
296 #[must_use]
314 pub fn lines(&self) -> usize {
315 match &self {
316 Self::SingleLine { .. } => 1,
317 Self::MultiLine { inner, .. } => inner.len(),
318 }
319 }
320
321 fn values(&'a self) -> Vec<Option<&'a str>> {
322 match self {
323 Value::SingleLine { inner, .. } => {
324 vec![inner.as_ref().map(std::convert::AsRef::as_ref)]
325 }
326 Value::MultiLine { inner, .. } => inner
327 .iter()
328 .map(|v| v.as_ref().map(std::convert::AsRef::as_ref))
329 .collect(),
330 }
331 }
332
333 pub fn with_content(&self) -> Vec<&str> {
350 match self {
351 Self::SingleLine { inner, .. } => {
352 if let Some(inner) = inner {
353 vec![inner]
354 } else {
355 vec![]
356 }
357 }
358 Self::MultiLine { inner, .. } => inner.iter().flatten().map(AsRef::as_ref).collect(),
359 }
360 }
361
362 #[must_use]
377 pub fn is_empty(&self) -> bool {
378 self.with_content().is_empty()
379 }
380
381 fn into_specification<TargetSpec: Specification>(self) -> Value<'a, TargetSpec> {
383 match self {
384 Value::SingleLine { inner, .. } => Value::SingleLine {
385 inner,
386 _spec: PhantomData,
387 },
388 Value::MultiLine { inner, .. } => Value::MultiLine {
389 inner,
390 _spec: PhantomData,
391 },
392 }
393 }
394
395 pub(crate) fn into_raw(self) -> Value<'a> {
397 self.into_specification::<Raw>()
398 }
399
400 #[must_use]
401 pub fn into_owned(self) -> Value<'static, Spec> {
403 match self {
404 Value::SingleLine { inner, .. } => Value::SingleLine {
405 inner: inner.map(|v| Cow::Owned(v.into_owned())),
406 _spec: PhantomData,
407 },
408 Value::MultiLine { inner, .. } => Value::MultiLine {
409 inner: inner
410 .into_iter()
411 .map(|v| v.map(|v| Cow::Owned(v.into_owned())))
412 .collect(),
413 _spec: PhantomData,
414 },
415 }
416 }
417}
418
419impl Value<'static, Raw> {
420 pub fn new_single<V>(value: V) -> Self
422 where
423 V: Into<String>,
424 {
425 Self::SingleLine {
426 inner: coerce_empty_value(value.into()).map(Cow::Owned),
427 _spec: PhantomData,
428 }
429 }
430
431 pub fn new_multi<I, V>(values: I) -> Self
436 where
437 I: IntoIterator<Item = V>,
438 V: Into<String>,
439 {
440 let s = Self::MultiLine {
441 inner: values
442 .into_iter()
443 .map(|v| coerce_empty_value(v.into()).map(Cow::Owned))
444 .collect(),
445 _spec: PhantomData,
446 };
447 assert!(s.lines() >= 2, "multi line values need at least two lines");
448 s
449 }
450}
451
452impl<'a> Value<'a, Raw> {
453 pub(crate) fn from_parsed_single(value: &'a str) -> Self {
455 Self::SingleLine {
456 inner: coerce_empty_value(value).map(Cow::Borrowed),
457 _spec: PhantomData,
458 }
459 }
460
461 pub(crate) fn from_parsed_multi<I>(values: I) -> Self
466 where
467 I: IntoIterator<Item = &'a str>,
468 {
469 let s = Self::MultiLine {
470 inner: values
471 .into_iter()
472 .map(|v| coerce_empty_value(v).map(Cow::Borrowed))
473 .collect(),
474 _spec: PhantomData,
475 };
476 assert!(s.lines() >= 2, "multi line values need at least two lines");
477 s
478 }
479}
480
481impl FromStr for Value<'static, Raw> {
482 type Err = Infallible;
483
484 fn from_str(value: &str) -> Result<Self, Self::Err> {
486 Ok(Self::new_single(value))
487 }
488}
489
490impl From<&str> for Value<'static, Raw> {
491 fn from(value: &str) -> Self {
492 Self::new_single(value)
493 }
494}
495
496impl From<String> for Value<'static, Raw> {
497 fn from(value: String) -> Self {
498 Self::new_single(value)
499 }
500}
501
502impl<S> From<Vec<S>> for Value<'static, Raw>
503where
504 S: Into<String>,
505{
506 fn from(values: Vec<S>) -> Self {
515 let v: Vec<String> = values.into_iter().map(Into::into).collect();
516
517 match v.len() {
518 0 => Value::new_single(String::new()),
519 1 => Value::new_single(v.into_iter().next().expect("vec has at least one value")),
520 _ => Value::new_multi(v),
521 }
522 }
523}
524
525impl<'a, Spec: Specification> From<Value<'a, Spec>> for Vec<Option<String>> {
526 fn from(value: Value<'a, Spec>) -> Self {
527 match value {
528 Value::SingleLine { inner, .. } => vec![inner.map(|v| v.to_string())],
529 Value::MultiLine { inner, .. } => inner
530 .into_iter()
531 .map(|v| v.map(|v| v.to_string()))
532 .collect(),
533 }
534 }
535}
536
537impl<Spec: Specification> PartialEq<&str> for Value<'_, Spec> {
538 fn eq(&self, other: &&str) -> bool {
539 match &self {
540 Self::MultiLine { .. } => false,
541 Self::SingleLine { inner, .. } => match inner {
542 Some(value) => value == *other,
543 None => coerce_empty_value(other).is_none(),
544 },
545 }
546 }
547}
548
549impl<Spec: Specification> PartialEq<Vec<&str>> for Value<'_, Spec> {
550 fn eq(&self, other: &Vec<&str>) -> bool {
551 if self.lines() != other.len() {
552 return false;
553 }
554
555 match &self {
556 Self::SingleLine { inner, .. } => {
557 let s = inner.as_deref();
558 let other_coerced = coerce_empty_value(other[0]);
559 s == other_coerced
560 }
561 Self::MultiLine { inner, .. } => {
562 let s = inner.iter().map(|v| v.as_deref());
563 let other_coerced = other.iter().map(|&v| coerce_empty_value(v));
564 s.eq(other_coerced)
565 }
566 }
567 }
568}
569
570impl<Spec: Specification> PartialEq<Vec<Option<&str>>> for Value<'_, Spec> {
571 fn eq(&self, other: &Vec<Option<&str>>) -> bool {
572 if self.lines() != other.len() {
573 return false;
574 }
575
576 match &self {
577 Self::SingleLine { inner, .. } => {
578 let s = inner.as_deref();
579 let other = other[0];
580 s == other
581 }
582 Self::MultiLine { inner, .. } => {
583 let s = inner.iter().map(|v| v.as_deref());
584 let other = other.iter().map(|v| v.as_deref());
585 s.eq(other)
586 }
587 }
588 }
589}
590
591fn coerce_empty_value<S>(value: S) -> Option<S>
593where
594 S: AsRef<str>,
595{
596 if value.as_ref().trim().is_empty() {
597 None
598 } else {
599 Some(value)
600 }
601}
602
603#[cfg(test)]
604mod tests {
605 use rstest::*;
606 #[cfg(feature = "serde")]
607 use serde_test::{assert_ser_tokens, Token};
608
609 use super::*;
610
611 #[rstest]
612 #[case(
613 Attribute::unchecked_single("ASNumber", "32934"),
614 "ASNumber: 32934\n"
615 )]
616 #[case(Attribute::unchecked_single("ASNumber", None), "ASNumber:\n")]
617 #[case(
618 Attribute::unchecked_single("ASName", "FACEBOOK"),
619 "ASName: FACEBOOK\n"
620 )]
621 #[case(
622 Attribute::unchecked_single("RegDate", "2004-08-24"),
623 "RegDate: 2004-08-24\n"
624 )]
625 #[case(
626 Attribute::unchecked_single("Ref", "https://rdap.arin.net/registry/autnum/32934"),
627 "Ref: https://rdap.arin.net/registry/autnum/32934\n"
628 )]
629 fn attribute_display_single_line<Spec: Specification>(
630 #[case] attribute: Attribute<Spec>,
631 #[case] expected: &str,
632 ) {
633 assert_eq!(attribute.to_string(), expected);
634 }
635
636 #[rstest]
637 #[case(
638 Attribute::unchecked_multi(
639 "remarks",
640 [
641 "AS1299 is matching RPKI validation state and reject",
642 "invalid prefixes from peers and customers."
643 ]
644
645 ),
646 concat!(
647 "remarks: AS1299 is matching RPKI validation state and reject\n",
648 " invalid prefixes from peers and customers.\n",
649 )
650 )]
651 #[case(
652 Attribute::unchecked_multi(
653 "remarks",
654 [
655 None,
656 None
657 ]
658 ),
659 concat!(
660 "remarks:\n",
661 " \n",
662 )
663 )]
664 fn attribute_display_multi_line<Spec: Specification>(
665 #[case] attribute: Attribute<Spec>,
666 #[case] expected: &str,
667 ) {
668 assert_eq!(attribute.to_string(), expected);
669 }
670
671 #[rstest]
672 #[case(
673 Attribute::unchecked_single("ASNumber", "32934"),
674 &[
675 Token::Struct { name: "Attribute", len: 2 },
676 Token::Str("name"),
677 Token::Str("ASNumber"),
678 Token::Str("values"),
679 Token::Seq { len: Some(1) },
680 Token::Some,
681 Token::Str("32934"),
682 Token::SeqEnd,
683 Token::StructEnd,
684 ],
685 )]
686 #[case(
687 Attribute::unchecked_multi(
688 "address",
689 ["Packet Street 6", "128 Series of Tubes", "Internet"]
690 ),
691 &[
692 Token::Struct { name: "Attribute", len: 2 },
693 Token::Str("name"),
694 Token::Str("address"),
695 Token::Str("values"),
696 Token::Seq { len: Some(3) },
697 Token::Some,
698 Token::Str("Packet Street 6"),
699 Token::Some,
700 Token::Str("128 Series of Tubes"),
701 Token::Some,
702 Token::Str("Internet"),
703 Token::SeqEnd,
704 Token::StructEnd,
705 ],
706 )]
707 #[cfg(feature = "serde")]
708 fn attribute_serialize<Spec: Specification>(
709 #[case] attribute: Attribute<Spec>,
710 #[case] expected: &[Token],
711 ) {
712 assert_ser_tokens(&attribute, expected);
713 }
714
715 #[rstest]
716 #[case("Ref", Name { inner: Cow::Owned("Ref".to_string()), _spec: PhantomData })]
717 fn name_new<S: Into<String>>(#[case] s: S, #[case] expected: Name<Raw>) {
718 let attribute = Name::new(s);
719 assert_eq!(attribute, expected);
720 }
721
722 #[test]
723 fn name_display() {
724 let name_display = Name::new("address").to_string();
725 assert_eq!(name_display, "address");
726 }
727
728 #[rstest]
729 #[case("role")]
730 #[case("person")]
731 fn name_deref(#[case] s: &str) {
732 let name = Name::new(s);
733 assert_eq!(*name, *s);
734 }
735
736 #[rstest]
737 #[case("role")]
738 #[case("person")]
739 fn name_from_str(#[case] s: &str) {
740 assert_eq!(Name::from_str(s).unwrap(), Name::new(s));
741 }
742
743 #[rstest]
744 #[case(Name::new("ASNumber"), Token::Str("ASNumber"))]
745 #[cfg(feature = "serde")]
746 fn name_serialize(#[case] name: Name, #[case] expected: Token) {
747 assert_ser_tokens(&name, &[expected]);
748 }
749
750 #[rstest]
751 #[case(
752 "This is a valid attribute value",
753 Value::SingleLine {
754 inner: Some(Cow::Owned("This is a valid attribute value".to_string())),
755 _spec: PhantomData
756 }
757 )]
758 fn value_new_single<S: Into<String>>(#[case] s: S, #[case] expected: Value<Raw>) {
759 let attribute = Value::new_single(s);
760 assert_eq!(attribute, expected);
761 }
762
763 #[rstest]
764 #[case(
765 vec!["Packet Street 6", "128 Series of Tubes", "Internet"],
766 Value::MultiLine {
767 inner: vec![
768 Some(Cow::Owned("Packet Street 6".to_string())),
769 Some(Cow::Owned("128 Series of Tubes".to_string())),
770 Some(Cow::Owned("Internet".to_string()))
771 ],
772 _spec: PhantomData
773 }
774 )]
775 fn value_new_multi<I, V>(#[case] i: I, #[case] expected: Value<Raw>)
776 where
777 I: IntoIterator<Item = V>,
778 V: Into<String>,
779 {
780 let attribute = Value::new_multi(i);
781 assert_eq!(attribute, expected);
782 }
783
784 #[rstest]
785 #[case("This is a valid attribute value", Value::new_single("This is a valid attribute value".to_string()))]
786 fn value_from_str(#[case] s: &str, #[case] expected: Value) {
787 assert_eq!(Value::from_str(s).unwrap(), expected);
788 }
789
790 #[rstest]
791 fn value_from_empty_str(#[values("", " ")] s: &str) {
792 let expected = Value::SingleLine {
793 inner: None,
794 _spec: PhantomData,
795 };
796 assert_eq!(Value::from_str(s).unwrap(), expected);
797 }
798
799 #[rstest]
800 #[case(
801 Value::new_single("32934"),
802 &[
803 Token::Seq { len: Some(1) },
804 Token::Some,
805 Token::Str("32934"),
806 Token::SeqEnd,
807 ],
808 )]
809 #[case(
810 Value::new_single(""),
811 &[
812 Token::Seq { len: Some(1) },
813 Token::None,
814 Token::SeqEnd,
815 ],
816 )]
817 #[case(
818 Value::new_multi(["Packet Street 6", "128 Series of Tubes", "Internet"]),
819 &[
820 Token::Seq { len: Some(3) },
821 Token::Some,
822 Token::Str("Packet Street 6"),
823 Token::Some,
824 Token::Str("128 Series of Tubes"),
825 Token::Some,
826 Token::Str("Internet"),
827 Token::SeqEnd,
828 ],
829 )]
830 #[cfg(feature = "serde")]
831 fn value_serialize(#[case] value: Value, #[case] expected: &[Token]) {
832 assert_ser_tokens(&value, expected);
833 }
834
835 #[rstest]
836 #[case(
837 Value::new_single(""),
838 Value::SingleLine {
839 inner: None,
840 _spec: PhantomData
841 }
842 )]
843 #[case(
844 Value::new_single(" "),
845 Value::SingleLine {
846 inner: None,
847 _spec: PhantomData
848 }
849 )]
850 #[case(
851 Value::new_multi(["", " ", " "]),
852 Value::MultiLine {
853 inner: vec![None, None, None],
854 _spec: PhantomData
855 }
856 )]
857 fn value_new_empty_is_none(#[case] value: Value, #[case] expected: Value) {
859 assert_eq!(value, expected);
860 }
861
862 #[test]
863 #[should_panic(expected = "multi line values need at least two lines")]
864 fn value_new_multi_with_single_value_panics() {
866 Value::new_multi(["just one"]);
867 }
868
869 #[rstest]
870 #[case(
871 vec!["Packet Street 6"],
872 Value::new_single("Packet Street 6")
873 )]
874 #[case(
875 vec![],
876 Value::new_single("")
877 )]
878 #[case(
879 vec!["Packet Street 6", "128 Series of Tubes", "Internet"],
880 Value::new_multi(vec![
881 "Packet Street 6",
882 "128 Series of Tubes",
883 "Internet"
884 ])
885 )]
886 #[case(
887 vec!["", "128 Series of Tubes", "Internet"],
888 Value::new_multi(vec![
889 "",
890 "128 Series of Tubes",
891 "Internet"
892 ])
893 )]
894 #[case(
895 vec!["", " ", " "],
896 Value::new_multi(vec!["", "", ""])
897 )]
898 fn value_from_vec_of_str(#[case] v: Vec<&str>, #[case] expected: Value) {
899 let value = Value::from(v);
900 assert_eq!(value, expected);
901 }
902
903 #[rstest]
904 #[case("single value", 1)]
905 #[case(vec!["multi", "value", "attribute"].into(), 3)]
906 fn value_lines(#[case] value: Value, #[case] expected: usize) {
907 assert_eq!(value.lines(), expected);
908 }
909
910 #[rstest]
911 #[case(Value::new_single(""), vec![])]
912 #[case(Value::new_single("single value"), vec!["single value"])]
913 #[case(
914 Value::new_multi(["", "128 Series of Tubes", "Internet"]),
915 vec!["128 Series of Tubes", "Internet"]
916 )]
917 #[case(
918 Value::new_multi(["Packet Street 6", "128 Series of Tubes", "Internet"]),
919 vec!["Packet Street 6", "128 Series of Tubes", "Internet"]
920 )]
921 fn value_with_content(#[case] value: Value, #[case] expected: Vec<&str>) {
922 let content = value.with_content();
923 assert_eq!(content, expected);
924 }
925
926 #[rstest]
927 #[case("a value")]
928 #[case("single value")]
929 fn value_partialeq_str_eq_is_eq(#[case] s: &str) {
931 let value = Value::new_single(s);
932 assert_eq!(value, s);
933 }
934
935 #[rstest]
936 #[case(Value::new_single("a value"), "a different value")]
937 #[case(
938 Value::new_multi([
939 "multi",
940 "value"
941 ]),
942 "single value"
943 )]
944 fn value_partialeq_str_ne_is_ne(#[case] value: Value, #[case] s: &str) {
946 assert_ne!(value, s);
947 }
948
949 #[rstest]
950 #[case(
951 Value::new_single("single value"),
952 vec!["single value"]
953 )]
954 #[case(
955 Value::new_single(""),
956 vec![" "]
957 )]
958 #[case(
959 Value::new_multi([
960 "multi",
961 "value",
962 "attribute"
963 ]),
964 vec!["multi", "value", "attribute"]
965 )]
966 #[case(
967 Value::new_multi(["multi", "", "attribute"]),
968 vec!["multi", " ", "attribute"]
969 )]
970 fn value_partialeq_vec_str_eq_is_eq(#[case] value: Value, #[case] v: Vec<&str>) {
972 assert_eq!(value, v);
973 }
974
975 #[rstest]
976 #[case(Value::new_single("single value"), vec!["multi", "value"])]
977 #[case(
978 Value::new_single("single value"),
979 vec!["other single value"]
980 )]
981 #[case(
982 Value::new_multi(["multi", "value", "attribute"]),
983 vec!["different", "multi", "value", "attribute"]
984 )]
985 fn value_partialeq_vec_str_ne_is_ne(#[case] value: Value, #[case] v: Vec<&str>) {
987 assert_ne!(value, v);
988 }
989
990 #[rstest]
991 #[case(Value::new_single("single value"), vec![Some("single value")])]
992 #[case(
993 Value::new_multi(["multi", "value", "attribute"]),
994 vec![Some("multi"), Some("value"), Some("attribute")]
995 )]
996 #[case(
997 Value::new_multi(["multi", "", "attribute"]),
998 vec![Some("multi"), None, Some("attribute")]
999 )]
1000 fn value_partialeq_vec_option_str_eq_is_eq(#[case] value: Value, #[case] v: Vec<Option<&str>>) {
1002 assert_eq!(value, v);
1003 }
1004
1005 #[rstest]
1006 #[case(
1007 Value::new_single("single value"),
1008 vec![Some("multi"), Some("value")]
1009 )]
1010 #[case(
1011 Value::new_single("single value"),
1012 vec![Some("other single value")]
1013 )]
1014 #[case(Value::new_single(""), vec![Some(" ")])]
1015 #[case(
1016 Value::new_multi(["multi", "value", "attribute"]),
1017 vec![Some("different"), Some("multi"), Some("value"), Some("attribute")]
1018 )]
1019 fn value_partialeq_vec_option_str_ne_is_ne(#[case] value: Value, #[case] v: Vec<Option<&str>>) {
1021 assert_ne!(value, v);
1022 }
1023
1024 #[rstest]
1025 #[case(
1026 Value::new_single("single value"),
1027 vec![Some("single value".to_string())]
1028 )]
1029 #[case(
1030 Value::new_multi(["multiple", "values"]),
1031 vec![Some("multiple".to_string()), Some("values".to_string())]
1032 )]
1033 #[case(
1034 Value::new_multi(["multiple", "", "separated", "values"]),
1035 vec![
1036 Some("multiple".to_string()),
1037 None,
1038 Some("separated".to_string()),
1039 Some("values".to_string())
1040 ]
1041 )]
1042 fn value_into_vec_of_option_string(
1043 #[case] value: Value,
1044 #[case] expected: Vec<Option<String>>,
1045 ) {
1046 let vec: Vec<Option<String>> = value.into();
1047 assert_eq!(vec, expected);
1048 }
1049}