isp_sdk/icsp/
mod.rs

1use candid::{Decode, Encode, Nat};
2use garcon::Delay;
3use ic_agent::agent::http_transport::ReqwestHttpReplicaV2Transport;
4use ic_agent::{identity::Secp256k1Identity, Agent};
5use std::fs::{self};
6use std::path::Path;
7use uuid::Uuid;
8mod icsp_did;
9pub use icsp_did::{Buckets, FileBufExt, StoreArgs};
10
11const UPDATE_SIZE: usize = 2031616;
12
13/// Get all ic files 's key from user 's icsp
14///
15/// # Examples
16///
17/// ``` no_run
18/// use isp_sdk::icsp;
19///
20/// pub async fn get_all_ic_file_key() {
21///   println!(
22///     "get all ic file key result: {:?}",
23///     icsp::get_all_ic_file_key("identities/identity.pem", "4radi-oqaaa-aaaan-qapwa-cai").await
24///  );
25/// }
26/// ```
27pub async fn get_all_ic_file_key(
28    pem_identity_path: &str,
29    icsp_canister_id_text: &str,
30) -> Vec<String> {
31    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
32    let response_blob = build_agent(pem_identity_path)
33        .query(&canister_id, "getAllIcFileKey")
34        .with_arg(Encode!().expect("encode piece failed"))
35        .call()
36        .await
37        .expect("response error");
38    Decode!(&response_blob, Vec<String>).unwrap()
39}
40
41/// Get file's information
42///
43/// # Examples
44///
45/// ``` no_run
46/// use isp_sdk::icsp;
47/// pub async fn get_file_info() {
48///    println!("get file info result:");
49///    match icsp::get_file_info(
50///        "identities/identity.pem",
51///        "4radi-oqaaa-aaaan-qapwa-cai",
52///        "49c1dadd-6fa6-4f15-b963-1a1e6f111028".to_string(),
53///    )
54///        .await
55///    {
56///        None => println!("do not have this file"),
57///        Some(file_info) => {
58///            println!("bucket_id: {:?}", file_info.bucket_id.to_text());
59///            println!("total_index: {:?}", file_info.total_index);
60///            println!("received chunk_number: {:?}", file_info.received);
61///            println!("wrote_page: {:?}", file_info.wrote_page);
62///            println!("file type: {:?}", file_info.file_type);
63///            println!("is_http_open: {:?}", file_info.is_http_open);
64///            println!("total_size: {:?}", file_info.total_size);
65///        }
66///    };
67/// }
68/// ```
69pub async fn get_file_info(
70    pem_identity_path: &str,
71    icsp_canister_id_text: &str,
72    file_key: String,
73) -> Option<FileBufExt> {
74    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
75    let response_blob = build_agent(pem_identity_path)
76        .query(&canister_id, "getFileInfo")
77        .with_arg(Encode!(&file_key).expect("encode piece failed"))
78        .call()
79        .await
80        .expect("response error");
81    Decode!(&response_blob, Option<FileBufExt>).unwrap()
82}
83
84/// Get icsp 's cycle balance
85///
86/// The cycle balance is e12s
87///
88/// # Examples
89///
90/// ``` no_run
91/// use isp_sdk::icsp;
92///
93/// pub async fn get_cycle_balance() {
94///     println!(
95///         "icsp cycle balance:{:?}\n",
96///         icsp::get_cycle_balance("identities/identity.pem", "4radi-oqaaa-aaaan-qapwa-cai").await
97///     );
98/// }
99/// ```
100pub async fn get_cycle_balance(pem_identity_path: &str, icsp_canister_id_text: &str) -> Nat {
101    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
102    let response_blob = build_agent(pem_identity_path)
103        .query(&canister_id, "getCycleBalance")
104        .with_arg(Encode!().expect("encode piece failed"))
105        .call()
106        .await
107        .expect("response error");
108    Decode!(&response_blob, Nat).unwrap()
109}
110
111/// Get the bucket where the file is stored
112///
113/// # Examples
114///
115/// ``` no_run
116/// use isp_sdk::icsp;
117///
118/// pub async fn get_bucket_of_file() {
119///     println!(
120///         "the file in bucekt:{:?}\n",
121///         icsp::get_bucket_of_file(
122///             "identities/identity.pem",
123///             "4radi-oqaaa-aaaan-qapwa-cai",
124///             "bf0efa3d-6639-4d62-a81d-c90974cc6925",
125///         )
126///             .await
127///             .expect("no bucket have this file")
128///             .to_text()
129///     );
130/// }
131/// ```
132pub async fn get_bucket_of_file(
133    pem_identity_path: &str,
134    icsp_canister_id_text: &str,
135    file_key: &str,
136) -> Option<candid::Principal> {
137    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
138    let response_blob = build_agent(pem_identity_path)
139        .query(&canister_id, "getBucketOfFile")
140        .with_arg(Encode!(&file_key).expect("encode piece failed"))
141        .call()
142        .await
143        .expect("response error");
144    Decode!(&response_blob, Option<candid::Principal>).unwrap()
145}
146
147/// Get buckets of user's icsp
148///
149/// # Examples
150///
151/// ```no_run
152/// use isp_sdk::icsp;
153///
154/// pub async fn get_icsp_buckets() {
155///     let response =
156///         icsp::get_icsp_buckets("identities/identity.pem", "4radi-oqaaa-aaaan-qapwa-cai").await;
157///     match response {
158///         Some(response) => {
159///             println!("dead buckets:");
160///             for i in &response.dead_buckets {
161///                 println!(
162///                     "canister_id: {:?}; used_memory: {:?}",
163///                     i.canister_id.to_text(),
164///                     i.used_memory
165///                 );
166///             }
167///             println!("Live Buckets:");
168///             for i in &response.live_buckets {
169///                 println!(
170///                     "canister_id:{:?}, used_memory:{:?}",
171///                     i.canister_id.to_text(),
172///                     i.used_memory,
173///                 );
174///             }
175///         }
176///         None => println!("icsp do not have buckets"),
177///     }
178/// }
179/// ```
180pub async fn get_icsp_buckets(
181    pem_identity_path: &str,
182    icsp_canister_id_text: &str,
183) -> Option<Buckets> {
184    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
185    let response_blob = build_agent(pem_identity_path)
186        .query(&canister_id, "getBuckets")
187        .with_arg(Encode!().expect("encode piece failed"))
188        .call()
189        .await
190        .expect("response error");
191    Decode!(&response_blob, Option<Buckets>).unwrap()
192}
193
194/// Get icsp 's admins
195///
196/// # Examples
197///
198/// ``` no_run
199/// use isp_sdk::icsp;
200/// pub async fn get_icsp_admins() {
201///     println!("icsp admins:");
202///     for i in &icsp::get_icsp_admins("identities/identity.pem", "4radi-oqaaa-aaaan-qapwa-cai").await
203///     {
204///         println!("{:?}", i.to_text());
205///     }
206/// }
207/// ```
208pub async fn get_icsp_admins(
209    pem_identity_path: &str,
210    icsp_canister_id_text: &str,
211) -> Vec<candid::Principal> {
212    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
213    let response_blob = build_agent(pem_identity_path)
214        .query(&canister_id, "getAdmins")
215        .with_arg(Encode!().expect("encode error"))
216        .call()
217        .await
218        .expect("response error");
219    Decode!(&response_blob, Vec<candid::Principal>).unwrap()
220}
221
222/// Store files from folder_path
223///
224/// If http open,url format: icsp_canister_id.raw.ic0.app/ic/file_key
225///
226/// # Examples
227///
228/// ``` no_run
229/// use isp_sdk::icsp;
230///
231/// pub async fn store_files() {
232///     // url format : icsp_canister_id.raw.ic0.app/'option location'/file_key
233///     // icsp_canister_id.raw.ic0.app/ic/file_key
234///     // icsp_canister_id.raw.ic0.app/ipfs/file_key
235///     // icsp_canister_id.raw.ic0.app/ar/file_key
236///     for i in &icsp::store_files(
237///         "identities/identity.pem",
238///         "source/",
239///         "4radi-oqaaa-aaaan-qapwa-cai",
240///         true,
241///     )
242///         .await
243///     {
244///         println!("file_name:{:?},file_key:{:?}", i.0, i.1);
245///     }
246/// }
247/// ```
248pub async fn store_files(
249    pem_identity_path: &str,
250    folder_path: &str,
251    icsp_canister_id_text: &str,
252    is_http_open: bool,
253) -> Vec<(String, String)> {
254    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
255    let agent = build_agent(pem_identity_path);
256
257    let mut ans: Vec<(String, String)> = Vec::new();
258    let paths = fs::read_dir(&folder_path).unwrap();
259    for path in paths {
260        let file_path = path.unwrap().file_name().into_string().unwrap();
261        let pos: Vec<&str> = file_path.split(".").collect();
262        let file_name = String::from(pos[0]);
263        let file_type = String::from(pos[1]);
264        let file_extension = String::from(get_file_type(&file_type));
265        let s = folder_path.to_owned() + &file_path;
266
267        let (file_size, data_slice) = get_file_from_source(&s);
268        let file_key = Uuid::new_v4().to_string();
269        let puts = build_store_args(
270            file_key.clone(),
271            file_extension,
272            file_size.try_into().unwrap(),
273            &data_slice,
274            is_http_open,
275        );
276        for put in &puts {
277            let _response_blob = agent
278                .update(&canister_id, "store")
279                .with_arg(Encode!(put).expect("encode piece failed"))
280                .call_and_wait()
281                .await
282                .expect("response error");
283        }
284        ans.push((file_name.clone(), file_key.clone()));
285    }
286    ans
287}
288
289/// Store a file from file_path
290///
291/// return (file_name, file_key)
292///
293/// If http open,url format: icsp_canister_id.raw.ic0.app/ic/file_key
294///
295/// # Examples
296///
297/// ``` no_run
298/// use isp_sdk::icsp;
299///
300/// pub async fn store_file() {
301///     // url format : icsp_canister_id.raw.ic0.app/'option location'/file_key
302///     // icsp_canister_id.raw.ic0.app/ic/file_key
303///     // icsp_canister_id.raw.ic0.app/ipfs/file_key
304///     // icsp_canister_id.raw.ic0.app/ar/file_key
305///     let respoonse = icsp::store_file(
306///         "identities/identity.pem",
307///         "source/bitcoin.pdf",
308///         "4radi-oqaaa-aaaan-qapwa-cai",
309///         true,
310///     )
311///         .await;
312///     println!("file_name:{:?},file_key:{:?}", respoonse.0, respoonse.1);
313/// }
314/// ```
315pub async fn store_file(
316    pem_identity_path: &str,
317    file_path_str: &str,
318    icsp_canister_id_text: &str,
319    is_http_open: bool,
320) -> (String, String) {
321    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
322    let agent = build_agent(pem_identity_path);
323    let file_path = Path::new(file_path_str);
324    let file_name = file_path.file_stem().unwrap().to_str().unwrap().to_owned();
325    let file_extension = String::from(get_file_type(
326        file_path.extension().unwrap().to_str().unwrap(),
327    ));
328
329    let (file_size, data_slice) = get_file_from_source(file_path_str);
330    let file_key = Uuid::new_v4().to_string();
331    let puts = build_store_args(
332        file_key.clone(),
333        file_extension,
334        file_size.try_into().unwrap(),
335        &data_slice,
336        is_http_open,
337    );
338    for put in &puts {
339        let _response_blob = agent
340            .update(&canister_id, "store")
341            .with_arg(Encode!(put).expect("encode piece failed"))
342            .call_and_wait()
343            .await
344            .expect("response error");
345    }
346    (file_name, file_key.clone())
347}
348
349/// Store file with given key
350///
351/// return (file_name, file_key)
352///
353/// If http open,url format: icsp_canister_id.raw.ic0.app/ic/file_key
354///
355/// # Examples
356///
357/// ``` no_run
358/// use isp_sdk::icsp;
359///
360/// pub async fn store_file_by_key() {
361///     let respoonse = icsp::store_file_by_key(
362///         "identities/identity.pem",
363///         "source/bitcoin.pdf",
364///         "4radi-oqaaa-aaaan-qapwa-cai",
365///         true,
366///         "test_key".to_string(),
367///     )
368///         .await;
369///     println!("file_name:{:?},file_key:{:?}", respoonse.0, respoonse.1);
370/// }
371/// ```
372pub async fn store_file_by_key(
373    pem_identity_path: &str,
374    file_path_str: &str,
375    icsp_canister_id_text: &str,
376    is_http_open: bool,
377    file_key: String,
378) -> (String, String) {
379    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
380    let agent = build_agent(pem_identity_path);
381    let file_path = Path::new(file_path_str);
382    let file_name = file_path.file_stem().unwrap().to_str().unwrap().to_owned();
383    let file_extension = String::from(get_file_type(
384        file_path.extension().unwrap().to_str().unwrap(),
385    ));
386
387    let (file_size, data_slice) = get_file_from_source(file_path_str);
388    let puts = build_store_args(
389        file_key.clone(),
390        file_extension,
391        file_size.try_into().unwrap(),
392        &data_slice,
393        is_http_open,
394    );
395    for put in &puts {
396        let _response_blob = agent
397            .update(&canister_id, "store")
398            .with_arg(Encode!(put).expect("encode piece failed"))
399            .call_and_wait()
400            .await
401            .expect("response error");
402    }
403    (file_name, file_key.clone())
404}
405
406/// Delete file by file_key
407///
408/// # Examples
409///
410/// ``` no_run
411/// use isp_sdk::icsp;
412///
413/// pub async fn delete_file() {
414///     let _respoonse = icsp::delete_file(
415///         "identities/identity.pem",
416///         "5ekwd-fyaaa-aaaan-qaxlq-cai",
417///         "64b9eb91-feaa-43f0-aa39-3040c035c5bb",
418///     )
419///         .await;
420///     println!("complete delete file func");
421/// }
422/// ```
423pub async fn delete_file(pem_identity_path: &str, icsp_canister_id_text: &str, file_key: &str) {
424    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
425    let agent = build_agent(pem_identity_path);
426    let _ = agent
427        .update(&canister_id, "delete")
428        .with_arg(Encode!(&file_key.to_string()).expect("encode piece failed"))
429        .call_and_wait()
430        .await
431        .expect("response error");
432}
433
434/// Store str data
435///
436/// If http open,url format: icsp_canister_id.raw.ic0.app/ic/file_key
437///
438/// # Examples
439///
440/// ``` no_run
441/// use isp_sdk::icsp;
442///
443/// pub async fn store_str() {
444///     // url format : icsp_canister_id.raw.ic0.app/'option location'/file_key
445///     // icsp_canister_id.raw.ic0.app/ic/file_key
446///     // icsp_canister_id.raw.ic0.app/ipfs/file_key
447///     // icsp_canister_id.raw.ic0.app/ar/file_key
448///     println!(
449///         "store_str, file_key: {:?}",
450///         icsp::store_str(
451///             "identities/identity.pem",
452///             "4radi-oqaaa-aaaan-qapwa-cai",
453///             "test_isp_sdk_store_str",
454///             true,
455///         )
456///             .await
457///     );
458/// }
459/// ```
460pub async fn store_str(
461    pem_identity_path: &str,
462    icsp_canister_id_text: &str,
463    data: &str,
464    is_http_open: bool,
465) -> String {
466    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
467    let file_extension = "text/plain".to_string();
468    let file_size = data.len();
469    let file_key = Uuid::new_v4().to_string();
470
471    let put = StoreArgs {
472        key: file_key.clone(),
473        value: data.as_bytes().to_owned(),
474        total_index: Nat::from(1),
475        file_type: file_extension.clone(),
476        total_size: file_size.clone() as u64,
477        is_http_open: is_http_open.clone(),
478        index: Nat::from(0),
479    };
480    let _ = build_agent(pem_identity_path)
481        .update(&canister_id, "store")
482        .with_arg(Encode!(&put).expect("encode piece failed"))
483        .call_and_wait()
484        .await
485        .expect("response error");
486
487    file_key
488}
489
490/// Replace the value str corresponding to the key
491/// # Examples
492///
493/// ``` no_run
494/// use isp_sdk::icsp;
495///
496/// pub async fn replace_str() {
497///     icsp::replace_str(
498///         "identities/identity.pem",
499///         "4radi-oqaaa-aaaan-qapwa-cai",
500///         "8225a448-7eff-4162-bb52-313884bbde4e",
501///         "test_isp_sdk_replace_str",
502///         true,
503///     )
504///         .await;
505///     println!("replace_str complete ");
506/// }
507/// ```
508pub async fn replace_str(
509    pem_identity_path: &str,
510    icsp_canister_id_text: &str,
511    file_key: &str,
512    data: &str,
513    is_http_open: bool,
514) {
515    delete_file(pem_identity_path, icsp_canister_id_text, file_key).await;
516
517    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
518    let put = StoreArgs {
519        key: file_key.to_string().to_owned(),
520        value: data.as_bytes().to_owned(),
521        total_index: Nat::from(1),
522        file_type: "text/plain".to_string().clone(),
523        total_size: data.len().clone() as u64,
524        is_http_open: is_http_open.clone(),
525        index: Nat::from(0),
526    };
527    let _ = build_agent(pem_identity_path)
528        .update(&canister_id, "store")
529        .with_arg(Encode!(&put).expect("encode piece failed"))
530        .call_and_wait()
531        .await
532        .expect("response error");
533}
534
535/// Get file from icsp, return (data, file_type)
536///
537/// # Examples
538///
539/// ``` no_run
540/// use isp_sdk::icsp;
541/// use std::io::Write;
542/// pub async fn get_file() {
543///     let response = icsp::get_file(
544///         "identities/identity.pem",
545///         "4radi-oqaaa-aaaan-qapwa-cai",
546///         "3166112af0dcc940f8e7f2199a4200cfb5e2efb40796391201b8fe9e4ff7ca84",
547///     )
548///         .await;
549///
550///     let mut file = std::fs::File::create("output/bitcoin.pdf").expect("create failed");
551///     file.write_all(&response.0).expect("write failed");
552///
553///     println!(
554///         "file out put at folder output/ , file_type:{:?}",
555///         response.1
556///     );
557/// }
558/// ```
559pub async fn get_file(
560    pem_identity_path: &str,
561    icsp_canister_id_text: &str,
562    file_key: &str,
563) -> (Vec<u8>, String) {
564    let bucket_canister_id = get_bucket_of_file(pem_identity_path, icsp_canister_id_text, file_key)
565        .await
566        .expect("can not find bucket have this file");
567    let agent = build_agent(pem_identity_path);
568
569    let total_index_blob = agent
570        .update(
571            &candid::Principal::from_text(bucket_canister_id.to_text()).unwrap(),
572            "getFileTotalIndex",
573        )
574        .with_arg(Encode!(&file_key).expect("encode failed"))
575        .call_and_wait()
576        .await
577        .expect("response error");
578    let total_index = Decode!(&total_index_blob, Nat).unwrap();
579    if total_index < Nat::from(1) {
580        return (vec![], "".to_string());
581    }
582    let mut index = 0;
583    let mut payload: Vec<u8> = Vec::new();
584    let mut file_type = "".to_string();
585    while Nat::from(index) < total_index {
586        let response_blob = agent
587            .query(
588                &candid::Principal::from_text(bucket_canister_id.to_text()).unwrap(),
589                "get",
590            )
591            .with_arg(Encode!(&file_key, &Nat::from(index)).expect("encode failed"))
592            .call()
593            .await
594            .expect("response error");
595        let mut response = Decode!(&response_blob, Option<(Vec<u8>, String)>)
596            .unwrap()
597            .expect("assets not have this file");
598        payload.append(&mut response.0);
599        index += 1;
600        if Nat::from(index) == total_index {
601            file_type = response.1;
602        }
603    }
604
605    (payload, file_type)
606}
607
608/// Add admin of icsp
609///
610/// # Examples
611///
612/// ``` no_run
613/// use isp_sdk::icsp;
614///
615/// pub async fn add_icsp_admin() {
616///     icsp::add_icsp_admin(
617///         "identities/identity.pem",
618///         "4radi-oqaaa-aaaan-qapwa-cai",
619///         "bxgws-37y5d-tgmpr-hekbp-y3uxo-yicgs-fo7p3-ccnta-kidrz-74onh-pae",
620///     )
621///         .await
622/// }
623/// ```
624pub async fn add_icsp_admin(
625    pem_identity_path: &str,
626    icsp_canister_id_text: &str,
627    new_admin_text: &str,
628) {
629    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
630    let new_admin = candid::Principal::from_text(new_admin_text).unwrap();
631    let _ = build_agent(pem_identity_path)
632        .update(&canister_id, "addAdmin")
633        .with_arg(Encode!(&new_admin).expect("encode error"))
634        .call_and_wait()
635        .await
636        .expect("response error");
637}
638
639/// Delete admin of icsp
640///
641/// # Examples
642///
643/// ``` no_run
644/// use isp_sdk::icsp;
645///
646/// pub async fn delete_icsp_admin() {
647///     icsp::delete_icsp_admin(
648///         "identities/identity.pem",
649///         "4radi-oqaaa-aaaan-qapwa-cai",
650///         "bxgws-37y5d-tgmpr-hekbp-y3uxo-yicgs-fo7p3-ccnta-kidrz-74onh-pae",
651///     )
652///         .await
653/// }
654/// ```
655pub async fn delete_icsp_admin(
656    pem_identity_path: &str,
657    icsp_canister_id_text: &str,
658    old_admin_text: &str,
659) {
660    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
661    let old_admin = candid::Principal::from_text(old_admin_text).unwrap();
662    let _ = build_agent(pem_identity_path)
663        .update(&canister_id, "deleteAdmin")
664        .with_arg(Encode!(&old_admin).expect("encode error"))
665        .call_and_wait()
666        .await
667        .expect("response error");
668}
669
670/// Top up every bucket some Cycles by using icsp's Cycles
671///
672/// # Examples
673///
674/// ``` no_run
675/// use isp_sdk::icsp;
676///
677/// pub async fn top_up_bucket() {
678///     // 0.1 T Cycles
679///     icsp::top_up_bucket(
680///         "identities/identity.pem",
681///         "4radi-oqaaa-aaaan-qapwa-cai",
682///         100_000_000_000 as u64,
683///     )
684///         .await;
685///     println!("complete top_up_bucket func, top up every bucket 0.1 T Cycles");
686/// }
687/// ```
688pub async fn top_up_bucket(pem_identity_path: &str, icsp_canister_id_text: &str, amount: u64) {
689    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
690    let _ = build_agent(pem_identity_path)
691        .update(&canister_id, "topUpBucket")
692        .with_arg(Encode!(&Nat::from(amount)).expect("encode error"))
693        .call_and_wait()
694        .await
695        .expect("response error");
696}
697
698/// Get ICSP's WASM version
699///
700/// # Examples
701///
702/// ``` no_run
703/// use isp_sdk::icsp;
704///
705/// pub async fn get_icsp_version() {
706///     println!(
707///         "icsp version: {:?}",
708///         icsp::get_version("identities/identity.pem", "4radi-oqaaa-aaaan-qapwa-cai").await
709///     );
710/// }
711/// ```
712pub async fn get_version(pem_identity_path: &str, icsp_canister_id_text: &str) -> String {
713    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
714    let response_blob = build_agent(pem_identity_path)
715        .query(&canister_id, "getVersion")
716        .with_arg(Encode!().expect("encode error"))
717        .call()
718        .await
719        .expect("response error");
720    Decode!(&response_blob, String).unwrap()
721}
722
723/// Query the number of ic files stored in icsp
724///
725/// # Examples
726///
727/// ``` no_run
728/// use isp_sdk::icsp;
729///
730/// pub async fn get_ic_file_numbers(pem_identity_path: &str, icsp_canister_id_text: &str) -> Nat {
731///     println!(
732///         "icsp 's ic file numbers: {:?}",
733///         icsp::get_ic_file_numbers("identities/identity.pem", "4radi-oqaaa-aaaan-qapwa-cai").await
734///     );
735/// }
736/// ```
737pub async fn get_ic_file_numbers(
738    pem_identity_path: &str,
739    icsp_canister_id_text: &str,
740) -> Option<Nat> {
741    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
742    let response_blob = build_agent(pem_identity_path)
743        .query(&canister_id, "getIcFileNums")
744        .with_arg(Encode!().expect("encode error"))
745        .call()
746        .await
747        .expect("response error");
748    Decode!(&response_blob, Option<Nat>).unwrap()
749}
750
751/// Slice all files by page_number and return the information of file_info at page_index
752///
753/// # Examples
754///
755/// ``` no_run
756/// use isp_sdk::icsp;
757///
758/// pub async fn get_field_file_infos() {
759///     let page_num: u64 = 10;
760///     let page_index: u64 = 2;
761///     println!(
762///         "every page have {:?} file_info, query the {:?} page\n",
763///         page_num, page_index
764///     );
765///     let mut index = 0;
766///     for file_info in &icsp::get_field_file_infos(
767///         "identities/identity.pem",
768///         "4radi-oqaaa-aaaan-qapwa-cai",
769///         page_num,
770///         page_index,
771///     )
772///         .await
773///     {
774///         index += 1;
775///         println!("the file_info index: {:?}", index);
776///         println!("bucket_id: {:?}", file_info.bucket_id.to_text());
777///         println!("total_index: {:?}", file_info.total_index);
778///         println!("received chunk_number: {:?}", file_info.received);
779///         println!("wrote_page: {:?}", file_info.wrote_page);
780///         println!("file type: {:?}", file_info.file_type);
781///         println!("is_http_open: {:?}", file_info.is_http_open);
782///         println!("total_size: {:?}", file_info.total_size);
783///         println!("\n");
784///     }
785/// }
786/// ```
787pub async fn get_field_file_infos(
788    pem_identity_path: &str,
789    icsp_canister_id_text: &str,
790    page_number: u64,
791    page_index: u64,
792) -> Vec<FileBufExt> {
793    let canister_id = candid::Principal::from_text(icsp_canister_id_text).unwrap();
794    let response_blob = build_agent(pem_identity_path)
795        .query(&canister_id, "getFieldFileInfos")
796        .with_arg(Encode!(&Nat::from(page_number), &Nat::from(page_index)).expect("encode error"))
797        .call()
798        .await
799        .expect("response error");
800    Decode!(&response_blob, Vec<FileBufExt>).unwrap()
801}
802
803fn get_waiter() -> Delay {
804    let waiter = garcon::Delay::builder()
805        .throttle(std::time::Duration::from_millis(500))
806        .timeout(std::time::Duration::from_secs(60 * 5))
807        .build();
808    waiter
809}
810
811fn build_agent(pem_identity_path: &str) -> Agent {
812    let url = "https://ic0.app".to_string();
813    let identity = Secp256k1Identity::from_pem_file(String::from(pem_identity_path)).unwrap();
814    let transport = ReqwestHttpReplicaV2Transport::create(url).expect("transport error");
815    let agent = Agent::builder()
816        .with_transport(transport)
817        .with_identity(identity)
818        .build()
819        .expect("build agent error");
820    agent
821}
822
823// Access file from file path, slice and return [each slice] array
824fn get_file_from_source(path: &str) -> (usize, Vec<Vec<u8>>) {
825    let context = fs::read(path).expect("read file failed");
826    let size = context.len();
827    let slice_size = if context.len() % UPDATE_SIZE == 0 {
828        context.len() / UPDATE_SIZE
829    } else {
830        context.len() / UPDATE_SIZE + 1
831    };
832    let mut res = Vec::new();
833    for index in 0..slice_size {
834        if index == slice_size - 1 {
835            res.push(context[index * UPDATE_SIZE..context.len()].to_owned())
836        } else {
837            res.push(context[index * UPDATE_SIZE..(index + 1) * UPDATE_SIZE].to_owned())
838        }
839    }
840    (size, res)
841}
842
843fn build_store_args(
844    file_key: String,
845    file_extension: String,
846    total_size: u128,
847    data_slice: &Vec<Vec<u8>>,
848    is_open: bool,
849) -> Vec<StoreArgs> {
850    let mut order = 0;
851    let mut puts = vec![];
852    for data in data_slice {
853        puts.push(StoreArgs {
854            key: file_key.clone(),
855            value: data.to_owned(),
856            total_index: Nat::from(data_slice.len() as u128),
857            file_type: file_extension.clone(),
858            total_size: total_size.clone() as u64,
859            is_http_open: is_open,
860            index: Nat::from(order.clone()),
861        });
862        order += 1;
863    }
864    puts
865}
866
867fn get_file_type(file_type: &str) -> &str {
868    if file_type == "pdf" {
869        return "application/pdf";
870    } else if file_type == "jpg" || file_type == "jpeg" {
871        return "image/jpg";
872    } else if file_type == "png" {
873        return "image/png";
874    } else if file_type == "mp4" {
875        return "video/mp4";
876    } else if file_type == "mp3" {
877        return "audio/mp3";
878    } else if file_type == "gif" {
879        return "image/gif";
880    } else if file_type == "txt" {
881        return "text/plain";
882    } else if file_type == "ppt" || file_type == "pptx" {
883        return "application/vnd.ms-powerpoint";
884    } else if file_type == "html" || file_type == "xhtml" {
885        return "text/html";
886    } else if file_type == "doc" || file_type == "docx" {
887        return "application/msword";
888    } else if file_type == "xls" {
889        return "application/x-xls";
890    } else if file_type == "apk" {
891        return "application/vnd.android.package-archive";
892    } else if file_type == "svg" {
893        return "text/xml";
894    } else if file_type == "wmv" {
895        return "video/x-ms-wmv";
896    } else {
897        return "application/octet-stream";
898    }
899}