encode.rs (9702B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{ 3 string::{String, ToString}, 4 vec, 5 vec::Vec, 6 }; 7 8 use radroots_event::{ 9 calendar::{ 10 AuthoredCalendar, AuthoredCalendarDateEvent, AuthoredCalendarEventRsvp, 11 AuthoredCalendarTimeEvent, CalendarEventAuthorReference, CalendarEventFreeBusy, 12 CalendarEventReference, CalendarEventRevisionReference, CalendarEventRsvpStatus, 13 CalendarParticipant, CalendarRequest, CalendarUri, covered_utc_days, 14 }, 15 envelope::kind::{ 16 KIND_CALENDAR, KIND_CALENDAR_DATE_EVENT, KIND_CALENDAR_EVENT_RSVP, KIND_CALENDAR_TIME_EVENT, 17 }, 18 tag::name::{ 19 TAG_A, TAG_D, TAG_D_DAY, TAG_DESCRIPTION, TAG_E, TAG_END, TAG_END_TZID, TAG_FREE_BUSY, 20 TAG_G, TAG_IMAGE, TAG_LOCATION, TAG_P, TAG_R, TAG_START, TAG_START_TZID, TAG_STATUS, 21 TAG_SUMMARY, TAG_T, TAG_TITLE, 22 }, 23 }; 24 25 use crate::error::EventEncodeError; 26 use crate::field_helpers::{push_optional_tag, push_tag}; 27 use crate::social_helpers::push_participants; 28 use radroots_event::wire::Nip01EventWireParts; 29 30 pub fn calendar_date_event_build_tags( 31 event: &AuthoredCalendarDateEvent, 32 ) -> Result<Vec<Vec<String>>, EventEncodeError> { 33 validate_authored_date_event(event)?; 34 let mut tags = Vec::new(); 35 push_tag(&mut tags, TAG_D, event.d_tag().as_str()); 36 push_tag(&mut tags, TAG_TITLE, event.title()); 37 push_tag(&mut tags, TAG_START, event.start().as_str()); 38 push_optional_tag(&mut tags, TAG_END, event.end().map(|end| end.as_str())); 39 push_authored_calendar_common_tags( 40 &mut tags, 41 event.locations(), 42 event.geohash(), 43 event.summary(), 44 event.image().map(|image| image.descriptor().url().as_str()), 45 event.participants(), 46 event.categories(), 47 event.references(), 48 event.calendar_requests(), 49 ); 50 Ok(tags) 51 } 52 53 pub fn calendar_time_event_build_tags( 54 event: &AuthoredCalendarTimeEvent, 55 ) -> Result<Vec<Vec<String>>, EventEncodeError> { 56 validate_authored_time_event(event)?; 57 let mut tags = Vec::new(); 58 push_tag(&mut tags, TAG_D, event.d_tag().as_str()); 59 push_tag(&mut tags, TAG_TITLE, event.title()); 60 push_tag(&mut tags, TAG_START, event.start().to_string()); 61 if let Some(end) = event.end() { 62 push_tag(&mut tags, TAG_END, end.to_string()); 63 } 64 for day in covered_utc_days(event.start(), event.end()) 65 .map_err(|_| EventEncodeError::InvalidField("end"))? 66 { 67 push_tag(&mut tags, TAG_D_DAY, day.to_string()); 68 } 69 push_optional_tag( 70 &mut tags, 71 TAG_START_TZID, 72 event.start_tzid().map(|tzid| tzid.as_str()), 73 ); 74 push_optional_tag( 75 &mut tags, 76 TAG_END_TZID, 77 event.end_tzid().map(|tzid| tzid.as_str()), 78 ); 79 push_authored_calendar_common_tags( 80 &mut tags, 81 event.locations(), 82 event.geohash(), 83 event.summary(), 84 event.image().map(|image| image.descriptor().url().as_str()), 85 event.participants(), 86 event.categories(), 87 event.references(), 88 event.calendar_requests(), 89 ); 90 Ok(tags) 91 } 92 93 pub fn calendar_collection_build_tags( 94 calendar: &AuthoredCalendar, 95 ) -> Result<Vec<Vec<String>>, EventEncodeError> { 96 let mut tags = Vec::new(); 97 push_tag(&mut tags, TAG_D, calendar.uid().as_str()); 98 push_tag(&mut tags, TAG_TITLE, calendar.title()); 99 for event in calendar.event_references() { 100 push_calendar_event_reference(&mut tags, event); 101 } 102 push_optional_tag(&mut tags, TAG_DESCRIPTION, calendar.list_description()); 103 push_optional_tag( 104 &mut tags, 105 TAG_IMAGE, 106 calendar 107 .image() 108 .map(|image| image.descriptor().url().as_str()), 109 ); 110 Ok(tags) 111 } 112 113 pub fn rsvp_build_tags( 114 rsvp: &AuthoredCalendarEventRsvp, 115 ) -> Result<Vec<Vec<String>>, EventEncodeError> { 116 let mut tags = Vec::new(); 117 push_tag(&mut tags, TAG_D, rsvp.uid().as_str()); 118 push_calendar_event_reference(&mut tags, rsvp.event_reference()); 119 push_tag(&mut tags, TAG_STATUS, rsvp_status_as_str(rsvp.status())); 120 if let Some(reference) = rsvp.revision_reference() { 121 push_calendar_revision_reference(&mut tags, reference); 122 } 123 if let Some(free_busy) = rsvp.observed_free_busy() { 124 push_tag(&mut tags, TAG_FREE_BUSY, free_busy_as_str(free_busy)); 125 } 126 if let Some(author_hint) = rsvp.author_hint() { 127 push_calendar_author_reference(&mut tags, author_hint); 128 } 129 Ok(tags) 130 } 131 132 pub fn date_to_wire_parts( 133 event: &AuthoredCalendarDateEvent, 134 ) -> Result<Nip01EventWireParts, EventEncodeError> { 135 date_to_wire_parts_with_kind(event, KIND_CALENDAR_DATE_EVENT) 136 } 137 138 pub fn time_to_wire_parts( 139 event: &AuthoredCalendarTimeEvent, 140 ) -> Result<Nip01EventWireParts, EventEncodeError> { 141 time_to_wire_parts_with_kind(event, KIND_CALENDAR_TIME_EVENT) 142 } 143 144 pub fn calendar_to_wire_parts( 145 calendar: &AuthoredCalendar, 146 ) -> Result<Nip01EventWireParts, EventEncodeError> { 147 calendar_to_wire_parts_with_kind(calendar, KIND_CALENDAR) 148 } 149 150 pub fn rsvp_to_wire_parts( 151 rsvp: &AuthoredCalendarEventRsvp, 152 ) -> Result<Nip01EventWireParts, EventEncodeError> { 153 rsvp_to_wire_parts_with_kind(rsvp, KIND_CALENDAR_EVENT_RSVP) 154 } 155 156 pub fn date_to_wire_parts_with_kind( 157 event: &AuthoredCalendarDateEvent, 158 kind: u32, 159 ) -> Result<Nip01EventWireParts, EventEncodeError> { 160 if kind != KIND_CALENDAR_DATE_EVENT { 161 return Err(EventEncodeError::InvalidKind(kind)); 162 } 163 Ok(Nip01EventWireParts { 164 kind, 165 content: event.description().unwrap_or_default().to_string(), 166 tags: calendar_date_event_build_tags(event)?, 167 }) 168 } 169 170 pub fn time_to_wire_parts_with_kind( 171 event: &AuthoredCalendarTimeEvent, 172 kind: u32, 173 ) -> Result<Nip01EventWireParts, EventEncodeError> { 174 if kind != KIND_CALENDAR_TIME_EVENT { 175 return Err(EventEncodeError::InvalidKind(kind)); 176 } 177 Ok(Nip01EventWireParts { 178 kind, 179 content: event.description().unwrap_or_default().to_string(), 180 tags: calendar_time_event_build_tags(event)?, 181 }) 182 } 183 184 pub fn calendar_to_wire_parts_with_kind( 185 calendar: &AuthoredCalendar, 186 kind: u32, 187 ) -> Result<Nip01EventWireParts, EventEncodeError> { 188 if kind != KIND_CALENDAR { 189 return Err(EventEncodeError::InvalidKind(kind)); 190 } 191 Ok(Nip01EventWireParts { 192 kind, 193 content: calendar.content().to_string(), 194 tags: calendar_collection_build_tags(calendar)?, 195 }) 196 } 197 198 pub fn rsvp_to_wire_parts_with_kind( 199 rsvp: &AuthoredCalendarEventRsvp, 200 kind: u32, 201 ) -> Result<Nip01EventWireParts, EventEncodeError> { 202 if kind != KIND_CALENDAR_EVENT_RSVP { 203 return Err(EventEncodeError::InvalidKind(kind)); 204 } 205 Ok(Nip01EventWireParts { 206 kind, 207 content: rsvp.note().unwrap_or_default().to_string(), 208 tags: rsvp_build_tags(rsvp)?, 209 }) 210 } 211 212 fn validate_authored_date_event( 213 _event: &AuthoredCalendarDateEvent, 214 ) -> Result<(), EventEncodeError> { 215 Ok(()) 216 } 217 218 fn validate_authored_time_event(event: &AuthoredCalendarTimeEvent) -> Result<(), EventEncodeError> { 219 covered_utc_days(event.start(), event.end()) 220 .map_err(|_| EventEncodeError::InvalidField("end"))?; 221 Ok(()) 222 } 223 224 #[allow(clippy::too_many_arguments)] 225 fn push_authored_calendar_common_tags( 226 tags: &mut Vec<Vec<String>>, 227 locations: &[String], 228 geohash: Option<&str>, 229 summary: Option<&str>, 230 image: Option<&str>, 231 participants: Option<&Vec<CalendarParticipant>>, 232 categories: &[String], 233 references: &[CalendarUri], 234 calendar_requests: &[CalendarRequest], 235 ) { 236 for location in locations { 237 push_tag(tags, TAG_LOCATION, location); 238 } 239 push_optional_tag(tags, TAG_G, geohash); 240 push_optional_tag(tags, TAG_SUMMARY, summary); 241 push_optional_tag(tags, TAG_IMAGE, image); 242 push_participants(tags, participants); 243 for category in categories { 244 push_tag(tags, TAG_T, category); 245 } 246 for reference in references { 247 push_tag(tags, TAG_R, reference.as_str()); 248 } 249 for request in calendar_requests { 250 let mut tag = vec![TAG_A.to_string(), request.calendar().as_str().to_string()]; 251 if let Some(relay) = request.relay() { 252 tag.push(relay.to_string()); 253 } 254 tags.push(tag); 255 } 256 } 257 258 fn push_calendar_event_reference(tags: &mut Vec<Vec<String>>, reference: &CalendarEventReference) { 259 let mut tag = vec![ 260 TAG_A.to_string(), 261 reference.coordinate().as_str().to_string(), 262 ]; 263 if let Some(relay) = reference.relay() { 264 tag.push(relay.to_string()); 265 } 266 tags.push(tag); 267 } 268 269 fn push_calendar_revision_reference( 270 tags: &mut Vec<Vec<String>>, 271 reference: &CalendarEventRevisionReference, 272 ) { 273 let mut tag = vec![TAG_E.to_string(), reference.event_id().to_hex()]; 274 if let Some(relay) = reference.relay() { 275 tag.push(relay.to_string()); 276 } 277 tags.push(tag); 278 } 279 280 fn push_calendar_author_reference( 281 tags: &mut Vec<Vec<String>>, 282 reference: &CalendarEventAuthorReference, 283 ) { 284 let mut tag = vec![TAG_P.to_string(), reference.pubkey().to_hex()]; 285 if let Some(relay) = reference.relay() { 286 tag.push(relay.to_string()); 287 } 288 tags.push(tag); 289 } 290 291 fn rsvp_status_as_str(status: &CalendarEventRsvpStatus) -> &'static str { 292 match status { 293 CalendarEventRsvpStatus::Accepted => "accepted", 294 CalendarEventRsvpStatus::Declined => "declined", 295 CalendarEventRsvpStatus::Tentative => "tentative", 296 } 297 } 298 299 fn free_busy_as_str(free_busy: &CalendarEventFreeBusy) -> &'static str { 300 match free_busy { 301 CalendarEventFreeBusy::Free => "free", 302 CalendarEventFreeBusy::Busy => "busy", 303 } 304 }