mod export;
mod hatch;
mod import;
mod patch;
use core::{error, fmt};
use alloc::string::{String, ToString};
use serde_json::Value;
use crate::ical::Ical;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IcalJscalendarError {
NotAnObject,
NotAGroup(String),
}
impl fmt::Display for IcalJscalendarError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotAnObject => f.write_str("JSCalendar value is not an object"),
Self::NotAGroup(kind) => write!(
f,
"JSCalendar object is a `{kind}`, not a `Group`, an `Event` or a `Task`"
),
}
}
}
impl error::Error for IcalJscalendarError {}
impl Ical<'_> {
pub fn to_jscalendar(&self) -> Value {
export::group(self)
}
}
impl<'a> Ical<'a> {
pub fn from_jscalendar(jscalendar: &'a Value) -> Result<Self, IcalJscalendarError> {
let object = jscalendar
.as_object()
.ok_or(IcalJscalendarError::NotAnObject)?;
match object.get("@type").and_then(Value::as_str) {
None | Some("Group") => Ok(import::ical(object)),
Some("Event" | "Task") => Ok(import::of_entry(jscalendar)),
Some(kind) => Err(IcalJscalendarError::NotAGroup(kind.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use alloc::{borrow::Cow, vec};
use crate::{
component::{IcalComponent, IcalComponentKind},
ical::Ical,
jscalendar::IcalJscalendarError,
prop::{IcalProp, IcalPropKind},
value::{IcalValue, datetime::IcalDateTime, text::IcalText},
version::IcalVersion,
};
fn calendar() -> Ical<'static> {
Ical {
version: IcalVersion::V2_0,
props: vec![],
components: vec![IcalComponent {
name: IcalComponentKind::VEvent.into(),
props: vec![
IcalProp {
name: IcalPropKind::Uid.into(),
params: vec![],
value: IcalValue::Text(IcalText(Cow::Borrowed("42@example.com"))),
},
IcalProp {
name: IcalPropKind::DtStart.into(),
params: vec![],
value: IcalValue::DateTime(IcalDateTime(Cow::Borrowed("20260102T120000Z"))),
},
IcalProp {
name: IcalPropKind::Summary.into(),
params: vec![],
value: IcalValue::Text(IcalText(Cow::Borrowed("Lunch"))),
},
],
components: vec![],
}],
}
}
#[test]
fn a_group_survives_a_conversion_with_no_parser() {
let group = calendar().to_jscalendar();
let back = Ical::from_jscalendar(&group).expect("a Group");
assert_eq!(back.to_jscalendar(), group);
}
#[test]
fn refuses_an_object_that_is_no_calendar_of_ours() {
let value = serde_json::json!({ "@type": "Alert" });
assert_eq!(
Ical::from_jscalendar(&value),
Err(IcalJscalendarError::NotAGroup("Alert".into()))
);
}
}