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