cyfs_core/trans/
trans_context.rs

1use 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    /*
217    if context_path starts with $, then will treat as global context(without dec specified!)
218    a/b -> /a/b
219    /a/b/ -> /a/b
220    / -> /
221    */
222    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        // self.body_mut().as_mut().unwrap().increase_update_time(bucky_time_now());
282        &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}