use std::time::Duration;
use microsandbox_protocol::codec::MAX_FRAME_SIZE;
use tokio::time::Instant;
use crate::{ClientError, ClientResult, ErrorKind};
#[derive(Debug, Clone)]
pub struct ClientLimits {
pub max_frame_size: u32,
pub max_in_flight: usize,
pub queued_writes: usize,
pub queued_responses: usize,
pub buffered_bytes: u32,
pub incomplete_frame_timeout: Option<Duration>,
pub request_timeout: Option<Duration>,
}
#[derive(Debug, Clone)]
pub struct ConnectOptions {
pub setup_timeout: Duration,
pub limits: ClientLimits,
}
#[derive(Debug, Clone, Default)]
pub struct RequestOptions {
pub request_timeout: Option<Duration>,
}
impl ConnectOptions {
pub fn setup_timeout(mut self, timeout: Duration) -> Self {
self.setup_timeout = timeout;
self
}
pub fn limits(mut self, configure: impl FnOnce(ClientLimits) -> ClientLimits) -> Self {
self.limits = configure(self.limits);
self
}
}
impl RequestOptions {
pub fn request_timeout(mut self, timeout: Duration) -> Self {
self.request_timeout = Some(timeout);
self
}
}
impl ClientLimits {
pub fn validate(&self) -> ClientResult<()> {
for timeout in [self.incomplete_frame_timeout, self.request_timeout]
.into_iter()
.flatten()
{
checked_deadline(timeout)?;
}
if self.max_frame_size < 5
|| self.max_frame_size > MAX_FRAME_SIZE
|| self.max_in_flight == 0
|| self.queued_writes == 0
|| self.queued_responses == 0
|| self.queued_writes > tokio::sync::Semaphore::MAX_PERMITS
|| self.queued_responses > tokio::sync::Semaphore::MAX_PERMITS
|| u64::from(self.buffered_bytes) > tokio::sync::Semaphore::MAX_PERMITS as u64
|| self.buffered_bytes < self.max_frame_size + 4
{
return Err(ClientError::new(ErrorKind::InvalidOptions));
}
Ok(())
}
}
impl Default for ConnectOptions {
fn default() -> Self {
Self {
setup_timeout: Duration::from_secs(10),
limits: ClientLimits::default(),
}
}
}
impl Default for ClientLimits {
fn default() -> Self {
Self {
max_frame_size: MAX_FRAME_SIZE,
max_in_flight: 1024,
queued_writes: 256,
queued_responses: 1024,
buffered_bytes: 8 * 1024 * 1024,
incomplete_frame_timeout: None,
request_timeout: None,
}
}
}
pub(crate) fn checked_deadline(timeout: Duration) -> ClientResult<Instant> {
Instant::now()
.checked_add(timeout)
.ok_or_else(|| ClientError::new(ErrorKind::InvalidOptions))
}