vortex_protocol/tlv/
download_cache_piece.rs1use crate::error::{Error, Result};
18use bytes::{BufMut, Bytes, BytesMut};
19use std::convert::TryFrom;
20
21pub const TASK_ID_SIZE: usize = 64;
23
24pub const PIECE_NUMBER_SIZE: usize = 4;
26
27#[derive(Debug, Clone)]
39pub struct DownloadCachePiece {
40 task_id: String,
41 piece_number: u32,
42}
43
44impl DownloadCachePiece {
46 pub fn new(task_id: String, piece_number: u32) -> Self {
48 Self {
49 task_id,
50 piece_number,
51 }
52 }
53
54 pub fn task_id(&self) -> &str {
56 &self.task_id
57 }
58
59 pub fn piece_number(&self) -> u32 {
61 self.piece_number
62 }
63
64 pub fn len(&self) -> usize {
66 TASK_ID_SIZE + PIECE_NUMBER_SIZE
67 }
68
69 pub fn is_empty(&self) -> bool {
71 self.task_id.is_empty()
72 }
73}
74
75impl TryFrom<Bytes> for DownloadCachePiece {
77 type Error = Error;
78
79 fn try_from(bytes: Bytes) -> Result<Self> {
81 if bytes.len() != TASK_ID_SIZE + PIECE_NUMBER_SIZE {
82 return Err(Error::InvalidLength(format!(
83 "expected {} bytes for DownloadCachePiece, got {}",
84 TASK_ID_SIZE + PIECE_NUMBER_SIZE,
85 bytes.len()
86 )));
87 }
88
89 Ok(DownloadCachePiece {
90 task_id: String::from_utf8(
91 bytes
92 .get(..TASK_ID_SIZE)
93 .ok_or(Error::InvalidPacket(
94 "insufficient bytes for task id".to_string(),
95 ))?
96 .to_vec(),
97 )?,
98 piece_number: u32::from_be_bytes(
99 bytes
100 .get(TASK_ID_SIZE..TASK_ID_SIZE + PIECE_NUMBER_SIZE)
101 .ok_or(Error::InvalidPacket(
102 "insufficient bytes for piece number".to_string(),
103 ))?
104 .try_into()?,
105 ),
106 })
107 }
108}
109
110impl From<DownloadCachePiece> for Bytes {
112 fn from(piece: DownloadCachePiece) -> Self {
114 let mut bytes = BytesMut::with_capacity(piece.len());
115 bytes.extend_from_slice(piece.task_id.as_bytes());
116 bytes.put_u32(piece.piece_number);
117 bytes.freeze()
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use bytes::Bytes;
125
126 #[test]
127 fn test_new() {
128 let task_id = "a".repeat(64);
129 let piece_number = 42;
130 let download_cache_piece = DownloadCachePiece::new(task_id.clone(), piece_number);
131
132 assert_eq!(download_cache_piece.task_id(), task_id);
133 assert_eq!(download_cache_piece.piece_number(), piece_number);
134 assert_eq!(download_cache_piece.len(), TASK_ID_SIZE + PIECE_NUMBER_SIZE);
135 }
136
137 #[test]
138 fn test_is_empty() {
139 let download_cache_piece_empty = DownloadCachePiece::new("".to_string(), 0);
140 let download_cache_piece_non_empty = DownloadCachePiece::new("a".repeat(32), 1);
141
142 assert!(download_cache_piece_empty.is_empty());
143 assert!(!download_cache_piece_non_empty.is_empty());
144 }
145
146 #[test]
147 fn test_valid_conversion() {
148 let task_id = "a".repeat(64);
149 let piece_number = 42;
150 let download_cache_piece = DownloadCachePiece::new(task_id.clone(), piece_number);
151
152 let bytes: Bytes = download_cache_piece.into();
153 let download_cache_piece = DownloadCachePiece::try_from(bytes).unwrap();
154
155 assert_eq!(download_cache_piece.task_id(), task_id);
156 assert_eq!(download_cache_piece.piece_number(), piece_number);
157 }
158
159 #[test]
160 fn test_invalid_conversion() {
161 let invalid_bytes =
162 Bytes::from("c993dfb0ecfbe1b4e158891bafff709e5d29d3fcd522e09b183aeb5db1db50111111111");
163 let result = DownloadCachePiece::try_from(invalid_bytes);
164 assert!(result.is_err());
165 assert!(matches!(result.unwrap_err(), Error::InvalidLength(_)));
166
167 let invalid_bytes = Bytes::from("task_id");
168 let result = DownloadCachePiece::try_from(invalid_bytes);
169 assert!(result.is_err());
170 assert!(matches!(result.unwrap_err(), Error::InvalidLength(_)));
171
172 let invalid_bytes = Bytes::from("");
173 let result = DownloadCachePiece::try_from(invalid_bytes);
174 assert!(result.is_err());
175 assert!(matches!(result.unwrap_err(), Error::InvalidLength(_)));
176 }
177}