cyfs_core/trans/
trans_context.rs1use std::borrow::Cow;
2
3use crate::codec::protos::core_objects as protos;
4use cyfs_base::*;
5use cyfs_bdt::*;
6
7use crate::CoreObjectType;
8use serde::Serialize;
9
10#[derive(ProtobufEncode, ProtobufDecode, ProtobufTransform, Clone, Serialize)]
11#[cyfs_protobuf_type(crate::codec::protos::TransContextDescContent)]
12pub struct TransContextDescContent {
13 pub context_path: String,
14}
15
16impl DescContent for TransContextDescContent {
17 fn obj_type() -> u16 {
18 CoreObjectType::TransContext as u16
19 }
20
21 fn format(&self) -> u8 {
22 OBJECT_CONTENT_CODEC_FORMAT_PROTOBUF
23 }
24
25 type OwnerType = SubDescNone;
26 type AreaType = SubDescNone;
27 type AuthorType = SubDescNone;
28 type PublicKeyType = SubDescNone;
29}
30
31#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
32pub struct TransContextDevice {
33 pub target: DeviceId,
34 pub chunk_codec_desc: ChunkCodecDesc,
35}
36
37impl TransContextDevice {
38 pub fn default_stream(target: DeviceId) -> Self {
39 Self {
40 target,
41 chunk_codec_desc: ChunkCodecDesc::Stream(None, None, None),
42 }
43 }
44
45 pub fn default_raptor(target: DeviceId) -> Self {
46 Self {
47 target,
48 chunk_codec_desc: ChunkCodecDesc::Raptor(None, None, None),
49 }
50 }
51}
52
53#[derive(Clone, Serialize)]
54pub struct TransContextBodyContent {
55 pub device_list: Vec<TransContextDevice>,
56}
57
58impl BodyContent for TransContextBodyContent {
59 fn version(&self) -> u8 {
60 0
61 }
62
63 fn format(&self) -> u8 {
64 OBJECT_CONTENT_CODEC_FORMAT_PROTOBUF
65 }
66}
67
68impl TryFrom<protos::TransContextDevice> for TransContextDevice {
69 type Error = BuckyError;
70
71 fn try_from(mut value: protos::TransContextDevice) -> BuckyResult<Self> {
72 let target = cyfs_base::ProtobufCodecHelper::decode_buf(value.take_target())?;
73
74 let info = if value.has_chunk_codec_info() {
75 Some(value.take_chunk_codec_info())
76 } else {
77 None
78 };
79
80 let chunk_codec_desc = match value.chunk_codec_desc {
81 protos::TransContextDevice_ChunkCodecDesc::Unknown => ChunkCodecDesc::Unknown,
82 _ => {
83 let info = info.ok_or_else(|| {
84 let msg = format!(
85 "chunk_codec_info field missing! type={:?}",
86 value.chunk_codec_desc
87 );
88 error!("{}", msg);
89 BuckyError::new(BuckyErrorCode::InvalidData, msg)
90 })?;
91
92 let start = if info.has_start() {
93 Some(info.get_start())
94 } else {
95 None
96 };
97 let end = if info.has_end() {
98 Some(info.get_end())
99 } else {
100 None
101 };
102 let step = if info.has_step() {
103 Some(info.get_step())
104 } else {
105 None
106 };
107
108 match value.chunk_codec_desc {
109 protos::TransContextDevice_ChunkCodecDesc::Stream => {
110 ChunkCodecDesc::Stream(start, end, step)
111 }
112 protos::TransContextDevice_ChunkCodecDesc::Raptor => {
113 ChunkCodecDesc::Raptor(start, end, step)
114 }
115 _ => unreachable!(),
116 }
117 }
118 };
119
120 Ok(Self {
121 target,
122 chunk_codec_desc,
123 })
124 }
125}
126
127impl TryFrom<&TransContextDevice> for protos::TransContextDevice {
128 type Error = BuckyError;
129
130 fn try_from(value: &TransContextDevice) -> BuckyResult<Self> {
131 let mut ret = protos::TransContextDevice::new();
132 ret.set_target(value.target.to_vec().unwrap());
133
134 match value.chunk_codec_desc {
135 ChunkCodecDesc::Unknown => {
136 ret.set_chunk_codec_desc(protos::TransContextDevice_ChunkCodecDesc::Unknown);
137 }
138 _ => {
139 let (start, end, step) = match value.chunk_codec_desc {
140 ChunkCodecDesc::Stream(start, end, step) => {
141 ret.set_chunk_codec_desc(protos::TransContextDevice_ChunkCodecDesc::Stream);
142 (start, end, step)
143 }
144 ChunkCodecDesc::Raptor(start, end, step) => {
145 ret.set_chunk_codec_desc(protos::TransContextDevice_ChunkCodecDesc::Raptor);
146 (start, end, step)
147 }
148 _ => unreachable!(),
149 };
150
151 let mut info = protos::TransContextDeviceChunkCodecInfo::new();
152 if let Some(v) = start {
153 info.set_start(v);
154 }
155 if let Some(v) = end {
156 info.set_end(v);
157 }
158 if let Some(v) = step {
159 info.set_step(v);
160 }
161
162 ret.set_chunk_codec_info(info);
163 }
164 }
165
166 Ok(ret)
167 }
168}
169
170impl_default_protobuf_raw_codec!(TransContextDevice);
171
172impl TryFrom<protos::TransContextBodyContent> for TransContextBodyContent {
173 type Error = BuckyError;
174
175 fn try_from(mut value: protos::TransContextBodyContent) -> BuckyResult<Self> {
176 let device_list =
177 cyfs_base::ProtobufCodecHelper::decode_nested_list(value.take_device_list())?;
178 Ok(Self { device_list })
179 }
180}
181
182impl TryFrom<&TransContextBodyContent> for protos::TransContextBodyContent {
183 type Error = BuckyError;
184
185 fn try_from(value: &TransContextBodyContent) -> BuckyResult<Self> {
186 let mut ret = protos::TransContextBodyContent::new();
187
188 ret.set_device_list(cyfs_base::ProtobufCodecHelper::encode_nested_list(
189 &value.device_list,
190 )?);
191
192 Ok(ret)
193 }
194}
195
196impl_default_protobuf_raw_codec!(TransContextBodyContent);
197
198pub type TransContextType = NamedObjType<TransContextDescContent, TransContextBodyContent>;
199pub type TransContextBuilder = NamedObjectBuilder<TransContextDescContent, TransContextBodyContent>;
200pub type TransContext = NamedObjectBase<TransContextType>;
201
202
203pub struct TransContextPath;
204
205
206impl TransContextPath {
207
208 pub fn verify(path: &str) -> bool {
209 if path == "/" {
210 return true;
211 }
212
213 path.starts_with('/') && !path.ends_with('/')
214 }
215
216 pub fn fix_path(path: &str) -> Cow<str> {
223 if path == "/" {
224 return Cow::Borrowed(path);
225 }
226
227 let path = path.trim_start_matches('$').trim_end_matches('/');
228 if path.starts_with('/') {
229 Cow::Borrowed(path)
230 } else {
231 let path = format!("/{}", path);
232 Cow::Owned(path)
233 }
234 }
235}
236
237pub trait TransContextObject {
238 fn new(dec_id: Option<ObjectId>, context_path: &str) -> Self;
239 fn gen_context_id(dec_id: Option<ObjectId>, context_path: &str) -> ObjectId;
240
241 fn context_path(&self) -> &str;
242 fn device_list(&self) -> &Vec<TransContextDevice>;
243 fn device_list_mut(&mut self) -> &mut Vec<TransContextDevice>;
244}
245
246impl TransContextObject for TransContext {
247 fn new(dec_id: Option<ObjectId>, context_path: &str) -> Self {
248 let context_path = TransContextPath::fix_path(context_path).to_string();
249
250 let desc = TransContextDescContent { context_path };
251 let body = TransContextBodyContent {
252 device_list: vec![],
253 };
254
255 TransContextBuilder::new(desc, body)
256 .no_create_time()
257 .option_dec_id(dec_id)
258 .build()
259 }
260
261 fn gen_context_id(dec_id: Option<ObjectId>, context_path: &str) -> ObjectId {
262 let context_path = TransContextPath::fix_path(context_path).to_string();
263
264 let desc = TransContextDescContent { context_path };
265 NamedObjectDescBuilder::new(TransContextDescContent::obj_type(), desc)
266 .option_create_time(None)
267 .option_dec_id(dec_id)
268 .build()
269 .calculate_id()
270 }
271
272 fn context_path(&self) -> &str {
273 self.desc().content().context_path.as_str()
274 }
275
276 fn device_list(&self) -> &Vec<TransContextDevice> {
277 &self.body().as_ref().unwrap().content().device_list
278 }
279
280 fn device_list_mut(&mut self) -> &mut Vec<TransContextDevice> {
281 &mut self.body_mut().as_mut().unwrap().content_mut().device_list
283 }
284}
285
286
287#[cfg(test)]
288mod test {
289 use crate::*;
290 use cyfs_base::*;
291 use cyfs_bdt::*;
292
293 use std::str::FromStr;
294
295 #[test]
296 fn test_path() {
297 let path = "/a";
298 let ret = path.rsplit_once("/").unwrap();
299 assert_eq!(ret.0, "");
300 assert_eq!(ret.1, "a");
301
302 let path = "/a/b";
303 let ret = path.rsplit_once("/").unwrap();
304 assert_eq!(ret.0, "/a");
305 assert_eq!(ret.1, "b");
306 }
307
308 #[test]
309 fn test() {
310 let id = ObjectId::from_str("5r4MYfFdfQ5dvAvD2WZ8wd7iKPFpWLSiAnMuTui912xL").unwrap();
311 let mut context = TransContext::new(Some(id), "/a/b/c");
312
313 let device = TransContextDevice {
314 target: DeviceId::from_str("5bnZHzXvMmqiiua3iodiaYqWR24QbZE5o8r35bH8y9Yh").unwrap(),
315 chunk_codec_desc: ChunkCodecDesc::Stream(Some(1), Some(100), Some(1)),
316 };
317 context.device_list_mut().push(device);
318
319 let device = TransContextDevice {
320 target: DeviceId::from_str("5bnZHzXvMmqiiua3iodiaYqWR24QbZE5o8r35bH8y9Yh").unwrap(),
321 chunk_codec_desc: ChunkCodecDesc::Raptor(Some(100), Some(1000), None),
322 };
323 context.device_list_mut().push(device);
324 context.body_mut().as_mut().unwrap().increase_update_time(bucky_time_now());
325
326 let value = context.to_vec().unwrap();
327 let context2 = TransContext::clone_from_slice(&value).unwrap();
328
329 assert_eq!(context.device_list(), context2.device_list());
330 }
331}