Skip to main content

rpsl/
attribute.rs

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/// An attribute of an [`Object`](crate::Object).
9///
10/// # Example
11/// ```
12/// # use rpsl::{parse_object, Attribute};
13/// let object = parse_object("
14/// name:           ACME Company
15///
16/// ")?;
17/// let attribute = Attribute::new("name", "ACME Company");
18/// assert_eq!(object[0], attribute);
19/// # Ok::<(), Box<dyn std::error::Error>>(())
20/// ```
21#[derive(Debug, PartialEq, Eq, Clone)]
22#[cfg_attr(feature = "serde", derive(Serialize), serde(bound = ""))]
23pub struct Attribute<'a, Spec: Specification = Raw> {
24    /// The name of the attribute.
25    pub name: Name<'a, Spec>,
26    /// The value of the attribute.
27    #[cfg_attr(feature = "serde", serde(rename = "values"))]
28    pub value: Value<'a, Spec>,
29}
30
31impl<'a, Spec: Specification> Attribute<'a, Spec> {
32    /// Create a new attribute.
33    #[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    /// Validate that the attribute conforms to a target specification.
46    ///
47    /// # Errors
48    /// Returns an [`AttributeError`] if the attribute does not conform to the target specification.
49    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    /// Convert the attribute into a target specification.
58    ///
59    /// # Errors
60    /// Returns an [`AttributeError`] if the attribute does not conform to the target specification.
61    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    /// Convert this attribute into an owned (`'static`) variant.
73    #[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    /// Test helper to create an attribute with a single line value.
85    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    /// Test helper to create an attribute with a multi line value.
98    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/// The name of an [`Attribute`].
145#[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    /// Create a [`Name`].
155    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    /// Create a name from parsed RPSL.
168    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    /// Convert this name into a different specification after validation.
178    fn into_specification<TargetSpec: Specification>(self) -> Name<'a, TargetSpec> {
179        Name {
180            inner: self.inner,
181            _spec: PhantomData,
182        }
183    }
184
185    /// Convert this name into the [`Raw`] specification.
186    pub(crate) fn into_raw(self) -> Name<'a> {
187        self.into_specification::<Raw>()
188    }
189
190    #[must_use]
191    /// Convert this name into an owned (`'static`) version.
192    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    /// Create a new [`Name`] from a string slice.
204    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/// The value of an [`Attribute`].
242/// Since only some values contain multiple lines and single line values do not require
243/// additional heap allocation, an Enum is used to represent both variants.
244#[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    /// A single line value.
252    ///
253    /// # Example
254    /// ```
255    /// # use rpsl::{parse_object, Value};
256    /// let object = parse_object("
257    /// name:           ACME Company
258    ///
259    /// ")?;
260    /// let value = Value::new_single("ACME Company");
261    /// assert_eq!(object[0].value, value);
262    /// # Ok::<(), Box<dyn std::error::Error>>(())
263    /// ```
264    SingleLine {
265        /// The value.
266        inner: Option<Cow<'a, str>>,
267        /// The values specification.
268        #[cfg_attr(feature = "serde", serde(skip))]
269        _spec: PhantomData<Spec>,
270    },
271    /// A value spanning over multiple lines.
272    ///
273    /// # Example
274    /// ```
275    /// # use rpsl::{parse_object, Value};
276    /// let object = parse_object("
277    /// remarks:        Packet Street 6
278    ///                 128 Series of Tubes
279    ///                 Internet
280    ///
281    /// ")?;
282    /// let value: Value = vec!["Packet Street 6", "128 Series of Tubes", "Internet"].into();
283    /// assert_eq!(object[0].value, value);
284    /// # Ok::<(), Box<dyn std::error::Error>>(())
285    /// ```
286    MultiLine {
287        /// The value.
288        inner: Vec<Option<Cow<'a, str>>>,
289        /// The values specification.
290        #[cfg_attr(feature = "serde", serde(skip))]
291        _spec: PhantomData<Spec>,
292    },
293}
294
295impl<'a, Spec: Specification> Value<'a, Spec> {
296    /// The number of lines contained.
297    ///
298    /// # Examples
299    ///
300    /// A value with a single line.
301    /// ```
302    /// # use rpsl::Value;
303    /// let value = Value::new_single("ACME Company");
304    /// assert_eq!(value.lines(), 1);
305    /// ```
306    ///
307    /// A value with multiple lines.
308    /// ```
309    /// # use rpsl::Value;
310    /// let value: Value = vec!["Packet Street 6", "128 Series of Tubes", "Internet"].into();
311    /// assert_eq!(value.lines(), 3);
312    /// ```
313    #[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    /// The lines that contain content and are non empty.
334    ///
335    /// # Example
336    /// ```
337    /// # use rpsl::parse_object;
338    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
339    /// let remarks = parse_object("
340    /// remarks:        I have lots
341    ///                 
342    ///                 to say.
343    ///
344    /// ")?;
345    /// assert_eq!(remarks[0].value.with_content(), vec!["I have lots", "to say."]);
346    /// # Ok(())
347    /// # }
348    /// ```
349    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    /// Returns `true` if the value is empty.
363    ///
364    /// # Example
365    /// ```
366    /// # use rpsl::parse_object;
367    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
368    /// let remarks = parse_object("
369    /// remarks:
370    ///
371    /// ")?;
372    /// assert_eq!(remarks[0].value.is_empty(), true);
373    /// # Ok(())
374    /// # }
375    /// ```
376    #[must_use]
377    pub fn is_empty(&self) -> bool {
378        self.with_content().is_empty()
379    }
380
381    /// Convert this value into a different specification after passing validation.
382    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    /// Convert this value into the [`Raw`] specification.
396    pub(crate) fn into_raw(self) -> Value<'a> {
397        self.into_specification::<Raw>()
398    }
399
400    #[must_use]
401    /// Convert this value into an owned (`'static`) version.
402    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    /// Create a single line value. Empty values are coerced to [`None`].
421    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    /// Create a multi line value. Empty values are coerced to [`None`].
432    ///
433    /// # Panics
434    /// If the given iterator contains less than two values.
435    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    /// Create a single line value from parsed RPSL. Empty values are coerced to [`None`].
454    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    /// Create a multi line value from parsed RPSL. Empty values are coerced to [`None`].
462    ///
463    /// # Panics
464    /// If the given iterator contains less than two values.
465    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    /// Create a new single line value from a string slice.
485    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    /// Create a new value from an iterator of value lines.
507    ///
508    /// # Example
509    /// ```
510    /// # use rpsl::Value;
511    /// let value: Value = vec!["Packet Street 6", "128 Series of Tubes", "Internet"].into();
512    /// assert_eq!(value.lines(), 3);
513    /// ```
514    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
591/// Coerce an empty value to `None`.
592fn 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    /// Creating values from empty strings results in None values.
858    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    /// Multi line attributes cannot be created with only a single value.
865    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    /// A value and &str evaluate as equal if the contents match.
930    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    /// A value and &str do not evaluate as equal if the contents differ.
945    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    /// A value and a Vec<&str> evaluate as equal if the contents match.
971    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    /// A value and a Vec<&str> do not evaluate as equal if the contents differ.
986    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    /// A value and a Vec<Option<&str>> evaluate as equal if the contents match.
1001    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    /// A value and a Vec<Option<&str>> do not evaluate as equal if the contents differ.
1020    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}