Skip to main content

ical/
version.rs

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