use std::collections::VecDeque;
use std::io::{self, BufReader, BufWriter, Write};
use std::net::{Shutdown, TcpStream, ToSocketAddrs};
use std::{error, fmt};
use tephra_proto::convert as wire;
use tephra_proto::tephra as pb;
use tephra_proto::{DEFAULT_MAX_FRAME_LEN, FrameError, read_frame, write_frame};
pub use tephra_types::{
AppendCondition, EventType, NameError, Position, Query, QueryItem, Tag, Tags, TagsError,
};
pub use tephra_proto::convert::ErrorCode;
pub use tephra_proto::tephra as proto;
const UNATTRIBUTED_REQUEST_ID: u64 = 0;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Event {
event_type: EventType,
tags: Tags,
payload: Vec<u8>,
}
impl Event {
pub fn new(
event_type: impl AsRef<str>,
tags: &[&str],
payload: impl Into<Vec<u8>>,
) -> Result<Event, BuildError> {
let event_type = EventType::new(event_type).map_err(BuildError::Name)?;
let mut collected: Vec<Tag> = Vec::with_capacity(tags.len());
for tag in tags {
collected.push(Tag::new(*tag).map_err(BuildError::Name)?);
}
let tags = Tags::new(collected).map_err(BuildError::Tags)?;
Ok(Event {
event_type,
tags,
payload: payload.into(),
})
}
pub fn event_type(&self) -> &str {
self.event_type.as_str()
}
pub fn tags(&self) -> impl ExactSizeIterator<Item = &str> {
self.tags.iter().map(|tag| tag.as_str())
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SequencedEvent {
position: Position,
event: Event,
}
impl SequencedEvent {
pub fn position(&self) -> Position {
self.position
}
pub fn event(&self) -> &Event {
&self.event
}
pub fn into_event(self) -> Event {
self.event
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AppendResult {
pub first: Position,
pub last: Position,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SubEvent {
Event(SequencedEvent),
CaughtUp(Position),
}
#[derive(Debug, PartialEq, Eq)]
pub enum BuildError {
Name(NameError),
Tags(TagsError),
}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BuildError::Name(err) => write!(f, "{err}"),
BuildError::Tags(err) => write!(f, "{err}"),
}
}
}
impl error::Error for BuildError {}
#[derive(Debug)]
pub enum ClientError {
Frame(FrameError),
UnexpectedEof,
Protocol(String),
Server {
code: ErrorCode,
message: String,
retryable: bool,
conflict_position: Option<Position>,
},
}
impl fmt::Display for ClientError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ClientError::Frame(err) => write!(f, "{err}"),
ClientError::UnexpectedEof => write!(f, "server closed the connection unexpectedly"),
ClientError::Protocol(msg) => write!(f, "protocol error: {msg}"),
ClientError::Server {
code,
message,
retryable,
..
} => write!(
f,
"server error ({code:?}, retryable={retryable}): {message}"
),
}
}
}
impl error::Error for ClientError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
ClientError::Frame(err) => Some(err),
_ => None,
}
}
}
impl From<FrameError> for ClientError {
fn from(err: FrameError) -> Self {
ClientError::Frame(err)
}
}
impl From<io::Error> for ClientError {
fn from(err: io::Error) -> Self {
ClientError::Frame(FrameError::Io(err))
}
}
pub struct Client {
reader: BufReader<TcpStream>,
writer: BufWriter<TcpStream>,
next_id: u64,
max_frame_len: u32,
}
impl Client {
pub fn connect(addr: impl ToSocketAddrs) -> io::Result<Client> {
let stream = TcpStream::connect(addr)?;
stream.set_nodelay(true)?;
let reader = BufReader::new(stream.try_clone()?);
let writer = BufWriter::new(stream);
Ok(Client {
reader,
writer,
next_id: 1,
max_frame_len: DEFAULT_MAX_FRAME_LEN,
})
}
pub fn set_max_frame_len(&mut self, max_frame_len: u32) {
self.max_frame_len = max_frame_len;
}
fn next_id(&mut self) -> u64 {
let id = self.next_id;
self.next_id += 1;
id
}
fn send(&mut self, request: &pb::Request) -> Result<(), ClientError> {
write_frame(&mut self.writer, request, self.max_frame_len)?;
self.writer.flush()?;
Ok(())
}
pub fn append(
&mut self,
events: impl IntoIterator<Item = Event>,
condition: Option<AppendCondition>,
) -> Result<AppendResult, ClientError> {
let id = self.next_id();
let mut append = pb::AppendRequest::new();
for event in events {
append.events_mut().push(event_to_pb(&event));
}
if let Some(condition) = condition {
append.set_condition(wire::condition_to_pb(&condition));
}
let mut request = pb::Request::new();
request.set_request_id(id);
request.set_append(append);
self.send(&request)?;
let response = self.recv()?;
check_response_id(&response, id)?;
match response.kind() {
pb::response::KindOneof::Append(append) => Ok(AppendResult {
first: Position::new(append.first()),
last: Position::new(append.last()),
}),
pb::response::KindOneof::Error(error) => Err(server_error(error)),
other => Err(ClientError::Protocol(format!(
"unexpected response to append: {other:?}"
))),
}
}
pub fn read(&mut self, query: Query, after: Position) -> Result<ReadStream<'_>, ClientError> {
let id = self.next_id();
let mut read = pb::ReadRequest::new();
read.set_query(wire::query_to_pb(&query));
read.set_after(after.get());
let mut request = pb::Request::new();
request.set_request_id(id);
request.set_read(read);
self.send(&request)?;
Ok(ReadStream {
reader: &mut self.reader,
max_frame_len: self.max_frame_len,
request_id: id,
buffered: VecDeque::new(),
watermark: None,
done: false,
})
}
pub fn read_all(
&mut self,
query: Query,
after: Position,
) -> Result<(Vec<SequencedEvent>, Position), ClientError> {
let mut stream = self.read(query, after)?;
let mut events = Vec::new();
for item in stream.by_ref() {
events.push(item?);
}
let watermark = stream
.watermark()
.ok_or_else(|| ClientError::Protocol("read ended without a watermark".to_string()))?;
Ok((events, watermark))
}
pub fn subscribe(
&mut self,
query: Query,
after: Position,
) -> Result<(SubscribeStream<'_>, SubscribeCancel), ClientError> {
let id = self.next_id();
let mut subscribe = pb::SubscribeRequest::new();
subscribe.set_query(wire::query_to_pb(&query));
subscribe.set_after(after.get());
let mut request = pb::Request::new();
request.set_request_id(id);
request.set_subscribe(subscribe);
self.send(&request)?;
let cancel = SubscribeCancel {
stream: self.reader.get_ref().try_clone()?,
};
let stream = SubscribeStream {
reader: &mut self.reader,
max_frame_len: self.max_frame_len,
request_id: id,
buffered: VecDeque::new(),
done: false,
};
Ok((stream, cancel))
}
fn recv(&mut self) -> Result<pb::Response, ClientError> {
read_frame(&mut self.reader, self.max_frame_len)?.ok_or(ClientError::UnexpectedEof)
}
}
pub struct SubscribeCancel {
stream: TcpStream,
}
impl SubscribeCancel {
pub fn cancel(self) {
let _ = self.stream.shutdown(Shutdown::Both);
}
}
pub struct SubscribeStream<'a> {
reader: &'a mut BufReader<TcpStream>,
max_frame_len: u32,
request_id: u64,
buffered: VecDeque<SubEvent>,
done: bool,
}
impl SubscribeStream<'_> {
fn fill(&mut self) -> Result<(), ClientError> {
loop {
let response = match read_frame::<pb::Response, _>(self.reader, self.max_frame_len)? {
Some(response) => response,
None => {
self.done = true;
return Ok(());
}
};
let got = response.request_id();
if got != self.request_id && got != UNATTRIBUTED_REQUEST_ID {
self.done = true;
return Err(ClientError::Protocol(format!(
"response for request {got} does not match subscribe request {}",
self.request_id
)));
}
match response.kind() {
pb::response::KindOneof::ReadEvents(events) => {
for sequenced in events.events().iter() {
self.buffered
.push_back(SubEvent::Event(sequenced_from_pb(sequenced)?));
}
if !self.buffered.is_empty() {
return Ok(());
}
}
pb::response::KindOneof::CaughtUp(caught_up) => {
self.buffered
.push_back(SubEvent::CaughtUp(Position::new(caught_up.watermark())));
return Ok(());
}
pb::response::KindOneof::Error(error) => {
self.done = true;
return Err(server_error(error));
}
other => {
self.done = true;
return Err(ClientError::Protocol(format!(
"unexpected response during subscribe: {other:?}"
)));
}
}
}
}
}
impl Iterator for SubscribeStream<'_> {
type Item = Result<SubEvent, ClientError>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(event) = self.buffered.pop_front() {
return Some(Ok(event));
}
if self.done {
return None;
}
match self.fill() {
Ok(()) => self.buffered.pop_front().map(Ok),
Err(err) => {
self.done = true;
Some(Err(err))
}
}
}
}
pub struct ReadStream<'a> {
reader: &'a mut BufReader<TcpStream>,
max_frame_len: u32,
request_id: u64,
buffered: VecDeque<SequencedEvent>,
watermark: Option<Position>,
done: bool,
}
impl ReadStream<'_> {
pub fn watermark(&self) -> Option<Position> {
self.watermark
}
fn fill(&mut self) -> Result<(), ClientError> {
loop {
let response = read_frame::<pb::Response, _>(self.reader, self.max_frame_len)?
.ok_or(ClientError::UnexpectedEof)?;
let got = response.request_id();
if got != self.request_id && got != UNATTRIBUTED_REQUEST_ID {
self.done = true;
return Err(ClientError::Protocol(format!(
"response for request {got} does not match read request {}",
self.request_id
)));
}
match response.kind() {
pb::response::KindOneof::ReadEvents(events) => {
for sequenced in events.events().iter() {
self.buffered.push_back(sequenced_from_pb(sequenced)?);
}
if !self.buffered.is_empty() {
return Ok(());
}
}
pb::response::KindOneof::ReadEnd(end) => {
self.watermark = Some(Position::new(end.watermark()));
self.done = true;
return Ok(());
}
pb::response::KindOneof::Error(error) => {
self.done = true;
return Err(server_error(error));
}
other => {
self.done = true;
return Err(ClientError::Protocol(format!(
"unexpected response during read: {other:?}"
)));
}
}
}
}
fn drain(&mut self) {
self.done = true;
loop {
match read_frame::<pb::Response, _>(self.reader, self.max_frame_len) {
Ok(Some(response)) => match response.kind() {
pb::response::KindOneof::ReadEnd(_) | pb::response::KindOneof::Error(_) => {
return;
}
_ => {}
},
Ok(None) | Err(_) => return,
}
}
}
}
impl Iterator for ReadStream<'_> {
type Item = Result<SequencedEvent, ClientError>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(sequenced) = self.buffered.pop_front() {
return Some(Ok(sequenced));
}
if self.done {
return None;
}
match self.fill() {
Ok(()) => self.buffered.pop_front().map(Ok),
Err(err) => {
self.done = true;
Some(Err(err))
}
}
}
}
impl Drop for ReadStream<'_> {
fn drop(&mut self) {
if !self.done {
self.drain();
}
}
}
fn event_to_pb(event: &Event) -> pb::Event {
let mut out = pb::Event::new();
out.set_type(event.event_type.as_str());
for tag in event.tags.iter() {
out.tags_mut().push(tag.as_str());
}
out.set_payload(&event.payload);
out
}
fn sequenced_from_pb(view: pb::SequencedEventView<'_>) -> Result<SequencedEvent, ClientError> {
let position = Position::new(view.position());
let ev = view.event();
let event_type = EventType::new(wire::as_str(ev.r#type()).map_err(protocol)?)
.map_err(|err| ClientError::Protocol(format!("server sent an invalid event type: {err}")))?;
let tags = wire::tags_from_pb(ev.tags().iter()).map_err(protocol)?;
let event = Event {
event_type,
tags,
payload: ev.payload().to_vec(),
};
Ok(SequencedEvent { position, event })
}
fn protocol(err: wire::ConvertError) -> ClientError {
ClientError::Protocol(format!("server sent a malformed event: {err}"))
}
fn check_response_id(response: &pb::Response, expected: u64) -> Result<(), ClientError> {
let got = response.request_id();
if got != expected && got != UNATTRIBUTED_REQUEST_ID {
return Err(ClientError::Protocol(format!(
"response for request {got} does not match request {expected}"
)));
}
Ok(())
}
fn server_error(error: pb::ErrorResponseView<'_>) -> ClientError {
ClientError::Server {
code: ErrorCode::from(error.code()),
message: error.message().to_str().unwrap_or_default().to_string(),
retryable: error.retryable(),
conflict_position: error
.has_conflict_position()
.then(|| Position::new(error.conflict_position())),
}
}