Skip to main content

vcard/
version.rs

1//! # Version
2//!
3//! The card version indicator.
4//!
5//! [`VcardVersion`] is the decoded `VERSION` line: one of the three defined
6//! versions (2.1 / 3.0 / 4.0), an unrecognised or missing one normalising to
7//! [`V4_0`](VcardVersion::V4_0) at decode time.
8//!
9//! It sits apart from the other properties because the syntax tree, which is
10//! what preserves the raw `VERSION` line byte for byte, treats it as part of
11//! the card envelope. Pure model, no syntax dependency.
12
13use core::{error, fmt, ops, str};
14
15use alloc::string::{String, ToString};
16
17/// Parse vCard version error.
18#[derive(Debug)]
19pub struct VcardVersionParseError(
20    /// The vCard version that cannot be parsed.
21    String,
22);
23
24impl fmt::Display for VcardVersionParseError {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        write!(f, "Cannot parse vCard version `{}`", self.0)
27    }
28}
29
30impl error::Error for VcardVersionParseError {}
31
32/// The vCard version: one of the three defined versions. An unrecognised or
33/// missing version normalises to [`V4_0`](Self::V4_0) (see the module docs).
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub enum VcardVersion {
36    /// vCard 2.1 (versitcard).
37    V2_1,
38    /// vCard 3.0 (RFC 2426).
39    V3_0,
40    /// vCard 4.0 (RFC 6350).
41    V4_0,
42}
43
44impl str::FromStr for VcardVersion {
45    type Err = VcardVersionParseError;
46
47    /// The defined version for a wire string (`2.1`, `3.0`, `4.0`).
48    fn from_str(version: &str) -> Result<Self, Self::Err> {
49        match version {
50            "2.1" => Ok(Self::V2_1),
51            "3.0" => Ok(Self::V3_0),
52            "4.0" => Ok(Self::V4_0),
53            _ => Err(VcardVersionParseError(version.to_string())),
54        }
55    }
56}
57
58impl ops::Deref for VcardVersion {
59    type Target = str;
60
61    fn deref(&self) -> &Self::Target {
62        match self {
63            Self::V2_1 => "2.1",
64            Self::V3_0 => "3.0",
65            Self::V4_0 => "4.0",
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use alloc::string::ToString;
73
74    use crate::version::VcardVersion;
75
76    #[test]
77    fn maps_known_wire_strings_both_ways() {
78        assert_eq!("2.1".parse().ok(), Some(VcardVersion::V2_1));
79        assert_eq!(VcardVersion::V3_0.to_string(), "3.0");
80        assert_eq!(&*VcardVersion::V4_0, "4.0");
81    }
82
83    #[test]
84    fn rejects_unknown_versions() {
85        let error = "5.0".parse::<VcardVersion>().unwrap_err();
86        assert!(error.to_string().contains("5.0"));
87    }
88}