1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use cyfs_base::*;

pub struct SNDirParser;

impl SNDirParser {
    pub fn parse(id: Option<&ObjectId>, object_raw: &[u8]) -> BuckyResult<Vec<(DeviceId, Device)>> {
        let dir = Dir::clone_from_slice(&object_raw)?;
        let id = id.cloned().unwrap_or(dir.desc().calculate_id());

        let mut sn_list = vec![];
        match dir.desc().content().obj_list() {
            NDNObjectInfo::ObjList(list) => {
                for (path, node) in list.object_map() {
                    let path = path.trim_start_matches('/');
                    if path.starts_with("list/") {
                        let (sn_id, buf) = Self::load_sn_node(&id, &dir, &node)?;
                        match Device::clone_from_slice(&buf) {
                            Ok(device) => {
                                let real_id = device.desc().device_id();
                                if real_id != *sn_id {
                                    let msg = format!(
                                        "sn device id not matched with configed in dir! dir={}, config={}, real={}",
                                        id, sn_id, real_id,
                                    );
                                    error!("{}", msg);
                                    return Err(BuckyError::new(BuckyErrorCode::InvalidData, msg));
                                }

                                info!("got sn device from dir config: dir={}, sn={}", id, real_id);
                                sn_list.push((real_id, device));
                            }
                            Err(e) => {
                                let msg = format!(
                                    "invalid sn device object: dir={}, sn={}, {}",
                                    id, sn_id, e
                                );
                                error!("{}", msg);
                                return Err(BuckyError::new(BuckyErrorCode::InvalidData, msg));
                            }
                        }
                    }
                }
            }
            NDNObjectInfo::Chunk(_) => {
                let msg = format!("dir chunk mode for sn config not support! id={}", id);
                error!("{}", msg);
                return Err(BuckyError::new(BuckyErrorCode::NotSupport, msg));
            }
        }

        Ok(sn_list)
    }

    pub fn load_sn_node<'a>(
        id: &ObjectId,
        dir: &'a Dir,
        node: &'a InnerNodeInfo,
    ) -> BuckyResult<(&'a ObjectId, &'a Vec<u8>)> {
        match node.node() {
            InnerNode::ObjId(sn_id) => {
                match dir.body() {
                    Some(body) => {
                        match body.content() {
                            DirBodyContent::ObjList(list) => {
                                match list.get(sn_id) {
                                    Some(value) => Ok((sn_id, value)),
                                    None => {
                                        let msg = format!("load sn item from dir body but not found! id={}, sn={}", id, sn_id);
                                        error!("{}", msg);
                                        Err(BuckyError::new(BuckyErrorCode::NotFound, msg))
                                    }
                                }
                            }
                            _ => {
                                let msg = format!("dir body chunk content format for sn config not support! id={}", id);
                                error!("{}", msg);
                                Err(BuckyError::new(BuckyErrorCode::NotSupport, msg))
                            }
                        }
                    }
                    None => {
                        let msg = format!("dir body content not exists! id={}", id);
                        error!("{}", msg);
                        Err(BuckyError::new(BuckyErrorCode::InvalidData, msg))
                    }
                }
            }
            _ => {
                let msg = format!(
                    "dir desc inner node format for sn config not support! id={}, node={:?}",
                    id, node
                );
                error!("{}", msg);
                Err(BuckyError::new(BuckyErrorCode::NotSupport, msg))
            }
        }
    }
}

use std::collections::HashMap;
use std::path::Path;

use super::bdt_util::load_device_objects_list;

pub struct SNDirGenerator;

impl SNDirGenerator {
    pub fn gen_from_dir(owner_id: &Option<ObjectId>, root: &Path) -> BuckyResult<Dir> {
        let list = load_device_objects_list(root);
        if list.is_empty() {
            let msg = format!("sn device folder is empty! dir={}", root.display());
            error!("{}", msg);
            return Err(BuckyError::new(BuckyErrorCode::InvalidData, msg));
        }

        Self::gen_from_list(owner_id, list)
    }

    pub fn gen_from_list(
        owner_id: &Option<ObjectId>,
        list: Vec<(DeviceId, Device)>,
    ) -> BuckyResult<Dir> {
        let mut object_map = HashMap::new();
        let mut body_map = HashMap::new();
        for (id, sn) in list {
            let inner_node = InnerNodeInfo::new(
                Attributes::default(),
                InnerNode::ObjId(id.object_id().clone()),
            );

            let path = format!("list/{}", id);
            info!("will add sn item to dir: {}", path);
            if let Some(_prev) = object_map.insert(path, inner_node) {
                let msg = format!("sn device item already exists in desc! id={}", id);
                error!("{}", msg);
                return Err(BuckyError::new(BuckyErrorCode::AlreadyExists, msg));
            }

            let buf = sn.to_vec()?;
            if let Some(_prev) = body_map.insert(id.object_id().clone(), buf) {
                let msg = format!("sn device item already exists in body! id={}", id);
                error!("{}", msg);
                return Err(BuckyError::new(BuckyErrorCode::AlreadyExists, msg));
            }
        }

        let list = NDNObjectList {
            parent_chunk: None,
            object_map,
        };

        let attr = Attributes::new(0);
        let builder = Dir::new(attr, NDNObjectInfo::ObjList(list.clone()), body_map);
        let dir = builder
            .option_owner(owner_id.to_owned())
            .no_create_time()
            .update_time(bucky_time_now())
            .build();
        let dir_id = dir.desc().calculate_id();

        info!("gen sn dir complete! dir={}", dir_id);
        Ok(dir)
    }
}

#[cfg(test)]
mod test {
    use cyfs_base::*;

    use crate::*;

    #[test]
    fn test() {
        crate::init_log("test-sn-dir", None);

        let root = crate::get_cyfs_root_path_ref().join("etc");
        let dir = SNDirGenerator::gen_from_dir(&None, &root).unwrap();
        let object_raw = dir.to_vec().unwrap();
        let dir_id = dir.desc().calculate_id();
        let list = SNDirParser::parse(Some(&dir_id), &object_raw).unwrap();
        for item in list {
            info!("got sn item: {}", item.0);
        }
    }
}