• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lennart-k / ical-rs / #73

12 Jan 2026 01:50PM UTC coverage: 77.771% (-0.03%) from 77.802%
#73

Pull #2

lennart-k
IcalCalendarObjectBuilder: Make attributes public
Pull Request #2: Major overhaul

908 of 1184 new or added lines in 30 files covered. (76.69%)

32 existing lines in 10 files now uncovered.

1221 of 1570 relevant lines covered (77.77%)

2.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.0
/src/parser/ical/component/timezone.rs
1
use crate::{
2
    PropertyParser,
3
    parser::{Component, ComponentMut, ParserError},
4
    property::ContentLine,
5
};
6
use std::{collections::HashMap, io::BufRead};
7

8
#[derive(Debug, Clone, Default)]
9
pub struct IcalTimeZone<const VERIFIED: bool = true> {
10
    pub properties: Vec<ContentLine>,
11
    pub transitions: Vec<IcalTimeZoneTransition<true>>,
12
}
13

14
impl IcalTimeZone {
15
    pub fn get_tzid(&self) -> &str {
3✔
16
        self.get_property("TZID")
6✔
17
            .and_then(|prop| prop.value.as_ref())
9✔
18
            .expect("we already verified this exists")
19
    }
20

21
    /// This is a common property containing a timezone identifier from the IANA TZDB
22
    pub fn get_lic_location(&self) -> Option<&str> {
3✔
23
        self.get_property("X-LIC-LOCATION")
3✔
24
            .and_then(|prop| prop.value.as_deref())
5✔
25
    }
26
}
27

28
#[cfg(feature = "chrono-tz")]
29
impl From<&IcalTimeZone> for Option<chrono_tz::Tz> {
30
    fn from(value: &IcalTimeZone) -> Self {
1✔
31
        use crate::types::get_proprietary_tzid;
32
        use std::str::FromStr;
33

34
        // Try X-LIC-LOCATION
35
        if let Some(loc) = value.get_lic_location()
1✔
36
            && let Ok(tz) = chrono_tz::Tz::from_str(loc)
2✔
37
        {
38
            return Some(tz);
1✔
39
        };
40

41
        // Try using TZID in Olson DB
42
        let tzid = value.get_tzid();
1✔
43
        if let Ok(tz) = chrono_tz::Tz::from_str(tzid) {
2✔
44
            return Some(tz);
1✔
45
        }
46
        // Try map of proprietary timezone IDs (mostly for Microsoft products)
47
        get_proprietary_tzid(tzid)
1✔
48
    }
49
}
50

51
impl IcalTimeZone<false> {
52
    pub fn new() -> IcalTimeZone<false> {
1✔
53
        IcalTimeZone {
54
            properties: Vec::new(),
1✔
55
            transitions: Vec::new(),
1✔
56
        }
57
    }
58
}
59

60
impl<const VERIFIED: bool> Component for IcalTimeZone<VERIFIED> {
61
    const NAMES: &[&str] = &["VTIMEZONE"];
62
    type Unverified = IcalTimeZone<false>;
63

64
    fn get_properties(&self) -> &Vec<ContentLine> {
6✔
65
        &self.properties
×
66
    }
67

UNCOV
68
    fn mutable(self) -> Self::Unverified {
×
69
        IcalTimeZone {
UNCOV
70
            properties: self.properties,
×
UNCOV
71
            transitions: self.transitions,
×
72
        }
73
    }
74
}
75

76
impl ComponentMut for IcalTimeZone<false> {
77
    type Verified = IcalTimeZone<true>;
78

79
    fn get_properties_mut(&mut self) -> &mut Vec<ContentLine> {
2✔
80
        &mut self.properties
81
    }
82

83
    fn add_sub_component<B: BufRead>(
2✔
84
        &mut self,
85
        value: &str,
86
        line_parser: &mut PropertyParser<B>,
87
    ) -> Result<(), ParserError> {
88
        use self::IcalTimeZoneTransitionType::{DAYLIGHT, STANDARD};
89

90
        match value {
1✔
91
            "STANDARD" => {
1✔
92
                let mut transition = IcalTimeZoneTransition::new(STANDARD);
1✔
93
                transition.parse(line_parser)?;
2✔
94
                self.transitions.push(transition.build(None)?);
1✔
95
            }
96
            "DAYLIGHT" => {
1✔
97
                let mut transition = IcalTimeZoneTransition::new(DAYLIGHT);
1✔
98
                transition.parse(line_parser)?;
2✔
99
                self.transitions.push(transition.build(None)?);
1✔
100
            }
NEW
101
            _ => return Err(ParserError::InvalidComponent(value.to_owned())),
×
102
        };
103

104
        Ok(())
1✔
105
    }
106

107
    fn build(
2✔
108
        self,
109
        _timezones: Option<&HashMap<String, Option<chrono_tz::Tz>>>,
110
    ) -> Result<IcalTimeZone<true>, ParserError> {
111
        if !matches!(
8✔
112
            self.get_property("TZID"),
8✔
113
            Some(&ContentLine { value: Some(_), .. }),
114
        ) {
115
            return Err(ParserError::MissingProperty("TZID"));
×
116
        }
117

118
        let verified = IcalTimeZone {
119
            properties: self.properties,
3✔
120
            transitions: self.transitions,
3✔
121
        };
122

123
        #[cfg(feature = "test")]
124
        {
125
            // Verify that the conditions for our getters are actually met
126
            verified.get_tzid();
3✔
127
            verified.get_lic_location();
3✔
128
        }
129

130
        Ok(verified)
2✔
131
    }
132
}
133

134
#[derive(Debug, Clone, Default)]
135
#[cfg_attr(
136
    feature = "rkyv",
137
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
138
)]
139
pub enum IcalTimeZoneTransitionType {
140
    #[default]
141
    STANDARD,
142
    DAYLIGHT,
143
}
144

145
#[derive(Debug, Clone, Default)]
146
pub struct IcalTimeZoneTransition<const VERIFIED: bool = true> {
147
    pub transition: IcalTimeZoneTransitionType,
148
    pub properties: Vec<ContentLine>,
149
}
150

151
impl IcalTimeZoneTransition<false> {
152
    pub fn new(transition: IcalTimeZoneTransitionType) -> Self {
1✔
153
        Self {
154
            transition,
155
            properties: Vec::new(),
1✔
156
        }
157
    }
158
}
159

160
impl<const VERIFIED: bool> Component for IcalTimeZoneTransition<VERIFIED> {
161
    const NAMES: &[&str] = &["STANDARD", "DAYLIGHT"];
162
    type Unverified = IcalTimeZoneTransition<false>;
163

NEW
164
    fn get_comp_name(&self) -> &'static str {
×
NEW
165
        match self.transition {
×
NEW
166
            IcalTimeZoneTransitionType::STANDARD => "STANDARD",
×
NEW
167
            IcalTimeZoneTransitionType::DAYLIGHT => "DAYLIGHT",
×
168
        }
169
    }
170

NEW
171
    fn get_properties(&self) -> &Vec<ContentLine> {
×
UNCOV
172
        &self.properties
×
173
    }
174

175
    fn mutable(self) -> Self::Unverified {
×
176
        IcalTimeZoneTransition {
177
            transition: self.transition,
×
178
            properties: self.properties,
×
179
        }
180
    }
181
}
182

183
impl ComponentMut for IcalTimeZoneTransition<false> {
184
    type Verified = IcalTimeZoneTransition<true>;
185

186
    fn get_properties_mut(&mut self) -> &mut Vec<ContentLine> {
2✔
187
        &mut self.properties
188
    }
189

190
    #[cfg(not(tarpaulin_include))]
191
    fn add_sub_component<B: BufRead>(
192
        &mut self,
193
        value: &str,
194
        _: &mut PropertyParser<B>,
195
    ) -> Result<(), ParserError> {
196
        Err(ParserError::InvalidComponent(value.to_owned()))
197
    }
198

199
    fn build(
1✔
200
        self,
201
        _timezones: Option<&HashMap<String, Option<chrono_tz::Tz>>>,
202
    ) -> Result<IcalTimeZoneTransition<true>, ParserError> {
203
        Ok(IcalTimeZoneTransition {
2✔
204
            transition: self.transition,
1✔
205
            properties: self.properties,
1✔
206
        })
207
    }
208
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc