1
use crate::common::{Encoding, Status, TimeTransparency, Value};
2
use crate::parser::param::{other_params, property_params};
3
use crate::parser::property::recur::prop_value_recur;
4
use crate::parser::property::uri::param_value_uri;
5
use crate::parser::types::{
6
    Action, ActionProperty, AttachProperty, AttachValue, AttendeeProperty, CategoriesProperty,
7
    Classification, ClassificationProperty, CommentProperty, ContactProperty, DateOrDateTime,
8
    DateOrDateTimeOrPeriod, DateTimeCompletedProperty, DateTimeCreatedProperty,
9
    DateTimeDueProperty, DateTimeEndProperty, DateTimeStampProperty, DateTimeStartProperty,
10
    DescriptionProperty, DurationOrDateTime, DurationProperty, ExceptionDateTimesProperty,
11
    FreeBusyTimeProperty, GeographicPositionProperty, LastModifiedProperty, LocationProperty,
12
    OrganizerProperty, ParamValue, PercentCompleteProperty, PriorityProperty,
13
    RecurrenceDateTimesProperty, RecurrenceIdProperty, RecurrenceRuleProperty, RelatedToProperty,
14
    RepeatProperty, RequestStatusProperty, ResourcesProperty, SequenceProperty, StatusProperty,
15
    SummaryProperty, TimeTransparencyProperty, TimeZoneIdProperty, TimeZoneNameProperty,
16
    TimeZoneOffsetProperty, TimeZoneUrlProperty, TriggerProperty, UniqueIdentifierProperty,
17
    UrlProperty,
18
};
19
use crate::parser::{iana_token, read_int, x_name, Error, InnerError};
20
use crate::parser::{
21
    prop_value_binary, prop_value_calendar_user_address, prop_value_date, prop_value_date_time,
22
    prop_value_duration, prop_value_float, prop_value_integer, prop_value_period, prop_value_text,
23
    prop_value_utc_offset,
24
};
25
use nom::branch::alt;
26
use nom::bytes::complete::{tag_no_case, take_while1};
27
use nom::bytes::streaming::tag;
28
use nom::character::is_digit;
29
use nom::character::streaming::char;
30
use nom::combinator::{cut, map_res, opt, recognize, verify};
31
use nom::error::ParseError;
32
use nom::multi::{fold_many_m_n, separated_list1};
33
use nom::sequence::tuple;
34
use nom::{IResult, Parser};
35

            
36
/// Parse an ATTACH property.
37
///
38
/// RFC 5545, section 3.8.1.1
39
507
pub fn prop_attach<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], AttachProperty<'a>, E>
40
507
where
41
507
    E: ParseError<&'a [u8]>
42
507
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
43
507
        + From<Error<'a>>,
44
507
{
45
34
    let (input, (_, params, _)) =
46
507
        tuple((tag_no_case("ATTACH"), cut(property_params), char(':')))(input)?;
47

            
48
44
    let is_base_64 = params.iter().any(|p| {
49
34
        matches!(
50
6
            p,
51
            ParamValue::Encoding {
52
                encoding: Encoding::Base64,
53
            }
54
        )
55
44
    });
56
34

            
57
46
    let is_binary = params.iter().any(|p| {
58
28
        matches!(
59
10
            p,
60
            ParamValue::ValueType {
61
                value: Value::Binary,
62
            }
63
        )
64
46
    });
65
34

            
66
34
    // Use OR here rather than AND. It's not valid to set one of these and not the other so assume the
67
34
    // value is more likely to be binary if one is set and let the error happen later if so.
68
34
    if is_base_64 || is_binary {
69
8
        let (input, (v, _)) = tuple((cut(prop_value_binary), tag("\r\n")))(input)?;
70

            
71
8
        Ok((
72
8
            input,
73
8
            AttachProperty {
74
8
                params,
75
8
                value: AttachValue::Binary(v),
76
8
            },
77
8
        ))
78
    } else {
79
26
        let (input, (v, _)) = tuple((cut(recognize(param_value_uri)), tag("\r\n")))(input)?;
80

            
81
26
        Ok((
82
26
            input,
83
26
            AttachProperty {
84
26
                params,
85
26
                value: AttachValue::Uri(v),
86
26
            },
87
26
        ))
88
    }
89
507
}
90

            
91
/// Parse a CATEGORIES property.
92
///
93
/// RFC 5545, section 3.8.1.2
94
358
pub fn prop_categories<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], CategoriesProperty<'a>, E>
95
358
where
96
358
    E: ParseError<&'a [u8]>
97
358
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
98
358
        + From<Error<'a>>,
99
358
{
100
358
    let (input, (_, (params, _, value, _))) = tuple((
101
358
        tag_no_case("CATEGORIES"),
102
358
        cut(tuple((
103
358
            property_params,
104
358
            char(':'),
105
358
            separated_list1(char(','), prop_value_text),
106
358
            tag("\r\n"),
107
358
        ))),
108
358
    ))(input)?;
109

            
110
23
    Ok((input, CategoriesProperty { params, value }))
111
358
}
112

            
113
/// Parse a CLASS property.
114
///
115
/// RFC 5545, section 3.8.1.3
116
926
pub fn prop_classification<'a, E>(
117
926
    input: &'a [u8],
118
926
) -> IResult<&'a [u8], ClassificationProperty<'a>, E>
119
926
where
120
926
    E: ParseError<&'a [u8]> + From<Error<'a>>,
121
926
{
122
926
    let (input, (_, (other_params, _, value, _))) = tuple((
123
926
        tag_no_case("CLASS"),
124
926
        cut(tuple((
125
926
            other_params,
126
926
            char(':'),
127
926
            alt((
128
926
                tag_no_case("PUBLIC").map(|_| Classification::Public),
129
926
                tag_no_case("PRIVATE").map(|_| Classification::Private),
130
926
                tag_no_case("CONFIDENTIAL").map(|_| Classification::Confidential),
131
926
                x_name.map(Classification::XName),
132
926
                iana_token.map(Classification::IanaToken),
133
926
            )),
134
926
            tag("\r\n"),
135
926
        ))),
136
926
    ))(input)?;
137

            
138
35
    Ok((
139
35
        input,
140
35
        ClassificationProperty {
141
35
            other_params,
142
35
            value,
143
35
        },
144
35
    ))
145
926
}
146

            
147
/// Parse a COMMENT property.
148
///
149
/// RFC 5545, section 3.8.1.4
150
513
pub fn prop_comment<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], CommentProperty<'a>, E>
151
513
where
152
513
    E: ParseError<&'a [u8]>
153
513
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
154
513
        + From<Error<'a>>,
155
513
{
156
513
    let (input, (_, (params, _, value, _))) = tuple((
157
513
        tag_no_case("COMMENT"),
158
513
        cut(tuple((
159
513
            property_params,
160
513
            char(':'),
161
513
            prop_value_text,
162
513
            tag("\r\n"),
163
513
        ))),
164
513
    ))(input)?;
165

            
166
16
    Ok((input, CommentProperty { params, value }))
167
513
}
168

            
169
/// Parse a DESCRIPTION property.
170
///
171
/// RFC 5545, section 3.8.1.5
172
902
pub fn prop_description<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], DescriptionProperty<'a>, E>
173
902
where
174
902
    E: ParseError<&'a [u8]>
175
902
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
176
902
        + From<Error<'a>>,
177
902
{
178
902
    let (input, (_, (params, _, value, _))) = tuple((
179
902
        tag_no_case("DESCRIPTION"),
180
902
        cut(tuple((
181
902
            property_params,
182
902
            char(':'),
183
902
            prop_value_text.map(|v| v),
184
902
            tag("\r\n"),
185
902
        ))),
186
902
    ))(input)?;
187

            
188
79
    Ok((input, DescriptionProperty { params, value }))
189
902
}
190

            
191
/// Parse a GEO property.
192
///
193
/// RFC 5545, section 3.8.1.6
194
707
pub fn prop_geographic_position<'a, E>(
195
707
    input: &'a [u8],
196
707
) -> IResult<&'a [u8], GeographicPositionProperty<'a>, E>
197
707
where
198
707
    E: ParseError<&'a [u8]> + From<Error<'a>>,
199
707
{
200
707
    let (input, (_, (other_params, _, (latitude, _, longitude), _))) = tuple((
201
707
        tag_no_case("GEO"),
202
707
        cut(tuple((
203
707
            other_params,
204
707
            char(':'),
205
707
            tuple((prop_value_float, char(';'), prop_value_float)),
206
707
            tag("\r\n"),
207
707
        ))),
208
707
    ))(input)?;
209

            
210
14
    Ok((
211
14
        input,
212
14
        GeographicPositionProperty {
213
14
            other_params,
214
14
            latitude,
215
14
            longitude,
216
14
        },
217
14
    ))
218
707
}
219

            
220
/// Parse a LOCATION property.
221
///
222
/// RFC 5545, section 3.8.1.7
223
685
pub fn prop_location<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], LocationProperty<'a>, E>
224
685
where
225
685
    E: ParseError<&'a [u8]>
226
685
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
227
685
        + From<Error<'a>>,
228
685
{
229
685
    let (input, (_, (params, _, value, _))) = tuple((
230
685
        tag_no_case("LOCATION"),
231
685
        cut(tuple((
232
685
            property_params,
233
685
            char(':'),
234
685
            prop_value_text,
235
685
            tag("\r\n"),
236
685
        ))),
237
685
    ))(input)?;
238

            
239
19
    Ok((input, LocationProperty { params, value }))
240
685
}
241

            
242
/// Parse a PERCENT-COMPLETE property.
243
///
244
/// RFC 5545, section 3.8.1.8
245
140
pub fn prop_percent_complete<'a, E>(
246
140
    input: &'a [u8],
247
140
) -> IResult<&'a [u8], PercentCompleteProperty<'a>, E>
248
140
where
249
140
    E: ParseError<&'a [u8]> + From<Error<'a>>,
250
140
{
251
140
    let (input, (_, (other_params, _, value, _))) = tuple((
252
140
        tag_no_case("PERCENT-COMPLETE"),
253
140
        cut(tuple((
254
140
            other_params,
255
140
            char(':'),
256
140
            verify(prop_value_integer, |v| 0 <= *v && *v <= 100).map(|v| v as u8),
257
140
            tag("\r\n"),
258
140
        ))),
259
140
    ))(input)?;
260

            
261
8
    Ok((
262
8
        input,
263
8
        PercentCompleteProperty {
264
8
            other_params,
265
8
            value,
266
8
        },
267
8
    ))
268
140
}
269

            
270
/// Parse a PRIORITY property.
271
///
272
/// RFC 5545, section 3.8.1.9
273
645
pub fn prop_priority<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], PriorityProperty<'a>, E>
274
645
where
275
645
    E: ParseError<&'a [u8]> + From<Error<'a>>,
276
645
{
277
645
    let (input, (_, (other_params, _, value, _))) = tuple((
278
645
        tag_no_case("PRIORITY"),
279
645
        cut(tuple((
280
645
            other_params,
281
645
            char(':'),
282
645
            verify(prop_value_integer, |v| 0 <= *v && *v <= 9).map(|v| v as u8),
283
645
            tag("\r\n"),
284
645
        ))),
285
645
    ))(input)?;
286

            
287
16
    Ok((
288
16
        input,
289
16
        PriorityProperty {
290
16
            other_params,
291
16
            value,
292
16
        },
293
16
    ))
294
645
}
295

            
296
/// Parse a RESOURCES property.
297
///
298
/// RFC 5545, section 3.8.1.10
299
279
pub fn prop_resources<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], ResourcesProperty<'a>, E>
300
279
where
301
279
    E: ParseError<&'a [u8]>
302
279
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
303
279
        + From<Error<'a>>,
304
279
{
305
279
    let (input, (_, (params, _, value, _))) = tuple((
306
279
        tag_no_case("RESOURCES"),
307
279
        cut(tuple((
308
279
            property_params,
309
279
            char(':'),
310
279
            separated_list1(char(','), prop_value_text),
311
279
            tag("\r\n"),
312
279
        ))),
313
279
    ))(input)?;
314

            
315
6
    Ok((input, ResourcesProperty { params, value }))
316
279
}
317

            
318
/// Parse a STATUS property.
319
///
320
/// RFC 5545, section 3.8.1.11
321
681
pub fn prop_status<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], StatusProperty<'a>, E>
322
681
where
323
681
    E: ParseError<&'a [u8]> + From<Error<'a>>,
324
681
{
325
681
    let (input, (_, (other_params, _, value, _))) = tuple((
326
681
        tag_no_case("STATUS"),
327
681
        cut(tuple((
328
681
            other_params,
329
681
            char(':'),
330
681
            alt((
331
681
                tag_no_case("TENTATIVE").map(|_| Status::Tentative),
332
681
                tag_no_case("CONFIRMED").map(|_| Status::Confirmed),
333
681
                tag_no_case("CANCELLED").map(|_| Status::Cancelled),
334
681
                tag_no_case("NEEDS-ACTION").map(|_| Status::NeedsAction),
335
681
                tag_no_case("COMPLETED").map(|_| Status::Completed),
336
681
                tag_no_case("IN-PROCESS").map(|_| Status::InProcess),
337
681
                tag_no_case("DRAFT").map(|_| Status::Draft),
338
681
                tag_no_case("FINAL").map(|_| Status::Final),
339
681
            )),
340
681
            tag("\r\n"),
341
681
        ))),
342
681
    ))(input)?;
343

            
344
26
    Ok((
345
26
        input,
346
26
        StatusProperty {
347
26
            other_params,
348
26
            value,
349
26
        },
350
26
    ))
351
681
}
352

            
353
/// Parse a SUMMARY property.
354
///
355
/// RFC 5545, section 3.8.1.12
356
737
pub fn prop_summary<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], SummaryProperty<'a>, E>
357
737
where
358
737
    E: ParseError<&'a [u8]>
359
737
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
360
737
        + From<Error<'a>>,
361
737
{
362
737
    let (input, (_, (params, _, value, _))) = tuple((
363
737
        tag_no_case("SUMMARY"),
364
737
        cut(tuple((
365
737
            property_params,
366
737
            char(':'),
367
737
            prop_value_text,
368
737
            tag("\r\n"),
369
737
        ))),
370
737
    ))(input)?;
371

            
372
53
    Ok((input, SummaryProperty { params, value }))
373
737
}
374

            
375
/// Parse a COMPLETED property.
376
///
377
/// RFC 5545, section 3.8.2.1
378
192
pub fn prop_date_time_completed<'a, E>(
379
192
    input: &'a [u8],
380
192
) -> IResult<&'a [u8], DateTimeCompletedProperty<'a>, E>
381
192
where
382
192
    E: ParseError<&'a [u8]> + From<Error<'a>>,
383
192
{
384
192
    let (input, (_, (other_params, _, value, _))) = tuple((
385
192
        tag_no_case("COMPLETED"),
386
192
        cut(tuple((
387
192
            other_params,
388
192
            char(':'),
389
192
            prop_value_date_time,
390
192
            tag("\r\n"),
391
192
        ))),
392
192
    ))(input)?;
393

            
394
6
    Ok((
395
6
        input,
396
6
        DateTimeCompletedProperty {
397
6
            other_params,
398
6
            value,
399
6
        },
400
6
    ))
401
192
}
402

            
403
/// Parse a DTEND property.
404
///
405
/// RFC 5545, section 3.8.2.2
406
392
pub fn prop_date_time_end<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], DateTimeEndProperty<'a>, E>
407
392
where
408
392
    E: ParseError<&'a [u8]>
409
392
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
410
392
        + From<Error<'a>>,
411
392
{
412
392
    let (input, (_, (params, _, value, _))) = tuple((
413
392
        tag_no_case("DTEND"),
414
392
        cut(tuple((
415
392
            property_params,
416
392
            char(':'),
417
392
            alt((
418
392
                prop_value_date_time.map(DateOrDateTime::DateTime),
419
392
                prop_value_date.map(DateOrDateTime::Date),
420
392
            )),
421
392
            tag("\r\n"),
422
392
        ))),
423
392
    ))(input)?;
424

            
425
37
    Ok((input, DateTimeEndProperty { params, value }))
426
392
}
427

            
428
/// Parse a DUE property.
429
///
430
/// RFC 5545, section 3.8.2.3
431
84
pub fn prop_date_time_due<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], DateTimeDueProperty<'a>, E>
432
84
where
433
84
    E: ParseError<&'a [u8]>
434
84
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
435
84
        + From<Error<'a>>,
436
84
{
437
84
    let (input, (_, (params, _, value, _))) = tuple((
438
84
        tag_no_case("DUE"),
439
84
        cut(tuple((
440
84
            property_params,
441
84
            char(':'),
442
84
            alt((
443
84
                prop_value_date_time.map(DateOrDateTime::DateTime),
444
84
                prop_value_date.map(DateOrDateTime::Date),
445
84
            )),
446
84
            tag("\r\n"),
447
84
        ))),
448
84
    ))(input)?;
449

            
450
18
    Ok((input, DateTimeDueProperty { params, value }))
451
84
}
452

            
453
/// Parse a DTSTART property.
454
///
455
/// RFC 5545, section 3.8.2.4
456
1399
pub fn prop_date_time_start<'a, E>(
457
1399
    input: &'a [u8],
458
1399
) -> IResult<&'a [u8], DateTimeStartProperty<'a>, E>
459
1399
where
460
1399
    E: ParseError<&'a [u8]>
461
1399
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
462
1399
        + From<Error<'a>>,
463
1399
{
464
1399
    let (input, (_, (params, _, value, _))) = tuple((
465
1399
        tag_no_case("DTSTART"),
466
1399
        cut(tuple((
467
1399
            property_params,
468
1399
            char(':'),
469
1399
            alt((
470
1399
                prop_value_date_time.map(DateOrDateTime::DateTime),
471
1399
                prop_value_date.map(DateOrDateTime::Date),
472
1399
            )),
473
1399
            tag("\r\n"),
474
1399
        ))),
475
1399
    ))(input)?;
476

            
477
153
    Ok((input, DateTimeStartProperty { params, value }))
478
1399
}
479

            
480
/// Parse a DURATION property.
481
///
482
/// RFC 5545, section 3.8.2.5
483
541
pub fn prop_duration<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], DurationProperty<'a>, E>
484
541
where
485
541
    E: ParseError<&'a [u8]> + From<Error<'a>>,
486
541
{
487
541
    let (input, (_, (other_params, _, value, _))) = tuple((
488
541
        tag_no_case("DURATION"),
489
541
        cut(tuple((
490
541
            other_params,
491
541
            char(':'),
492
541
            prop_value_duration,
493
541
            tag("\r\n"),
494
541
        ))),
495
541
    ))(input)?;
496

            
497
50
    Ok((
498
50
        input,
499
50
        DurationProperty {
500
50
            other_params,
501
50
            value,
502
50
        },
503
50
    ))
504
541
}
505

            
506
/// Parse a FREEBUSY property.
507
///
508
/// RFC 5545, section 3.8.2.6
509
36
pub fn prop_free_busy_time<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], FreeBusyTimeProperty<'a>, E>
510
36
where
511
36
    E: ParseError<&'a [u8]>
512
36
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
513
36
        + From<Error<'a>>,
514
36
{
515
36
    let (input, (_, (params, _, value, _))) = tuple((
516
36
        tag_no_case("FREEBUSY"),
517
36
        cut(tuple((
518
36
            property_params,
519
36
            char(':'),
520
36
            separated_list1(char(','), prop_value_period),
521
36
            tag("\r\n"),
522
36
        ))),
523
36
    ))(input)?;
524

            
525
14
    Ok((input, FreeBusyTimeProperty { params, value }))
526
36
}
527

            
528
/// Parse a TRANSP property.
529
///
530
/// RFC 5545, section 3.8.2.7
531
478
pub fn prop_time_transparency<'a, E>(
532
478
    input: &'a [u8],
533
478
) -> IResult<&'a [u8], TimeTransparencyProperty<'a>, E>
534
478
where
535
478
    E: ParseError<&'a [u8]> + From<Error<'a>>,
536
478
{
537
478
    let (input, (_, (other_params, _, value, _))) = tuple((
538
478
        tag_no_case("TRANSP"),
539
478
        cut(tuple((
540
478
            other_params,
541
478
            char(':'),
542
478
            alt((
543
478
                tag_no_case("OPAQUE").map(|_| TimeTransparency::Opaque),
544
478
                tag_no_case("TRANSPARENT").map(|_| TimeTransparency::Transparent),
545
478
            )),
546
478
            tag("\r\n"),
547
478
        ))),
548
478
    ))(input)?;
549

            
550
16
    Ok((
551
16
        input,
552
16
        TimeTransparencyProperty {
553
16
            other_params,
554
16
            value,
555
16
        },
556
16
    ))
557
478
}
558

            
559
/// Parse a TZID property.
560
///
561
/// RFC 5545, section 3.8.3.1
562
168
pub fn prop_time_zone_id<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], TimeZoneIdProperty<'a>, E>
563
168
where
564
168
    E: ParseError<&'a [u8]> + From<Error<'a>>,
565
168
{
566
168
    let (input, (_, (other_params, _, (unique, value), _))) = tuple((
567
168
        tag_no_case("TZID"),
568
168
        cut(tuple((
569
168
            other_params,
570
168
            char(':'),
571
168
            tuple((opt(char('/')), prop_value_text)),
572
168
            tag("\r\n"),
573
168
        ))),
574
168
    ))(input)?;
575

            
576
35
    Ok((
577
35
        input,
578
35
        TimeZoneIdProperty {
579
35
            other_params,
580
35
            unique_registry_id: unique.is_some(),
581
35
            value,
582
35
        },
583
35
    ))
584
168
}
585

            
586
/// Parse a TZNAME property.
587
///
588
/// RFC 5545, section 3.8.3.2
589
134
pub fn prop_time_zone_name<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], TimeZoneNameProperty<'a>, E>
590
134
where
591
134
    E: ParseError<&'a [u8]>
592
134
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
593
134
        + From<Error<'a>>,
594
134
{
595
134
    let (input, (_, (params, _, value, _))) = tuple((
596
134
        tag_no_case("TZNAME"),
597
134
        cut(tuple((
598
134
            property_params,
599
134
            char(':'),
600
134
            prop_value_text,
601
134
            tag("\r\n"),
602
134
        ))),
603
134
    ))(input)?;
604

            
605
56
    Ok((input, TimeZoneNameProperty { params, value }))
606
134
}
607

            
608
/// Parse a TZOFFSETFROM property.
609
///
610
/// RFC 5545, section 3.8.3.3
611
242
pub fn prop_time_zone_offset_from<'a, E>(
612
242
    input: &'a [u8],
613
242
) -> IResult<&'a [u8], TimeZoneOffsetProperty<'a>, E>
614
242
where
615
242
    E: ParseError<&'a [u8]> + From<Error<'a>>,
616
242
{
617
242
    let (input, (_, (other_params, _, value, _))) = tuple((
618
242
        tag_no_case("TZOFFSETFROM"),
619
242
        cut(tuple((
620
242
            other_params,
621
242
            char(':'),
622
242
            prop_value_utc_offset,
623
242
            tag("\r\n"),
624
242
        ))),
625
242
    ))(input)?;
626

            
627
64
    Ok((
628
64
        input,
629
64
        TimeZoneOffsetProperty {
630
64
            other_params,
631
64
            value,
632
64
        },
633
64
    ))
634
242
}
635

            
636
/// Parse a TZOFFSETTO property.
637
///
638
/// RFC 5545, section 3.8.3.4
639
304
pub fn prop_time_zone_offset_to<'a, E>(
640
304
    input: &'a [u8],
641
304
) -> IResult<&'a [u8], TimeZoneOffsetProperty<'a>, E>
642
304
where
643
304
    E: ParseError<&'a [u8]> + From<Error<'a>>,
644
304
{
645
304
    let (input, (_, (other_params, _, value, _))) = tuple((
646
304
        tag_no_case("TZOFFSETTO"),
647
304
        cut(tuple((
648
304
            other_params,
649
304
            char(':'),
650
304
            prop_value_utc_offset,
651
304
            tag("\r\n"),
652
304
        ))),
653
304
    ))(input)?;
654

            
655
64
    Ok((
656
64
        input,
657
64
        TimeZoneOffsetProperty {
658
64
            other_params,
659
64
            value,
660
64
        },
661
64
    ))
662
304
}
663

            
664
/// Parse a TZURL property.
665
///
666
/// RFC 5545, section 3.8.3.5
667
115
pub fn prop_time_zone_url<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], TimeZoneUrlProperty<'a>, E>
668
115
where
669
115
    E: ParseError<&'a [u8]>
670
115
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
671
115
        + From<Error<'a>>,
672
115
{
673
115
    let (input, (_, (other_params, _, value, _))) = tuple((
674
115
        tag_no_case("TZURL"),
675
115
        cut(tuple((
676
115
            other_params,
677
115
            char(':'),
678
115
            cut(recognize(param_value_uri)),
679
115
            tag("\r\n"),
680
115
        ))),
681
115
    ))(input)?;
682

            
683
10
    Ok((
684
10
        input,
685
10
        TimeZoneUrlProperty {
686
10
            other_params,
687
10
            value,
688
10
        },
689
10
    ))
690
115
}
691

            
692
/// Parse an ATTENDEE property.
693
///
694
/// RFC 5545, section 3.8.4.1
695
485
pub fn prop_attendee<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], AttendeeProperty<'a>, E>
696
485
where
697
485
    E: ParseError<&'a [u8]>
698
485
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
699
485
        + From<Error<'a>>,
700
485
{
701
485
    let (input, (_, (params, _, uri, _))) = tuple((
702
485
        tag_no_case("ATTENDEE"),
703
485
        cut(tuple((
704
485
            property_params,
705
485
            char(':'),
706
485
            cut(recognize(prop_value_calendar_user_address)),
707
485
            tag("\r\n"),
708
485
        ))),
709
485
    ))(input)?;
710

            
711
27
    Ok((input, AttendeeProperty { params, value: uri }))
712
485
}
713

            
714
/// Parse a CONTACT property.
715
///
716
/// RFC 5545, section 3.8.4.2
717
431
pub fn prop_contact<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], ContactProperty<'a>, E>
718
431
where
719
431
    E: ParseError<&'a [u8]>
720
431
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
721
431
        + From<Error<'a>>,
722
431
{
723
431
    let (input, (_, (params, _, value, _))) = tuple((
724
431
        tag_no_case("CONTACT"),
725
431
        cut(tuple((
726
431
            property_params,
727
431
            char(':'),
728
431
            prop_value_text,
729
431
            tag("\r\n"),
730
431
        ))),
731
431
    ))(input)?;
732

            
733
16
    Ok((input, ContactProperty { params, value }))
734
431
}
735

            
736
/// Parse an ORGANIZER property.
737
///
738
/// RFC 5545, section 3.8.4.3
739
826
pub fn prop_organizer<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], OrganizerProperty<'a>, E>
740
826
where
741
826
    E: ParseError<&'a [u8]>
742
826
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
743
826
        + From<Error<'a>>,
744
826
{
745
826
    let (input, (_, (params, _, uri, _))) = tuple((
746
826
        tag_no_case("ORGANIZER"),
747
826
        cut(tuple((
748
826
            property_params,
749
826
            char(':'),
750
826
            recognize(prop_value_calendar_user_address),
751
826
            tag("\r\n"),
752
826
        ))),
753
826
    ))(input)?;
754

            
755
43
    Ok((input, OrganizerProperty { params, value: uri }))
756
826
}
757

            
758
/// Parse a RECURRENCE-ID property.
759
///
760
/// RFC 5545, section 3.8.4.4
761
664
pub fn prop_recurrence_id<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], RecurrenceIdProperty<'a>, E>
762
664
where
763
664
    E: ParseError<&'a [u8]>
764
664
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
765
664
        + From<Error<'a>>,
766
664
{
767
664
    let (input, (_, (params, _, value, _))) = tuple((
768
664
        tag_no_case("RECURRENCE-ID"),
769
664
        cut(tuple((
770
664
            property_params,
771
664
            char(':'),
772
664
            alt((
773
664
                prop_value_date_time.map(DateOrDateTime::DateTime),
774
664
                prop_value_date.map(DateOrDateTime::Date),
775
664
            )),
776
664
            tag("\r\n"),
777
664
        ))),
778
664
    ))(input)?;
779

            
780
22
    Ok((input, RecurrenceIdProperty { params, value }))
781
664
}
782

            
783
/// Parse a RELATED-TO property.
784
///
785
/// RFC 5545, section 3.8.4.5
786
307
pub fn prop_related_to<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], RelatedToProperty<'a>, E>
787
307
where
788
307
    E: ParseError<&'a [u8]>
789
307
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
790
307
        + From<Error<'a>>,
791
307
{
792
307
    let (input, (_, (params, _, value, _))) = tuple((
793
307
        tag_no_case("RELATED-TO"),
794
307
        cut(tuple((
795
307
            property_params,
796
307
            char(':'),
797
307
            prop_value_text,
798
307
            tag("\r\n"),
799
307
        ))),
800
307
    ))(input)?;
801

            
802
8
    Ok((input, RelatedToProperty { params, value }))
803
307
}
804

            
805
/// Parse a URL property.
806
///
807
/// RFC 5545, section 3.8.4.6
808
658
pub fn prop_url<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], UrlProperty<'a>, E>
809
658
where
810
658
    E: ParseError<&'a [u8]>
811
658
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
812
658
        + From<Error<'a>>,
813
658
{
814
658
    let (input, (_, (other_params, _, value, _))) = tuple((
815
658
        tag_no_case("URL"),
816
658
        cut(tuple((
817
658
            other_params,
818
658
            char(':'),
819
658
            param_value_uri,
820
658
            tag("\r\n"),
821
658
        ))),
822
658
    ))(input)?;
823

            
824
30
    Ok((
825
30
        input,
826
30
        UrlProperty {
827
30
            other_params,
828
30
            value,
829
30
        },
830
30
    ))
831
658
}
832

            
833
/// Parse a UID property.
834
///
835
/// RFC 5545, section 3.8.4.7
836
1244
pub fn prop_unique_identifier<'a, E>(
837
1244
    input: &'a [u8],
838
1244
) -> IResult<&'a [u8], UniqueIdentifierProperty<'a>, E>
839
1244
where
840
1244
    E: ParseError<&'a [u8]> + From<Error<'a>>,
841
1244
{
842
1244
    let (input, (_, (other_params, _, value, _))) = tuple((
843
1244
        tag_no_case("UID"),
844
1244
        cut(tuple((
845
1244
            other_params,
846
1244
            char(':'),
847
1244
            prop_value_text,
848
1244
            tag("\r\n"),
849
1244
        ))),
850
1244
    ))(input)?;
851

            
852
169
    Ok((
853
169
        input,
854
169
        UniqueIdentifierProperty {
855
169
            other_params,
856
169
            value,
857
169
        },
858
169
    ))
859
1244
}
860

            
861
/// Parse an EXDATE property.
862
///
863
/// RFC 5545, section 3.8.5.1
864
319
pub fn prop_exception_date_times<'a, E>(
865
319
    input: &'a [u8],
866
319
) -> IResult<&'a [u8], ExceptionDateTimesProperty<'a>, E>
867
319
where
868
319
    E: ParseError<&'a [u8]>
869
319
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
870
319
        + From<Error<'a>>,
871
319
{
872
319
    let (input, (_, (params, _, value, _))) = tuple((
873
319
        tag_no_case("EXDATE"),
874
319
        cut(tuple((
875
319
            property_params,
876
319
            char(':'),
877
319
            separated_list1(
878
319
                char(','),
879
319
                alt((
880
319
                    prop_value_date_time.map(DateOrDateTime::DateTime),
881
319
                    prop_value_date.map(DateOrDateTime::Date),
882
319
                )),
883
319
            ),
884
319
            tag("\r\n"),
885
319
        ))),
886
319
    ))(input)?;
887

            
888
10
    Ok((input, ExceptionDateTimesProperty { params, value }))
889
319
}
890

            
891
/// Parse an RDATE property.
892
///
893
/// RFC 5545, section 3.8.5.2
894
437
pub fn prop_recurrence_date_times<'a, E>(
895
437
    input: &'a [u8],
896
437
) -> IResult<&'a [u8], RecurrenceDateTimesProperty<'a>, E>
897
437
where
898
437
    E: ParseError<&'a [u8]>
899
437
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
900
437
        + From<Error<'a>>,
901
437
{
902
437
    let (input, (_, (params, _, value, _))) = tuple((
903
437
        tag_no_case("RDATE"),
904
437
        cut(tuple((
905
437
            property_params,
906
437
            char(':'),
907
437
            separated_list1(
908
437
                char(','),
909
437
                alt((
910
437
                    prop_value_period.map(DateOrDateTimeOrPeriod::Period),
911
437
                    prop_value_date_time.map(DateOrDateTimeOrPeriod::DateTime),
912
437
                    prop_value_date.map(DateOrDateTimeOrPeriod::Date),
913
437
                )),
914
437
            ),
915
437
            tag("\r\n"),
916
437
        ))),
917
437
    ))(input)?;
918

            
919
20
    Ok((input, RecurrenceDateTimesProperty { params, value }))
920
437
}
921

            
922
/// Parse an RRULE property.
923
///
924
/// RFC 5545, section 3.8.5.3
925
758
pub fn prop_recurrence_rule<'a, E>(
926
758
    input: &'a [u8],
927
758
) -> IResult<&'a [u8], RecurrenceRuleProperty<'a>, E>
928
758
where
929
758
    E: ParseError<&'a [u8]>
930
758
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
931
758
        + From<Error<'a>>,
932
758
{
933
758
    let (input, (_, (other_params, _, value, _))) = tuple((
934
758
        tag_no_case("RRULE"),
935
758
        cut(tuple((
936
758
            other_params,
937
758
            char(':'),
938
758
            prop_value_recur,
939
758
            tag("\r\n"),
940
758
        ))),
941
758
    ))(input)?;
942

            
943
186
    Ok((
944
186
        input,
945
186
        RecurrenceRuleProperty {
946
186
            other_params,
947
186
            value,
948
186
        },
949
186
    ))
950
758
}
951

            
952
/// Parse an ACTION property.
953
///
954
/// RFC 5545, section 3.8.6.1
955
272
pub fn prop_action<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], ActionProperty<'a>, E>
956
272
where
957
272
    E: ParseError<&'a [u8]> + From<Error<'a>>,
958
272
{
959
272
    let (input, (_, (other_params, _, value, _))) = tuple((
960
272
        tag_no_case("ACTION"),
961
272
        cut(tuple((
962
272
            other_params,
963
272
            char(':'),
964
272
            alt((
965
272
                tag_no_case("AUDIO").map(|_| Action::Audio),
966
272
                tag_no_case("DISPLAY").map(|_| Action::Display),
967
272
                tag_no_case("EMAIL").map(|_| Action::Email),
968
272
                x_name.map(Action::XName),
969
272
                iana_token.map(Action::IanaToken),
970
272
            )),
971
272
            tag("\r\n"),
972
272
        ))),
973
272
    ))(input)?;
974

            
975
46
    Ok((
976
46
        input,
977
46
        ActionProperty {
978
46
            other_params,
979
46
            value,
980
46
        },
981
46
    ))
982
272
}
983

            
984
/// Parse a REPEAT property.
985
///
986
/// RFC 5545, section 3.8.6.2
987
156
pub fn prop_repeat<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], RepeatProperty<'a>, E>
988
156
where
989
156
    E: ParseError<&'a [u8]> + From<Error<'a>>,
990
156
{
991
156
    let (input, (_, (other_params, _, value, _))) = tuple((
992
156
        tag_no_case("REPEAT"),
993
156
        cut(tuple((
994
156
            other_params,
995
156
            char(':'),
996
156
            prop_value_integer.map(|v| v as u32),
997
156
            tag("\r\n"),
998
156
        ))),
999
156
    ))(input)?;
34
    Ok((
34
        input,
34
        RepeatProperty {
34
            other_params,
34
            value,
34
        },
34
    ))
156
}
/// Parse a TRIGGER property.
///
/// RFC 5545, section 3.8.6.3
232
pub fn prop_trigger<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], TriggerProperty<'a>, E>
232
where
232
    E: ParseError<&'a [u8]>
232
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
232
        + From<Error<'a>>,
232
{
232
    let (input, (_, (params, _))) = tuple((
232
        tag_no_case("TRIGGER"),
232
        cut(tuple((property_params, char(':')))),
232
    ))(input)?;
46
    let value_choice = params
46
        .iter()
56
        .filter_map(|p| match p {
            ParamValue::ValueType {
                value: Value::Duration,
4
            } => Some(1),
            ParamValue::ValueType {
                value: Value::DateTime,
10
            } => Some(2),
18
            _ => None,
56
        })
46
        .collect::<Vec<_>>();
46
    let (input, value) = match value_choice.as_slice() {
46
        [1] | [] => cut(prop_value_duration)
36
            .map(DurationOrDateTime::Duration)
36
            .parse(input),
10
        [2] => cut(prop_value_date_time)
10
            .map(DurationOrDateTime::DateTime)
10
            .parse(input),
        _ => {
            return Err(nom::Err::Error(
                Error::new(input, InnerError::InvalidValueParam).into(),
            ))
        }
    }?;
46
    let (input, _) = cut(tag("\r\n"))(input)?;
46
    Ok((input, TriggerProperty { params, value }))
232
}
/// Parse a CREATED property.
///
/// RFC 5545, section 3.8.7.1
891
pub fn prop_date_time_created<'a, E>(
891
    input: &'a [u8],
891
) -> IResult<&'a [u8], DateTimeCreatedProperty<'a>, E>
891
where
891
    E: ParseError<&'a [u8]> + From<Error<'a>>,
891
{
891
    let (input, (_, (other_params, _, value, _))) = tuple((
891
        tag_no_case("CREATED"),
891
        cut(tuple((
891
            other_params,
891
            char(':'),
891
            prop_value_date_time,
891
            tag("\r\n"),
891
        ))),
891
    ))(input)?;
25
    Ok((
25
        input,
25
        DateTimeCreatedProperty {
25
            other_params,
25
            value,
25
        },
25
    ))
891
}
/// Parse a DTSTAMP property.
///
/// RFC 5545, section 3.8.7.2
1411
pub fn prop_date_time_stamp<'a, E>(
1411
    input: &'a [u8],
1411
) -> IResult<&'a [u8], DateTimeStampProperty<'a>, E>
1411
where
1411
    E: ParseError<&'a [u8]> + From<Error<'a>>,
1411
{
1411
    let (input, (_, (other_params, _, value, _))) = tuple((
1411
        tag_no_case("DTSTAMP"),
1411
        cut(tuple((
1411
            other_params,
1411
            char(':'),
1411
            prop_value_date_time,
1411
            tag("\r\n"),
1411
        ))),
1411
    ))(input)?;
169
    Ok((
169
        input,
169
        DateTimeStampProperty {
169
            other_params,
169
            value,
169
        },
169
    ))
1411
}
/// Parse a LAST-MODIFIED property.
///
/// RFC 5545, section 3.8.7.3
920
pub fn prop_last_modified<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], LastModifiedProperty<'a>, E>
920
where
920
    E: ParseError<&'a [u8]> + From<Error<'a>>,
920
{
920
    let (input, (_, (other_params, _, value, _))) = tuple((
920
        tag_no_case("LAST-MODIFIED"),
920
        cut(tuple((
920
            other_params,
920
            char(':'),
920
            prop_value_date_time,
920
            tag("\r\n"),
920
        ))),
920
    ))(input)?;
40
    Ok((
40
        input,
40
        LastModifiedProperty {
40
            other_params,
40
            value,
40
        },
40
    ))
920
}
/// Parse a SEQUENCE property.
///
/// RFC 5545, section 3.8.7.4
699
pub fn prop_sequence<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], SequenceProperty<'a>, E>
699
where
699
    E: ParseError<&'a [u8]> + From<Error<'a>>,
699
{
699
    let (input, (_, (other_params, _, value, _))) = tuple((
699
        tag_no_case("SEQUENCE"),
699
        cut(tuple((
699
            other_params,
699
            char(':'),
699
            prop_value_integer.map(|v| v as u32),
699
            tag("\r\n"),
699
        ))),
699
    ))(input)?;
20
    Ok((
20
        input,
20
        SequenceProperty {
20
            other_params,
20
            value,
20
        },
20
    ))
699
}
/// Parse a REQUEST-STATUS property.
///
/// RFC 5545, section 3.8.8.3
331
pub fn prop_request_status<'a, E>(
331
    input: &'a [u8],
331
) -> IResult<&'a [u8], RequestStatusProperty<'a>, E>
331
where
331
    E: ParseError<&'a [u8]>
331
        + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
331
        + From<Error<'a>>,
331
{
12
    fn status_code<'a, E>(input: &'a [u8]) -> IResult<&'a [u8], Vec<u32>, E>
12
    where
12
        E: ParseError<&'a [u8]>
12
            + nom::error::FromExternalError<&'a [u8], nom::Err<E>>
12
            + From<Error<'a>>,
12
    {
12
        let (input, (num, mut nums)) = tuple((
12
            map_res(
12
                verify(take_while1(is_digit), |v: &[u8]| v.len() == 1),
12
                |v| read_int::<E, u32>(v),
12
            ),
12
            fold_many_m_n(
12
                1,
12
                2,
12
                map_res(tuple((char('.'), take_while1(is_digit))), |(_, v)| {
12
                    read_int::<E, u32>(v)
12
                }),
12
                Vec::new,
12
                |mut acc, item| {
12
                    acc.push(item);
12
                    acc
12
                },
12
            ),
12
        ))(input)?;
12
        nums.insert(0, num);
12
        Ok((input, nums))
12
    }
12
    let (input, (_, (params, _, status_code, _, status_description, extra_data, _))) =
331
        tuple((
331
            tag_no_case("REQUEST-STATUS"),
331
            cut(tuple((
331
                property_params,
331
                char(':'),
331
                status_code,
331
                char(';'),
331
                prop_value_text,
331
                opt(tuple((char(';'), prop_value_text)).map(|(_, v)| v)),
331
                tag("\r\n"),
331
            ))),
331
        ))(input)?;
12
    Ok((
12
        input,
12
        RequestStatusProperty {
12
            params,
12
            status_code,
12
            status_description,
12
            exception_data: extra_data,
12
        },
12
    ))
331
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::common::FreeBusyTimeType;
    use crate::common::RecurFreq;
    use crate::common::{
        LanguageTag, ParticipationStatusUnknown, Range, Role, TriggerRelationship, Value,
    };
    use crate::parser::types::ParamValue;
    use crate::parser::types::RecurRulePart;
    use crate::parser::types::{Authority, Host, Uri};
    use crate::parser::types::{Date, DateTime, Duration, Period, PeriodEnd, Time, UtcOffset};
    use crate::test_utils::check_rem;
    use base64::Engine;
    #[test]
2
    fn attach_uri() {
2
        let (rem, prop) =
2
            prop_attach::<Error>(b"ATTACH:CID:jsmith.part3.960817T083000.xyzMail@example.com\r\n;")
2
                .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            AttachProperty {
2
                params: vec![],
2
                value: AttachValue::Uri(b"CID:jsmith.part3.960817T083000.xyzMail@example.com"),
2
            }
2
        );
2
    }
    #[test]
2
    fn attach_binary() {
2
        let (rem, prop) =
2
            prop_attach::<Error>(b"ATTACH;VALUE=BINARY;ENCODING=BASE64:dGVzdA==\r\n;").unwrap();
2
        check_rem(rem, 1);
2

            
2
        let r = base64::prelude::BASE64_STANDARD.encode("test");
2

            
2
        assert_eq!(
2
            prop,
2
            AttachProperty {
2
                params: vec![
2
                    ParamValue::ValueType {
2
                        value: Value::Binary
2
                    },
2
                    ParamValue::Encoding {
2
                        encoding: Encoding::Base64,
2
                    },
2
                ],
2
                value: AttachValue::Binary(r.as_bytes()),
2
            }
2
        );
2
    }
    #[test]
2
    fn categories() {
2
        let (rem, prop) =
2
            prop_categories::<Error>(b"CATEGORIES:APPOINTMENT,EDUCATION\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            CategoriesProperty {
2
                params: vec![],
2
                value: vec![b"APPOINTMENT".to_vec(), b"EDUCATION".to_vec()],
2
            }
2
        );
2
    }
    #[test]
2
    fn classification_public() {
2
        let (rem, prop) = prop_classification::<Error>(b"CLASS:PUBLIC\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            ClassificationProperty {
2
                other_params: vec![],
2
                value: Classification::Public,
2
            }
2
        );
2
    }
    #[test]
2
    fn comment() {
2
        let (rem, prop) = prop_comment::<Error>(b"COMMENT:The meeting really needs to include both ourselves and the customer. We can't hold this meeting without them. As a matter of fact\\, the venue for the meeting ought to be at their site. - - John\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            CommentProperty {
2
                params: vec![],
2
                value: b"The meeting really needs to include both ourselves and the customer. We can't hold this meeting without them. As a matter of fact, the venue for the meeting ought to be at their site. - - John".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn description() {
2
        let (rem, prop) = prop_description::<Error>(b"DESCRIPTION:Meeting to provide technical review for \"Phoenix\"\r\n  design.\\nHappy Face Conference Room. Phoenix design team\r\n  MUST attend this meeting.\\nRSVP to team leader.\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DescriptionProperty {
2
                params: vec![],
2
                value: br#"Meeting to provide technical review for "Phoenix" design.
2
Happy Face Conference Room. Phoenix design team MUST attend this meeting.
2
RSVP to team leader."#
2
                    .to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn geographic_position() {
2
        let (rem, prop) =
2
            prop_geographic_position::<Error>(b"GEO:37.386013;-122.082932\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            GeographicPositionProperty {
2
                other_params: vec![],
2
                latitude: 37.386013,
2
                longitude: -122.082932,
2
            }
2
        );
2
    }
    #[test]
2
    fn location() {
2
        let (rem, prop) =
2
            prop_location::<Error>(b"LOCATION:Conference Room - F123\\, Bldg. 002\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            LocationProperty {
2
                params: vec![],
2
                value: b"Conference Room - F123, Bldg. 002".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn location_with_params() {
2
        let (rem, prop) = prop_location::<Error>(b"LOCATION;ALTREP=\"http://xyzcorp.com/conf-rooms/f123.vcf\":\r\n Conference Room - F123\\, Bldg. 002\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            LocationProperty {
2
                params: vec![ParamValue::AltRep {
2
                    uri: b"http://xyzcorp.com/conf-rooms/f123.vcf",
2
                },],
2
                value: b"Conference Room - F123, Bldg. 002".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn percent_complete() {
2
        let (rem, prop) = prop_percent_complete::<Error>(b"PERCENT-COMPLETE:39\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            PercentCompleteProperty {
2
                other_params: vec![],
2
                value: 39,
2
            }
2
        );
2
    }
    #[test]
2
    fn priority() {
2
        let (rem, prop) = prop_priority::<Error>(b"PRIORITY:1\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            PriorityProperty {
2
                other_params: vec![],
2
                value: 1,
2
            }
2
        );
2
    }
    #[test]
2
    fn resources() {
2
        let (rem, prop) = prop_resources::<Error>(b"RESOURCES:EASEL,PROJECTOR,VCR\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            ResourcesProperty {
2
                params: vec![],
2
                value: vec![b"EASEL".to_vec(), b"PROJECTOR".to_vec(), b"VCR".to_vec()],
2
            }
2
        );
2
    }
    #[test]
2
    fn status() {
2
        let (rem, prop) = prop_status::<Error>(b"STATUS:TENTATIVE\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            StatusProperty {
2
                other_params: vec![],
2
                value: Status::Tentative,
2
            }
2
        );
2
    }
    #[test]
2
    fn summary() {
2
        let (rem, prop) = prop_summary::<Error>(b"SUMMARY:Department Party\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            SummaryProperty {
2
                params: vec![],
2
                value: b"Department Party".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_completed() {
2
        let (rem, prop) =
2
            prop_date_time_completed::<Error>(b"COMPLETED:19960401T150000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeCompletedProperty {
2
                other_params: vec![],
2
                value: DateTime {
2
                    date: Date {
2
                        year: 1996,
2
                        month: 4,
2
                        day: 1,
2
                    },
2
                    time: Time {
2
                        hour: 15,
2
                        minute: 0,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_end_date() {
2
        let (rem, prop) = prop_date_time_end::<Error>(b"DTEND;VALUE=DATE:19980704\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeEndProperty {
2
                params: vec![ParamValue::ValueType { value: Value::Date },],
2
                value: DateOrDateTime::Date(Date {
2
                    year: 1998,
2
                    month: 7,
2
                    day: 4,
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_end_datetime() {
2
        let (rem, prop) = prop_date_time_end::<Error>(b"DTEND:19960401T150000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeEndProperty {
2
                params: vec![],
2
                value: DateOrDateTime::DateTime(DateTime {
2
                    date: Date {
2
                        year: 1996,
2
                        month: 4,
2
                        day: 1,
2
                    },
2
                    time: Time {
2
                        hour: 15,
2
                        minute: 0,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_due_date() {
2
        let (rem, prop) = prop_date_time_due::<Error>(b"DUE;VALUE=DATE:19980401\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeDueProperty {
2
                params: vec![ParamValue::ValueType { value: Value::Date },],
2
                value: DateOrDateTime::Date(Date {
2
                    year: 1998,
2
                    month: 4,
2
                    day: 1,
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_due_datetime() {
2
        let (rem, prop) = prop_date_time_due::<Error>(b"DUE:19980430T000000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeDueProperty {
2
                params: vec![],
2
                value: DateOrDateTime::DateTime(DateTime {
2
                    date: Date {
2
                        year: 1998,
2
                        month: 4,
2
                        day: 30,
2
                    },
2
                    time: Time {
2
                        hour: 0,
2
                        minute: 0,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_start_date() {
2
        let (rem, prop) = prop_date_time_start::<Error>(b"DTSTART:19980118\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeStartProperty {
2
                params: vec![],
2
                value: DateOrDateTime::Date(Date {
2
                    year: 1998,
2
                    month: 1,
2
                    day: 18,
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_start_datetime() {
2
        let (rem, prop) = prop_date_time_start::<Error>(b"DTSTART:19980118T073000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeStartProperty {
2
                params: vec![],
2
                value: DateOrDateTime::DateTime(DateTime {
2
                    date: Date {
2
                        year: 1998,
2
                        month: 1,
2
                        day: 18,
2
                    },
2
                    time: Time {
2
                        hour: 7,
2
                        minute: 30,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn duration() {
2
        let (rem, prop) = prop_duration::<Error>(b"DURATION:PT1H0M0S\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DurationProperty {
2
                other_params: vec![],
2
                value: Duration {
2
                    sign: 1,
2
                    hours: Some(1),
2
                    minutes: Some(0),
2
                    seconds: Some(0),
2
                    ..Default::default()
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn free_busy() {
2
        let (rem, prop) = prop_free_busy_time::<Error>(
2
            b"FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:19970308T160000Z/PT8H30M\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            FreeBusyTimeProperty {
2
                params: vec![ParamValue::FreeBusyTimeType {
2
                    fb_type: FreeBusyTimeType::BusyUnavailable,
2
                },],
2
                value: vec![Period {
2
                    start: DateTime {
2
                        date: Date {
2
                            year: 1997,
2
                            month: 3,
2
                            day: 8,
2
                        },
2
                        time: Time {
2
                            hour: 16,
2
                            minute: 0,
2
                            second: 0,
2
                            is_utc: true,
2
                        },
2
                    },
2
                    end: PeriodEnd::Duration(Duration {
2
                        sign: 1,
2
                        hours: Some(8),
2
                        minutes: Some(30),
2
                        ..Default::default()
2
                    }),
2
                }],
2
            }
2
        );
2
    }
    #[test]
2
    fn free_busy_multiple() {
2
        let (rem, prop) = prop_free_busy_time::<Error>(
2
            b"FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            FreeBusyTimeProperty {
2
                params: vec![ParamValue::FreeBusyTimeType {
2
                    fb_type: FreeBusyTimeType::Free,
2
                },],
2
                value: vec![
2
                    Period {
2
                        start: DateTime {
2
                            date: Date {
2
                                year: 1997,
2
                                month: 3,
2
                                day: 8,
2
                            },
2
                            time: Time {
2
                                hour: 16,
2
                                minute: 0,
2
                                second: 0,
2
                                is_utc: true,
2
                            },
2
                        },
2
                        end: PeriodEnd::Duration(Duration {
2
                            sign: 1,
2
                            hours: Some(3),
2
                            ..Default::default()
2
                        }),
2
                    },
2
                    Period {
2
                        start: DateTime {
2
                            date: Date {
2
                                year: 1997,
2
                                month: 3,
2
                                day: 8,
2
                            },
2
                            time: Time {
2
                                hour: 20,
2
                                minute: 0,
2
                                second: 0,
2
                                is_utc: true,
2
                            },
2
                        },
2
                        end: PeriodEnd::Duration(Duration {
2
                            sign: 1,
2
                            hours: Some(1),
2
                            ..Default::default()
2
                        }),
2
                    },
2
                ],
2
            }
2
        );
2
    }
    #[test]
2
    fn transp_opaque() {
2
        let (rem, prop) = prop_time_transparency::<Error>(b"TRANSP:OPAQUE\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeTransparencyProperty {
2
                other_params: vec![],
2
                value: TimeTransparency::Opaque,
2
            }
2
        );
2
    }
    #[test]
2
    fn transp_transparent() {
2
        let (rem, prop) = prop_time_transparency::<Error>(b"TRANSP:TRANSPARENT\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeTransparencyProperty {
2
                other_params: vec![],
2
                value: TimeTransparency::Transparent,
2
            }
2
        );
2
    }
    #[test]
2
    fn time_zone_id() {
2
        let (rem, prop) = prop_time_zone_id::<Error>(b"TZID:America/New_York\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeZoneIdProperty {
2
                other_params: vec![],
2
                unique_registry_id: false,
2
                value: b"America/New_York".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn time_zone_id_custom() {
2
        let (rem, prop) =
2
            prop_time_zone_id::<Error>(b"TZID:/example.org/America/New_York\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeZoneIdProperty {
2
                other_params: vec![],
2
                unique_registry_id: true,
2
                value: b"example.org/America/New_York".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn time_zone_name() {
2
        let (rem, prop) = prop_time_zone_name::<Error>(b"TZNAME:EST\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeZoneNameProperty {
2
                params: vec![],
2
                value: b"EST".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn time_zone_name_with_params() {
2
        let (rem, prop) = prop_time_zone_name::<Error>(b"TZNAME;LANGUAGE=fr-CA:HNE\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeZoneNameProperty {
2
                params: vec![ParamValue::Language {
2
                    language: LanguageTag {
2
                        language: "fr".to_string(),
2
                        region: Some("CA".to_string()),
2
                        ..Default::default()
2
                    },
2
                },],
2
                value: b"HNE".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn time_zone_offset_from() {
2
        let (rem, prop) = prop_time_zone_offset_from::<Error>(b"TZOFFSETFROM:-0500\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeZoneOffsetProperty {
2
                other_params: vec![],
2
                value: UtcOffset {
2
                    sign: -1,
2
                    hours: 5,
2
                    minutes: 0,
2
                    seconds: None,
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn time_zone_offset_to() {
2
        let (rem, prop) = prop_time_zone_offset_to::<Error>(b"TZOFFSETTO:+1245\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeZoneOffsetProperty {
2
                other_params: vec![],
2
                value: UtcOffset {
2
                    sign: 1,
2
                    hours: 12,
2
                    minutes: 45,
2
                    seconds: None,
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn time_zone_url() {
2
        let (rem, prop) = prop_time_zone_url::<Error>(
2
            b"TZURL:http://timezones.example.org/tz/America-Los_Angeles.ics\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TimeZoneUrlProperty {
2
                other_params: vec![],
2
                value: b"http://timezones.example.org/tz/America-Los_Angeles.ics",
2
            }
2
        );
2
    }
    #[test]
2
    fn attendee() {
2
        let (rem, prop) = prop_attendee::<Error>(b"ATTENDEE;ROLE=REQ-PARTICIPANT;DELEGATED-FROM=\"mailto:bob@example.com\";PARTSTAT=ACCEPTED;CN=Jane Doe:mailto:jdoe@example.com\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            AttendeeProperty {
2
                params: vec![
2
                    ParamValue::Role {
2
                        role: Role::RequiredParticipant,
2
                    },
2
                    ParamValue::DelegatedFrom {
2
                        delegators: vec![b"mailto:bob@example.com"],
2
                    },
2
                    ParamValue::ParticipationStatus {
2
                        status: ParticipationStatusUnknown::Accepted,
2
                    },
2
                    ParamValue::CommonName {
2
                        name: "Jane Doe".to_string(),
2
                    },
2
                ],
2
                value: b"mailto:jdoe@example.com",
2
            }
2
        );
2
    }
    #[test]
2
    fn contact() {
2
        let (rem, prop) = prop_contact::<Error>(
2
            b"CONTACT:Jim Dolittle\\, ABC Industries\\, +1-919-555-1234\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            ContactProperty {
2
                params: vec![],
2
                value: b"Jim Dolittle, ABC Industries, +1-919-555-1234".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn contact_altrep() {
2
        let (rem, prop) = prop_contact::<Error>(b"CONTACT;ALTREP=\"ldap://example.com:6666/o=ABC%20Industries,c=US???(cn=Jim%20Dolittle)\":Jim Dolittle\\, ABC Industries\\, +1-919-555-1234\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            ContactProperty {
2
                params: vec![ParamValue::AltRep {
2
                    uri: b"ldap://example.com:6666/o=ABC%20Industries,c=US???(cn=Jim%20Dolittle)",
2
                },],
2
                value: b"Jim Dolittle, ABC Industries, +1-919-555-1234".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn organizer() {
2
        let (rem, prop) =
2
            prop_organizer::<Error>(b"ORGANIZER;CN=John Smith:mailto:jsmith@example.com\r\n;")
2
                .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            OrganizerProperty {
2
                params: vec![ParamValue::CommonName {
2
                    name: "John Smith".to_string(),
2
                },],
2
                value: b"mailto:jsmith@example.com",
2
            }
2
        );
2
    }
    #[test]
2
    fn organizer_with_params() {
2
        let (rem, prop) = prop_organizer::<Error>(b"ORGANIZER;CN=JohnSmith;DIR=\"ldap://example.com:6666/o=DC%20Associates,c=US???(cn=John%20Smith)\":mailto:jsmith@example.com\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            OrganizerProperty {
2
                params: vec![
2
                    ParamValue::CommonName {
2
                        name: "JohnSmith".to_string(),
2
                    },
2
                    ParamValue::DirectoryEntryReference {
2
                        uri: b"ldap://example.com:6666/o=DC%20Associates,c=US???(cn=John%20Smith)",
2
                    },
2
                ],
2
                value: b"mailto:jsmith@example.com",
2
            }
2
        );
2
    }
    #[test]
2
    fn organizer_with_sent_by_param() {
2
        let (rem, prop) = prop_organizer::<Error>(
2
            b"ORGANIZER;SENT-BY=\"mailto:jane_doe@example.com\":mailto:jsmith@example.com\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            OrganizerProperty {
2
                params: vec![ParamValue::SentBy {
2
                    address: b"mailto:jane_doe@example.com",
2
                },],
2
                value: b"mailto:jsmith@example.com",
2
            }
2
        );
2
    }
    #[test]
2
    fn recurrence_id_date() {
2
        let (rem, prop) =
2
            prop_recurrence_id::<Error>(b"RECURRENCE-ID;VALUE=DATE:19960401\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RecurrenceIdProperty {
2
                params: vec![ParamValue::ValueType { value: Value::Date },],
2
                value: DateOrDateTime::Date(Date {
2
                    year: 1996,
2
                    month: 4,
2
                    day: 1,
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn recurrence_id_datetime() {
2
        let (rem, prop) =
2
            prop_recurrence_id::<Error>(b"RECURRENCE-ID;RANGE=THISANDFUTURE:19960120T120000Z\r\n;")
2
                .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RecurrenceIdProperty {
2
                params: vec![ParamValue::Range {
2
                    range: Range::ThisAndFuture,
2
                },],
2
                value: DateOrDateTime::DateTime(DateTime {
2
                    date: Date {
2
                        year: 1996,
2
                        month: 1,
2
                        day: 20,
2
                    },
2
                    time: Time {
2
                        hour: 12,
2
                        minute: 0,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn related_to() {
2
        let (rem, prop) = prop_related_to::<Error>(
2
            b"RELATED-TO:jsmith.part7.19960817T083000.xyzMail@example.com\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RelatedToProperty {
2
                params: vec![],
2
                value: b"jsmith.part7.19960817T083000.xyzMail@example.com".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn url() {
2
        let (rem, prop) =
2
            prop_url::<Error>(b"URL:http://example.com/pub/calendars/jsmith/mytime.ics\r\n;")
2
                .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            UrlProperty {
2
                other_params: vec![],
2
                value: Uri {
2
                    scheme: b"http",
2
                    authority: Some(Authority {
2
                        user_info: None,
2
                        host: Host::RegName(b"example.com".to_vec()),
2
                        port: None,
2
                    }),
2
                    path: b"/pub/calendars/jsmith/mytime.ics".to_vec(),
2
                    query: None,
2
                    fragment: None,
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn unique_identifier() {
2
        let (rem, prop) = prop_unique_identifier::<Error>(
2
            b"UID:19960401T080045Z-4000F192713-0052@example.com\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            UniqueIdentifierProperty {
2
                other_params: vec![],
2
                value: b"19960401T080045Z-4000F192713-0052@example.com".to_vec(),
2
            }
2
        );
2
    }
    #[test]
2
    fn exception_date_times() {
2
        let (rem, prop) = prop_exception_date_times::<Error>(
2
            b"EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            ExceptionDateTimesProperty {
2
                params: vec![],
2
                value: vec![
2
                    DateOrDateTime::DateTime(DateTime {
2
                        date: Date {
2
                            year: 1996,
2
                            month: 4,
2
                            day: 2,
2
                        },
2
                        time: Time {
2
                            hour: 1,
2
                            minute: 0,
2
                            second: 0,
2
                            is_utc: true,
2
                        },
2
                    }),
2
                    DateOrDateTime::DateTime(DateTime {
2
                        date: Date {
2
                            year: 1996,
2
                            month: 4,
2
                            day: 3,
2
                        },
2
                        time: Time {
2
                            hour: 1,
2
                            minute: 0,
2
                            second: 0,
2
                            is_utc: true,
2
                        },
2
                    }),
2
                    DateOrDateTime::DateTime(DateTime {
2
                        date: Date {
2
                            year: 1996,
2
                            month: 4,
2
                            day: 4,
2
                        },
2
                        time: Time {
2
                            hour: 1,
2
                            minute: 0,
2
                            second: 0,
2
                            is_utc: true,
2
                        },
2
                    }),
2
                ],
2
            }
2
        );
2
    }
    #[test]
2
    fn recurrence_date_times_datetime() {
2
        let (rem, prop) = prop_recurrence_date_times::<Error>(
2
            b"RDATE;TZID=America/New_York:19970714T083000\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RecurrenceDateTimesProperty {
2
                params: vec![ParamValue::TimeZoneId {
2
                    tz_id: "America/New_York".to_string(),
2
                    unique: false,
2
                },],
2
                value: vec![DateOrDateTimeOrPeriod::DateTime(DateTime {
2
                    date: Date {
2
                        year: 1997,
2
                        month: 7,
2
                        day: 14,
2
                    },
2
                    time: Time {
2
                        hour: 8,
2
                        minute: 30,
2
                        second: 0,
2
                        is_utc: false,
2
                    },
2
                }),],
2
            }
2
        );
2
    }
    #[test]
2
    fn recurrence_date_times_periods() {
2
        let (rem, prop) = prop_recurrence_date_times::<Error>(
2
            b"RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RecurrenceDateTimesProperty {
2
                params: vec![ParamValue::ValueType {
2
                    value: Value::Period
2
                },],
2
                value: vec![
2
                    DateOrDateTimeOrPeriod::Period(Period {
2
                        start: DateTime {
2
                            date: Date {
2
                                year: 1996,
2
                                month: 4,
2
                                day: 3,
2
                            },
2
                            time: Time {
2
                                hour: 2,
2
                                minute: 0,
2
                                second: 0,
2
                                is_utc: true,
2
                            },
2
                        },
2
                        end: PeriodEnd::DateTime(DateTime {
2
                            date: Date {
2
                                year: 1996,
2
                                month: 4,
2
                                day: 3,
2
                            },
2
                            time: Time {
2
                                hour: 4,
2
                                minute: 0,
2
                                second: 0,
2
                                is_utc: true,
2
                            },
2
                        }),
2
                    }),
2
                    DateOrDateTimeOrPeriod::Period(Period {
2
                        start: DateTime {
2
                            date: Date {
2
                                year: 1996,
2
                                month: 4,
2
                                day: 4,
2
                            },
2
                            time: Time {
2
                                hour: 1,
2
                                minute: 0,
2
                                second: 0,
2
                                is_utc: true,
2
                            },
2
                        },
2
                        end: PeriodEnd::Duration(Duration {
2
                            sign: 1,
2
                            hours: Some(3),
2
                            ..Default::default()
2
                        }),
2
                    }),
2
                ],
2
            }
2
        );
2
    }
    #[test]
2
    fn recurrence_date_times_dates() {
2
        let (rem, prop) = prop_recurrence_date_times::<Error>(
2
            b"RDATE;VALUE=DATE:19970101,19970120,19970217,19970421\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RecurrenceDateTimesProperty {
2
                params: vec![ParamValue::ValueType { value: Value::Date },],
2
                value: vec![
2
                    DateOrDateTimeOrPeriod::Date(Date {
2
                        year: 1997,
2
                        month: 1,
2
                        day: 1,
2
                    }),
2
                    DateOrDateTimeOrPeriod::Date(Date {
2
                        year: 1997,
2
                        month: 1,
2
                        day: 20,
2
                    }),
2
                    DateOrDateTimeOrPeriod::Date(Date {
2
                        year: 1997,
2
                        month: 2,
2
                        day: 17,
2
                    }),
2
                    DateOrDateTimeOrPeriod::Date(Date {
2
                        year: 1997,
2
                        month: 4,
2
                        day: 21,
2
                    }),
2
                ],
2
            }
2
        );
2
    }
    #[test]
2
    fn recurrence_rule() {
2
        let (rem, prop) = prop_recurrence_rule::<Error>(b"RRULE:FREQ=DAILY;COUNT=10\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RecurrenceRuleProperty {
2
                other_params: vec![],
2
                value: vec![
2
                    RecurRulePart::Freq(RecurFreq::Daily),
2
                    RecurRulePart::Count(10),
2
                ],
2
            }
2
        );
2
    }
    #[test]
2
    fn created() {
2
        let (rem, prop) =
2
            prop_date_time_created::<Error>(b"CREATED:19980118T230000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeCreatedProperty {
2
                other_params: vec![],
2
                value: DateTime {
2
                    date: Date {
2
                        year: 1998,
2
                        month: 1,
2
                        day: 18,
2
                    },
2
                    time: Time {
2
                        hour: 23,
2
                        minute: 0,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn action() {
2
        let (rem, prop) = prop_action::<Error>(b"ACTION:DISPLAY\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            ActionProperty {
2
                other_params: vec![],
2
                value: Action::Display,
2
            }
2
        );
2
    }
    #[test]
2
    fn repeat() {
2
        let (rem, prop) = prop_repeat::<Error>(b"REPEAT:4\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RepeatProperty {
2
                other_params: vec![],
2
                value: 4,
2
            }
2
        );
2
    }
    #[test]
2
    fn trigger_duration() {
2
        let (rem, prop) = prop_trigger::<Error>(b"TRIGGER:-PT15M\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TriggerProperty {
2
                params: vec![],
2
                value: DurationOrDateTime::Duration(Duration {
2
                    sign: -1,
2
                    minutes: Some(15),
2
                    ..Default::default()
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn trigger_duration_related_end() {
2
        let (rem, prop) = prop_trigger::<Error>(b"TRIGGER;RELATED=END:PT5M\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TriggerProperty {
2
                params: vec![ParamValue::Related {
2
                    related: TriggerRelationship::End,
2
                },],
2
                value: DurationOrDateTime::Duration(Duration {
2
                    sign: 1,
2
                    minutes: Some(5),
2
                    ..Default::default()
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn trigger_date_time() {
2
        let (rem, prop) =
2
            prop_trigger::<Error>(b"TRIGGER;VALUE=DATE-TIME:19980101T050000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            TriggerProperty {
2
                params: vec![ParamValue::ValueType {
2
                    value: Value::DateTime,
2
                },],
2
                value: DurationOrDateTime::DateTime(DateTime {
2
                    date: Date {
2
                        year: 1998,
2
                        month: 1,
2
                        day: 1,
2
                    },
2
                    time: Time {
2
                        hour: 5,
2
                        minute: 0,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                }),
2
            }
2
        );
2
    }
    #[test]
2
    fn date_time_stamp() {
2
        let (rem, prop) =
2
            prop_date_time_created::<Error>(b"CREATED:19960329T133000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            DateTimeCreatedProperty {
2
                other_params: vec![],
2
                value: DateTime {
2
                    date: Date {
2
                        year: 1996,
2
                        month: 3,
2
                        day: 29,
2
                    },
2
                    time: Time {
2
                        hour: 13,
2
                        minute: 30,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn last_modified() {
2
        let (rem, prop) =
2
            prop_last_modified::<Error>(b"LAST-MODIFIED:19960817T133000Z\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            LastModifiedProperty {
2
                other_params: vec![],
2
                value: DateTime {
2
                    date: Date {
2
                        year: 1996,
2
                        month: 8,
2
                        day: 17,
2
                    },
2
                    time: Time {
2
                        hour: 13,
2
                        minute: 30,
2
                        second: 0,
2
                        is_utc: true,
2
                    },
2
                },
2
            }
2
        );
2
    }
    #[test]
2
    fn sequence() {
2
        let (rem, prop) = prop_sequence::<Error>(b"SEQUENCE:2\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            SequenceProperty {
2
                other_params: vec![],
2
                value: 2,
2
            }
2
        );
2
    }
    #[test]
2
    fn request_status() {
2
        let (rem, prop) = prop_request_status::<Error>(b"REQUEST-STATUS:2.0;Success\r\n;").unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RequestStatusProperty {
2
                params: vec![],
2
                status_code: vec![2, 0],
2
                status_description: b"Success".to_vec(),
2
                exception_data: None,
2
            }
2
        );
2
    }
    #[test]
2
    fn request_status_rejected() {
2
        let (rem, prop) = prop_request_status::<Error>(
2
            b"REQUEST-STATUS:3.1;Invalid property value;DTSTART:96-Apr-01\r\n;",
2
        )
2
        .unwrap();
2
        check_rem(rem, 1);
2
        assert_eq!(
2
            prop,
2
            RequestStatusProperty {
2
                params: vec![],
2
                status_code: vec![3, 1],
2
                status_description: b"Invalid property value".to_vec(),
2
                exception_data: Some(b"DTSTART:96-Apr-01".to_vec()),
2
            }
2
        );
2
    }
}