1
mod component;
2
mod object;
3
mod param;
4
mod property;
5
mod value;
6

            
7
use crate::error::AetoliaResult;
8
use std::io::Write;
9

            
10
pub trait WriteModel {
11
    fn write_model<W: Write>(&self, writer: &mut W) -> AetoliaResult<()>;
12
}
13

            
14
#[cfg(test)]
15
mod tests {
16
    use crate::convert::ToModel;
17
    use crate::parser::Error;
18
    use crate::serialize::WriteModel;
19
    use crate::test_utils::check_rem;
20

            
21
    #[test]
22
2
    fn rtt_single_event() {
23
2
        let content = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTAMP:20211010T000000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
24
2
        round_trip_ical_object(content);
25
2
    }
26

            
27
    // Check with data taken from RFC 5545, section 3.6.1
28
    #[test]
29
2
    fn rtt_event() {
30
2
        let example_1 = "BEGIN:VCALENDAR\r\n\
31
2
BEGIN:VEVENT\r\n\
32
2
UID:19970901T130000Z-123401@example.com\r\n\
33
2
DTSTAMP:19970901T130000Z\r\n\
34
2
DTSTART:19970903T163000Z\r\n\
35
2
DTEND:19970903T190000Z\r\n\
36
2
SUMMARY:Annual Employee Review\r\n\
37
2
CLASS:PRIVATE\r\n\
38
2
CATEGORIES:BUSINESS,HUMAN RESOURCES\r\n\
39
2
END:VEVENT\r\n\
40
2
END:VCALENDAR\r\n";
41
2

            
42
2
        round_trip_ical_object(example_1);
43
2

            
44
2
        let example_2 = "BEGIN:VCALENDAR\r\n\
45
2
BEGIN:VEVENT\r\n\
46
2
UID:19970901T130000Z-123402@example.com\r\n\
47
2
DTSTAMP:19970901T130000Z\r\n\
48
2
DTSTART:19970401T163000Z\r\n\
49
2
DTEND:19970402T010000Z\r\n\
50
2
SUMMARY:Laurel is in sensitivity awareness class.\r\n\
51
2
CLASS:PUBLIC\r\n\
52
2
CATEGORIES:BUSINESS,HUMAN RESOURCES\r\n\
53
2
TRANSP:TRANSPARENT\r\n\
54
2
END:VEVENT\r\n\
55
2
END:VCALENDAR\r\n";
56
2

            
57
2
        round_trip_ical_object(example_2);
58
2

            
59
2
        let example_3 = "BEGIN:VCALENDAR\r\n\
60
2
BEGIN:VEVENT\r\n\
61
2
UID:19970901T130000Z-123403@example.com\r\n\
62
2
DTSTAMP:19970901T130000Z\r\n\
63
2
DTSTART;VALUE=DATE:19971102\r\n\
64
2
SUMMARY:Our Blissful Anniversary\r\n\
65
2
TRANSP:TRANSPARENT\r\n\
66
2
CLASS:CONFIDENTIAL\r\n\
67
2
CATEGORIES:ANNIVERSARY,PERSONAL,SPECIAL OCCASION\r\n\
68
2
RRULE:FREQ=YEARLY\r\n\
69
2
END:VEVENT\r\n\
70
2
END:VCALENDAR\r\n";
71
2

            
72
2
        round_trip_ical_object(example_3);
73
2

            
74
2
        let example_4 = "BEGIN:VCALENDAR\r\n\
75
2
BEGIN:VEVENT\r\n\
76
2
UID:20070423T123432Z-541111@example.com\r\n\
77
2
DTSTAMP:20070423T123432Z\r\n\
78
2
DTSTART;VALUE=DATE:20070628\r\n\
79
2
DTEND;VALUE=DATE:20070709\r\n\
80
2
SUMMARY:Festival International de Jazz de Montreal\r\n\
81
2
TRANSP:TRANSPARENT\r\n\
82
2
END:VEVENT\r\n\
83
2
END:VCALENDAR\r\n";
84
2

            
85
2
        round_trip_ical_object(example_4);
86
2
    }
87

            
88
    // Check with data taken from RFC 5545, section 3.6.2
89
    #[test]
90
2
    fn rtt_to_do() {
91
2
        let example_1 = "BEGIN:VCALENDAR\r\n\
92
2
BEGIN:VTODO\r\n\
93
2
UID:20070313T123432Z-456553@example.com\r\n\
94
2
DTSTAMP:20070313T123432Z\r\n\
95
2
DUE;VALUE=DATE:20070501\r\n\
96
2
SUMMARY:Submit Quebec Income Tax Return for 2006\r\n\
97
2
CLASS:CONFIDENTIAL\r\n\
98
2
CATEGORIES:FAMILY,FINANCE\r\n\
99
2
STATUS:NEEDS-ACTION\r\n\
100
2
END:VTODO\r\n\
101
2
END:VCALENDAR\r\n";
102
2

            
103
2
        round_trip_ical_object(example_1);
104
2

            
105
2
        let example_2 = "BEGIN:VCALENDAR\r\n\
106
2
BEGIN:VTODO\r\n\
107
2
UID:20070514T103211Z-123404@example.com\r\n\
108
2
DTSTAMP:20070514T103211Z\r\n\
109
2
DTSTART:20070514T110000Z\r\n\
110
2
DUE:20070709T130000Z\r\n\
111
2
COMPLETED:20070707T100000Z\r\n\
112
2
SUMMARY:Submit Revised Internet-Draft\r\n\
113
2
PRIORITY:1\r\n\
114
2
STATUS:NEEDS-ACTION\r\n\
115
2
END:VTODO\r\n\
116
2
END:VCALENDAR\r\n";
117
2

            
118
2
        round_trip_ical_object(example_2);
119
2
    }
120

            
121
    #[test]
122
2
    fn rtt_journal() {
123
2
        let example_1 = "BEGIN:VCALENDAR\r\n\
124
2
BEGIN:VJOURNAL\r\n\
125
2
UID:19970901T130000Z-123405@example.com\r\n\
126
2
DTSTAMP:19970901T130000Z\r\n\
127
2
DTSTART;VALUE=DATE:19970317\r\n\
128
2
SUMMARY:Staff meeting minutes\r\n\
129
2
DESCRIPTION:1. Staff meeting: Participants include Joe\\, Lisa\\, and Bob. Aurora project plans were reviewed. There is currently no budget reserves for this project. Lisa will escalate to management. Next meeting on Tuesday.\\n 2. Telephone Conference: ABC Corp. sales representative called to discuss new printer. Promised to get us a demo by Friday.\\n3. Henry Miller (Handsoff Insurance): Car was totaled by tree. Is looking into a loaner car. 555-2323 (tel).\r\n\
130
2
END:VJOURNAL\r\n\
131
2
END:VCALENDAR\r\n";
132
2

            
133
2
        round_trip_ical_object(example_1);
134
2
    }
135

            
136
    #[test]
137
2
    fn rtt_free_busy() {
138
2
        let example_1 = "BEGIN:VCALENDAR\r\n\
139
2
BEGIN:VFREEBUSY\r\n\
140
2
UID:19970901T082949Z-FA43EF@example.com\r\n\
141
2
ORGANIZER:mailto:jane_doe@example.com\r\n\
142
2
ATTENDEE:mailto:john_public@example.com\r\n\
143
2
DTSTART:19971015T050000Z\r\n\
144
2
DTEND:19971016T050000Z\r\n\
145
2
DTSTAMP:19970901T083000Z\r\n\
146
2
END:VFREEBUSY\r\n\
147
2
END:VCALENDAR\r\n";
148
2

            
149
2
        round_trip_ical_object(example_1);
150
2

            
151
2
        let example_2 = "BEGIN:VCALENDAR\r\n\
152
2
BEGIN:VFREEBUSY\r\n\
153
2
UID:19970901T095957Z-76A912@example.com\r\n\
154
2
ORGANIZER:mailto:jane_doe@example.com\r\n\
155
2
ATTENDEE:mailto:john_public@example.com\r\n\
156
2
DTSTAMP:19970901T100000Z\r\n\
157
2
FREEBUSY:19971015T050000Z/PT8H30M,19971015T160000Z/PT5H30M,19971015T223000Z/PT6H30M\r\n\
158
2
URL:http://example.com/pub/busy/jpublic-01.ifb\r\n\
159
2
COMMENT:This iCalendar file contains busy time information for the next three months.\r\n\
160
2
END:VFREEBUSY\r\n\
161
2
END:VCALENDAR\r\n";
162
2

            
163
2
        round_trip_ical_object(example_2);
164
2

            
165
2
        let example_3 = "BEGIN:VCALENDAR\r\n\
166
2
BEGIN:VFREEBUSY\r\n\
167
2
UID:19970901T115957Z-76A912@example.com\r\n\
168
2
DTSTAMP:19970901T120000Z\r\n\
169
2
ORGANIZER:mailto:jsmith@example.com\r\n\
170
2
DTSTART:19980313T141711Z\r\n\
171
2
DTEND:19980410T141711Z\r\n\
172
2
FREEBUSY:19980314T233000Z/19980315T003000Z\r\n\
173
2
FREEBUSY:19980316T153000Z/19980316T163000Z\r\n\
174
2
FREEBUSY:19980318T030000Z/19980318T040000Z\r\n\
175
2
URL:http://www.example.com/calendar/busytime/jsmith.ifb\r\n\
176
2
END:VFREEBUSY\r\n\
177
2
END:VCALENDAR\r\n";
178
2

            
179
2
        round_trip_ical_object(example_3);
180
2
    }
181

            
182
    #[test]
183
2
    fn rtt_time_zone() {
184
2
        let example_1 = "BEGIN:VCALENDAR\r\n\
185
2
BEGIN:VTIMEZONE\r\n\
186
2
TZID:America/New_York\r\n\
187
2
LAST-MODIFIED:20050809T050000Z\r\n\
188
2
BEGIN:DAYLIGHT\r\n\
189
2
DTSTART:19670430T020000\r\n\
190
2
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19730429T070000Z\r\n\
191
2
TZOFFSETFROM:-0500\r\n\
192
2
TZOFFSETTO:-0400\r\n\
193
2
TZNAME:EDT\r\n\
194
2
END:DAYLIGHT\r\n\
195
2
BEGIN:STANDARD\r\n\
196
2
DTSTART:19671029T020000\r\n\
197
2
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU;UNTIL=20061029T060000Z\r\n\
198
2
TZOFFSETFROM:-0400\r\n\
199
2
TZOFFSETTO:-0500\r\n\
200
2
TZNAME:EST\r\n\
201
2
END:STANDARD\r\n\
202
2
BEGIN:DAYLIGHT\r\n\
203
2
DTSTART:19740106T020000\r\n\
204
2
RDATE:19750223T020000\r\n\
205
2
TZOFFSETFROM:-0500\r\n\
206
2
TZOFFSETTO:-0400\r\n\
207
2
TZNAME:EDT\r\n\
208
2
END:DAYLIGHT\r\n\
209
2
BEGIN:DAYLIGHT\r\n\
210
2
DTSTART:19760425T020000\r\n\
211
2
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=-1SU;UNTIL=19860427T070000Z\r\n\
212
2
TZOFFSETFROM:-0500\r\n\
213
2
TZOFFSETTO:-0400\r\n\
214
2
TZNAME:EDT\r\n\
215
2
END:DAYLIGHT\r\n\
216
2
BEGIN:DAYLIGHT\r\n\
217
2
DTSTART:19870405T020000\r\n\
218
2
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=20060402T070000Z\r\n\
219
2
TZOFFSETFROM:-0500\r\n\
220
2
TZOFFSETTO:-0400\r\n\
221
2
TZNAME:EDT\r\n\
222
2
END:DAYLIGHT\r\n\
223
2
BEGIN:DAYLIGHT\r\n\
224
2
DTSTART:20070311T020000\r\n\
225
2
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\n\
226
2
TZOFFSETFROM:-0500\r\n\
227
2
TZOFFSETTO:-0400\r\n\
228
2
TZNAME:EDT\r\n\
229
2
END:DAYLIGHT\r\n\
230
2
BEGIN:STANDARD\r\n\
231
2
DTSTART:20071104T020000\r\n\
232
2
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\n\
233
2
TZOFFSETFROM:-0400\r\n\
234
2
TZOFFSETTO:-0500\r\n\
235
2
TZNAME:EST\r\n\
236
2
END:STANDARD\r\n\
237
2
END:VTIMEZONE\r\n\
238
2
END:VCALENDAR\r\n";
239
2

            
240
2
        round_trip_ical_object(example_1);
241
2

            
242
2
        let example_2 = "BEGIN:VCALENDAR\r\n\
243
2
BEGIN:VTIMEZONE\r\n\
244
2
TZID:America/New_York\r\n\
245
2
LAST-MODIFIED:20050809T050000Z\r\n\
246
2
BEGIN:STANDARD\r\n\
247
2
DTSTART:20071104T020000\r\n\
248
2
TZOFFSETFROM:-0400\r\n\
249
2
TZOFFSETTO:-0500\r\n\
250
2
TZNAME:EST\r\n\
251
2
END:STANDARD\r\n\
252
2
BEGIN:DAYLIGHT\r\n\
253
2
DTSTART:20070311T020000\r\n\
254
2
TZOFFSETFROM:-0500\r\n\
255
2
TZOFFSETTO:-0400\r\n\
256
2
TZNAME:EDT\r\n\
257
2
END:DAYLIGHT\r\n\
258
2
END:VTIMEZONE\r\n\
259
2
END:VCALENDAR\r\n";
260
2

            
261
2
        round_trip_ical_object(example_2);
262
2

            
263
2
        let example_3 = "BEGIN:VCALENDAR\r\n\
264
2
BEGIN:VTIMEZONE\r\n\
265
2
TZID:America/New_York\r\n\
266
2
LAST-MODIFIED:20050809T050000Z\r\n\
267
2
TZURL:http://zones.example.com/tz/America-New_York.ics\r\n\
268
2
BEGIN:STANDARD\r\n\
269
2
DTSTART:20071104T020000\r\n\
270
2
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r\n\
271
2
TZOFFSETFROM:-0400\r\n\
272
2
TZOFFSETTO:-0500\r\n\
273
2
TZNAME:EST\r\n\
274
2
END:STANDARD\r\n\
275
2
BEGIN:DAYLIGHT\r\n\
276
2
DTSTART:20070311T020000\r\n\
277
2
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r\n\
278
2
TZOFFSETFROM:-0500\r\n\
279
2
TZOFFSETTO:-0400\r\n\
280
2
TZNAME:EDT\r\n\
281
2
END:DAYLIGHT\r\n\
282
2
END:VTIMEZONE\r\n\
283
2
END:VCALENDAR\r\n";
284
2

            
285
2
        round_trip_ical_object(example_3);
286
2

            
287
2
        let example_4 = "BEGIN:VCALENDAR\r\n\
288
2
BEGIN:VTIMEZONE\r\n\
289
2
TZID:Fictitious\r\n\
290
2
LAST-MODIFIED:19870101T000000Z\r\n\
291
2
BEGIN:STANDARD\r\n\
292
2
DTSTART:19671029T020000\r\n\
293
2
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n\
294
2
TZOFFSETFROM:-0400\r\n\
295
2
TZOFFSETTO:-0500\r\n\
296
2
TZNAME:EST\r\n\
297
2
END:STANDARD\r\n\
298
2
BEGIN:DAYLIGHT\r\n\
299
2
DTSTART:19870405T020000\r\n\
300
2
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=19980404T070000Z\r\n\
301
2
TZOFFSETFROM:-0500\r\n\
302
2
TZOFFSETTO:-0400\r\n\
303
2
TZNAME:EDT\r\n\
304
2
END:DAYLIGHT\r\n\
305
2
END:VTIMEZONE\r\n\
306
2
END:VCALENDAR\r\n";
307
2

            
308
2
        round_trip_ical_object(example_4);
309
2

            
310
2
        let example_5 = "BEGIN:VCALENDAR\r\n\
311
2
BEGIN:VTIMEZONE\r\n\
312
2
TZID:Fictitious\r\n\
313
2
LAST-MODIFIED:19870101T000000Z\r\n\
314
2
BEGIN:STANDARD\r\n\
315
2
DTSTART:19671029T020000\r\n\
316
2
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r\n\
317
2
TZOFFSETFROM:-0400\r\n\
318
2
TZOFFSETTO:-0500\r\n\
319
2
TZNAME:EST\r\n\
320
2
END:STANDARD\r\n\
321
2
BEGIN:DAYLIGHT\r\n\
322
2
DTSTART:19870405T020000\r\n\
323
2
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4;UNTIL=19980404T070000Z\r\n\
324
2
TZOFFSETFROM:-0500\r\n\
325
2
TZOFFSETTO:-0400\r\n\
326
2
TZNAME:EDT\r\n\
327
2
END:DAYLIGHT\r\n\
328
2
BEGIN:DAYLIGHT\r\n\
329
2
DTSTART:19990424T020000\r\n\
330
2
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=4\r\n\
331
2
TZOFFSETFROM:-0500\r\n\
332
2
TZOFFSETTO:-0400\r\n\
333
2
TZNAME:EDT\r\n\
334
2
END:DAYLIGHT\r\n\
335
2
END:VTIMEZONE\r\n\
336
2
END:VCALENDAR\r\n";
337
2

            
338
2
        round_trip_ical_object(example_5);
339
2
    }
340

            
341
    #[test]
342
2
    fn rtt_alarm() {
343
2
        let example_1 = "BEGIN:VCALENDAR\r\n\
344
2
BEGIN:VEVENT\r\n\
345
2
BEGIN:VALARM\r\n\
346
2
TRIGGER;VALUE=DATE-TIME:19970317T133000Z\r\n\
347
2
REPEAT:4\r\n\
348
2
DURATION:PT15M\r\n\
349
2
ACTION:AUDIO\r\n\
350
2
ATTACH;FMTTYPE=audio/basic:ftp://example.com/pub/sounds/bell-01.aud\r\n\
351
2
END:VALARM\r\n\
352
2
END:VEVENT\r\n\
353
2
END:VCALENDAR\r\n";
354
2

            
355
2
        round_trip_ical_object(example_1);
356
2

            
357
2
        let example_2 = "BEGIN:VCALENDAR\r\n\
358
2
BEGIN:VEVENT\r\n\
359
2
BEGIN:VALARM\r\n\
360
2
TRIGGER:-PT30M\r\n\
361
2
REPEAT:2\r\n\
362
2
DURATION:PT15M\r\n\
363
2
ACTION:DISPLAY\r\n\
364
2
DESCRIPTION:Breakfast meeting with executive\\n team at 8:30 AM EST.\r\n\
365
2
END:VALARM\r\n\
366
2
END:VEVENT\r\n\
367
2
END:VCALENDAR\r\n";
368
2

            
369
2
        round_trip_ical_object(example_2);
370
2

            
371
2
        let example_3 = "BEGIN:VCALENDAR\r\n\
372
2
BEGIN:VEVENT\r\n\
373
2
BEGIN:VALARM\r\n\
374
2
TRIGGER;RELATED=END:-P2D\r\n\
375
2
ACTION:EMAIL\r\n\
376
2
ATTENDEE:mailto:john_doe@example.com\r\n\
377
2
SUMMARY:*** REMINDER: SEND AGENDA FOR WEEKLY STAFF MEETING ***\r\n\
378
2
DESCRIPTION:A draft agenda needs to be sent out to the attendees to the weekly managers meeting (MGR-LIST). Attached is a pointer the document template for the agenda file.\r\n\
379
2
ATTACH;FMTTYPE=application/msword:http://example.com/templates/agenda.doc\r\n\
380
2
END:VALARM\r\n\
381
2
END:VEVENT\r\n\
382
2
END:VCALENDAR\r\n";
383
2

            
384
2
        round_trip_ical_object(example_3);
385
2
    }
386

            
387
38
    fn round_trip_ical_object(content: &str) {
388
38
        let (rem, object) = crate::parser::ical_object::<Error>(content.as_bytes()).unwrap();
389
38
        check_rem(rem, 0);
390
38
        let model = object.to_model().unwrap();
391
38

            
392
38
        let mut buffer = Vec::new();
393
38
        model.write_model(&mut buffer).unwrap();
394
38
        let out_content = String::from_utf8_lossy(&buffer);
395
38

            
396
38
        similar_asserts::assert_eq!(content, out_content);
397
38
    }
398
}