Skip to main content

iscsi_client_rs/client/
pdu_connection.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (C) 2012-2025 Andrei Maltsev
3
4use anyhow::Result;
5
6use crate::models::{
7    common::{BasicHeaderSegment, Builder},
8    opcode::BhsOpcode,
9};
10
11/// A trait for serializing a Protocol Data Unit (PDU) into a byte
12/// representation for transmission.
13///
14/// This trait provides functionality to convert PDU structures into their
15/// binary format suitable for sending over the network according to iSCSI
16/// protocol specifications.
17pub trait ToBytes: Sized {
18    // The fixed length of the PDU header in bytes.
19    // rust now don`t support compile time array length
20    type Header: AsRef<[u8]>;
21    type Body: AsRef<[u8]>;
22
23    /// Consume the PDU builder or object and produce:
24    /// - A fixed-size array of `HEADER_LEN` bytes representing the PDU header.
25    /// - A `Vec<u8>` containing the variable-length data segment.
26    /// - A `Option<Vec<u8>>` containing the data-digest data segment.
27    fn to_bytes(
28        &mut self,
29        max_recv_data_segment_length: usize,
30    ) -> Result<(Self::Header, Self::Body)>;
31}
32
33/// A trait for deserializing a Protocol Data Unit (PDU) from a raw byte stream.
34///
35/// This trait provides functionality to parse incoming binary data into
36/// structured PDU objects. It requires the implementing type to also implement
37/// BasicHeaderSegment for header access.
38pub trait FromBytes: Sized + BasicHeaderSegment {
39    /// Parse the full PDU from a contiguous byte buffer.
40    ///
41    /// The parsed `Response` (often a tuple of header struct, payload bytes,
42    /// and digest), or an error if parsing fails.
43    fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
44        let _ = BhsOpcode::try_from(bytes[0])
45            .map_err(|e| anyhow::anyhow!("invalid opcode: {}", e))?;
46        Self::from_bhs_bytes(bytes)
47    }
48}
49
50impl<B> ToBytes for B
51where B: Builder
52{
53    type Body = B::Body;
54    type Header = B::Header;
55
56    fn to_bytes(
57        &mut self,
58        max_recv_data_segment_length: usize,
59    ) -> Result<(Self::Header, Self::Body)> {
60        self.build(max_recv_data_segment_length)
61    }
62}