blob: 87c611350512dfd7de8f15361fc3bf5f0feb6017 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
std::io::{self, Write},
thiserror::Error,
};
/// An Event from an http sse stream
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Event {
event_type: String,
data: String,
}
impl Event {
pub fn from_type_and_data(
event_type: impl Into<String>,
data: impl Into<String>,
) -> Result<Self, EventError> {
let event_type = event_type.into();
if event_type.contains('\r') {
return Err(EventError::TypeHasCarriageReturn);
}
if event_type.contains('\n') {
return Err(EventError::TypeHasNewline);
}
let data = data.into();
if data.is_empty() {
return Err(EventError::DataIsEmpty);
}
if data.contains('\r') {
return Err(EventError::DataHasCarriageReturn);
}
Ok(Self { event_type, data })
}
/// Serialize the `Event` to bytes suitable for writing to an http sse stream.
pub fn to_writer(&self, mut writer: impl Write) -> io::Result<()> {
if !self.event_type.is_empty() {
write!(&mut writer, "event: {}\n", self.event_type)?;
}
for line in self.data.split('\n') {
write!(&mut writer, "data: {}\n", line)?;
}
writer.write_all(b"\n")?;
Ok(())
}
/// Serialize the `Event` to bytes suitable for writing to an http sse stream.
pub fn to_vec(&self) -> Vec<u8> {
let mut ret = vec![];
// to_writer only errors if the Writer errors, and write for Vec does not error
self.to_writer(&mut ret).unwrap();
ret
}
pub fn event_type(&self) -> &str {
&self.event_type
}
pub fn data(&self) -> &str {
&self.data
}
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum EventError {
#[error("event type cannot contain carriage returns")]
TypeHasCarriageReturn,
#[error("event type cannot contain newlines")]
TypeHasNewline,
#[error("event data cannot be empty")]
DataIsEmpty,
#[error("event data cannot contain carriage returns")]
DataHasCarriageReturn,
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_to_writer(event: &Event, expected: &str) {
let mut bytes = vec![];
event.to_writer(&mut bytes).unwrap();
assert_eq!(std::str::from_utf8(&bytes).unwrap(), expected, "event: {:?}", event);
}
#[test]
fn error_if_type_has_carriage_return() {
assert_eq!(Event::from_type_and_data("\r", "data"), Err(EventError::TypeHasCarriageReturn));
}
#[test]
fn error_if_type_has_newline() {
assert_eq!(Event::from_type_and_data("\n", "data"), Err(EventError::TypeHasNewline));
}
#[test]
fn error_if_data_is_empty() {
assert_eq!(Event::from_type_and_data("", ""), Err(EventError::DataIsEmpty));
}
#[test]
fn error_if_data_has_carriage_return() {
assert_eq!(Event::from_type_and_data("", "\r"), Err(EventError::DataHasCarriageReturn));
}
#[test]
fn to_writer_type_and_data() {
let event = Event::from_type_and_data("type", "data").unwrap();
assert_to_writer(&event, "event: type\ndata: data\n\n");
}
#[test]
fn to_writer_no_type() {
let event = Event::from_type_and_data("", "data").unwrap();
assert_to_writer(&event, "data: data\n\n");
}
#[test]
fn to_writer_data_trailing_newline() {
let event = Event::from_type_and_data("", "data\n").unwrap();
assert_to_writer(&event, "data: data\ndata: \n\n");
}
#[test]
fn to_writer_two_line_data() {
let event = Event::from_type_and_data("", "data1\ndata2").unwrap();
assert_to_writer(&event, "data: data1\ndata: data2\n\n");
}
#[test]
fn to_writer_two_line_data_trailing_newline() {
let event = Event::from_type_and_data("", "data1\ndata2\n").unwrap();
assert_to_writer(&event, "data: data1\ndata: data2\ndata: \n\n");
}
#[test]
fn to_writer_consecutive_newlines() {
let event = Event::from_type_and_data("", "\n\n").unwrap();
assert_to_writer(&event, "data: \ndata: \ndata: \n\n");
}
}