event.gleam
1 //// Typed event decoders for browser-originated event payloads. 2 3 import lightspeed/form 4 5 /// Browser-originated inbound event frame payload. 6 pub type InboundEvent { 7 InboundEvent(name: String, payload: String) 8 } 9 10 /// Typed event decoder errors. 11 pub type DecodeError { 12 UnexpectedEvent(expected: String, actual: String) 13 InvalidForm(form.FormError) 14 } 15 16 /// Build an inbound event. 17 pub fn inbound(name: String, payload: String) -> InboundEvent { 18 InboundEvent(name: name, payload: payload) 19 } 20 21 /// Event name. 22 pub fn name(event: InboundEvent) -> String { 23 event.name 24 } 25 26 /// Raw payload string. 27 pub fn payload(event: InboundEvent) -> String { 28 event.payload 29 } 30 31 /// Decode an event with no payload requirement. 32 pub fn decode_unit( 33 event: InboundEvent, 34 expected_name: String, 35 message: msg, 36 ) -> Result(msg, DecodeError) { 37 case event.name == expected_name { 38 True -> Ok(message) 39 False -> Error(UnexpectedEvent(expected: expected_name, actual: event.name)) 40 } 41 } 42 43 /// Decode an event payload through form bindings. 44 pub fn decode_form( 45 event: InboundEvent, 46 expected_name: String, 47 with: fn(form.FormData) -> Result(msg, form.FormError), 48 ) -> Result(msg, DecodeError) { 49 case event.name == expected_name { 50 False -> Error(UnexpectedEvent(expected: expected_name, actual: event.name)) 51 True -> 52 event.payload 53 |> form.parse_payload 54 |> with 55 |> map_form_error 56 } 57 } 58 59 /// Convert decoder errors to stable log strings. 60 pub fn error_to_string(error: DecodeError) -> String { 61 case error { 62 UnexpectedEvent(expected, actual) -> 63 "unexpected_event:" <> expected <> ":" <> actual 64 InvalidForm(form_error) -> 65 "invalid_form:" <> form.error_to_string(form_error) 66 } 67 } 68 69 fn map_form_error( 70 result: Result(msg, form.FormError), 71 ) -> Result(msg, DecodeError) { 72 case result { 73 Ok(message) -> Ok(message) 74 Error(error) -> Error(InvalidForm(error)) 75 } 76 }