aimcal_ical/parameter/
definition.rs

1// SPDX-FileCopyrightText: 2025-2026 Zexin Yuan <aim@yzx9.xyz>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use std::fmt;
6
7use crate::keyword::{
8    KW_BINARY, KW_BOOLEAN, KW_CAL_ADDRESS, KW_CUTYPE_GROUP, KW_CUTYPE_INDIVIDUAL,
9    KW_CUTYPE_RESOURCE, KW_CUTYPE_ROOM, KW_CUTYPE_UNKNOWN, KW_DATE, KW_DATETIME, KW_DURATION,
10    KW_ENCODING_8BIT, KW_ENCODING_BASE64, KW_FBTYPE_BUSY, KW_FBTYPE_BUSY_TENTATIVE,
11    KW_FBTYPE_BUSY_UNAVAILABLE, KW_FBTYPE_FREE, KW_FLOAT, KW_INTEGER, KW_PARTSTAT_ACCEPTED,
12    KW_PARTSTAT_COMPLETED, KW_PARTSTAT_DECLINED, KW_PARTSTAT_DELEGATED, KW_PARTSTAT_IN_PROCESS,
13    KW_PARTSTAT_NEEDS_ACTION, KW_PARTSTAT_TENTATIVE, KW_PERIOD, KW_RANGE_THISANDFUTURE,
14    KW_RELATED_END, KW_RELATED_START, KW_RELTYPE_CHILD, KW_RELTYPE_PARENT, KW_RELTYPE_SIBLING,
15    KW_ROLE_CHAIR, KW_ROLE_NON_PARTICIPANT, KW_ROLE_OPT_PARTICIPANT, KW_ROLE_REQ_PARTICIPANT,
16    KW_RRULE, KW_RSVP_FALSE, KW_RSVP_TRUE, KW_TEXT, KW_TIME, KW_URI, KW_UTC_OFFSET,
17};
18use crate::parameter::util::{ParseResult, parse_single, parse_single_not_quoted};
19use crate::parameter::{Parameter, ParameterKind};
20use crate::string_storage::{Segments, StringStorage};
21use crate::syntax::{RawParameter, RawParameterValue};
22use crate::typed::TypedError;
23
24/// Parse RSVP expectation parameter.
25///
26/// # Errors
27///
28/// Returns an error if the parameter value is not `TRUE` or `FALSE`.
29pub fn parse_rsvp(mut param: RawParameter<Segments<'_>>) -> ParseResult<'_> {
30    let span = param.span;
31    parse_single(&mut param, ParameterKind::RsvpExpectation).and_then(|v| {
32        if v.value.eq_str_ignore_ascii_case(KW_RSVP_TRUE) {
33            Ok(Parameter::RsvpExpectation { value: true, span })
34        } else if v.value.eq_str_ignore_ascii_case(KW_RSVP_FALSE) {
35            Ok(Parameter::RsvpExpectation { value: false, span })
36        } else {
37            Err(vec![TypedError::ParameterValueInvalid {
38                parameter: ParameterKind::RsvpExpectation,
39                value: v.value,
40                span,
41            }])
42        }
43    })
44}
45
46/// Parse timezone identifier parameter.
47///
48/// # Errors
49///
50/// Returns an error if:
51/// - The parameter does not have exactly one value (when jiff feature is enabled)
52/// - The timezone identifier is not valid (when jiff feature is enabled)
53pub fn parse_tzid<'src>(mut param: RawParameter<Segments<'src>>) -> ParseResult<'src> {
54    let span = param.span;
55
56    #[cfg(feature = "jiff")]
57    let op = |v: RawParameterValue<Segments<'src>>| {
58        // Use jiff to validate time zone identifier
59        let tzid_str = v.value.resolve();
60        match jiff::tz::TimeZone::get(tzid_str.as_ref()) {
61            Ok(tz) => Ok(Parameter::TimeZoneIdentifier {
62                value: v.value,
63                span,
64                tz,
65            }),
66            Err(_) => Err(vec![TypedError::ParameterValueInvalid {
67                parameter: ParameterKind::TimeZoneIdentifier,
68                value: v.value,
69                span,
70            }]),
71        }
72    };
73
74    #[cfg(not(feature = "jiff"))]
75    let op = |v: RawParameterValue<Segments<'src>>| {
76        Ok(Parameter::TimeZoneIdentifier {
77            value: v.value,
78            span,
79        })
80    };
81
82    parse_single(&mut param, ParameterKind::TimeZoneIdentifier).and_then(op)
83}
84
85define_param_enum_with_unknown! {
86    #[derive(Default)]
87    pub enum CalendarUserType {
88        /// An individual
89        #[default]
90        Individual => KW_CUTYPE_INDIVIDUAL,
91        /// A group of individuals
92        Group      => KW_CUTYPE_GROUP,
93        /// A physical resource
94        Resource   => KW_CUTYPE_RESOURCE,
95        /// A room resource
96        Room       => KW_CUTYPE_ROOM,
97        /// Otherwise not known
98        Unknown    => KW_CUTYPE_UNKNOWN,
99    }
100
101    parser = pub fn parse_cutype;
102}
103
104define_param_enum! {
105    /// This parameter identifies the inline encoding used in a property value.
106    #[derive(Default)]
107    pub enum Encoding {
108        /// The default encoding is "8BIT", corresponding to a property value
109        /// consisting of text.
110        #[default]
111        Bit8   => KW_ENCODING_8BIT,
112        /// The "BASE64" encoding type corresponds to a property value encoded
113        /// using the "BASE64" encoding defined in [RFC2045].
114        Base64 => KW_ENCODING_BASE64,
115    }
116
117    parser  = pub fn parse_encoding;
118}
119
120define_param_enum_with_unknown! {
121    /// This parameter defines the free or busy time type for a time
122    #[derive(Default)]
123    pub enum FreeBusyType {
124        /// The time interval is free for scheduling
125        #[default]
126        Free             => KW_FBTYPE_FREE,
127        /// The time interval is busy because one or more events have been
128        /// scheduled for that interval
129        Busy             => KW_FBTYPE_BUSY,
130        /// The time interval is busy and that the interval can not be scheduled.
131        BusyUnavailable  => KW_FBTYPE_BUSY_UNAVAILABLE,
132        /// The time interval is busy because one or more events have been
133        /// tentatively scheduled for that interval.
134        BusyTentative    => KW_FBTYPE_BUSY_TENTATIVE,
135    }
136
137    parser = pub fn parse_fbtype;
138}
139
140define_param_enum_with_unknown! {
141    pub enum ParticipationStatus {
142        NeedsAction  => KW_PARTSTAT_NEEDS_ACTION,
143        Accepted     => KW_PARTSTAT_ACCEPTED,
144        Declined     => KW_PARTSTAT_DECLINED,
145        Tentative    => KW_PARTSTAT_TENTATIVE,
146        Delegated    => KW_PARTSTAT_DELEGATED,
147        Completed    => KW_PARTSTAT_COMPLETED,
148        InProcess    => KW_PARTSTAT_IN_PROCESS,
149    }
150
151    parser = pub fn parse_partstat;
152}
153
154define_param_enum! {
155    pub enum RecurrenceIdRange {
156        /// A range defined by the recurrence identifier and all subsequent
157        /// instances
158        ThisAndFuture => KW_RANGE_THISANDFUTURE,
159
160        // The value "THISANDPRIOR" is deprecated by this revision of iCalendar
161        // and MUST NOT be generated by applications.
162    }
163
164    parser = pub fn parse_range;
165}
166
167define_param_enum! {
168    /// This parameter defines the relationship of the alarm trigger to the
169    #[derive(Default)]
170    pub enum AlarmTriggerRelationship {
171        /// The parameter value START will set the alarm to trigger off the
172        /// start of the calendar component;
173        #[default]
174        Start => KW_RELATED_START,
175        /// the parameter value END will set the alarm to trigger off the end
176        /// of the calendar component.
177        End   => KW_RELATED_END,
178    }
179
180    parser = pub fn parse_alarm_trigger_relationship;
181}
182
183define_param_enum_with_unknown! {
184    #[derive(Default)]
185    pub enum RelationshipType {
186        /// The referenced calendar component is a superior of calendar component
187        #[default]
188        Parent  => KW_RELTYPE_PARENT,
189        /// The referenced calendar component is a subordinate of the calendar
190        /// component
191        Child   => KW_RELTYPE_CHILD,
192        /// The referenced calendar component is a peer of the calendar component
193        Sibling => KW_RELTYPE_SIBLING,
194    }
195
196    parser = pub fn parse_reltype;
197}
198
199define_param_enum_with_unknown! {
200    #[derive(Default)]
201    pub enum ParticipationRole {
202        Chair             => KW_ROLE_CHAIR,
203        #[default]
204        ReqParticipant    => KW_ROLE_REQ_PARTICIPANT,
205        OptParticipant    => KW_ROLE_OPT_PARTICIPANT,
206        NonParticipant    => KW_ROLE_NON_PARTICIPANT,
207    }
208
209    parser = pub fn parse_role;
210}
211
212define_param_enum_with_unknown! {
213    pub enum ValueType {
214        Binary              => KW_BINARY,
215        Boolean             => KW_BOOLEAN,
216        CalendarUserAddress => KW_CAL_ADDRESS,
217        Date                => KW_DATE,
218        DateTime            => KW_DATETIME,
219        Duration            => KW_DURATION,
220        Float               => KW_FLOAT,
221        Integer             => KW_INTEGER,
222        Period              => KW_PERIOD,
223        RecurrenceRule      => KW_RRULE,
224        Text                => KW_TEXT,
225        Time                => KW_TIME,
226        Uri                 => KW_URI,
227        UtcOffset           => KW_UTC_OFFSET,
228    }
229
230    parser = pub fn parse_value_type;
231    gen_eq_known;
232}
233
234impl<S: StringStorage> From<ValueType<&S>> for ValueType<S> {
235    fn from(value: ValueType<&S>) -> Self {
236        match value {
237            ValueType::Binary => ValueType::Binary,
238            ValueType::Boolean => ValueType::Boolean,
239            ValueType::CalendarUserAddress => ValueType::CalendarUserAddress,
240            ValueType::Date => ValueType::Date,
241            ValueType::DateTime => ValueType::DateTime,
242            ValueType::Duration => ValueType::Duration,
243            ValueType::Float => ValueType::Float,
244            ValueType::Integer => ValueType::Integer,
245            ValueType::Period => ValueType::Period,
246            ValueType::RecurrenceRule => ValueType::RecurrenceRule,
247            ValueType::Text => ValueType::Text,
248            ValueType::Time => ValueType::Time,
249            ValueType::Uri => ValueType::Uri,
250            ValueType::UtcOffset => ValueType::UtcOffset,
251            ValueType::XName(v) => ValueType::XName(v.to_owned()),
252            ValueType::Unrecognized(v) => ValueType::Unrecognized(v.to_owned()),
253        }
254    }
255}