leyline-http 0.1.1

HTTP client that mimics browsers on the wire
Documentation
use std::fmt;

use crate::core::error::{Error, Kind};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum LimitSource {
    Session,
    Caller,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Overflow {
    Fail,
    Truncate,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Cap {
    pub(crate) limit: BodyLimit,
    pub(crate) overflow: Overflow,
}

impl Cap {
    pub(crate) fn fail(limit: BodyLimit) -> Self {
        Self {
            limit,
            overflow: Overflow::Fail,
        }
    }

    pub(crate) fn truncate_at(session: BodyLimit, caller: usize) -> Self {
        Self {
            limit: session.tighter(u64::try_from(caller).ok()),
            overflow: Overflow::Truncate,
        }
    }

    pub(crate) fn room(self, used: usize) -> usize {
        self.limit.bytes.saturating_sub(used)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BodyLimit {
    pub(crate) bytes: usize,
    pub(crate) source: LimitSource,
}

impl BodyLimit {
    pub(crate) fn session(bytes: usize) -> Self {
        Self {
            bytes,
            source: LimitSource::Session,
        }
    }

    pub(crate) fn caller(bytes: usize) -> Self {
        Self {
            bytes,
            source: LimitSource::Caller,
        }
    }

    pub(crate) fn tighter(self, caller: Option<u64>) -> Self {
        match caller.map(|bytes| usize::try_from(bytes).unwrap_or(usize::MAX)) {
            Some(bytes) if bytes <= self.bytes => Self::caller(bytes),
            _ => self,
        }
    }

    pub(crate) fn error(self) -> Error {
        Error::new(Kind::Body)
            .with_message(self.to_string())
            .with_source(self)
    }

    pub(crate) fn into_io(self) -> std::io::Error {
        std::io::Error::other(self)
    }

    pub(crate) fn of_io(error: &std::io::Error) -> Option<Self> {
        error.get_ref()?.downcast_ref::<Self>().copied()
    }
}

impl fmt::Display for BodyLimit {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.source {
            LimitSource::Session => write!(
                f,
                "response body exceeds max_body_size ({} bytes)",
                self.bytes
            ),
            LimitSource::Caller => write!(
                f,
                "response body exceeds the caller's limit ({} bytes)",
                self.bytes
            ),
        }
    }
}

impl std::error::Error for BodyLimit {}