summaryrefslogtreecommitdiffstats
path: root/lib/src/types
diff options
context:
space:
mode:
Diffstat (limited to 'lib/src/types')
-rw-r--r--lib/src/types/datetime.rs35
-rw-r--r--lib/src/types/mod.rs8
-rw-r--r--lib/src/types/node.rs88
-rw-r--r--lib/src/types/payload.rs72
4 files changed, 203 insertions, 0 deletions
diff --git a/lib/src/types/datetime.rs b/lib/src/types/datetime.rs
new file mode 100644
index 0000000..00d739a
--- /dev/null
+++ b/lib/src/types/datetime.rs
@@ -0,0 +1,35 @@
+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/lib/src/types/mod.rs b/lib/src/types/mod.rs
new file mode 100644
index 0000000..7382f16
--- /dev/null
+++ b/lib/src/types/mod.rs
@@ -0,0 +1,8 @@
+mod node;
+pub use node::*;
+
+mod datetime;
+pub use datetime::*;
+
+mod payload;
+pub use payload::*;
diff --git a/lib/src/types/node.rs b/lib/src/types/node.rs
new file mode 100644
index 0000000..eb5679b
--- /dev/null
+++ b/lib/src/types/node.rs
@@ -0,0 +1,88 @@
+use anyhow::Result;
+
+use std::convert::TryFrom;
+
+#[derive(Debug, Eq, PartialEq, getset::Getters)]
+pub struct Node {
+ /// Version
+ #[getset(get = "pub")]
+ version: String,
+
+ /// Parent Nodes, identified by cid
+ parents: Vec<ipfs::Cid>,
+
+ /// The actual payload of the node, which is stored in another document identified by this cid
+ 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 TryFrom<ipfs::Ipld> for Node {
+ 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 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<ipfs::Cid>, payload: ipfs::Cid) -> Self {
+ Self {
+ version,
+ parents,
+ payload,
+ }
+ }
+
+ pub fn parents(&self) -> Vec<ipfs::Cid> {
+ self.parents.clone()
+ }
+
+ pub fn payload(&self) -> ipfs::Cid {
+ self.payload.clone()
+ }
+}
diff --git a/lib/src/types/payload.rs b/lib/src/types/payload.rs
new file mode 100644
index 0000000..a11b215
--- /dev/null
+++ b/lib/src/types/payload.rs
@@ -0,0 +1,72 @@
+use std::convert::TryFrom;
+
+use anyhow::Result;
+
+use crate::types::DateTime;
+
+#[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
+ #[getset(get = "pub")]
+ mime: String,
+
+ #[getset(get = "pub")]
+ timestamp: DateTime,
+
+ 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: ipfs::Cid) -> Self {
+ Self { mime, timestamp, content: content.into() }
+ }
+
+ pub fn content(&self) -> ipfs::Cid {
+ self.content.clone()
+ }
+}