ics_core/vcalendar.rs
1//! `VCalendar` — the typed top-level container.
2//!
3//! Today (ADR-001 Migration Step 2) the typed surface covers RFC required
4//! and common fields plus the `events: Vec<VEvent>` collection.
5//! Calendar-level vendor extension bundles (`microsoft`, `google`,
6//! `icloud`) land in Steps 4 onward. `X-WR-*` and `unknown` at the
7//! calendar level land alongside the round-trip strategy refinements
8//! (ADR-018).
9//!
10//! `unrecognized_components` captures `VTIMEZONE`, `VJOURNAL`, etc. for
11//! round-trip preservation.
12
13use serde::Serialize;
14
15use crate::event::VEvent;
16use crate::raw::RawComponent;
17
18#[derive(Debug, Clone, PartialEq, Serialize)]
19pub struct VCalendar {
20 /// Required RFC field. Typically `"2.0"`.
21 pub version: String,
22
23 /// Required RFC field. Producer ID, e.g. `-//makeholiday//EN`.
24 pub prodid: String,
25
26 /// Optional RFC field. Calendar scale; almost always `GREGORIAN`.
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub calscale: Option<String>,
29
30 /// Optional RFC field. Method (e.g. `PUBLISH`, `REQUEST`).
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub method: Option<String>,
33
34 /// Typed events in this calendar.
35 pub events: Vec<VEvent>,
36
37 /// Calendar-level components the typed model does not understand
38 /// (e.g. `VTIMEZONE`, `VJOURNAL`). Preserved verbatim per ADR-018.
39 #[serde(skip_serializing_if = "Vec::is_empty")]
40 pub unrecognized_components: Vec<RawComponent>,
41}
42
43impl VCalendar {
44 /// Construct an empty calendar with the given producer ID and
45 /// `VERSION:2.0`.
46 pub fn new(prodid: impl Into<String>) -> Self {
47 Self {
48 version: "2.0".to_string(),
49 prodid: prodid.into(),
50 calscale: None,
51 method: None,
52 events: Vec::new(),
53 unrecognized_components: Vec::new(),
54 }
55 }
56}