use serde::{Deserialize, Serialize};
use crate::{codes, Cursor, StepRow};
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct SubscribeRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub harness: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session: Option<String>,
}
impl SubscribeRequest {
pub fn new() -> Self {
Self::default()
}
pub fn for_session(mut self, harness: impl Into<String>, session: impl Into<String>) -> Self {
self.harness = Some(harness.into());
self.session = Some(session.into());
self
}
pub fn check_pairing(&self) -> Result<(), &'static str> {
pairing::check(self.harness.is_some(), self.session.is_some())
}
}
pub(crate) mod pairing {
pub(crate) fn check(harness: bool, session: bool) -> Result<(), &'static str> {
match (harness, session) {
(true, true) | (false, false) => Ok(()),
(true, false) => Err("request names `harness` without `session`"),
(false, true) => Err("request names `session` without `harness`"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Notice {
pub through: Cursor,
}
impl Notice {
pub fn new(through: impl Into<Cursor>) -> Self {
Self {
through: through.into(),
}
}
pub fn check_closed(value: &serde_json::Value) -> Result<(), crate::NotClosed> {
crate::row::check_members("notice", value, &["through"])
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ReadRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub after: Option<Cursor>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub harness: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session: Option<String>,
}
impl ReadRequest {
pub fn new() -> Self {
Self::default()
}
pub fn after(mut self, cursor: impl Into<Cursor>) -> Self {
self.after = Some(cursor.into());
self
}
pub fn with_limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
pub fn for_session(mut self, harness: impl Into<String>, session: impl Into<String>) -> Self {
self.harness = Some(harness.into());
self.session = Some(session.into());
self
}
pub fn check_pairing(&self) -> Result<(), &'static str> {
pairing::check(self.harness.is_some(), self.session.is_some())
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReadReply {
pub rows: Vec<StepRow>,
#[serde(default)]
pub next: Option<Cursor>,
pub through: Cursor,
}
impl ReadReply {
pub fn new(rows: Vec<StepRow>, next: Option<Cursor>, through: impl Into<Cursor>) -> Self {
Self {
rows,
next,
through: through.into(),
}
}
pub fn resume_after(&self) -> &Cursor {
self.next.as_ref().unwrap_or(&self.through)
}
pub fn check_closed(value: &serde_json::Value) -> Result<(), crate::NotClosed> {
crate::row::check_members("reply", value, &["rows", "next", "through"])?;
if let Some(serde_json::Value::Array(rows)) = value.get("rows") {
for row in rows {
StepRow::check_closed(row)?;
}
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Refusal {
pub code: String,
#[serde(default)]
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detail: Option<serde_json::Value>,
}
impl Refusal {
pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
Self {
code: code.into(),
message: message.into(),
detail: None,
}
}
pub fn lagged(through: impl Into<Cursor>, message: impl Into<String>) -> Self {
Self::new(codes::LAGGED, message).with_through(through)
}
pub fn cancelled(message: impl Into<String>) -> Self {
Self::new(codes::CANCELLED, message)
}
pub fn cursor_expired(through: impl Into<Cursor>, message: impl Into<String>) -> Self {
Self::new(codes::CURSOR_EXPIRED, message).with_through(through)
}
fn with_through(mut self, through: impl Into<Cursor>) -> Self {
self.detail = Some(serde_json::json!({ "through": through.into() }));
self
}
pub fn through(&self) -> Option<Cursor> {
self.detail
.as_ref()?
.get("through")?
.as_str()
.map(Cursor::new)
}
}