summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2021-12-04 17:03:09 +0100
committerMatthias Beyer <mail@beyermatthias.de>2021-12-04 21:16:37 +0100
commite5d610270bed47a91ecca8d51adc705bed58cda7 (patch)
tree52a5d63edbbf5f8bbf154757969877bce17c3725 /src
parent3a3b342a4ae5a90003b6ef9faa65ca7f0a025962 (diff)
Rewrite using "ipfs" crate
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Diffstat (limited to 'src')
-rw-r--r--src/cid.rs14
-rw-r--r--src/client.rs103
-rw-r--r--src/ipfs_client.rs20
-rw-r--r--src/main.rs1
-rw-r--r--src/profile.rs144
-rw-r--r--src/types/datetime.rs23
-rw-r--r--src/types/encodable_cid.rs28
-rw-r--r--src/types/mod.rs2
-rw-r--r--src/types/node.rs83
-rw-r--r--src/types/payload.rs58
10 files changed, 248 insertions, 228 deletions
diff --git a/src/cid.rs b/src/cid.rs
index 5aef970..2957cc5 100644
--- a/src/cid.rs
+++ b/src/cid.rs
@@ -21,20 +21,6 @@ pub trait TryToCid {
fn try_to_cid(self) -> Result<Cid>;
}
-impl TryToCid for ipfs_api_backend_hyper::response::AddResponse {
- fn try_to_cid(self) -> Result<Cid> {
- log::debug!("Transforming to CID => {:?}", self);
- string_to_cid(self.hash)
- }
-}
-
-impl TryToCid for ipfs_api_backend_hyper::response::DagPutResponse {
- fn try_to_cid(self) -> Result<Cid> {
- log::debug!("Transforming to CID => {:?}", self);
- string_to_cid(self.cid.cid_string)
- }
-}
-
impl daglib::NodeId for Cid {
}
diff --git a/src/client.rs b/src/client.rs
index f234e33..fd18ec8 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,13 +1,9 @@
-use std::io::Cursor;
+use std::convert::TryFrom;
use anyhow::Result;
-use futures::FutureExt;
-use futures::TryFutureExt;
use futures::TryStreamExt;
-use ipfs_api_backend_hyper::IpfsApi;
+use ipfs::Cid;
-use crate::cid::Cid;
-use crate::cid::TryToCid;
use crate::config::Config;
use crate::ipfs_client::IpfsClient;
use crate::types::Node;
@@ -34,13 +30,10 @@ impl Client {
}
pub async fn post_text_blob(&self, text: String) -> Result<Cid> {
- let reader = Cursor::new(text);
-
self.ipfs
- .add(reader)
+ .put_dag(text.into())
.await
.map_err(anyhow::Error::from)
- .and_then(crate::ipfs_client::backend::response::AddResponse::try_to_cid)
}
/// Post a text node
@@ -70,46 +63,39 @@ impl Client {
}
async fn post_payload(&self, payload: Payload) -> Result<Cid> {
- self.post_serializable(&payload).await
+ self.post(payload).await
}
async fn post_node(&self, node: Node) -> Result<Cid> {
- self.post_serializable(&node).await
+ self.post(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)
+ async fn post<S: Into<ipfs::Ipld>>(&self, s: S) -> Result<Cid> {
+ self.ipfs.put_dag(s.into()).await.map_err(anyhow::Error::from)
}
pub async fn get_node(&self, cid: Cid) -> Result<Node> {
- self.get_deserializeable::<Node>(cid).await
+ self.get::<Node>(cid).await
}
pub async fn get_payload(&self, cid: Cid) -> Result<Payload> {
- self.get_deserializeable::<Payload>(cid).await
+ self.get::<Payload>(cid).await
}
- async fn get_deserializeable<D: serde::de::DeserializeOwned>(&self, cid: Cid) -> Result<D> {
- let bytes = self.ipfs
- .dag_get(cid.as_ref())
- .map_ok(|chunk| chunk.to_vec())
- .try_concat()
+ async fn get<D: TryFrom<ipfs::Ipld, Error = anyhow::Error>>(&self, cid: Cid) -> Result<D> {
+ let ipld = self.ipfs
+ .get_dag(ipfs::IpfsPath::new(ipfs::path::PathRoot::Ipld(cid)))
.await?;
- let s = String::from_utf8(bytes)?;
- serde_json::from_str(&s).map_err(anyhow::Error::from)
+ D::try_from(ipld)
}
pub async fn get_content_text(&self, cid: Cid) -> Result<String> {
+ let starting_point = ipfs::path::IpfsPath::new(ipfs::path::PathRoot::Ipld(cid));
+
let bytes = self.ipfs
- .cat(cid.as_ref())
- .map_ok(|chunk| chunk.to_vec())
+ .cat_unixfs(starting_point, None)
+ .await?
.try_concat()
.await?;
@@ -123,8 +109,10 @@ fn now() -> DateTime {
#[cfg(test)]
mod tests {
- use ipfs_api_backend_hyper::TryFromUri;
- use crate::cid::string_to_cid;
+ use std::convert::TryFrom;
+
+ use cid::Cid;
+
use crate::client::Client;
use crate::config::Config;
use crate::ipfs_client::IpfsClient;
@@ -136,22 +124,32 @@ mod tests {
chrono::prelude::Utc.ymd(y, m, d).and_hms(hr, min, sec).into()
}
+ async fn mk_ipfs() -> IpfsClient {
+ let mut opts = ipfs::IpfsOptions::inmemory_with_generated_keys();
+ opts.mdns = false;
+ let (ipfs, fut): (ipfs::Ipfs<ipfs::Types>, _) = ipfs::UninitializedIpfs::new(opts).start().await.unwrap();
+ tokio::task::spawn(fut);
+ ipfs
+ }
+
#[tokio::test]
async fn test_post_text_blob() {
let _ = env_logger::try_init();
- let ipfs = IpfsClient::from_str("http://localhost:5001").unwrap();
+ let ipfs = mk_ipfs().await;
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");
+ let cid = cid.unwrap();
+ let expected_cid = Cid::try_from("bafyreienmqqpz622nxgi7xvcx2jf7p3lyagqkwcj5ieil3mhx2zckfl35u").unwrap();
+ assert_eq!(cid, expected_cid, "{} != {}", cid, expected_cid);
}
#[tokio::test]
async fn test_post_text_node() {
let _ = env_logger::try_init();
- let ipfs = IpfsClient::from_str("http://localhost:5001").unwrap();
+ let ipfs = mk_ipfs().await;
let config = Config::default();
let client = Client::new(ipfs, config);
@@ -159,13 +157,15 @@ mod tests {
let cid = client.post_text_node_with_datetime(Vec::new(), String::from("text"), datetime).await;
assert!(cid.is_ok());
- assert_eq!(cid.unwrap().as_ref(), "bafyreifah3uwad7vm6o2zz3dsluscbjznmlgrgqqstk3s3djrdyvwgsulq");
+ let cid = cid.unwrap();
+ let expected_cid = Cid::try_from("bafyreifah3uwad7vm6o2zz3dsluscbjznmlgrgqqstk3s3djrdyvwgsulq").unwrap();
+ assert_eq!(cid, expected_cid, "{} != {}", cid, expected_cid);
}
#[tokio::test]
async fn test_post_text_node_roundtrip() {
let _ = env_logger::try_init();
- let ipfs = IpfsClient::from_str("http://localhost:5001").unwrap();
+ let ipfs = mk_ipfs().await;
let config = Config::default();
let client = Client::new(ipfs, config);
@@ -176,7 +176,8 @@ mod tests {
let cid = client.post_text_node_with_datetime(Vec::new(), String::from(text), datetime.clone()).await;
assert!(cid.is_ok());
let cid = cid.unwrap();
- assert_eq!(cid.as_ref(), "bafyreiazg25u4bbymcpebwuadr42lwvhpf7diohojeenmsf3rt42v3kbdy");
+ let expected_cid = Cid::try_from("bafyreiazg25u4bbymcpebwuadr42lwvhpf7diohojeenmsf3rt42v3kbdy").unwrap();
+ assert_eq!(cid, expected_cid, "{} != {}", cid, expected_cid);
let node = client.get_node(cid).await;
assert!(node.is_ok());
@@ -202,7 +203,7 @@ mod tests {
#[tokio::test]
async fn test_post_text_chain() {
let _ = env_logger::try_init();
- let ipfs = IpfsClient::from_str("http://localhost:5001").unwrap();
+ let ipfs = mk_ipfs().await;
let config = Config::default();
let client = Client::new(ipfs, config);
@@ -212,7 +213,7 @@ mod tests {
(mkdate(2021, 11, 27, 12, 32, 0), "text3", "bafyreica4fz6spaiuk3nd6ybfquj3ysn6nlxuoxcd54xblibpirisjlhkm"),
];
- let mut prev: Option<crate::cid::Cid> = None;
+ let mut prev: Option<ipfs::Cid> = None;
for (datetime, text, expected_cid) in chain_elements {
let parents = if let Some(previous) = prev.as_ref() {
vec![previous.clone()]
@@ -223,7 +224,8 @@ mod tests {
let cid = client.post_text_node_with_datetime(parents, String::from(text), datetime.clone()).await;
assert!(cid.is_ok());
let cid = cid.unwrap();
- assert_eq!(cid.as_ref(), expected_cid);
+ let expected_cid = Cid::try_from(expected_cid).unwrap();
+ assert_eq!(cid, expected_cid, "{} != {}", cid, expected_cid);
prev = Some(cid);
}
}
@@ -231,12 +233,12 @@ mod tests {
#[tokio::test]
async fn test_post_text_dag() {
let _ = env_logger::try_init();
- let ipfs = IpfsClient::from_str("http://localhost:5001").unwrap();
+ let ipfs = mk_ipfs().await;
let config = Config::default();
let client = Client::new(ipfs, config);
async fn post_chain(client: &Client, chain_elements: &Vec<(DateTime, &str, &str)>) {
- let mut prev: Option<crate::cid::Cid> = None;
+ let mut prev: Option<ipfs::Cid> = None;
for (datetime, text, expected_cid) in chain_elements {
let parents = if let Some(previous) = prev.as_ref() {
vec![previous.clone()]
@@ -247,7 +249,8 @@ mod tests {
let cid = client.post_text_node_with_datetime(parents, String::from(*text), datetime.clone()).await;
assert!(cid.is_ok());
let cid = cid.unwrap();
- assert_eq!(cid.as_ref(), *expected_cid);
+ let expected_cid = Cid::try_from(*expected_cid).unwrap();
+ assert_eq!(cid, expected_cid, "{} != {}", cid, expected_cid);
prev = Some(cid);
}
}
@@ -277,14 +280,15 @@ mod tests {
let cid = client.post_text_node_with_datetime(Vec::new(), String::from("text6"), mkdate(2021, 11, 27, 12, 32, 0)).await;
assert!(cid.is_ok());
let cid = cid.unwrap();
- assert_eq!(cid.as_ref(), "bafyreihrqhbsmqfkzmbsxvkvwtp4eekeri6m6afejf2wmk6gj64b2qwgsa");
+ let expected_cid = Cid::try_from("bafyreihrqhbsmqfkzmbsxvkvwtp4eekeri6m6afejf2wmk6gj64b2qwgsa").unwrap();
+ assert_eq!(cid, expected_cid, "{} != {}", cid, expected_cid);
let parents = vec![
// latest node in chain_1_elements
- string_to_cid(String::from("bafyreica4fz6spaiuk3nd6ybfquj3ysn6nlxuoxcd54xblibpirisjlhkm")).unwrap(),
+ ipfs::Cid::try_from("bafyreica4fz6spaiuk3nd6ybfquj3ysn6nlxuoxcd54xblibpirisjlhkm").unwrap(),
// latest node in chain_2_elements
- string_to_cid(String::from("bafyreica4fz6spaiuk3nd6ybfquj3ysn6nlxuoxcd54xblibpirisjl2km")).unwrap(),
+ ipfs::Cid::try_from("bafyreica4fz6spaiuk3nd6ybfquj3ysn6nlxuoxcd54xblibpirisjl2km").unwrap(),
// single node "text6"
cid
@@ -293,7 +297,8 @@ mod tests {
let cid = client.post_text_node_with_datetime(parents, String::from("text7"), mkdate(2021, 11, 27, 12, 32, 0)).await;
assert!(cid.is_ok());
let cid = cid.unwrap();
- assert_eq!(cid.as_ref(), "bafyreibnwo6phfbi5m6lzjfiaem4xtvjpeq5nnbsicie6whlnkoakgiyua");
+ let expected_cid = Cid::try_from("bafyreibnwo6phfbi5m6lzjfiaem4xtvjpeq5nnbsicie6whlnkoakgiyua").unwrap();
+ assert_eq!(cid, expected_cid, "{} != {}", cid, expected_cid);
}
}
diff --git a/src/ipfs_client.rs b/src/ipfs_client.rs
index 13d8bde..97af6ff 100644
--- a/src/ipfs_client.rs
+++ b/src/ipfs_client.rs
@@ -1,19 +1 @@
-pub use ipfs_api_backend_hyper as backend;
-pub type IpfsClient = backend::IpfsClient;
-
-
-#[cfg(test)]
-mod tests {
- use ipfs_api_backend_hyper::TryFromUri;
- use super::IpfsClient;
-
- #[test]
- fn test_connect_str() {
- let _ = IpfsClient::from_str("http://localhost:5001").unwrap();
- }
-
- #[test]
- fn test_connect_host_and_port() {
- let _ = IpfsClient::from_host_and_port(http::uri::Scheme::HTTP, "localhost", 5001).unwrap();
- }
-}
+pub type IpfsClient = ipfs::Ipfs<ipfs::Types>;
diff --git a/src/main.rs b/src/main.rs
index f1fdcc0..ef413c6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,5 @@
use anyhow::Result;
-pub mod cid;
pub mod cli;
pub mod client;
pub mod config;
diff --git a/src/profile.rs b/src/profile.rs
index 21ab4e0..1b6f583 100644
--- a/src/profile.rs
+++ b/src/profile.rs
@@ -1,27 +1,62 @@
+use std::path::Path;
use std::path::PathBuf;
use anyhow::Result;
-use ipfs_api_backend_hyper::IpfsApi;
-use tokio::io::AsyncWriteExt;
use tokio::io::AsyncReadExt;
use crate::client::Client;
-use crate::cid::Cid;
+use crate::config::Config;
+use crate::ipfs_client::IpfsClient;
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
+#[derive(Debug)]
pub struct Profile {
- key_name: String,
- key_id: String,
+ client: Client,
}
impl Profile {
- pub async fn create(name: &str, client: &Client) -> Result<Self> {
- let key = client.ipfs.key_gen(name, ipfs_api_backend_hyper::KeyType::Ed25519, 64).await?;
+ pub async fn create(state_dir: &Path, name: &str, config: Config) -> Result<Self> {
+ let bootstrap = vec![]; // TODO
+ let mdns = false; // TODO
+ let keypair = ipfs::Keypair::generate_ed25519();
+ Self::write_to_statedir(state_dir, name, &keypair).await?;
+
+ let options = ipfs::IpfsOptions {
+ ipfs_path: Self::ipfs_path(state_dir, name).await?,
+ keypair,
+ bootstrap,
+ mdns,
+ kad_protocol: None,
+ listening_addrs: vec![],
+ span: Some(tracing::trace_span!("distrox-ipfs")),
+ };
+
+ let (ipfs, fut): (ipfs::Ipfs<_>, _) = ipfs::UninitializedIpfs::<ipfs::Types>::new(options)
+ .start()
+ .await?;
+ tokio::task::spawn(fut);
+ Ok(Self::new(ipfs, config))
+ }
+
+ async fn new_inmemory(config: Config) -> Result<Self> {
+ let mut opts = ipfs::IpfsOptions::inmemory_with_generated_keys();
+ opts.mdns = false;
+ let (ipfs, fut): (ipfs::Ipfs<ipfs::Types>, _) = ipfs::UninitializedIpfs::new(opts).start().await.unwrap();
+ tokio::task::spawn(fut);
+ Ok(Self::new(ipfs, config))
+ }
+
+ fn new(ipfs: IpfsClient, config: Config) -> Self {
+ Profile { client: Client::new(ipfs, config) }
+ }
- Ok(Profile {
- key_name: key.name,
- key_id: key.id
- })
+ async fn write_to_statedir(_state_dir: &Path, _name: &str, _keypair: &ipfs::Keypair) -> Result<()> {
+ unimplemented!()
+ }
+
+ async fn ipfs_path(state_dir: &Path, name: &str) -> Result<PathBuf> {
+ let path = state_dir.join(name).join("ipfs");
+ tokio::fs::create_dir_all(&path).await?;
+ Ok(path)
}
pub fn config_path(name: &str) -> String {
@@ -38,62 +73,13 @@ impl Profile {
})
}
- /// Store the Profile on disk
- pub async fn write_to_filesystem(&self) -> Result<()> {
- let config_path = Self::config_file_path(&self.key_name)?;
-
- let mut config_file = tokio::fs::OpenOptions::new()
- .write(true)
- .create(true)
- .truncate(true)
- .open(config_path)
- .await?;
-
- let config = serde_json::to_string(&self)?;
- config_file.write_all(config.as_bytes()).await?;
- config_file.sync_all().await?;
- Ok(())
- }
-
/// Load the Profile from disk and ensure the keys exist in IPFS
- pub async fn load_from_filesystem(name: &str, client: &Client) -> Result<Option<Self>> {
- let config_path = Self::config_file_path(name)?;
- let file_reader = tokio::fs::OpenOptions::new()
- .read(true)
- .open(config_path)
- .await
- .map(tokio::io::BufReader::new)?;
-
- Self::load_from_reader(file_reader, name, client).await
- }
-
- async fn load_from_reader<R: AsyncReadExt + std::marker::Unpin>(mut r: R, name: &str, client: &Client) -> Result<Option<Self>> {
- let mut buf = String::new();
- let _ = r.read_to_string(&mut buf).await?;
- let config: Self = serde_json::from_str(&buf)?;
-
- client.ipfs
- .key_list()
- .await?
- .keys
- .into_iter()
- .find(|keypair| keypair.name == name)
- .map(|_| Ok(config))
- .transpose()
+ pub async fn load_from_filesystem(_name: &str, _client: &Client) -> Result<Option<Self>> {
+ unimplemented!()
}
- pub async fn publish(&self, client: &Client, cid: Cid) -> Result<()> {
- let path = format!("/ipfs/{}", cid.as_ref());
- let resolve = true;
- let lifetime = Some("10m");
- let ttl = None;
-
- let publish_response = client.ipfs
- .name_publish(&path, resolve, lifetime, ttl, Some(&self.key_name))
- .await?;
-
- log::debug!("Publish response = {{ name: {}, value: {} }}", publish_response.name, publish_response.value);
- Ok(())
+ async fn load_from_reader<R: AsyncReadExt + std::marker::Unpin>(_r: R, _name: &str, _client: &Client) -> Result<Option<Self>> {
+ unimplemented!()
}
}
@@ -105,35 +91,11 @@ mod tests {
use crate::config::Config;
use crate::ipfs_client::IpfsClient;
- use ipfs_api_backend_hyper::TryFromUri;
-
- async fn mk_client() -> Client {
- let ipfs = IpfsClient::from_str("http://localhost:5001").unwrap();
- let config = Config::default();
- Client::new(ipfs, config)
- }
-
- macro_rules! run_test {
- ($name:ident, $client:ident, $test:block) => {
- $client.ipfs.key_rm($name).await;
- {
- $test
- }
- $client.ipfs.key_rm($name).await;
- }
- }
-
#[tokio::test]
async fn test_create_profile() {
let _ = env_logger::try_init();
- let client = mk_client().await;
- let name = "test_create_profile";
- run_test!(name, client,
- {
- let p = Profile::create(name, &client).await;
- assert!(p.is_ok());
- }
- );
+ let profile = Profile::new_inmemory(Config::default()).await;
+ assert!(profile.is_ok());
}
}
diff --git a/src/types/datetime.rs b/src/types/datetime.rs
index 6735731..00d739a 100644
--- a/src/types/datetime.rs
+++ b/src/types/datetime.rs
@@ -1,9 +1,32 @@
+use std::convert::TryFrom;
use anyhow::Error;
+use anyhow::Result;
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct DateTime(chrono::DateTime<chrono::Utc>);
+impl Into<ipfs::Ipld> for DateTime {
+ fn into(self) -> ipfs::Ipld {
+ ipfs::Ipld::String(self.0.to_rfc3339())
+ }
+}
+
+impl TryFrom<ipfs::Ipld> for DateTime {
+ type Error = Error;
+
+ fn try_from(ipld: ipfs::Ipld) -> Result<DateTime> {
+ match ipld {
+ ipfs::Ipld::String(s) => chrono::DateTime::parse_from_rfc3339(&s)
+ .map(|dt| dt.with_timezone(&chrono::Utc))
+ .map(DateTime)
+ .map_err(Error::from),
+ _ => anyhow::bail!("Expected string for timestamp"),
+ }
+ }
+}
+
+
impl From<chrono::DateTime<chrono::Utc>> for DateTime {
fn from(dt: chrono::DateTime<chrono::Utc>) -> Self {
DateTime(dt)
diff --git a/src/types/encodable_cid.rs b/src/types/encodable_cid.rs
deleted file mode 100644
index 014d661..0000000
--- a/src/types/encodable_cid.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-use std::collections::HashMap;
-
-/// An DAG-JSON encodable cid
-///
-/// this is a hack. DAG-JSON expects a linked CID to be of the form
-///
-/// "/": "<cid hash>"
-///
-/// (see https://ipld.io/docs/codecs/known/dag-json/)
-///
-/// so we have a wrapper type here to make the CID encodable
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
-pub struct EncodableCid(HashMap<String, crate::cid::Cid>);
-
-impl From<crate::cid::Cid> for EncodableCid {
- fn from(cid: crate::cid::Cid) -> Self {
- let mut hm = HashMap::new();
- hm.insert(String::from("/"), cid);
- Self(hm)
- }
-}
-
-impl Into<crate::cid::Cid> for EncodableCid {
- fn into(self) -> crate::cid::Cid {
- self.0.get("/").unwrap().clone()
- }
-}
-
diff --git a/src/types/mod.rs b/src/types/mod.rs
index 228c363..7382f16 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -1,5 +1,3 @@
-mod encodable_cid;
-
mod node;
pub use node::*;
diff --git a/src/types/node.rs b/src/types/node.rs
index aead32c..eb5679b 100644
--- a/src/types/node.rs
+++ b/src/types/node.rs
@@ -1,43 +1,88 @@
-#[derive(Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, getset::Getters)]
+use anyhow::Result;
+
+use std::convert::TryFrom;
+
+#[derive(Debug, Eq, PartialEq, getset::Getters)]
pub struct Node {
/// Version
- #[serde(rename = "v")]
#[getset(get = "pub")]
version: String,
/// Parent Nodes, identified by cid
- parents: Vec<crate::types::encodable_cid::EncodableCid>,
+ parents: Vec<ipfs::Cid>,
/// The actual payload of the node, which is stored in another document identified by this cid
- payload: crate::types::encodable_cid::EncodableCid,
+ payload: ipfs::Cid,
+}
+
+impl Into<ipfs::Ipld> for Node {
+ fn into(self) -> ipfs::Ipld {
+ let mut map = std::collections::BTreeMap::new();
+ map.insert(String::from("version"), ipfs::Ipld::String(self.version));
+ map.insert(String::from("parents"), ipfs::Ipld::List(self.parents.into_iter().map(ipfs::Ipld::Link).collect()));
+ map.insert(String::from("payload"), ipfs::Ipld::Link(self.payload));
+ ipfs::Ipld::Map(map)
+ }
}
-impl daglib::Node for Node {
- type Id = crate::cid::Cid;
+impl TryFrom<ipfs::Ipld> for Node {
+ type Error = anyhow::Error;
- fn parent_ids(&self) -> Vec<Self::Id> {
- self.parents()
+ fn try_from(ipld: ipfs::Ipld) -> Result<Self> {
+ let missing_field = |name: &'static str| move || anyhow::anyhow!("Missing field {}", name);
+ let field_wrong_type = |name: &str, expty: &str| anyhow::bail!("Field {} has wrong type, expected {}", name, expty);
+ match ipld {
+ ipfs::Ipld::Map(map) => {
+ let version = match map.get("version").ok_or_else(missing_field("version"))? {
+ ipfs::Ipld::String(s) => s.to_string(),
+ _ => return field_wrong_type("version", "String")
+ };
+
+ let parents = match map.get("parents").ok_or_else(missing_field("parents"))? {
+ ipfs::Ipld::List(s) => {
+ s.into_iter()
+ .map(|parent| -> Result<ipfs::Cid> {
+ match parent {
+ ipfs::Ipld::Link(cid) => Ok(cid.clone()),
+ _ => anyhow::bail!("Field in parents has wrong type, expected Link"),
+ }
+ })
+ .collect::<Result<Vec<ipfs::Cid>>>()?
+ },
+ _ => return field_wrong_type("parents", "Vec<Link>")
+ };
+
+ let payload = match map.get("payload").ok_or_else(missing_field("payload"))? {
+ ipfs::Ipld::Link(cid) => cid.clone(),
+ _ => return field_wrong_type("payload", "Link")
+ };
+
+ Ok(Node {
+ version,
+ parents,
+ payload
+ })
+ }
+
+ _ => anyhow::bail!("Unexpected type, expected map")
+ }
}
}
impl Node {
- pub fn new(version: String, parents: Vec<crate::cid::Cid>, payload: crate::cid::Cid) -> Self {
+ pub fn new(version: String, parents: Vec<ipfs::Cid>, payload: ipfs::Cid) -> Self {
Self {
version,
- parents: parents.into_iter().map(crate::types::encodable_cid::EncodableCid::from).collect(),
- payload: payload.into()
+ parents,
+ payload,
}
}
- pub fn parents(&self) -> Vec<crate::cid::Cid> {
- self.parents
- .clone()
- .into_iter()
- .map(crate::types::encodable_cid::EncodableCid::into)
- .collect()
+ pub fn parents(&self) -> Vec<ipfs::Cid> {
+ self.parents.clone()
}
- pub fn payload(&self) -> crate::cid::Cid {
- self.payload.clone().into()
+ pub fn payload(&self) -> ipfs::Cid {
+ self.payload.clone()
}
}
diff --git a/src/types/payload.rs b/src/types/payload.rs
index cb12845..a11b215 100644
--- a/src/types/payload.rs
+++ b/src/types/payload.rs
@@ -1,6 +1,10 @@
+use std::convert::TryFrom;
+
+use anyhow::Result;
+
use crate::types::DateTime;
-#[derive(Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, getset::Getters)]
+#[derive(Debug, Eq, PartialEq, getset::Getters)]
pub struct Payload {
// TODO: Make this a mime::Mime, but as this type does not impl Serialize/Deserialize, we
// cannot do this trivially yet
@@ -10,15 +14,59 @@ pub struct Payload {
#[getset(get = "pub")]
timestamp: DateTime,
- content: crate::types::encodable_cid::EncodableCid,
+ content: ipfs::Cid,
+}
+
+impl Into<ipfs::Ipld> for Payload {
+ fn into(self) -> ipfs::Ipld {
+ let mut map = std::collections::BTreeMap::new();
+ map.insert(String::from("mime"), ipfs::Ipld::String(self.mime));
+ map.insert(String::from("timestamp"), self.timestamp.into());
+ map.insert(String::from("content"), ipfs::Ipld::Link(self.content));
+ ipfs::Ipld::Map(map)
+ }
+}
+
+impl TryFrom<ipfs::Ipld> for Payload {
+ type Error = anyhow::Error;
+
+ fn try_from(ipld: ipfs::Ipld) -> Result<Self> {
+ let missing_field = |name: &'static str| move || anyhow::anyhow!("Missing field {}", name);
+ let field_wrong_type = |name: &str, expty: &str| anyhow::bail!("Field {} has wrong type, expected {}", name, expty);
+ match ipld {
+ ipfs::Ipld::Map(map) => {
+ let mime = match map.get("mime").ok_or_else(missing_field("mime"))? {
+ ipfs::Ipld::String(s) => s.to_owned(),
+ _ => return field_wrong_type("mime", "String")
+ };
+
+ let timestamp = map.get("timestamp")
+ .ok_or_else(missing_field("timestamp"))?;
+ let timestamp = DateTime::try_from(timestamp.clone())?; // TODO dont clone
+
+ let content = match map.get("content").ok_or_else(missing_field("content"))? {
+ ipfs::Ipld::Link(cid) => cid.clone(),
+ _ => return field_wrong_type("content", "Link")
+ };
+
+ Ok(Payload {
+ mime,
+ timestamp,
+ content
+ })
+ },
+
+ _ => anyhow::bail!("Unexpected type, expected map"),
+ }
+ }
}
impl Payload {
- pub fn new(mime: String, timestamp: DateTime, content: crate::cid::Cid) -> Self {
+ pub fn new(mime: String, timestamp: DateTime, content: ipfs::Cid) -> Self {
Self { mime, timestamp, content: content.into() }
}
- pub fn content(&self) -> crate::cid::Cid {
- self.content.clone().into()
+ pub fn content(&self) -> ipfs::Cid {
+ self.content.clone()
}
}