h3x 0.6.1

Peer-to-peer DHTTP/3 transport over QUIC
Documentation
use std::fmt;

use snafu::Snafu;

use crate::{
    error::{Code, H3ConnectionError},
    stream_id::StreamId,
};

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

impl WebTransportSessionId {
    pub const fn stream_id(self) -> StreamId {
        self.0
    }
}

impl TryFrom<StreamId> for WebTransportSessionId {
    type Error = InvalidSessionId;

    fn try_from(session_id: StreamId) -> Result<Self, Self::Error> {
        if session_id.is_client_initiated_bidirectional() {
            Ok(Self(session_id))
        } else {
            Err(InvalidSessionId { session_id })
        }
    }
}

impl From<WebTransportSessionId> for StreamId {
    fn from(session_id: WebTransportSessionId) -> Self {
        session_id.0
    }
}

impl fmt::Display for WebTransportSessionId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Snafu, Clone, Copy, PartialEq, Eq)]
#[snafu(display(
    "webtransport session id {session_id} is not a client-initiated bidirectional stream id"
))]
pub struct InvalidSessionId {
    session_id: StreamId,
}

impl InvalidSessionId {
    pub const fn session_id(&self) -> StreamId {
        self.session_id
    }
}

impl H3ConnectionError for InvalidSessionId {
    fn code(&self) -> Code {
        Code::H3_ID_ERROR
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        error::{Code, H3ConnectionError},
        stream_id::StreamId,
        varint::VarInt,
    };

    #[test]
    fn session_id_accepts_only_client_initiated_bidirectional_stream_id() {
        let raw = StreamId::from(VarInt::from_u32(0));
        let session_id = WebTransportSessionId::try_from(raw).expect("valid session id");

        assert_eq!(session_id.stream_id(), raw);
        assert_eq!(StreamId::from(session_id), raw);
    }

    #[test]
    fn invalid_session_id_is_h3_id_error() {
        let error = WebTransportSessionId::try_from(StreamId::from(VarInt::from_u32(3)))
            .expect_err("server uni cannot be a session id");

        assert_eq!(error.session_id(), StreamId::from(VarInt::from_u32(3)));
        assert_eq!(H3ConnectionError::code(&error), Code::H3_ID_ERROR);
    }
}