• 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

90.57
/src/parser/ical/component/event/builder.rs
1
use crate::{
2
    PropertyParser,
3
    component::{IcalAlarmBuilder, IcalEvent},
4
    parser::{
5
        Component, ComponentMut, GetProperty, IcalDTENDProperty, IcalDTSTARTProperty,
6
        IcalDURATIONProperty, IcalEXDATEProperty, IcalEXRULEProperty, IcalMETHODProperty,
7
        IcalRDATEProperty, IcalRECURIDProperty, IcalRRULEProperty, IcalSUMMARYProperty,
8
        IcalUIDProperty, ParserError,
9
    },
10
    property::ContentLine,
11
};
12
use std::{collections::HashMap, io::BufRead};
13

14
#[derive(Debug, Clone, Default)]
15
pub struct IcalEventBuilder {
16
    pub properties: Vec<ContentLine>,
17
    pub alarms: Vec<IcalAlarmBuilder>,
18
}
19

20
impl IcalEventBuilder {
21
    pub fn new() -> Self {
2✔
22
        Self {
23
            properties: Vec::new(),
2✔
24
            alarms: Vec::new(),
2✔
25
        }
26
    }
27
}
28

29
impl Component for IcalEventBuilder {
30
    const NAMES: &[&str] = &["VEVENT"];
31
    type Unverified = Self;
32

33
    fn get_properties(&self) -> &Vec<ContentLine> {
1✔
34
        &self.properties
35
    }
36

NEW
37
    fn mutable(self) -> Self::Unverified {
×
NEW
38
        self
×
39
    }
40
}
41

42
impl ComponentMut for IcalEventBuilder {
43
    type Verified = IcalEvent;
44

45
    fn get_properties_mut(&mut self) -> &mut Vec<ContentLine> {
1✔
46
        &mut self.properties
47
    }
48

49
    #[inline]
50
    fn add_sub_component<B: BufRead>(
2✔
51
        &mut self,
52
        value: &str,
53
        line_parser: &mut PropertyParser<B>,
54
    ) -> Result<(), ParserError> {
55
        match value {
2✔
56
            "VALARM" => {
2✔
57
                let mut alarm = IcalAlarmBuilder::new();
2✔
58
                alarm.parse(line_parser)?;
4✔
59
                self.alarms.push(alarm);
2✔
60
            }
NEW
61
            _ => return Err(ParserError::InvalidComponent(value.to_owned())),
×
62
        };
63

64
        Ok(())
2✔
65
    }
66

67
    fn build(
1✔
68
        self,
69
        timezones: Option<&HashMap<String, Option<chrono_tz::Tz>>>,
70
    ) -> Result<IcalEvent, ParserError> {
71
        // The following are REQUIRED, but MUST NOT occur more than once: dtstamp / uid
72
        let dtstamp = self.safe_get_required(timezones)?;
2✔
73
        let IcalUIDProperty(uid, _) = self.safe_get_required(timezones)?;
4✔
74
        // REQUIRED if METHOD not specified:
75
        // For now just ensure that no METHOD property exists
NEW
76
        assert!(
×
77
            self.safe_get_optional::<IcalMETHODProperty>(timezones)?
78
                .is_none()
79
        );
80
        let dtstart: IcalDTSTARTProperty = self.safe_get_required(timezones)?;
1✔
81

82
        // OPTIONAL, but NOT MORE THAN ONCE: class / created / description / geo / last-mod / location / organizer / priority / seq / status / summary / transp / url / recurid / rrule
83
        let summary = self.safe_get_optional::<IcalSUMMARYProperty>(timezones)?;
4✔
84
        let recurid = self.safe_get_optional::<IcalRECURIDProperty>(timezones)?;
3✔
85
        if let Some(recurid) = &recurid {
3✔
86
            recurid.validate_dtstart(&dtstart.0)?;
2✔
87
        }
88

89
        // OPTIONAL, but MUTUALLY EXCLUSIVE
90
        if self.has_prop::<IcalDTENDProperty>() && self.has_prop::<IcalDURATIONProperty>() {
5✔
NEW
91
            return Err(ParserError::PropertyConflict(
×
92
                "both DTEND and DURATION are defined",
93
            ));
94
        }
95
        let dtend = self.safe_get_optional::<IcalDTENDProperty>(timezones)?;
2✔
96
        let duration = self.safe_get_optional::<IcalDURATIONProperty>(timezones)?;
2✔
97

98
        // OPTIONAL, allowed multiple times: attach / attendee / categories / comment / contact / exdate / rstatus / related / resources / rdate / x-prop / iana-prop
99
        let rrule_dtstart = dtstart.0.utc().with_timezone(&rrule::Tz::UTC);
4✔
100
        let rdates = self.safe_get_all::<IcalRDATEProperty>(timezones)?;
4✔
101
        let exdates = self.safe_get_all::<IcalEXDATEProperty>(timezones)?;
4✔
102
        let rrules = self
6✔
103
            .safe_get_all::<IcalRRULEProperty>(timezones)?
2✔
104
            .into_iter()
105
            .map(|rrule| rrule.0.validate(rrule_dtstart))
6✔
106
            .collect::<Result<Vec<_>, _>>()?;
107
        let exrules = self
6✔
108
            .safe_get_all::<IcalEXRULEProperty>(timezones)?
2✔
109
            .into_iter()
110
            .map(|rrule| rrule.0.validate(rrule_dtstart))
2✔
111
            .collect::<Result<Vec<_>, _>>()?;
112

113
        Ok(IcalEvent {
2✔
114
            uid,
2✔
115
            dtstamp,
2✔
116
            dtstart,
2✔
117
            dtend,
2✔
118
            duration,
2✔
119
            rdates,
2✔
120
            rrules,
2✔
121
            exdates,
2✔
122
            exrules,
2✔
123
            recurid,
124
            summary,
2✔
125
            properties: self.properties,
2✔
126
            alarms: self
4✔
127
                .alarms
128
                .into_iter()
2✔
129
                .map(|alarm| alarm.build(timezones))
6✔
130
                .collect::<Result<Vec<_>, _>>()?,
2✔
131
        })
132
    }
133
}
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