semtexzv_ipfsapi/
pin.rs

1use crate::IpfsApi;
2
3use reqwest;
4use serde_json;
5use failure::{Error, err_msg};
6
7#[derive(Deserialize, Debug, PartialEq, Hash)]
8#[serde(rename_all = "PascalCase")]
9pub struct PinResponse {
10    pins: Vec<String>
11}
12
13#[derive(PartialEq)]
14pub enum PinType {
15    Direct,
16    Indirect,
17    Recursive,
18}
19
20pub struct PinnedHash {
21    pub hash: String,
22    pub pin_type: PinType,
23}
24
25impl IpfsApi {
26    /// Tells the IPFS server to pin the given object.
27    /// If 'recursive' is true, it will recursively pin all objects
28    /// that one depends on.
29    /// If 'progress' is true, it will return a percentage(?) progress
30    /// if the object has not been already pinned, or None if it has.
31    pub async fn pin_add(&self, hash: &str, recursive: bool) -> Result<PinResponse, Error> {
32        let mut url = self.get_url()?;
33        url.set_path("api/v0/pin/add");
34        url.query_pairs_mut()
35            .append_pair("arg", hash)
36            .append_pair("recursive", &recursive.to_string())
37            .append_pair("progress", "false");
38
39        Ok(reqwest::get(url).await?.json().await?)
40    }
41
42    /// Unpin the given object.
43    pub async fn pin_rm(&self, hash: &str, recursive: bool) -> Result<PinResponse, Error> {
44        let mut url = self.get_url()?;
45        url.set_path("api/v0/pin/rm");
46        url.query_pairs_mut()
47            .append_pair("arg", hash)
48            .append_pair("recursive", &recursive.to_string());
49
50        Ok(reqwest::get(url).await?.json().await?)
51    }
52
53
54    /// List pinned objects.
55    pub async fn pin_list(&self) -> Result<Vec<PinnedHash>, Error> {
56        let mut url = self.get_url()?;
57        url.set_path("api/v0/pin/ls");
58        let resp: serde_json::Value = reqwest::get(url).await?.json().await?;
59
60        let mut hashes = Vec::new();
61
62        let keys = resp.get("Keys").ok_or(err_msg(""))?.as_object().ok_or(err_msg(""))?;
63
64        for (key, value) in keys.iter() {
65            hashes.push(PinnedHash {
66                hash: key.clone(),
67                pin_type: match &value.get("Type").ok_or(err_msg(""))?.as_str().ok_or(err_msg(""))? {
68                    &"direct" => PinType::Direct,
69                    &"indirect" => PinType::Indirect,
70                    &"recursive" => PinType::Recursive,
71                    _ => PinType::Direct
72                },
73            });
74        }
75
76        Ok(hashes)
77    }
78}
79
80
81#[cfg(test)]
82mod tests {
83    use crate::IpfsApi;
84    use pin::PinType;
85
86    // Add a pin, list it and then remove it.
87    #[test]
88    fn test_pin_full() {
89        let api = IpfsApi::new("127.0.0.1", 5001);
90
91        // Hello world object
92        let hello = "QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u";
93
94        // Unpin everything first
95        for pin in api.pin_list().unwrap() {
96            if pin.pin_type == PinType::Direct || pin.pin_type == PinType::Recursive {
97                api.pin_rm(&pin.hash, true).unwrap();
98            }
99        }
100
101        // Add pin
102        let resp = api.pin_add(hello, false).unwrap();
103
104        // Check if pin is added
105        assert_eq!(resp.pins.len(), 1);
106        assert_eq!(resp.pins[0], "QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u".to_string());
107    }
108}