1
use crate::parser::component::alarm::component_alarm;
2
use crate::parser::property::{
3
    prop_attach, prop_attendee, prop_categories, prop_classification, prop_comment, prop_contact,
4
    prop_date_time_completed, prop_date_time_created, prop_date_time_due, prop_date_time_stamp,
5
    prop_date_time_start, prop_description, prop_duration, prop_exception_date_times,
6
    prop_geographic_position, prop_iana, prop_last_modified, prop_location, prop_organizer,
7
    prop_percent_complete, prop_priority, prop_recurrence_date_times, prop_recurrence_id,
8
    prop_recurrence_rule, prop_related_to, prop_request_status, prop_resources, prop_sequence,
9
    prop_status, prop_summary, prop_unique_identifier, prop_url, prop_x,
10
};
11
use crate::parser::types::CalendarComponent;
12
use crate::parser::types::ComponentProperty;
13
use crate::parser::Error;
14
use nom::branch::alt;
15
use nom::bytes::streaming::tag;
16
use nom::combinator::cut;
17
use nom::error::ParseError;
18
use nom::multi::many0;
19
use nom::sequence::tuple;
20
use nom::IResult;
21
use nom::Parser;
22

            
23
338
pub fn component_todo<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], CalendarComponent<'a>, E>
24
338
where
25
338
    E: ParseError<&'a [u8]>
26
338
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
27
338
        + From<Error<'a>>,
28
338
{
29
338
    let (input, (_, properties, alarms, _)) = tuple((
30
338
        tag("BEGIN:VTODO\r\n"),
31
338
        cut(many0(alt((
32
338
            alt((
33
338
                prop_date_time_stamp.map(ComponentProperty::DateTimeStamp),
34
338
                prop_unique_identifier.map(ComponentProperty::UniqueIdentifier),
35
338
                prop_classification.map(ComponentProperty::Classification),
36
338
                prop_date_time_completed.map(ComponentProperty::DateTimeCompleted),
37
338
                prop_date_time_created.map(ComponentProperty::DateTimeCreated),
38
338
                prop_description.map(ComponentProperty::Description),
39
338
                prop_date_time_start.map(ComponentProperty::DateTimeStart),
40
338
                prop_geographic_position.map(ComponentProperty::GeographicPosition),
41
338
                prop_last_modified.map(ComponentProperty::LastModified),
42
338
                prop_location.map(ComponentProperty::Location),
43
338
                prop_organizer.map(ComponentProperty::Organizer),
44
338
                prop_percent_complete.map(ComponentProperty::PercentComplete),
45
338
                prop_priority.map(ComponentProperty::Priority),
46
338
                prop_recurrence_id.map(ComponentProperty::RecurrenceId),
47
338
                prop_sequence.map(ComponentProperty::Sequence),
48
338
            )),
49
338
            alt((
50
338
                prop_status.map(ComponentProperty::Status),
51
338
                prop_summary.map(ComponentProperty::Summary),
52
338
                prop_url.map(ComponentProperty::Url),
53
338
                prop_recurrence_rule.map(ComponentProperty::RecurrenceRule),
54
338
                prop_date_time_due.map(ComponentProperty::DateTimeDue),
55
338
                prop_duration.map(ComponentProperty::Duration),
56
338
                prop_attach.map(ComponentProperty::Attach),
57
338
                prop_attendee.map(ComponentProperty::Attendee),
58
338
                prop_categories.map(ComponentProperty::Categories),
59
338
                prop_comment.map(ComponentProperty::Comment),
60
338
                prop_contact.map(ComponentProperty::Contact),
61
338
                prop_exception_date_times.map(ComponentProperty::ExceptionDateTimes),
62
338
                prop_request_status.map(ComponentProperty::RequestStatus),
63
338
                prop_related_to.map(ComponentProperty::RelatedTo),
64
338
                prop_resources.map(ComponentProperty::Resources),
65
338
                prop_recurrence_date_times.map(ComponentProperty::RecurrenceDateTimes),
66
338
            )),
67
338
            prop_x.map(ComponentProperty::XProperty),
68
338
            prop_iana.map(ComponentProperty::IanaProperty),
69
338
        )))),
70
338
        many0(component_alarm),
71
338
        tag("END:VTODO\r\n"),
72
338
    ))(input)?;
73

            
74
22
    Ok((input, CalendarComponent::ToDo { properties, alarms }))
75
338
}
76

            
77
#[cfg(test)]
78
mod tests {
79
    use super::*;
80
    use crate::common::{Status, Value};
81
    use crate::parser::types::{
82
        CategoriesProperty, Classification, ClassificationProperty, Date, DateOrDateTime, DateTime,
83
        DateTimeDueProperty, DateTimeStampProperty, ParamValue, StatusProperty, SummaryProperty,
84
        Time, UniqueIdentifierProperty,
85
    };
86
    use crate::parser::Error;
87
    use crate::test_utils::check_rem;
88

            
89
    #[test]
90
2
    fn test_component_todo() {
91
2
        let input = b"BEGIN:VTODO\r\n\
92
2
UID:20070313T123432Z-456553@example.com\r\n\
93
2
DTSTAMP:20070313T123432Z\r\n\
94
2
DUE;VALUE=DATE:20070501\r\n\
95
2
SUMMARY:Submit Quebec Income Tax Return for 2006\r\n\
96
2
CLASS:CONFIDENTIAL\r\n\
97
2
CATEGORIES:FAMILY,FINANCE\r\n\
98
2
STATUS:NEEDS-ACTION\r\n\
99
2
END:VTODO\r\n";
100
2

            
101
2
        let (rem, component) = component_todo::<Error>(input).unwrap();
102
2
        check_rem(rem, 0);
103
2

            
104
2
        match component {
105
2
            CalendarComponent::ToDo { properties, .. } => {
106
2
                assert_eq!(properties.len(), 7);
107

            
108
2
                assert_eq!(
109
2
                    properties[0],
110
2
                    ComponentProperty::UniqueIdentifier(UniqueIdentifierProperty {
111
2
                        other_params: vec![],
112
2
                        value: b"20070313T123432Z-456553@example.com".to_vec(),
113
2
                    })
114
2
                );
115

            
116
2
                assert_eq!(
117
2
                    properties[1],
118
2
                    ComponentProperty::DateTimeStamp(DateTimeStampProperty {
119
2
                        other_params: vec![],
120
2
                        value: DateTime {
121
2
                            date: Date {
122
2
                                year: 2007,
123
2
                                month: 3,
124
2
                                day: 13,
125
2
                            },
126
2
                            time: Time {
127
2
                                hour: 12,
128
2
                                minute: 34,
129
2
                                second: 32,
130
2
                                is_utc: true,
131
2
                            },
132
2
                        },
133
2
                    })
134
2
                );
135

            
136
2
                assert_eq!(
137
2
                    properties[2],
138
2
                    ComponentProperty::DateTimeDue(DateTimeDueProperty {
139
2
                        params: vec![ParamValue::ValueType { value: Value::Date },],
140
2
                        value: DateOrDateTime::Date(Date {
141
2
                            year: 2007,
142
2
                            month: 5,
143
2
                            day: 1,
144
2
                        }),
145
2
                    })
146
2
                );
147

            
148
2
                assert_eq!(
149
2
                    properties[3],
150
2
                    ComponentProperty::Summary(SummaryProperty {
151
2
                        params: vec![],
152
2
                        value: b"Submit Quebec Income Tax Return for 2006".to_vec(),
153
2
                    })
154
2
                );
155

            
156
2
                assert_eq!(
157
2
                    properties[4],
158
2
                    ComponentProperty::Classification(ClassificationProperty {
159
2
                        other_params: vec![],
160
2
                        value: Classification::Confidential,
161
2
                    })
162
2
                );
163

            
164
2
                assert_eq!(
165
2
                    properties[5],
166
2
                    ComponentProperty::Categories(CategoriesProperty {
167
2
                        params: vec![],
168
2
                        value: vec![b"FAMILY".to_vec(), b"FINANCE".to_vec()],
169
2
                    })
170
2
                );
171

            
172
2
                assert_eq!(
173
2
                    properties[6],
174
2
                    ComponentProperty::Status(StatusProperty {
175
2
                        other_params: vec![],
176
2
                        value: Status::NeedsAction,
177
2
                    })
178
2
                );
179
            }
180
            _ => panic!("Wrong component type"),
181
        }
182
2
    }
183
}