use std::collections::HashSet;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
use ruststream::Message;
use ruststream::schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, JsonSchema, Message)]
pub(crate) struct Order {
pub(crate) id: u64,
pub(crate) customer: String,
pub(crate) item: String,
pub(crate) quantity: u32,
}
#[derive(Debug, Clone, Deserialize, JsonSchema, Message)]
pub(crate) struct Payment {
pub(crate) order_id: u64,
pub(crate) customer: String,
pub(crate) amount_cents: u64,
}
#[derive(Debug, Clone, Deserialize, JsonSchema, Message)]
pub(crate) struct Clearing {
pub(crate) order_id: u64,
pub(crate) amount_cents: u64,
}
#[derive(Debug, Clone, Deserialize, JsonSchema, Message)]
pub(crate) struct Cancellation {
pub(crate) order_id: u64,
}
#[derive(Debug, Clone, Serialize, JsonSchema, Message)]
pub(crate) struct Confirmation {
pub(crate) order_id: u64,
pub(crate) accepted: bool,
}
#[derive(Debug, Clone, Serialize, JsonSchema, Message)]
pub(crate) struct Settlement {
pub(crate) order_id: u64,
pub(crate) amount_cents: u64,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub(crate) enum ServiceError {
#[error("repository is temporarily unavailable")]
Unavailable,
#[error("order {0} is unknown")]
UnknownOrder(u64),
}
impl ServiceError {
pub(crate) const fn is_transient(&self) -> bool {
matches!(self, Self::Unavailable)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Repository {
inner: Arc<RepoInner>,
}
#[derive(Debug)]
struct RepoInner {
orders: Mutex<HashSet<u64>>,
fail_next: AtomicBool,
}
impl Repository {
pub(crate) async fn open() -> Result<Self, ServiceError> {
tokio::task::yield_now().await; Ok(Self {
inner: Arc::new(RepoInner {
orders: Mutex::new(HashSet::new()),
fail_next: AtomicBool::new(false),
}),
})
}
pub(crate) async fn record_order(&self, id: u64) -> Result<(), ServiceError> {
tokio::task::yield_now().await;
if self.inner.fail_next.fetch_xor(true, Ordering::Relaxed) {
return Err(ServiceError::Unavailable);
}
self.inner.orders.lock().expect("orders lock").insert(id);
Ok(())
}
pub(crate) async fn charge(
&self,
_order_id: u64,
_amount_cents: u64,
) -> Result<(), ServiceError> {
tokio::task::yield_now().await;
Ok(())
}
pub(crate) async fn cancel(&self, order_id: u64) -> Result<(), ServiceError> {
tokio::task::yield_now().await;
if self
.inner
.orders
.lock()
.expect("orders lock")
.remove(&order_id)
{
Ok(())
} else {
Err(ServiceError::UnknownOrder(order_id))
}
}
pub(crate) async fn close(&self) {
tokio::task::yield_now().await;
}
}