Skip to main content

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}