mod cancel;
pub use cancel::CancelSignal;
use std::sync::Arc;
use std::time::{Duration, Instant};
use foundation_http::shared::context::ContextBag;
use foundation_netio::netcap::ConnectionContext;
use foundation_netio::shared::http::{Extensions, SimpleHeaders};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StreamType {
Unary,
ClientStream,
ServerStream,
BidiStream,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IdempotencyLevel {
Unknown,
NoSideEffects,
Idempotent,
}
#[derive(Debug, Clone)]
pub struct Spec {
pub stream_type: StreamType,
pub procedure: String,
pub is_client: bool,
pub idempotency: IdempotencyLevel,
}
impl Spec {
#[must_use]
pub fn empty(is_client: bool) -> Self {
Self {
stream_type: StreamType::Unary,
procedure: String::new(),
is_client,
idempotency: IdempotencyLevel::Unknown,
}
}
}
#[derive(Debug, Clone)]
pub struct Peer {
pub addr: String,
pub protocol: String,
}
impl Peer {
#[must_use]
pub fn empty() -> Self {
Self {
addr: String::new(),
protocol: String::new(),
}
}
}
#[derive(Clone)]
pub struct RequestContext {
pub spec: Arc<Spec>,
pub peer: Arc<Peer>,
pub headers: Arc<SimpleHeaders>,
pub deadline: Option<Instant>,
pub extensions: Extensions,
pub connection: Arc<ConnectionContext>,
cancel: CancelSignal,
}
impl RequestContext {
#[must_use]
pub fn empty(is_client: bool) -> Self {
Self {
spec: Arc::new(Spec::empty(is_client)),
peer: Arc::new(Peer::empty()),
headers: Arc::new(SimpleHeaders::new()),
deadline: None,
extensions: Extensions::new(),
connection: Arc::new(ConnectionContext::default()),
cancel: CancelSignal::new(),
}
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn for_dispatch(
spec: Spec,
peer: Peer,
headers: SimpleHeaders,
deadline: Option<Instant>,
extensions: Extensions,
connection: Arc<ConnectionContext>,
cancel: CancelSignal,
) -> Self {
Self {
spec: Arc::new(spec),
peer: Arc::new(peer),
headers: Arc::new(headers),
deadline,
extensions,
connection,
cancel,
}
}
#[must_use]
pub fn is_canceled(&self) -> bool {
self.cancel.is_canceled()
}
pub async fn cancelled(&self) {
self.cancel.cancelled().await;
}
#[must_use]
pub fn remaining_timeout(&self) -> Option<Duration> {
self.deadline
.map(|d| d.saturating_duration_since(Instant::now()))
}
#[must_use]
pub fn cancel_signal(&self) -> &CancelSignal {
&self.cancel
}
}
#[derive(Clone)]
pub struct Ctx {
pub bag: Arc<ContextBag>,
pub request: RequestContext,
}
impl Ctx {
#[must_use]
pub fn client(bag: Arc<ContextBag>) -> Ctx {
Ctx {
bag,
request: RequestContext::empty(true),
}
}
#[must_use]
pub fn background() -> Ctx {
Ctx {
bag: Arc::new(ContextBag::new()),
request: RequestContext::empty(false),
}
}
#[must_use]
pub fn with_deadline(mut self, d: Duration) -> Ctx {
self.request.deadline = Some(Instant::now() + d);
self
}
#[must_use]
pub fn with_extension<T: Send + Sync + 'static>(mut self, v: T) -> Ctx {
self.request.extensions.insert(v);
self
}
#[must_use]
pub fn with_cancellation(mut self, signal: CancelSignal) -> Ctx {
self.request.cancel = signal;
self
}
#[must_use]
pub fn spec(&self) -> &Spec {
&self.request.spec
}
#[must_use]
pub fn peer(&self) -> &Peer {
&self.request.peer
}
#[must_use]
pub fn extension<T: 'static>(&self) -> Option<&T> {
self.request.extensions.get::<T>()
}
#[must_use]
pub fn is_canceled(&self) -> bool {
self.request.is_canceled()
}
pub async fn cancelled(&self) {
self.request.cancelled().await;
}
#[must_use]
pub fn remaining_timeout(&self) -> Option<Duration> {
self.request.remaining_timeout()
}
#[must_use]
pub fn cancel_signal(&self) -> &CancelSignal {
self.request.cancel_signal()
}
}
impl core::fmt::Debug for Ctx {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Ctx")
.field("spec", &self.request.spec)
.field("peer", &self.request.peer)
.field("deadline", &self.request.deadline)
.field("canceled", &self.request.is_canceled())
.finish_non_exhaustive()
}
}