1use std::convert::TryFrom;
17
18use crate::keyword::{KW_ACTION_AUDIO, KW_ACTION_DISPLAY, KW_ACTION_EMAIL, KW_ACTION_PROCEDURE};
19use crate::parameter::{AlarmTriggerRelationship, Parameter, ValueType};
20use crate::property::common::{take_single_text, take_single_value};
21use crate::property::{DateTime, PropertyKind};
22use crate::string_storage::{Segments, StringStorage};
23use crate::syntax::RawParameter;
24use crate::typed::{ParsedProperty, TypedError};
25use crate::value::{Value, ValueDuration};
26
27define_prop_value_enum! {
28 pub enum ActionValue {
30 Audio => KW_ACTION_AUDIO,
32 Display => KW_ACTION_DISPLAY,
34 Email => KW_ACTION_EMAIL,
36 }
37}
38
39impl ActionValue {
40 #[must_use]
42 pub fn as_str(&self) -> &str {
43 match self {
44 Self::Audio => KW_ACTION_AUDIO,
45 Self::Display => KW_ACTION_DISPLAY,
46 Self::Email => KW_ACTION_EMAIL,
47 }
48 }
49}
50
51impl AsRef<str> for ActionValue {
52 fn as_ref(&self) -> &str {
53 self.as_str()
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct Action<S: StringStorage> {
60 pub value: ActionValue,
62 pub x_parameters: Vec<RawParameter<S>>,
64 pub retained_parameters: Vec<Parameter<S>>,
66 pub span: S::Span,
68}
69
70impl<'src> TryFrom<ParsedProperty<'src>> for Action<Segments<'src>> {
71 type Error = Vec<TypedError<'src>>;
72
73 fn try_from(prop: ParsedProperty<'src>) -> Result<Self, Self::Error> {
74 if !matches!(prop.kind, PropertyKind::Action) {
75 return Err(vec![TypedError::PropertyUnexpectedKind {
76 expected: PropertyKind::Action,
77 found: prop.kind,
78 span: prop.span,
79 }]);
80 }
81
82 let mut x_parameters = Vec::new();
83 let mut retained_parameters = Vec::new();
84
85 for param in prop.parameters {
86 match param {
87 Parameter::XName(raw) => x_parameters.push(raw),
88 p @ Parameter::Unrecognized { .. } => retained_parameters.push(p),
89 p => {
90 retained_parameters.push(p);
92 }
93 }
94 }
95
96 let value_span = prop.value.span();
97 let text = take_single_text(&PropertyKind::Action, prop.value)?;
98
99 if text.eq_str_ignore_ascii_case(KW_ACTION_PROCEDURE) {
101 return Err(vec![TypedError::PropertyInvalidValue {
102 property: PropertyKind::Action,
103 value: format!("{KW_ACTION_PROCEDURE} action has been deprecated"),
104 span: value_span,
105 }]);
106 }
107
108 let value = text.try_into().map_err(|text| {
109 vec![TypedError::PropertyInvalidValue {
110 property: PropertyKind::Action,
111 value: format!("Invalid alarm action: {text}"),
112 span: value_span,
113 }]
114 })?;
115
116 Ok(Action {
117 value,
118 x_parameters,
119 retained_parameters,
120 span: prop.span,
121 })
122 }
123}
124
125impl Action<Segments<'_>> {
126 #[must_use]
128 pub fn to_owned(&self) -> Action<String> {
129 Action {
130 value: self.value,
131 x_parameters: self
132 .x_parameters
133 .iter()
134 .map(RawParameter::to_owned)
135 .collect(),
136 retained_parameters: self
137 .retained_parameters
138 .iter()
139 .map(Parameter::to_owned)
140 .collect(),
141 span: (),
142 }
143 }
144}
145
146#[derive(Debug, Clone)]
150pub struct Repeat<S: StringStorage> {
151 pub value: u32,
153 pub x_parameters: Vec<RawParameter<S>>,
155 pub retained_parameters: Vec<Parameter<S>>,
157 pub span: S::Span,
159}
160
161impl<'src> TryFrom<ParsedProperty<'src>> for Repeat<Segments<'src>> {
162 type Error = Vec<TypedError<'src>>;
163
164 #[allow(clippy::cast_sign_loss)]
165 fn try_from(prop: ParsedProperty<'src>) -> Result<Self, Self::Error> {
166 if !matches!(prop.kind, PropertyKind::Repeat) {
167 return Err(vec![TypedError::PropertyUnexpectedKind {
168 expected: PropertyKind::Repeat,
169 found: prop.kind,
170 span: prop.span,
171 }]);
172 }
173
174 let mut x_parameters = Vec::new();
175 let mut retained_parameters = Vec::new();
176
177 for param in prop.parameters {
178 match param {
179 Parameter::XName(raw) => x_parameters.push(raw),
180 p @ Parameter::Unrecognized { .. } => retained_parameters.push(p),
181 p => {
182 retained_parameters.push(p);
184 }
185 }
186 }
187
188 let value_span = prop.value.span();
189 match prop.value {
190 Value::Integer { values, .. } if values.is_empty() => {
191 Err(vec![TypedError::PropertyMissingValue {
192 property: prop.kind,
193 span: prop.span,
194 }])
195 }
196 Value::Integer {
197 values: mut ints, ..
198 } if ints.len() == 1 => {
199 let i = ints.pop().unwrap();
200 if i >= 0 {
201 Ok(Repeat {
202 value: i as u32, x_parameters,
204 retained_parameters,
205 span: prop.span,
206 })
207 } else {
208 Err(vec![TypedError::PropertyInvalidValue {
209 property: prop.kind,
210 value: format!("Repeat count must be non-negative: {i}"),
211 span: value_span,
212 }])
213 }
214 }
215 Value::Integer { values: ints, .. } => {
216 Err(vec![TypedError::PropertyInvalidValueCount {
217 property: PropertyKind::Repeat,
218 expected: 1,
219 found: ints.len(),
220 span: value_span,
221 }])
222 }
223 v => {
224 const EXPECTED: &[ValueType<String>] = &[ValueType::Integer];
225 let span = v.span();
226 Err(vec![TypedError::PropertyUnexpectedValue {
227 property: prop.kind,
228 expected: EXPECTED,
229 found: v.kind().into(),
230 span,
231 }])
232 }
233 }
234 }
235}
236
237impl Repeat<Segments<'_>> {
238 #[must_use]
240 pub fn to_owned(&self) -> Repeat<String> {
241 Repeat {
242 value: self.value,
243 x_parameters: self
244 .x_parameters
245 .iter()
246 .map(RawParameter::to_owned)
247 .collect(),
248 retained_parameters: self
249 .retained_parameters
250 .iter()
251 .map(Parameter::to_owned)
252 .collect(),
253 span: (),
254 }
255 }
256}
257
258#[derive(Debug, Clone)]
260pub struct Trigger<S: StringStorage> {
261 pub value: TriggerValue<S>,
263 pub related: Option<AlarmTriggerRelationship>,
265 pub x_parameters: Vec<RawParameter<S>>,
267 pub retained_parameters: Vec<Parameter<S>>,
269 pub span: S::Span,
271}
272
273#[derive(Debug, Clone)]
275pub enum TriggerValue<S: StringStorage> {
276 Duration(ValueDuration),
278 DateTime(DateTime<S>),
280}
281
282impl<'src> TryFrom<ParsedProperty<'src>> for Trigger<Segments<'src>> {
283 type Error = Vec<TypedError<'src>>;
284
285 fn try_from(prop: ParsedProperty<'src>) -> Result<Self, Self::Error> {
286 if !matches!(prop.kind, PropertyKind::Trigger) {
287 return Err(vec![TypedError::PropertyUnexpectedKind {
288 expected: PropertyKind::Trigger,
289 found: prop.kind,
290 span: prop.span,
291 }]);
292 }
293
294 let mut errors = Vec::new();
295
296 let mut related = None;
298 let mut x_parameters = Vec::new();
299 let mut retained_parameters = Vec::new();
300
301 for param in prop.parameters {
302 match param {
303 p @ Parameter::AlarmTriggerRelationship { .. } if related.is_some() => {
304 errors.push(TypedError::ParameterDuplicated {
305 span: p.span(),
306 parameter: p.kind().into(),
307 });
308 }
309 Parameter::AlarmTriggerRelationship { value, .. } => related = Some(value),
310
311 Parameter::XName(raw) => x_parameters.push(raw),
312 p @ Parameter::Unrecognized { .. } => retained_parameters.push(p),
313 p => {
314 retained_parameters.push(p);
316 }
317 }
318 }
319
320 let value = take_single_value(&PropertyKind::Trigger, prop.value)?;
321
322 if !errors.is_empty() {
324 return Err(errors);
325 }
326
327 match value {
328 Value::Duration { values: durs, .. } if durs.len() == 1 => Ok(Trigger {
329 value: TriggerValue::Duration(durs.into_iter().next().unwrap()),
330 related: Some(related.unwrap_or(AlarmTriggerRelationship::Start)),
331 x_parameters,
332 retained_parameters,
333 span: prop.span,
334 }),
335 Value::DateTime { values: dts, .. } if dts.len() == 1 => {
336 let dt = dts.into_iter().next().unwrap();
337 Ok(Trigger {
338 value: TriggerValue::DateTime(DateTime::Floating {
339 date: dt.date,
340 time: dt.time.into(),
341 x_parameters: Vec::new(),
342 retained_parameters: Vec::new(),
343 }),
344 related: None,
345 x_parameters,
346 retained_parameters,
347 span: prop.span,
348 })
349 }
350 _ => Err(vec![TypedError::PropertyInvalidValue {
351 property: PropertyKind::Trigger,
352 value: "Expected duration or date-time value".to_string(),
353 span: value.span(),
354 }]),
355 }
356 }
357}
358
359impl Trigger<Segments<'_>> {
360 #[must_use]
362 pub fn to_owned(&self) -> Trigger<String> {
363 Trigger {
364 value: self.value.to_owned(),
365 related: self.related,
366 x_parameters: self
367 .x_parameters
368 .iter()
369 .map(RawParameter::to_owned)
370 .collect(),
371 retained_parameters: self
372 .retained_parameters
373 .iter()
374 .map(Parameter::to_owned)
375 .collect(),
376 span: (),
377 }
378 }
379}
380
381impl TriggerValue<Segments<'_>> {
382 #[must_use]
384 pub fn to_owned(&self) -> TriggerValue<String> {
385 match self {
386 TriggerValue::Duration(duration) => TriggerValue::Duration(*duration),
387 TriggerValue::DateTime(dt) => TriggerValue::DateTime(dt.to_owned()),
388 }
389 }
390}