metabox_sdk/databox/
mod.rs

1use ic_agent::{Agent, identity::Secp256k1Identity};
2use ic_agent::agent::http_transport::ReqwestHttpReplicaV2Transport;
3use garcon::Delay;
4use std::fs;
5use std::path::Path;
6use std::ffi::OsStr;
7use sha256::digest_bytes;
8use rayon::prelude::*;
9use candid::{Encode, Decode, Nat, Principal};
10mod databox_did;
11pub use databox_did::{ClearAllResult, DeleteKeyResult, UploadResult, Avatar, PUT, Chunk, FilePut, PutResult, DataErr, FileExt, GetAssetExtKeyResult, GET, GetPlainResult, CanisterStateResult, CycleBalanceResult, AvlSMResult, GetAssetExtsResult};
12
13const UPDATE_SIZE: usize = 1992288;
14
15#[derive(Debug)]
16pub enum UploadStatus {
17    Ok,
18    Err(DataErr),
19}
20
21#[derive(Debug)]
22pub struct PutPlainFileResult {
23    pub file_name: String,
24    pub file_extension: String,
25    pub file_key: String,
26    pub upload_status: UploadStatus,
27    pub databox_canister_id: Principal,
28    pub total_size: u64,
29    pub chunk_number: u64,
30}
31
32/// Put plain files
33///
34/// Example code :
35/// ``` no_run
36/// use metabox_sdk::databox::{self, PutPlainFileResult};
37///
38/// async fn put_plain_files(folder_path: &str, data_box_canister_id_text: &str,) -> Vec<PutPlainFileResult> {
39///     databox::put_plain_files("identities/identity.pem", folder_path, data_box_canister_id_text).await
40/// }
41///
42/// #[tokio::main]
43/// async fn main() {
44///     let response_1 = put_plain_files("source/", "4radi-oqaaa-aaaan-qapwa-cai").await;
45///     let mut index = 0;
46///     for i in &response_1 {
47///         index += 1;
48///         println!("file index: {:?}", index);
49///         println!("file name: {:?}", i.file_name);
50///         println!("file extension: {:?}", i.file_extension);
51///         println!("file key: {:?}", i.file_key);
52///         println!("file upload_status: {:?}", i.upload_status);
53///         println!("file in data box: {:?}", i.databox_canister_id.to_text());
54///         println!("file total_size: {:?}", i.total_size);
55///         println!("file chunk number: {:?}", i.chunk_number);
56///         println!("\n");
57///     }
58/// }
59/// ```
60pub async fn put_plain_files(pem_identity_path: &str, folder_path: &str, data_box_canister_id_text: &str,) -> Vec<PutPlainFileResult> {
61    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
62    let mut ans: Vec<PutPlainFileResult> = Vec::new();
63    let paths = fs::read_dir(&folder_path).unwrap();
64    for path in paths {
65        let file_path = path.unwrap().file_name().into_string().unwrap();
66        let pos: Vec<&str> = file_path.split(".").collect();
67        let file_name = String::from(pos[0]);
68        let file_extension = String::from(get_file_type(&String::from(pos[1])));
69        let s = folder_path.to_owned() + &file_path;
70
71        let (file_size, slice_size, data_slice) = get_file_from_source(&s);
72
73        let puts = build_put_plain_args(
74            file_name.clone(),
75            file_extension.clone(),
76            file_size.try_into().unwrap(),
77            slice_size.try_into().unwrap(),
78            &data_slice,
79        );
80        let file_key = match &puts[0] {
81            FilePut::PlainFilePut(put) => {
82                match put {
83                    PUT::segment {file_extension, order, chunk_number, chunk, aes_pub_key, file_name, file_key, total_size} => {
84                        file_key.clone()
85                    }
86                    _ => {"".to_string()}
87                }
88            }
89            _ => {"".to_string()}
90        };
91
92        let mut flag = false;
93        for put in &puts {
94            let _response_blob = build_agent(pem_identity_path)
95                .update(&canister_id, "put")
96                .with_arg(Encode!(&put).expect("encode piece failed"))
97                .call_and_wait(get_waiter())
98                .await
99                .expect("response error");
100            let _response = Decode!(&_response_blob, PutResult).unwrap();
101            match _response {
102                PutResult::ok(..) => {
103                },
104                PutResult::err(data_err) => {
105                    ans.push(PutPlainFileResult {
106                        file_name: file_name.clone(),
107                        file_extension: file_extension.clone(),
108                        file_key: file_key.clone(),
109                        upload_status: UploadStatus::Err(data_err),
110                        databox_canister_id: canister_id,
111                        total_size: file_size.try_into().unwrap(),
112                        chunk_number: slice_size.try_into().unwrap(),
113                    });
114                    flag = true;
115                    break;
116                }
117            }
118        }
119        if !flag { ans.push(PutPlainFileResult {
120            file_name: file_name.clone(),
121            file_extension: file_extension.clone(),
122            file_key: file_key.clone(),
123            upload_status: UploadStatus::Ok,
124            databox_canister_id: canister_id,
125            total_size: file_size.try_into().unwrap(),
126            chunk_number: slice_size.try_into().unwrap(),
127        }); }
128    }
129    ans
130}
131
132/// Put a plain file
133///
134/// Example code :
135/// ``` no_run
136/// use metabox_sdk::databox::{self, PutPlainFileResult};
137///
138/// async fn put_plain_file(file_path_str: &str, data_box_canister_id_text: &str,) -> PutPlainFileResult {
139///     databox::put_plain_file("identities/identity.pem", file_path_str, data_box_canister_id_text).await
140/// }
141///
142/// #[tokio::main]
143/// async fn main() {
144///     let response_2 = put_plain_file("source/bitcoin.pdf", "4radi-oqaaa-aaaan-qapwa-cai").await;
145///     println!("file name: {:?}", response_2.file_name);
146///     println!("file extension: {:?}", response_2.file_extension);
147///     println!("file key: {:?}", response_2.file_key);
148///     println!("file upload_status: {:?}", response_2.upload_status);
149///     println!("file in data box: {:?}", response_2.databox_canister_id.to_text());
150///     println!("file total_size: {:?}", response_2.total_size);
151///     println!("file chunk number: {:?}", response_2.chunk_number);
152/// }
153/// ```
154pub async fn put_plain_file(pem_identity_path: &str, file_path_str: &str, data_box_canister_id_text: &str,) -> PutPlainFileResult {
155    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
156    let file_path = Path::new(file_path_str);
157    let file_name = file_path.file_stem().unwrap().to_str().unwrap().to_owned();
158    let file_extension = String::from(get_file_type(file_path.extension().unwrap().to_str().unwrap()));
159
160    let (file_size, slice_size, data_slice) = get_file_from_source(file_path_str);
161    let puts = build_put_plain_args(
162        file_name.clone(),
163        file_extension.clone(),
164        file_size.try_into().unwrap(),
165        slice_size.try_into().unwrap(),
166        &data_slice,
167    );
168    let file_key = match &puts[0] {
169        FilePut::PlainFilePut(put) => {
170            match put {
171                PUT::segment {file_extension, order, chunk_number, chunk, aes_pub_key, file_name, file_key, total_size} => {
172                    file_key.clone()
173                }
174                _ => {"".to_string()}
175            }
176        }
177        _ => {"".to_string()}
178    };
179
180    for put in &puts {
181        let _response_blob = build_agent(pem_identity_path)
182            .update(&canister_id, "put")
183            .with_arg(Encode!(&put).expect("encode piece failed"))
184            .call_and_wait(get_waiter())
185            .await
186            .expect("response error");
187        let _response = Decode!(&_response_blob, PutResult).unwrap();
188        match _response {
189            PutResult::ok(..) => {
190            },
191            PutResult::err(data_err) => {
192                return PutPlainFileResult {
193                    file_name: file_name.clone(),
194                    file_extension: file_extension.clone(),
195                    file_key: file_key.clone(),
196                    upload_status: UploadStatus::Err(data_err),
197                    databox_canister_id: canister_id,
198                    total_size: file_size.try_into().unwrap(),
199                    chunk_number: slice_size.try_into().unwrap(),
200                };
201            }
202        }
203    }
204    PutPlainFileResult {
205        file_name: file_name.clone(),
206        file_extension: file_extension.clone(),
207        file_key: file_key.clone(),
208        upload_status: UploadStatus::Ok,
209        databox_canister_id: canister_id,
210        total_size: file_size.try_into().unwrap(),
211        chunk_number: slice_size.try_into().unwrap(),
212    }
213}
214
215/// Upload avatar
216///
217/// Example code :
218/// ``` no_run
219/// use metabox_sdk::databox::{self, UploadResult};
220///
221/// async fn upload_avatar(data_box_canister_id_text: &str, avatar_file_path: &str) -> UploadResult {
222///     databox::upload_avatar("identities/identity.pem", data_box_canister_id_text, avatar_file_path).await
223/// }
224///
225/// #[tokio::main]
226/// async fn main() {
227///     println!("upload avatar result:{:?}", upload_avatar("4radi-oqaaa-aaaan-qapwa-cai", "source/avatar.jpg").await);
228/// }
229/// ```
230pub async fn upload_avatar(pem_identity_path: &str, data_box_canister_id_text: &str, avatar_file_path: &str) -> UploadResult {
231    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
232    let context = fs::read(avatar_file_path).expect("read file failed");
233    let file_extension = String::from(get_file_type(Path::new(avatar_file_path).extension().unwrap().to_str().unwrap()));
234    let upload_args = Avatar {
235        data: context,
236        data_type: file_extension,
237    };
238    let response_blob = build_agent(pem_identity_path)
239        .update(&canister_id, "upload")
240        .with_arg(Encode!(&upload_args).expect("encode piece failed"))
241        .call_and_wait(get_waiter())
242        .await
243        .expect("response error");
244    let response = Decode!(&response_blob, UploadResult).unwrap();
245    response
246}
247
248/// Delete a file
249///
250/// Example code :
251/// ``` no_run
252/// use metabox_sdk::databox::{self, DeleteKeyResult};
253///
254/// async fn delete_file(data_box_canister_id_text: &str, file_key: String) -> DeleteKeyResult {
255///     databox::delete_file("identities/identity.pem", data_box_canister_id_text, file_key).await
256/// }
257///
258/// #[tokio::main]
259/// async fn main() {
260///     println!("delete file result:{:?}", delete_file("4radi-oqaaa-aaaan-qapwa-cai", "4da18028cb05cdb1a8e271c02c48dceef6ad89811adab9f9a3ab9e96db020fb9".to_string()).await);
261/// }
262/// ```
263pub async fn delete_file(pem_identity_path: &str, data_box_canister_id_text: &str, file_key: String) -> DeleteKeyResult {
264    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
265    let response_blob = build_agent(pem_identity_path)
266        .update(&canister_id, "deletekey")
267        .with_arg(Encode!(&file_key).expect("encode piece failed"))
268        .call_and_wait(get_waiter())
269        .await
270        .expect("response error");
271    let response = Decode!(&response_blob, DeleteKeyResult).unwrap();
272    response
273}
274
275/// Clear the DataBox
276///
277/// Example code :
278/// ``` no_run
279/// use metabox_sdk::databox::{self, ClearAllResult};
280///
281/// async fn clear_data_box(data_box_canister_id_text: &str,) -> ClearAllResult {
282///     databox::clear_data_box("identities/identity.pem", data_box_canister_id_text).await
283/// }
284///
285/// #[tokio::main]
286/// async fn main() {
287///     println!("clear data box result:{:?}", clear_data_box("4radi-oqaaa-aaaan-qapwa-cai").await);
288/// }
289/// ```
290pub async fn clear_data_box(pem_identity_path: &str, data_box_canister_id_text: &str,) -> ClearAllResult {
291    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
292    let response_blob = build_agent(pem_identity_path)
293        .update(&canister_id, "clearall")
294        .with_arg(Encode!().expect("encode piece failed"))
295        .call_and_wait(get_waiter())
296        .await
297        .expect("response error");
298    let response = Decode!(&response_blob, ClearAllResult).unwrap();
299    response
300}
301
302/// Get the plain file Data
303///
304/// Example code :
305/// ``` no_run
306/// use std::io::Write;
307/// use std::fs::OpenOptions;
308/// use metabox_sdk::databox::{self, DataErr};
309///
310/// async fn get_plain_file(data_box_canister_id_text: &str, file_key: &str) -> Result<Vec<u8>, DataErr> {
311///     databox::get_plain_file("identities/identity.pem", data_box_canister_id_text, file_key).await
312/// }
313///
314/// #[tokio::main]
315/// async fn main() {
316///     let response_3 = get_plain_file("4radi-oqaaa-aaaan-qapwa-cai", "14d37b8971e5c73a523de39e0682ba0c08df3a503c49f4f976fe282bc60abfef").await.unwrap();
317///     let mut file = std::fs::File::create("output/a.pdf").expect("create failed");
318///     file.write_all(&response_3).expect("write failed");
319/// }
320/// ```
321pub async fn get_plain_file(pem_identity_path: &str, data_box_canister_id_text: &str, file_key: &str) -> Result<Vec<u8>, DataErr> {
322    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
323    let agent = build_agent(pem_identity_path);
324    let file_ext = get_file_info(pem_identity_path, data_box_canister_id_text, file_key).await.unwrap();
325    match file_ext {
326        FileExt::PlainFileExt(asset_ext) => {
327            let waiter = get_waiter();
328            let mut i = 0;
329            let need_query_times = asset_ext.need_query_times;
330            let mut ans: Vec<u8> = Vec::new();
331            while Nat::from(i) < need_query_times {
332                let arg = GET {
333                    flag: Nat::from(i),
334                    file_key: file_key.to_string(),
335                };
336                let response_blob = agent
337                    .update(&canister_id, "getPlain")
338                    .with_arg(Encode!(&arg).expect("encode piece failed"))
339                    .call_and_wait(waiter.clone())
340                    .await
341                    .expect("response error");
342                i += 1;
343                let response = Decode!(&response_blob, GetPlainResult).unwrap();
344                match response {
345                    GetPlainResult::ok(mut payload) => {
346                        ans.append(&mut payload)
347                    },
348                    GetPlainResult::err(data_err) => {
349                        return Err(data_err);
350                    },
351                }
352            }
353            Ok(ans)
354        },
355        _ => {
356            Err(DataErr::FileKeyErr)
357        }
358    }
359}
360
361/// Get a file 's information
362///
363/// Example code :
364/// ``` no_run
365/// use metabox_sdk::databox::{self, FileExt, DataErr};
366///
367/// async fn get_file_info(data_box_canister_id_text: &str, file_key: &str) -> Result<FileExt, DataErr> {
368///     databox::get_file_info("identities/identity.pem", data_box_canister_id_text, file_key).await
369/// }
370///
371/// #[tokio::main]
372/// async fn main() {
373///     match get_file_info("4radi-oqaaa-aaaan-qapwa-cai", "3166112af0dcc940f8e7f2199a4200cfb5e2efb40796391201b8fe9e4ff7ca84").await {
374///         Ok(file_ext) => {
375///             match file_ext {
376///                 FileExt::PlainFileExt(asset_ext) => {
377///                     println!("file name: {:?}", asset_ext.file_name);
378///                     println!("file extension: {:?}", asset_ext.file_extension);
379///                     println!("file key: {:?}", asset_ext.file_key);
380///                     println!("file total_size: {:?}", asset_ext.total_size);
381///                     println!("file upload_status: {:?}", asset_ext.upload_status);
382///                     println!("file in data box: {:?}", asset_ext.bucket_id.to_text());
383///                     println!("file aes_pub_key: {:?}", asset_ext.aes_pub_key);
384///                     println!("file need_query_times: {:?}", asset_ext.need_query_times);
385///                 }
386///             _ => {}
387///             }
388///         }
389///         Err(error) => {
390///             println!("get file info error: {:?}", error);
391///         }
392///     }
393/// }
394/// ```
395pub async fn get_file_info(pem_identity_path: &str, data_box_canister_id_text: &str, file_key: &str) -> Result<FileExt, DataErr> {
396    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
397    let response_blob = build_agent(pem_identity_path)
398        .query(&canister_id, "getAssetextkey")
399        .with_arg(Encode!(&file_key).expect("encode piece failed"))
400        .call()
401        .await
402        .expect("response error");
403    let response = Decode!(&response_blob, GetAssetExtKeyResult).unwrap();
404    match response {
405        GetAssetExtKeyResult::ok(file_ext) => Ok(file_ext),
406        GetAssetExtKeyResult::err(data_err) => Err(data_err),
407    }
408}
409
410/// Get all plain files 's information
411///
412/// Example code :
413/// ``` no_run
414/// use metabox_sdk::databox::{self, FileExt, DataErr};
415///
416/// async fn get_all_plain_files_info(data_box_canister_id_text: &str) -> Result<Vec<FileExt>, DataErr> {
417///     databox::get_all_plain_files_info("identities/identity.pem", data_box_canister_id_text).await
418/// }
419///
420/// #[tokio::main]
421/// async fn main() {
422///     match get_all_plain_files_info("4radi-oqaaa-aaaan-qapwa-cai").await {
423///         Ok(file_ext_s) => {
424///             let mut index = 0;
425///             for i in &file_ext_s {
426///                 index += 1;
427///                 match i {
428///                     FileExt::PlainFileExt(asset_ext) => {
429///                         println!("file index: {:?}", index);
430///                         println!("file name: {:?}", asset_ext.file_name);
431///                         println!("file extension: {:?}", asset_ext.file_extension);
432///                         println!("file key: {:?}", asset_ext.file_key);
433///                         println!("file total_size: {:?}", asset_ext.total_size);
434///                         println!("file upload_status: {:?}", asset_ext.upload_status);
435///                         println!("file in data box: {:?}", asset_ext.bucket_id.to_text());
436///                         println!("file aes_pub_key: {:?}", asset_ext.aes_pub_key);
437///                         println!("file need_query_times: {:?}", asset_ext.need_query_times);
438///                         println!("\n");
439///                     }
440///                     _ => {}
441///                 }
442///             }
443///         }
444///         Err(error) => {
445///             println!("get all plain files info error: {:?}", error);
446///         }
447///     }
448/// }
449/// ```
450pub async fn get_all_plain_files_info(pem_identity_path: &str, data_box_canister_id_text: &str) -> Result<Vec<FileExt>, DataErr> {
451    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
452    let response_blob = build_agent(pem_identity_path)
453        .query(&canister_id, "getAssetexts")
454        .with_arg(Encode!().expect("encode piece failed"))
455        .call()
456        .await
457        .expect("response error");
458    let response = Decode!(&response_blob, GetAssetExtsResult).unwrap();
459    match response {
460        GetAssetExtsResult::ok(plain_assets, ..) => {
461            return Ok(plain_assets);
462        },
463        GetAssetExtsResult::err(data_err) => {
464            return Err(data_err);
465        },
466    }
467}
468
469/// Get DataBox version
470///
471/// Example code :
472/// ``` no_run
473/// use candid::Nat;
474/// use metabox_sdk::databox;
475///
476/// async fn get_version(data_box_canister_id_text: &str,) -> Nat {
477///     databox::get_version("identities/identity.pem", data_box_canister_id_text).await
478/// }
479///
480/// #[tokio::main]
481/// async fn main() {
482///     println!("data box version: {:?}", get_version("4radi-oqaaa-aaaan-qapwa-cai").await);
483/// }
484/// ```
485pub async fn get_version(pem_identity_path: &str, data_box_canister_id_text: &str,) -> Nat {
486    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
487    let response_blob = build_agent(pem_identity_path)
488        .query(&canister_id, "getVersion")
489        .with_arg(Encode!().expect("encode piece failed"))
490        .call()
491        .await
492        .expect("response error");
493    let response = Decode!(&response_blob, Nat).unwrap();
494    response
495}
496
497/// Get DataBox canister state
498///
499/// Example code :
500/// ``` no_run
501/// use metabox_sdk::databox::{self, CanisterStateResult};
502///
503/// async fn get_canister_state(data_box_canister_id_text: &str,) -> CanisterStateResult {
504///     databox::get_canister_state("identities/identity.pem", data_box_canister_id_text).await
505/// }
506///
507/// #[tokio::main]
508/// async fn main() {
509///     println!("data box canister state: {:?}", get_canister_state("4radi-oqaaa-aaaan-qapwa-cai").await);
510/// }
511/// ```
512pub async fn get_canister_state(pem_identity_path: &str, data_box_canister_id_text: &str,) -> CanisterStateResult {
513    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
514    let response_blob = build_agent(pem_identity_path)
515        .query(&canister_id, "canisterState")
516        .with_arg(Encode!().expect("encode piece failed"))
517        .call()
518        .await
519        .expect("response error");
520    let response = Decode!(&response_blob, CanisterStateResult).unwrap();
521    response
522}
523
524/// Get DataBox cycle balance
525///
526/// Example code :
527/// ``` no_run
528/// use metabox_sdk::databox::{self, CycleBalanceResult};
529///
530/// async fn get_cycle_balance(data_box_canister_id_text: &str,) -> CycleBalanceResult {
531///     databox::get_cycle_balance("identities/identity.pem", data_box_canister_id_text).await
532/// }
533///
534/// #[tokio::main]
535/// async fn main() {
536///     println!("data box cycle balance: {:?}", get_cycle_balance("4radi-oqaaa-aaaan-qapwa-cai").await);
537/// }
538/// ```
539pub async fn get_cycle_balance(pem_identity_path: &str, data_box_canister_id_text: &str,) -> CycleBalanceResult {
540    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
541    let response_blob = build_agent(pem_identity_path)
542        .query(&canister_id, "cycleBalance")
543        .with_arg(Encode!().expect("encode piece failed"))
544        .call()
545        .await
546        .expect("response error");
547    let response = Decode!(&response_blob, CycleBalanceResult).unwrap();
548    response
549}
550
551/// Get DataBox available stable memory
552///
553/// Example code :
554/// ``` no_run
555/// use metabox_sdk::databox::{self, AvlSMResult};
556///
557/// async fn get_avl_sm(data_box_canister_id_text: &str,) -> AvlSMResult {
558///     databox::get_avl_sm("identities/identity.pem", data_box_canister_id_text).await
559/// }
560///
561/// #[tokio::main]
562/// async fn main() {
563///     println!("data box available stable memory: {:?}", get_avl_sm("4radi-oqaaa-aaaan-qapwa-cai").await);
564/// }
565/// ```
566pub async fn get_avl_sm(pem_identity_path: &str, data_box_canister_id_text: &str,) -> AvlSMResult {
567    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
568    let response_blob = build_agent(pem_identity_path)
569        .query(&canister_id, "avlSM")
570        .with_arg(Encode!().expect("encode piece failed"))
571        .call()
572        .await
573        .expect("response error");
574    let response = Decode!(&response_blob, AvlSMResult).unwrap();
575    response
576}
577
578/// Get DataBox owner
579///
580/// Example code :
581/// ``` no_run
582/// use candid::Principal;
583/// use metabox_sdk::databox::{self, AvlSMResult};
584///
585/// async fn get_owner(data_box_canister_id_text: &str,) -> Principal {
586///     databox::get_owner("identities/identity.pem", data_box_canister_id_text).await
587/// }
588///
589/// #[tokio::main]
590/// async fn main() {
591///     println!("data box owner: {:?}", get_owner("4radi-oqaaa-aaaan-qapwa-cai").await.to_text());
592/// }
593/// ```
594pub async fn get_owner(pem_identity_path: &str, data_box_canister_id_text: &str,) -> candid::Principal {
595    let canister_id = Principal::from_text(data_box_canister_id_text).unwrap();
596    let response_blob = build_agent(pem_identity_path)
597        .query(&canister_id, "getOwner")
598        .with_arg(Encode!().expect("encode piece failed"))
599        .call()
600        .await
601        .expect("response error");
602    let response = Decode!(&response_blob, candid::Principal).unwrap();
603    response
604}
605
606// Access file from file path, slice and return [each slice] array
607fn get_file_from_source(path: &str) -> (usize, usize, Vec<Vec<u8>>) {
608    let context = fs::read(path).expect("read file failed");
609    let size = context.len();
610    let slice_size = if context.len() % UPDATE_SIZE == 0 {
611        context.len() / UPDATE_SIZE
612    } else {
613        context.len() / UPDATE_SIZE + 1
614    };
615    let mut res = Vec::new();
616    for index in 0..slice_size {
617        if index == slice_size - 1 {
618            res.push(context[index * UPDATE_SIZE..context.len()].to_owned())
619        } else {
620            res.push(context[index * UPDATE_SIZE..(index + 1) * UPDATE_SIZE].to_owned())
621        }
622    }
623    (size, slice_size, res)
624}
625
626fn build_put_plain_args(
627    file_name: String,
628    file_extension: String,
629    total_size: u64,
630    chunk_number: u64,
631    data_slice: &Vec<Vec<u8>>,
632) -> Vec<FilePut> {
633    let mut order = 0;
634    let mut puts: Vec<FilePut> = Vec::new();
635    let file_key = get_file_key(&get_file_sha256_digest(data_slice));
636    for data in data_slice {
637        puts.push(FilePut::PlainFilePut(PUT::segment {
638            aes_pub_key: None,
639            file_key: file_key.clone(),
640            file_name: file_name.clone(),
641            file_extension: file_extension.clone(),
642            chunk: Chunk {
643                data: data.clone(),
644            },
645            chunk_number: Nat::from(chunk_number),
646            order: Nat::from(order),
647            total_size: total_size.clone(),
648        }));
649        order += 1;
650    }
651    puts
652}
653
654fn get_file_sha256_digest(context: &Vec<Vec<u8>>) -> Vec<Vec<u8>> {
655    let mut digests = vec![vec![0x00 as u8]; context.len()];
656    let mut contents = digests.iter_mut().zip(context.iter()).collect::<Vec<_>>();
657    contents
658        .par_iter_mut()
659        .for_each(|(d, text)| **d = digest_bytes(*text).into_bytes()[..32].to_vec());
660    digests
661}
662
663fn get_file_key(digests: &Vec<Vec<u8>>) -> String {
664    let mut digest = vec![0x00 as u8; 32 * digests.len()];
665    let mut _index = 0;
666    for bytes in digests {
667        for byte in bytes {
668            digest.push(*byte);
669            _index += 1;
670        }
671    }
672    digest_bytes(&digest)
673}
674
675fn get_file_type(file_type: &str) -> &str {
676    if file_type == "pdf" {
677        return "application/pdf";
678    } else if file_type == "jpg" || file_type == "jpeg" {
679        return "image/jpg";
680    } else if file_type == "png" {
681        return "image/png";
682    } else if file_type == "mp4" {
683        return "video/mp4";
684    } else if file_type == "mp3" {
685        return "audio/mp3";
686    } else if file_type == "gif" {
687        return "image/gif";
688    } else if file_type == "txt" {
689        return "text/plain";
690    } else if file_type == "ppt" || file_type == "pptx" {
691        return "application/vnd.ms-powerpoint";
692    } else if file_type == "html" || file_type == "xhtml" {
693        return "text/html";
694    } else if file_type == "doc" || file_type == "docx" {
695        return "application/msword";
696    } else if file_type == "xls" {
697        return "application/x-xls";
698    } else if file_type == "apk" {
699        return "application/vnd.android.package-archive";
700    } else if file_type == "svg" {
701        return "text/xml";
702    } else if file_type == "wmv" {
703        return "video/x-ms-wmv";
704    } else {
705        return "application/octet-stream";
706    }
707}
708
709fn get_waiter() -> Delay {
710    let waiter = garcon::Delay::builder()
711        .throttle(std::time::Duration::from_millis(500))
712        .timeout(std::time::Duration::from_secs(60 * 5))
713        .build();
714    waiter
715}
716
717fn build_agent(pem_identity_path: &str) -> Agent {
718    let url = "https://ic0.app".to_string();
719    let identity = Secp256k1Identity::from_pem_file(String::from(pem_identity_path)).unwrap();
720    let transport = ReqwestHttpReplicaV2Transport::create(url).expect("transport error");
721    let agent = Agent::builder()
722        .with_transport(transport)
723        .with_identity(identity)
724        .build()
725        .expect("build agent error");
726    agent
727}
728
729// pub async fn put_encrypt_files(pem_identity_path: &str, folder_path: &str, databox_canister_id_text: &str,) -> Vec<PutPlainFileResult> {
730//     let canister_id = Principal::from_text(databox_canister_id_text).unwrap();
731//     let agent = build_agent(pem_identity_path);
732//     let waiter = get_waiter();
733//
734//     let mut ans: Vec<PutPlainFileResult> = Vec::new();
735//     let paths = fs::read_dir(&folder_path).unwrap();
736//     for path in paths {
737//         let file_path = path.unwrap().file_name().into_string().unwrap();
738//         let pos: Vec<&str> = file_path.split(".").collect();
739//         let file_name = String::from(pos[0]);
740//         let file_extension = String::from(get_file_type(&String::from(pos[1])));
741//         let s = folder_path.to_owned() + &file_path;
742//
743//         let (file_size, slice_size, data_slice) = get_file_from_source(&s);
744//
745//         let puts = build_put_plain_args(
746//             file_name.clone(),
747//             file_extension.clone(),
748//             file_size.try_into().unwrap(),
749//             slice_size.try_into().unwrap(),
750//             &data_slice,
751//         );
752//
753//         let file_key = match &puts[0] {
754//             FilePut::PlainFilePut(put) => {
755//                 match put {
756//                     PUT::segment {file_extension, order, chunk_number, chunk, aes_pub_key, file_name, file_key, total_size} => {
757//                         file_key.clone()
758//                     }
759//                     _ => {"".to_string()}
760//                 }
761//             }
762//             _ => {"".to_string()}
763//         };
764//
765//         let mut flag = false;
766//         for put in &puts {
767//             let _response_blob = agent
768//                 .update(&canister_id, "put")
769//                 .with_arg(Encode!(&put).expect("encode piece failed"))
770//                 .call_and_wait(waiter.clone())
771//                 .await
772//                 .expect("response error");
773//             let _response = Decode!(&_response_blob, PutResult).unwrap();
774//             match _response {
775//                 PutResult::ok(..) => {
776//                 },
777//                 PutResult::err(data_err) => {
778//                     ans.push(PutPlainFileResult {
779//                         file_name: file_name.clone(),
780//                         file_extension: file_extension.clone(),
781//                         file_key: file_key.clone(),
782//                         upload_status: UploadStatus::Err(data_err),
783//                         databox_canister_id: canister_id,
784//                         total_size: file_size.try_into().unwrap(),
785//                         chunk_number: slice_size.try_into().unwrap(),
786//                     });
787//                     flag = true;
788//                     break;
789//                 }
790//             }
791//         }
792//         if !flag { ans.push(PutPlainFileResult {
793//             file_name: file_name.clone(),
794//             file_extension: file_extension.clone(),
795//             file_key: file_key.clone(),
796//             upload_status: UploadStatus::Ok,
797//             databox_canister_id: canister_id,
798//             total_size: file_size.try_into().unwrap(),
799//             chunk_number: slice_size.try_into().unwrap(),
800//         }); }
801//     }
802//     ans
803// }
804
805/*fn build_ciphertext_put(
806    file_name: String,
807    file_extension: String,
808    total_size: u64,
809    data_slice: &Vec<Vec<u8>>,
810    aes_key_text: &Vec<u8>, // aes_key mingwen
811    rsa_key: &Rsa<Private>, // rsa密钥
812    iv: &Vec<u8>, //
813) -> Vec<FilePut> {
814    let mut order = 0;
815    let mut puts = vec![];
816    let mut encrypted_aes_key = vec![];
817    let _ = rsa_key.public_encrypt(aes_key_text, &mut encrypted_aes_key, Padding::PKCS1);
818    let encrypted_aes_key = hex::encode(&encrypted_aes_key);
819    let mut cipher_text = vec![vec![]; data_slice.len()];
820    let aes_key = AesKey::new_encrypt(aes_key_text).expect("get aes key failed");
821    cipher_text
822        .iter_mut()
823        .zip(data_slice.iter())
824        .collect::<Vec<_>>()
825        .par_iter_mut()
826        .for_each(|(cipher, data)| {
827            let mut iv_temp = iv.clone();
828            aes_ige(*data, *cipher, &aes_key, &mut iv_temp, Mode::Encrypt)
829        });
830    let file_key = get_file_key(&get_file_sha256_digest(&cipher_text));
831    for cipher in cipher_text {
832        puts.push(FilePut::EncryptFilePut(PUT::segment {
833            aes_pub_key: Some(encrypted_aes_key.clone()),
834            file_key: file_key.clone(),
835            file_name: file_name.clone(),
836            file_extension: file_extension.clone(),
837            chunk: Chunk {
838                data: cipher.to_vec(),
839            },
840            chunk_number: Nat::from(cipher.len()),
841            order: Nat::from(order),
842            total_size: total_size.clone(),
843        }));
844        order += 1;
845    }
846    puts
847}*/