use alloc::{borrow::ToOwned, format, string::ToString};
use serde_json::{Map, Value, json};
use crate::{
component::IcalComponent,
jscalendar::{
export::temporal::{local_text, utc},
export::{Builder, component_key, param, text},
hatch::IcalHatch,
},
param::IcalParamKind,
prop::{IcalProp, IcalPropKind, IcalPropName},
value::IcalValue,
};
impl Builder {
pub(super) fn alarm(&mut self, component: &IcalComponent<'_>) {
let mut alert = Map::new();
alert.insert("@type".to_owned(), Value::String("Alert".to_owned()));
let mut hatch = IcalHatch::new("valarm");
for prop in &component.props {
let IcalPropName::Kind(kind) = &prop.name else {
if !prop.name.eq_ignore_ascii_case("JSID") {
hatch.keep(prop);
}
continue;
};
match kind {
IcalPropKind::Trigger => match trigger(prop) {
Some(trigger) => {
alert.insert("trigger".to_owned(), trigger);
hatch.note(
"trigger",
prop,
&[IcalParamKind::Related, IcalParamKind::Value],
);
}
None => hatch.keep(prop),
},
IcalPropKind::Action => match text(prop).map(|action| action.to_lowercase()) {
Some(action) if action == "display" || action == "email" => {
alert.insert("action".to_owned(), Value::String(action));
hatch.note("action", prop, &[]);
}
_ => hatch.keep(prop),
},
IcalPropKind::Acknowledged => match utc(prop) {
Some(at) => {
alert.insert("acknowledged".to_owned(), Value::String(at));
hatch.note("acknowledged", prop, &[]);
}
None => hatch.keep(prop),
},
_ => hatch.keep(prop),
}
}
for child in &component.components {
hatch.keep_component(child);
}
if let Some(hatch) = hatch.into_value() {
alert.insert("iCalendar".to_owned(), hatch);
}
let key = component_key(&self.alerts, component);
self.alerts.insert(key, Value::Object(alert));
}
}
fn trigger(prop: &IcalProp<'_>) -> Option<Value> {
let text = text(prop)?;
if matches!(prop.value, IcalValue::DateTime(_) | IcalValue::Date(_)) {
let when = local_text(&text, false).map(|when| format!("{when}Z"))?;
return Some(json!({"@type": "AbsoluteTrigger", "when": when}));
}
let mut object = Map::new();
object.insert(
"@type".to_owned(),
Value::String("OffsetTrigger".to_owned()),
);
object.insert("offset".to_owned(), Value::String(text.to_string()));
if let Some(related) = param(prop, IcalParamKind::Related)
&& related.eq_ignore_ascii_case("END")
{
object.insert("relativeTo".to_owned(), Value::String("end".to_owned()));
}
Some(Value::Object(object))
}