# SOOP SDK Architecture
This document describes the current architecture and the intended direction for future refactors.
## Current Shape
The SDK currently exposes three main capabilities:
- HTTP API access through `SoopHttpClient` backed by shared `api` helpers.
- Live chat streaming through `SoopChatConnection`.
- VOD chat XML parsing through `parse_vod_chat_xml_with_start_time` and helper methods on `SoopHttpClient`.
Public re-exports are centered in `src/lib.rs`:
- `SoopHttpClient`
- `Error` and `Result`
- `Event`
- VOD models
- VOD chat XML parser
## HTTP Flow
Current flow:
```text
SoopHttpClient method
-> reqwest request
-> status check
-> raw response DTO
-> public model conversion
```
Important files:
- `src/api.rs`: shared request helpers, common headers, status validation, and decode helpers.
- `src/client.rs`: public HTTP methods and DTO conversion orchestration.
- `src/models.rs`: raw response models, public models, and conversion helpers.
- `src/constants.rs`: shared endpoint constants.
- `src/error.rs`: SDK error type.
Current HTTP methods:
- `get_live_status(streamer_id) -> Result<LiveStatus>`
- `get_station(streamer_id) -> Result<Station>`
- `get_signature_emoticon(streamer_id) -> Result<SignatureEmoticonData>`
- `get_vod_list(streamer_id, page) -> Result<Vec<VOD>>`
- `get_vod_detail(vod_id) -> Result<VODDetail>`
- `get_vod_chat(chat_url, start_time) -> Result<String>`
- `get_full_vod_chat(vod_id) -> Result<VodChatCollection>`
## Live Chat Flow
Current flow:
```text
SoopChatConnection::new
-> create command channel
-> create broadcast event channel
SoopChatConnection::start
-> fetch live detail with SoopHttpClient
-> build WebSocket URL
-> spawn background connection task
connection task
-> open WebSocket
-> emit Event::Connected
-> send Connect packet
-> receive server packets
-> MessageHandler::handle
-> parser dispatch
-> emit Event
```
Important files:
- `src/chat/connection.rs`: connection lifecycle, command receiver, ping loop.
- `src/chat/formatter.rs`: outgoing protocol packet formatting.
- `src/chat/commands.rs`: outgoing command and message type definitions.
- `src/chat/message.rs`: incoming message dispatch and event broadcasting.
- `src/chat/events.rs`: public event enum and payload structs.
Current lifecycle notes:
- A `SoopChatConnection` can be started only once.
- Subscribers may call `subscribe()` before or after `start()`.
- `Command::Shutdown` stops the active communication loop.
- Chat sending is not exposed in v0.3.
- The connection loop does not automatically reconnect. Terminal sessions emit `Disconnected` with a reason.
- WebSocket connection attempts use a 15-second timeout.
- TLS certificate verification is currently disabled for the live chat WebSocket connection.
## Parser Flow
Current flow:
```text
WebSocket binary frame
-> Diagnostic(RawFrame) broadcast when diagnostics is enabled
-> parse_message
-> RawMessage { code, ret_code, body, received_time, raw }
-> Diagnostic(DecodedFrame) broadcast when diagnostics is enabled
-> MessageHandler code match
-> parse_*_event
-> typed Event or Diagnostic(ParseFailed) broadcast when diagnostics is enabled
```
Raw chat packets use:
- 14-byte header.
- message code at header bytes `2..6`.
- return code at header bytes `12..14`.
- body after byte `14`.
- form-feed separator `0x0c` for body fields.
Important files:
- `src/chat/parser/raw.rs`: raw frame parsing.
- `src/chat/constants.rs`: protocol separators, message codes, common body indexes.
- `src/chat/parser/*.rs`: event-specific body parsers.
Current parser risk:
- Parsers use shared safe field accessors on `RawMessage`.
- Malformed packets become `Diagnostic(ParseFailed)` when diagnostics is enabled instead of panicking.
Current parser direction:
- Use shared safe field accessors on `RawMessage`.
- Return typed parse errors for missing fields, invalid numbers, and invalid JSON.
- Preserve raw packet context with diagnostic errors when diagnostics is enabled.
- Keep unknown message code reporting through `Event::Unknown`.
- Add replay tests for known raw frames.
## Event Model
`Event` is the public event enum. It is serialized with:
```rust
#[serde(tag = "type", content = "payload")]
```
This means JSON output uses a tagged form such as:
```json
{
"type": "Chat",
"payload": {
"received_time": "2026-07-12T00:00:00Z",
"comment": "hello"
}
}
```
Every structured event payload includes `EventMeta`, currently `received_time`.
Diagnostic events:
- `Event::Unknown(code)` means the SDK decoded the frame but does not have a handler for the message code.
- `Event::Diagnostic(...)` carries raw frames, decoded fields, unknown codes, and parse failure context when `SoopChatOptions::diagnostics` is enabled.
Target event direction:
- Keep stable user-facing events separate from internal protocol diagnostics.
- Consider adding a structured debug/protocol event type for parse errors, unknown fields, and raw body inspection.
- Avoid removing `Raw` while protocol coverage is incomplete.
## VOD Chat Flow
Current VOD flow:
```text
get_vod_detail(vod_id)
-> VODDetail files
-> get_vod_chat(file.chat, start_time)
-> XML string
-> parse_vod_chat_xml_with_start_time
-> Vec<Event>
```
Important files:
- `src/client.rs`: VOD list/detail/chat fetch helpers and `VodChatCollection` assembly.
- `src/vod_chat_parser.rs`: XML parser that maps VOD chat elements into `Event` values.
Current limitation:
- `get_full_vod_chat` records chunk-level successes and failures in `VodChatCollection`.
Target direction:
- Return structured collection results when partial VOD chat chunks fail.
- Add XML fixtures for each supported VOD chat element.
- Keep VOD events aligned with live `Event` payloads where possible.
## Target Refactor Map
Preferred future layers:
```text
api/
request builders, endpoint constants, status handling
models/
raw DTOs and public domain models
chat/session/
connection lifecycle, reconnect policy, shutdown
chat/protocol/
frame formatter, raw parser, message codes
chat/events/
public events and diagnostics
fixtures/
raw frames and VOD XML samples for parser tests
```
This layout is directional, not mandatory. Refactors should be incremental and keep public API compatibility where practical.