sse-frame 0.1.0

Streaming parser for Server-Sent Events frames as used by LLM APIs (OpenAI, Anthropic, Vertex). Push bytes, get back complete event records. Zero deps.
Documentation
use sse_frame::Parser;

#[test]
fn one_event_with_event_and_data() {
    let mut p = Parser::new();
    let evs = p.push(b"event: message\ndata: hello\n\n");
    assert_eq!(evs.len(), 1);
    assert_eq!(evs[0].event.as_deref(), Some("message"));
    assert_eq!(evs[0].data, "hello");
}

#[test]
fn multi_line_data_joined_by_newline() {
    let mut p = Parser::new();
    let evs = p.push(b"data: line1\ndata: line2\n\n");
    assert_eq!(evs[0].data, "line1\nline2");
}

#[test]
fn id_and_retry_parsed() {
    let mut p = Parser::new();
    let evs = p.push(b"id: 42\nretry: 5000\ndata: x\n\n");
    assert_eq!(evs[0].id.as_deref(), Some("42"));
    assert_eq!(evs[0].retry_ms, Some(5000));
}

#[test]
fn comments_ignored() {
    let mut p = Parser::new();
    let evs = p.push(b": ping\ndata: real\n\n");
    assert_eq!(evs.len(), 1);
    assert_eq!(evs[0].data, "real");
}

#[test]
fn crlf_handled() {
    let mut p = Parser::new();
    let evs = p.push(b"data: x\r\n\r\n");
    assert_eq!(evs.len(), 1);
    assert_eq!(evs[0].data, "x");
}

#[test]
fn split_across_pushes() {
    let mut p = Parser::new();
    let mut evs = p.push(b"data: hel");
    assert!(evs.is_empty());
    evs = p.push(b"lo\n\n");
    assert_eq!(evs[0].data, "hello");
}

#[test]
fn multiple_events_one_push() {
    let mut p = Parser::new();
    let evs = p.push(b"data: a\n\ndata: b\n\n");
    assert_eq!(evs.len(), 2);
    assert_eq!(evs[0].data, "a");
    assert_eq!(evs[1].data, "b");
}

#[test]
fn flush_returns_pending_without_blank_line() {
    let mut p = Parser::new();
    let evs = p.push(b"data: tail\n");
    assert!(evs.is_empty());
    let f = p.flush().unwrap();
    assert_eq!(f.data, "tail");
}