lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

encode.rs (10888B)


      1 #[cfg(not(feature = "std"))]
      2 use alloc::{format, string::ToString, vec, vec::Vec};
      3 
      4 use radroots_events::{
      5     calendar::{
      6         RadrootsCalendar, RadrootsCalendarDateEvent, RadrootsCalendarEventRsvp,
      7         RadrootsCalendarTimeEvent,
      8     },
      9     kinds::{
     10         KIND_CALENDAR, KIND_CALENDAR_DATE_EVENT, KIND_CALENDAR_EVENT_RSVP, KIND_CALENDAR_TIME_EVENT,
     11     },
     12     social::{
     13         RadrootsCalendarEventFreeBusy, RadrootsCalendarEventRsvpStatus, RadrootsSocialTarget,
     14     },
     15     tags::{
     16         TAG_A, TAG_D, TAG_D_DAY, TAG_E, TAG_END, TAG_END_TZID, TAG_FREE_BUSY, TAG_IMAGE, TAG_START,
     17         TAG_START_TZID, TAG_STATUS, TAG_SUMMARY, TAG_TITLE,
     18     },
     19 };
     20 
     21 use crate::d_tag::validate_d_tag;
     22 use crate::error::EventEncodeError;
     23 use crate::field_helpers::{
     24     parse_address_tag, push_optional_tag, push_tag, push_tag_values, validate_lowercase_hex_64,
     25     validate_non_empty_field,
     26 };
     27 use crate::social_helpers::{
     28     push_location_tags, push_participants, validate_date, validate_date_end_after_start,
     29     validate_end_after_start,
     30 };
     31 use crate::wire::WireEventParts;
     32 
     33 pub fn calendar_date_event_build_tags(
     34     event: &RadrootsCalendarDateEvent,
     35 ) -> Result<Vec<Vec<String>>, EventEncodeError> {
     36     validate_date_event(event)?;
     37     let mut tags = Vec::new();
     38     push_tag(&mut tags, TAG_D, event.d_tag.as_str());
     39     push_tag(&mut tags, TAG_TITLE, event.title.as_str());
     40     push_tag(&mut tags, TAG_START, event.start.as_str());
     41     push_optional_tag(&mut tags, TAG_END, event.end.as_deref());
     42     if let Some(days) = event.days.as_ref() {
     43         for day in days {
     44             validate_date(&day.value, "days")?;
     45             push_tag(&mut tags, TAG_D_DAY, day.value.as_str());
     46         }
     47     }
     48     if let Some(location) = event.location.as_ref() {
     49         push_location_tags(&mut tags, location);
     50     }
     51     push_optional_tag(&mut tags, TAG_SUMMARY, event.summary.as_deref());
     52     push_optional_tag(&mut tags, TAG_IMAGE, event.image.as_deref());
     53     push_participants(&mut tags, event.participants.as_ref());
     54     Ok(tags)
     55 }
     56 
     57 pub fn calendar_time_event_build_tags(
     58     event: &RadrootsCalendarTimeEvent,
     59 ) -> Result<Vec<Vec<String>>, EventEncodeError> {
     60     validate_time_event(event)?;
     61     let mut tags = Vec::new();
     62     push_tag(&mut tags, TAG_D, event.d_tag.as_str());
     63     push_tag(&mut tags, TAG_TITLE, event.title.as_str());
     64     push_tag(&mut tags, TAG_START, event.start.to_string());
     65     for date in &event.dates {
     66         validate_date(&date.value, "dates")?;
     67         push_tag(&mut tags, TAG_D_DAY, date.value.as_str());
     68     }
     69     if let Some(end) = event.end {
     70         push_tag(&mut tags, TAG_END, end.to_string());
     71     }
     72     push_optional_tag(&mut tags, TAG_START_TZID, event.start_tzid.as_deref());
     73     push_optional_tag(&mut tags, TAG_END_TZID, event.end_tzid.as_deref());
     74     if let Some(location) = event.location.as_ref() {
     75         push_location_tags(&mut tags, location);
     76     }
     77     push_optional_tag(&mut tags, TAG_SUMMARY, event.summary.as_deref());
     78     push_optional_tag(&mut tags, TAG_IMAGE, event.image.as_deref());
     79     push_participants(&mut tags, event.participants.as_ref());
     80     Ok(tags)
     81 }
     82 
     83 pub fn calendar_collection_build_tags(
     84     calendar: &RadrootsCalendar,
     85 ) -> Result<Vec<Vec<String>>, EventEncodeError> {
     86     validate_calendar_collection(calendar)?;
     87     let mut tags = Vec::new();
     88     push_tag(&mut tags, TAG_D, calendar.d_tag.as_str());
     89     push_tag(&mut tags, TAG_TITLE, calendar.title.as_str());
     90     push_optional_tag(&mut tags, TAG_SUMMARY, calendar.summary.as_deref());
     91     push_optional_tag(&mut tags, TAG_IMAGE, calendar.image.as_deref());
     92     for event in &calendar.events {
     93         push_calendar_event_address(&mut tags, event, "events")?;
     94     }
     95     Ok(tags)
     96 }
     97 
     98 pub fn rsvp_build_tags(
     99     rsvp: &RadrootsCalendarEventRsvp,
    100 ) -> Result<Vec<Vec<String>>, EventEncodeError> {
    101     validate_rsvp(rsvp)?;
    102     let mut tags = Vec::new();
    103     push_tag(&mut tags, TAG_D, rsvp.d_tag.as_str());
    104     push_calendar_event_address(&mut tags, &rsvp.event, "event")?;
    105     if let Some(event_id) = rsvp.event_id.as_deref() {
    106         let mut tag = vec![TAG_E.to_string(), event_id.to_string()];
    107         if let Some(relays) = calendar_event_relays(&rsvp.event) {
    108             tag.extend(
    109                 relays
    110                     .iter()
    111                     .filter(|relay| !relay.trim().is_empty())
    112                     .cloned(),
    113             );
    114         }
    115         tags.push(tag);
    116     }
    117     push_tag(&mut tags, TAG_STATUS, rsvp_status_as_str(&rsvp.status));
    118     if let Some(free_busy) = rsvp.free_busy.as_ref() {
    119         push_tag(&mut tags, TAG_FREE_BUSY, free_busy_as_str(free_busy));
    120     }
    121     push_participants(&mut tags, rsvp.participants.as_ref());
    122     Ok(tags)
    123 }
    124 
    125 pub fn date_to_wire_parts(
    126     event: &RadrootsCalendarDateEvent,
    127 ) -> Result<WireEventParts, EventEncodeError> {
    128     date_to_wire_parts_with_kind(event, KIND_CALENDAR_DATE_EVENT)
    129 }
    130 
    131 pub fn time_to_wire_parts(
    132     event: &RadrootsCalendarTimeEvent,
    133 ) -> Result<WireEventParts, EventEncodeError> {
    134     time_to_wire_parts_with_kind(event, KIND_CALENDAR_TIME_EVENT)
    135 }
    136 
    137 pub fn calendar_to_wire_parts(
    138     calendar: &RadrootsCalendar,
    139 ) -> Result<WireEventParts, EventEncodeError> {
    140     calendar_to_wire_parts_with_kind(calendar, KIND_CALENDAR)
    141 }
    142 
    143 pub fn rsvp_to_wire_parts(
    144     rsvp: &RadrootsCalendarEventRsvp,
    145 ) -> Result<WireEventParts, EventEncodeError> {
    146     rsvp_to_wire_parts_with_kind(rsvp, KIND_CALENDAR_EVENT_RSVP)
    147 }
    148 
    149 pub fn date_to_wire_parts_with_kind(
    150     event: &RadrootsCalendarDateEvent,
    151     kind: u32,
    152 ) -> Result<WireEventParts, EventEncodeError> {
    153     if kind != KIND_CALENDAR_DATE_EVENT {
    154         return Err(EventEncodeError::InvalidKind(kind));
    155     }
    156     Ok(WireEventParts {
    157         kind,
    158         content: event.description.clone().unwrap_or_default(),
    159         tags: calendar_date_event_build_tags(event)?,
    160     })
    161 }
    162 
    163 pub fn time_to_wire_parts_with_kind(
    164     event: &RadrootsCalendarTimeEvent,
    165     kind: u32,
    166 ) -> Result<WireEventParts, EventEncodeError> {
    167     if kind != KIND_CALENDAR_TIME_EVENT {
    168         return Err(EventEncodeError::InvalidKind(kind));
    169     }
    170     Ok(WireEventParts {
    171         kind,
    172         content: event.description.clone().unwrap_or_default(),
    173         tags: calendar_time_event_build_tags(event)?,
    174     })
    175 }
    176 
    177 pub fn calendar_to_wire_parts_with_kind(
    178     calendar: &RadrootsCalendar,
    179     kind: u32,
    180 ) -> Result<WireEventParts, EventEncodeError> {
    181     if kind != KIND_CALENDAR {
    182         return Err(EventEncodeError::InvalidKind(kind));
    183     }
    184     Ok(WireEventParts {
    185         kind,
    186         content: calendar.description.clone().unwrap_or_default(),
    187         tags: calendar_collection_build_tags(calendar)?,
    188     })
    189 }
    190 
    191 pub fn rsvp_to_wire_parts_with_kind(
    192     rsvp: &RadrootsCalendarEventRsvp,
    193     kind: u32,
    194 ) -> Result<WireEventParts, EventEncodeError> {
    195     if kind != KIND_CALENDAR_EVENT_RSVP {
    196         return Err(EventEncodeError::InvalidKind(kind));
    197     }
    198     Ok(WireEventParts {
    199         kind,
    200         content: rsvp.note.clone().unwrap_or_default(),
    201         tags: rsvp_build_tags(rsvp)?,
    202     })
    203 }
    204 
    205 fn validate_date_event(event: &RadrootsCalendarDateEvent) -> Result<(), EventEncodeError> {
    206     validate_d_tag(&event.d_tag, "d_tag")?;
    207     validate_non_empty_field(&event.title, "title")?;
    208     validate_date(&event.start, "start")?;
    209     if let Some(end) = event.end.as_deref() {
    210         validate_date(end, "end")?;
    211     }
    212     validate_date_end_after_start(&event.start, event.end.as_deref(), "end")?;
    213     Ok(())
    214 }
    215 
    216 fn validate_time_event(event: &RadrootsCalendarTimeEvent) -> Result<(), EventEncodeError> {
    217     validate_d_tag(&event.d_tag, "d_tag")?;
    218     validate_non_empty_field(&event.title, "title")?;
    219     validate_end_after_start(event.start, event.end, "end")?;
    220     if event.dates.is_empty() {
    221         return Err(EventEncodeError::EmptyRequiredField("dates"));
    222     }
    223     for date in &event.dates {
    224         validate_date(&date.value, "dates")?;
    225     }
    226     Ok(())
    227 }
    228 
    229 fn validate_calendar_collection(calendar: &RadrootsCalendar) -> Result<(), EventEncodeError> {
    230     validate_d_tag(&calendar.d_tag, "d_tag")?;
    231     validate_non_empty_field(&calendar.title, "title")?;
    232     if calendar.events.is_empty() {
    233         return Err(EventEncodeError::EmptyRequiredField("events"));
    234     }
    235     Ok(())
    236 }
    237 
    238 fn validate_rsvp(rsvp: &RadrootsCalendarEventRsvp) -> Result<(), EventEncodeError> {
    239     validate_d_tag(&rsvp.d_tag, "d_tag")?;
    240     validate_calendar_event_address(&rsvp.event, "event")?;
    241     if let Some(event_id) = rsvp.event_id.as_deref() {
    242         validate_lowercase_hex_64(event_id, "event_id")?;
    243     }
    244     Ok(())
    245 }
    246 
    247 fn push_calendar_event_address(
    248     tags: &mut Vec<Vec<String>>,
    249     target: &RadrootsSocialTarget,
    250     field: &'static str,
    251 ) -> Result<(), EventEncodeError> {
    252     let RadrootsSocialTarget::Address {
    253         address,
    254         event_kind,
    255         relays,
    256         ..
    257     } = target
    258     else {
    259         return Err(EventEncodeError::InvalidField(field));
    260     };
    261     let address =
    262         parse_address_tag(address, field).map_err(|_| EventEncodeError::InvalidField(field))?;
    263     if !is_calendar_event_kind(address.kind) {
    264         return Err(EventEncodeError::InvalidField(field));
    265     }
    266     if let Some(event_kind) = event_kind
    267         && *event_kind != address.kind
    268     {
    269         return Err(EventEncodeError::InvalidField(field));
    270     }
    271     let value = format!("{}:{}:{}", address.kind, address.pubkey, address.d_tag);
    272     if let Some(relays) = relays.as_ref() {
    273         let mut values = Vec::with_capacity(1 + relays.len());
    274         values.push(value);
    275         values.extend(
    276             relays
    277                 .iter()
    278                 .filter(|relay| !relay.trim().is_empty())
    279                 .cloned(),
    280         );
    281         push_tag_values(tags, TAG_A, values);
    282     } else {
    283         push_tag(tags, TAG_A, value);
    284     }
    285     Ok(())
    286 }
    287 
    288 fn validate_calendar_event_address(
    289     target: &RadrootsSocialTarget,
    290     field: &'static str,
    291 ) -> Result<(), EventEncodeError> {
    292     let mut tags = Vec::new();
    293     push_calendar_event_address(&mut tags, target, field)
    294 }
    295 
    296 fn calendar_event_relays(target: &RadrootsSocialTarget) -> Option<&Vec<String>> {
    297     match target {
    298         RadrootsSocialTarget::Address {
    299             relays: Some(relays),
    300             ..
    301         } => Some(relays),
    302         _ => None,
    303     }
    304 }
    305 
    306 fn is_calendar_event_kind(kind: u32) -> bool {
    307     matches!(kind, KIND_CALENDAR_DATE_EVENT | KIND_CALENDAR_TIME_EVENT)
    308 }
    309 
    310 fn rsvp_status_as_str(status: &RadrootsCalendarEventRsvpStatus) -> &'static str {
    311     match status {
    312         RadrootsCalendarEventRsvpStatus::Accepted => "accepted",
    313         RadrootsCalendarEventRsvpStatus::Declined => "declined",
    314         RadrootsCalendarEventRsvpStatus::Tentative => "tentative",
    315     }
    316 }
    317 
    318 fn free_busy_as_str(free_busy: &RadrootsCalendarEventFreeBusy) -> &'static str {
    319     match free_busy {
    320         RadrootsCalendarEventFreeBusy::Free => "free",
    321         RadrootsCalendarEventFreeBusy::Busy => "busy",
    322     }
    323 }