summaryrefslogtreecommitdiffstats
path: root/src/client.rs
blob: a000407a82f4fc2925996a883745ec50f8474676 (plain)
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
use std::io::Cursor;

use anyhow::Result;
use futures::FutureExt;
use futures::TryFutureExt;
use ipfs_api_backend_hyper::IpfsApi;

use crate::cid::Cid;
use crate::cid::TryToCid;
use crate::config::Config;
use crate::ipfs_client::IpfsClient;
use crate::types::Node;
use crate::types::Payload;
use crate::types::DateTime;

pub struct Client {
    ipfs: IpfsClient,
    config: Config,
}

impl std::fmt::Debug for Client {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Client {{ config: {:?} }}", self.config)
    }
}

impl Client {
    pub fn new(ipfs: IpfsClient, config: Config) -> Self {
        Client {
            ipfs,
            config
        }
    }

    pub async fn post_text_blob(&self, text: String) -> Result<Cid> {
        let reader = Cursor::new(text);

        self.ipfs
            .add(reader)
            .await
            .map_err(anyhow::Error::from)
            .and_then(crate::ipfs_client::backend::response::AddResponse::try_to_cid)
    }

    /// Post a text block
    ///
    /// Pass in the parents if there are any.
    ///
    /// # Note
    ///
    /// Does not verify if the `parents` cids point to actual Nodes!
    ///
    /// # Returns
    ///
    /// Returns the Cid of the newly created block, or an error
    pub async fn post_text_block(&self, parents: Vec<Cid>, text: String) -> Result<Cid> {
        self.post_text_block_with_datetime(parents, text, now()).await
    }

    // For testing
    async fn post_text_block_with_datetime(&self, parents: Vec<Cid>, text: String, datetime: DateTime) -> Result<Cid> {
        let text_blob_cid = self.post_text_blob(text).await?;

        let payload = Payload::new(mime::TEXT_PLAIN_UTF_8.as_ref().to_string(), datetime, text_blob_cid);
        let payload_cid = self.post_payload(payload).await?;

        let node = Node::new(crate::consts::protocol_version(), parents, payload_cid);
        self.post_node(node).await
    }

    async fn post_payload(&self, payload: Payload) -> Result<Cid> {
        self.post_serializable(&payload).await
    }

    async fn post_node(&self, node: Node) -> Result<Cid> {
        self.post_serializable(&node).await
    }

    async fn post_serializable<S: serde::Serialize>(&self, s: &S) -> Result<Cid> {
        let payload_s = serde_json::to_string(s)?;
        let payload_c = Cursor::new(payload_s);
        self.ipfs
            .dag_put(payload_c)
            .await
            .map_err(anyhow::Error::from)
            .and_then(crate::ipfs_client::backend::response::DagPutResponse::try_to_cid)
    }
}

fn now() -> DateTime {
    chrono::offset::Utc::now().into()
}

#[cfg(test)]
mod tests {
    use ipfs_api_backend_hyper::TryFromUri;
    use crate::client::Client;
    use crate::config::Config;
    use crate::ipfs_client::IpfsClient;

    #[tokio::test]
    async fn test_post_text_blob() {
        let _ = env_logger::try_init();
        let ipfs  = IpfsClient::from_str("http://localhost:5001").unwrap();
        let config = Config::default();
        let client = Client::new(ipfs, config);

        let cid = client.post_text_blob(String::from("text")).await;
        assert!(cid.is_ok());
        assert_eq!(cid.unwrap().as_ref(), "QmY2T5EfgLn8qWCt8eus6VX1gJuAp1nmUSdmoehgMxznAf");
    }

    #[tokio::test]
    async fn test_post_text_block() {
        use chrono::TimeZone;

        let _ = env_logger::try_init();
        let ipfs  = IpfsClient::from_str("http://localhost:5001").unwrap();
        let config = Config::default();
        let client = Client::new(ipfs, config);

        let datetime = chrono::prelude::Utc.ymd(2021, 11, 27)
            .and_hms(12, 30, 0)
            .into();

        let cid = client.post_text_block_with_datetime(Vec::new(), String::from("text"), datetime).await;
        assert!(cid.is_ok());
        assert_eq!(cid.unwrap().as_ref(), "bafyreifqa7jqsazxvl53jb6sflzbk4nkv4j7b5jos6hlzh4fq55bjbvk3m");
    }

}