h3x 0.6.1

Peer-to-peer DHTTP/3 transport over QUIC
Documentation
use std::{convert::Infallible, io};

use snafu::{ResultExt, Snafu};
use tokio::io::{AsyncRead, AsyncWrite};

use crate::{
    buflist::BufList,
    codec::{DecodeExt, DecodeFrom, EncodeExt, EncodeInto},
    varint::VarInt,
};

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct WebTransportStreamCount(VarInt);

impl WebTransportStreamCount {
    pub const ZERO: Self = Self(VarInt::from_u32(0));
    pub const MAX_VALUE: VarInt = match VarInt::from_u64(0x0fff_ffff_ffff_ffff) {
        Ok(value) => value,
        Err(_) => panic!("2^60 - 1 is a valid QUIC varint"),
    };

    pub const fn into_varint(self) -> VarInt {
        self.0
    }

    pub fn checked_increment(self) -> Result<Self, InvalidWebTransportStreamCount> {
        let next = VarInt::from_u64(self.0.into_inner() + 1)
            .expect("a valid webtransport stream count increment is a valid QUIC varint");
        Self::try_from(next)
    }
}

impl TryFrom<VarInt> for WebTransportStreamCount {
    type Error = InvalidWebTransportStreamCount;

    fn try_from(value: VarInt) -> Result<Self, Self::Error> {
        if value <= Self::MAX_VALUE {
            Ok(Self(value))
        } else {
            Err(InvalidWebTransportStreamCount { value })
        }
    }
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Snafu, Clone, Copy, PartialEq, Eq)]
#[snafu(display("webtransport stream count {value} exceeds 2^60 - 1"))]
pub struct InvalidWebTransportStreamCount {
    value: VarInt,
}

impl InvalidWebTransportStreamCount {
    pub const fn value(&self) -> VarInt {
        self.value
    }
}

#[derive(Debug, Snafu)]
#[snafu(module(decode_webtransport_stream_count_error), visibility(pub(super)))]
pub enum DecodeWebTransportStreamCountError {
    #[snafu(display("failed to decode webtransport stream count"))]
    Decode { source: io::Error },
    #[snafu(display("invalid webtransport stream count"))]
    Invalid {
        source: InvalidWebTransportStreamCount,
    },
}

impl<S> DecodeFrom<S> for WebTransportStreamCount
where
    S: AsyncRead + Unpin + Send,
{
    type Error = DecodeWebTransportStreamCountError;

    async fn decode_from(mut stream: S) -> Result<Self, Self::Error> {
        let value = stream
            .decode_one::<VarInt>()
            .await
            .context(decode_webtransport_stream_count_error::DecodeSnafu)?;
        Self::try_from(value).context(decode_webtransport_stream_count_error::InvalidSnafu)
    }
}

impl<'s, S> EncodeInto<&'s mut S> for WebTransportStreamCount
where
    S: AsyncWrite + Unpin + Send,
{
    type Output = ();
    type Error = io::Error;

    async fn encode_into(self, stream: &'s mut S) -> Result<Self::Output, Self::Error> {
        self.into_varint().encode_into(stream).await
    }
}

impl EncodeInto<BufList> for WebTransportStreamCount {
    type Output = BufList;
    type Error = Infallible;

    async fn encode_into(self, mut stream: BufList) -> Result<Self::Output, Self::Error> {
        stream
            .encode_one(self)
            .await
            .expect("encoding a webtransport stream count into a BufList is infallible");
        Ok(stream)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn stream_count_accepts_webtransport_boundaries() {
        let zero = WebTransportStreamCount::try_from(VarInt::from_u32(0)).expect("zero");
        assert_eq!(zero.into_varint(), VarInt::from_u32(0));

        let max =
            WebTransportStreamCount::try_from(WebTransportStreamCount::MAX_VALUE).expect("max");
        assert_eq!(max.into_varint(), WebTransportStreamCount::MAX_VALUE);
    }

    #[test]
    fn stream_count_rejects_above_webtransport_limit() {
        let value = VarInt::from_u64(1 << 60).expect("valid varint");
        let error = WebTransportStreamCount::try_from(value).expect_err("above stream-count limit");

        assert_eq!(error.value(), value);
    }

    #[test]
    fn checked_increment_preserves_varint_domain() {
        let count = WebTransportStreamCount::try_from(VarInt::from_u32(7)).expect("count");
        assert_eq!(
            count.checked_increment().expect("increment").into_varint(),
            VarInt::from_u32(8)
        );
    }
}