diff --git a/src/components.rs b/src/components.rs index e9b87f9..84ec7f8 100644 --- a/src/components.rs +++ b/src/components.rs @@ -107,10 +107,8 @@ pub trait Component { write_crlf!(out, "UID:{}", Uuid::new_v4())?; } - for properties in self.multi_properties().values() { - for property in properties { - property.fmt_write(out)?; - } + for property in self.multi_properties().values().flatten() { + property.fmt_write(out)?; } for component in self.components() { diff --git a/src/parser/components.rs b/src/parser/components.rs index e048925..bd2295d 100644 --- a/src/parser/components.rs +++ b/src/parser/components.rs @@ -131,23 +131,22 @@ impl From> for Other { impl From> for InnerComponent { fn from(component: Component) -> Self { - fn is_multi(property: &Property) -> bool { - property.name.to_string() == "ATTENDEE" - } - - let (multi, single): (Vec<_>, Vec<_>) = - component.properties.into_iter().partition(is_multi); - let mut from_component = Self { - properties: single - .into_iter() - .map(|p| (p.name.clone().into_owned().into(), p.into())) + properties: component + .properties + .iter() + .filter(|p| !p.is_multi_property()) + .map(|p| (p.name.clone().into_owned().into(), p.to_owned().into())) .collect(), components: component.components.into_iter().map(Other::from).collect(), multi_properties: Default::default(), }; - for p in multi.into_iter() { + for p in component + .properties + .into_iter() + .filter(|p| p.is_multi_property()) + { from_component.insert_multi(p); } @@ -487,6 +486,7 @@ fn test_multi_properties() { .into(), ], ); + assert_parser!( inner_component, r#" diff --git a/src/parser/properties.rs b/src/parser/properties.rs index b0f0005..839bbcc 100644 --- a/src/parser/properties.rs +++ b/src/parser/properties.rs @@ -59,6 +59,28 @@ impl Property<'_> { write_crlf!(out, "{}", fold_line(&line))?; Ok(()) } + + pub(crate) fn is_multi_property(&self) -> bool { + // [RFC-5545](https://datatracker.ietf.org/doc/html/rfc5545) states that the following + // "MAY occur more than once" in a VEVENT, VTODO, VJOURNAL, and VFREEBUSY. + // Note: A VJOURNAL can also contain multiple DECRIPTION but this is not covered here. + [ + "ATTACH", + "ATTENDEE", + "CATEGORIES", + "COMMENT", + "CONTACT", + "EXDATE", + "FREEBUSY", + "IANA-PROP", + "RDATE", + "RELATED", + "RESOURCES", + "RSTATUS", + "X-PROP", + ] + .contains(&self.name.as_str()) + } } impl fmt::Display for Property<'_> {