ical/tree/component/
spec.rs1use crate::{
8 component::IcalComponentKind,
9 prop::IcalPropKind,
10 tree::component::{
11 available, daylight, participant, standard, valarm, vavailability, vcalendar, vevent,
12 vfreebusy, vjournal, vlocation, vresource, vtimezone, vtodo,
13 },
14};
15
16pub trait IcalComponentSpec {
21 const KIND: IcalComponentKind;
23
24 fn allowed_children() -> &'static [IcalComponentKind] {
26 &[]
27 }
28
29 fn required_props() -> &'static [IcalPropKind] {
31 &[]
32 }
33}
34
35#[allow(dead_code)]
38pub(crate) struct IcalComponentSpecFns {
39 pub kind: IcalComponentKind,
42 pub allowed_children: fn() -> &'static [IcalComponentKind],
44 pub required_props: fn() -> &'static [IcalPropKind],
46}
47
48fn spec_fns<C: IcalComponentSpec>() -> IcalComponentSpecFns {
50 IcalComponentSpecFns {
51 kind: C::KIND,
52 allowed_children: C::allowed_children,
53 required_props: C::required_props,
54 }
55}
56
57pub(crate) fn component_spec(component: IcalComponentKind) -> IcalComponentSpecFns {
59 use IcalComponentKind::*;
60
61 match component {
62 VCalendar => spec_fns::<vcalendar::VCALENDAR>(),
63 VEvent => spec_fns::<vevent::VEVENT>(),
64 VTodo => spec_fns::<vtodo::VTODO>(),
65 VJournal => spec_fns::<vjournal::VJOURNAL>(),
66 VFreeBusy => spec_fns::<vfreebusy::VFREEBUSY>(),
67 VTimezone => spec_fns::<vtimezone::VTIMEZONE>(),
68 Standard => spec_fns::<standard::STANDARD>(),
69 Daylight => spec_fns::<daylight::DAYLIGHT>(),
70 VAlarm => spec_fns::<valarm::VALARM>(),
71 Participant => spec_fns::<participant::PARTICIPANT>(),
72 VLocation => spec_fns::<vlocation::VLOCATION>(),
73 VResource => spec_fns::<vresource::VRESOURCE>(),
74 VAvailability => spec_fns::<vavailability::VAVAILABILITY>(),
75 Available => spec_fns::<available::AVAILABLE>(),
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use alloc::vec::Vec;
82
83 use crate::{component::IcalComponentKind, tree::component::spec::component_spec};
84
85 #[test]
86 fn dispatches_every_component_onto_its_own_marker() {
87 for kind in IcalComponentKind::ALL {
88 assert_eq!(component_spec(kind).kind, kind, "{}", &*kind);
89 }
90 }
91
92 #[test]
93 fn every_required_property_is_one_the_component_could_hold() {
94 for kind in IcalComponentKind::ALL {
95 let spec = component_spec(kind);
96
97 let mut required: Vec<&str> = (spec.required_props)().iter().map(|p| &**p).collect();
102 let count = required.len();
103 required.sort_unstable();
104 required.dedup();
105
106 assert_eq!(
107 required.len(),
108 count,
109 "{} requires a property twice",
110 &*kind
111 );
112 }
113 }
114}