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}