//! Tunnel error surfaces: the crate-level [`TunnelError`], the typed //! session-open error ([`TunnelOpenError`], ADR-049 §4 surface), and //! the session data-plane error ([`TunnelIoError`]). use alkcall::channels::client::ChannelOpenError; use alkcall::protocol::wire::CallError; use crate::wire::DatagramCodecError; /// The crate-level error type (thiserror; AGENTS.md convention 2). #[derive(Debug, thiserror::Error)] pub enum TunnelError { /// The data-plane codec rejected an operation (UDP framing). #[error("codec: {0}")] Codec(String), /// A substrate-shaped operation was attempted on the wrong /// data plane (e.g. `send_datagram` on a stream tunnel). #[error("wrong substrate for this operation")] WrongSubstrate, /// An I/O failure on the session's data plane. #[error("io: {0}")] Io(#[from] std::io::Error), } /// A failed tunnel open or adopt. A failure never yields a session /// (no phantom session, mirroring the no-phantom-channel property — /// ADR-005). Branch on [`ChannelOpenError::establishment_reason`] /// through [`TunnelOpenError::open_ref`] for `channel:open_failed`'s /// reason (`dial_failed` / `unknown_resource` / `resource_shortage` / /// `handler_error` / `timeout`), or the pre-establishment codes /// (`FORBIDDEN`, `channel:too_many_channels`). #[derive(Debug, thiserror::Error)] pub enum TunnelOpenError { /// The open op failed, or the reply/adopt failed — the typed /// [`ChannelOpenError`] carries the wire [`CallError`] verbatim /// (adopt failures surface as `AdoptFailed`). #[error("tunnel open failed: {0}")] Open(#[from] ChannelOpenError), } impl TunnelOpenError { /// The underlying [`ChannelOpenError`] (the ADR-049 typed surface). pub fn open_ref(&self) -> &ChannelOpenError { match self { TunnelOpenError::Open(e) => e, } } } /// An I/O failure on the session's data plane, or a substrate-shaped /// operation attempted on the wrong plane (the POC's shape). #[derive(Debug, thiserror::Error)] pub enum TunnelIoError { /// The underlying stream failed. #[error("io: {0}")] Io(#[from] std::io::Error), /// The datagram codec rejected the operation (e.g. `Oversize` — /// a >65535-byte send, rejected at frame time per ADR-003). #[error("codec: {0}")] Codec(#[from] DatagramCodecError), /// The stream ended mid-datagram — declared length not satisfied /// (a clean peer never does this; the fail-loud posture, OQ-TN-13 /// / ADR-003: never a silent partial datagram). #[error("stream ended mid-datagram (truncated)")] TruncatedDatagram, /// A substrate-shaped operation was attempted on the wrong data /// plane (e.g. `send_datagram` on a stream tunnel). #[error("wrong substrate for this operation")] WrongSubstrate, /// The session's channel halves were already taken — by /// `take_halves` (caller-driven) or `pump_against` (pump-owned); /// no data plane remains on the session. #[error("channel halves already taken from this session")] ChannelTaken, } /// The reverse-path open call failed (`open_reverse_channel` — the /// assembly layer calls it before [`crate::consumer::TunnelSession:: /// adopt`]; the hub's call surface is a `CallConnection`, so the /// two-step is the honest API). #[derive(Debug, thiserror::Error)] pub enum ReverseOpenError { /// The open op failed — the wire [`CallError`]; branch on /// `details.reason` of `channel:open_failed` (ADR-049 §4). #[error("open call failed: {0:?}")] Call(CallError), /// The open op succeeded but the reply carried no `channel_id` /// (malformed responder). #[error("open reply missing channel_id")] NoChannelId, }