Skip to content

Commit

Permalink
refactor: to/from json, traits for cid (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeeshan Lakhani committed Jun 16, 2023
1 parent a4246a8 commit fc53a42
Show file tree
Hide file tree
Showing 32 changed files with 519 additions and 703 deletions.
565 changes: 109 additions & 456 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ rust-version = "1.66.0"

[workspace.dependencies]
anyhow = { version = "1.0", features = ["backtrace"] }
enum-assoc = " 1.1"
enum-as-inner = "0.6"
thiserror = "1.0"
tokio = { version = "1.26", features = ["fs", "io-util", "io-std", "macros", "rt", "rt-multi-thread"] }
tracing = "0.1"
Expand Down
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
'';

xFuncTest = pkgs.writeScriptBin "x-test" ''
cargo watch -c -s "cargo nextest run && cargo test --doc"
cargo watch -c -s "cargo nextest run --nocapture && cargo test --doc"
'';

xFuncTestAll = pkgs.writeScriptBin "x-test-all" ''
Expand Down
17 changes: 9 additions & 8 deletions homestar-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,24 @@ doctest = true
# https://github.com/DevinR528/cargo-sort/issues/47
anyhow = { workspace = true }
diesel = { version = "2.0", features = ["sqlite"] }
enum-as-inner = "0.5"
enum-assoc = "0.4"
generic-array = "0.14"
enum-as-inner = { workspace = true }
enum-assoc = { workspace = true }
generic-array = { version = "0.14", features = ["serde"] }
indexmap = "1.9"
libipld = "0.16"
libipld = { version = "0.16", features = ["serde-codec"] }
libsqlite3-sys = { version = "0.26", features = ["bundled"] }
proptest = { version = "1.1", optional = true }
proptest = { version = "1.2", optional = true }
serde = { version = "1.0", features = ["derive"] }
signature = "2.0"
thiserror = { workspace = true }
tracing = { workspace = true }
ucan = "0.1"
url = "2.3"
ucan = "0.3"
url = { version = "2.3", features = ["serde"] }
uuid = { version = "1.3", default-features = false, features = ["v4", "fast-rng"] }
xid = "1.0"

[dev-dependencies]
criterion = "0.4"
criterion = "0.5"
json = "0.12"

[features]
Expand Down
45 changes: 45 additions & 0 deletions homestar-core/src/ipld/dag_cbor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Traits related to [Ipld] and [DagCbor] encoding/decoding.
//!
//! [DagCbor]: DagCborCodec

use crate::{consts::DAG_CBOR, workflow::Error, Unit};
use libipld::{
cbor::DagCborCodec,
multihash::{Code, MultihashDigest},
prelude::Codec,
Cid, Ipld,
};

/// Trait for [DagCbor]-related encode/decode.
///
/// [DagCbor]: DagCborCodec
pub trait DagCbor
where
Self: Sized,
Ipld: From<Self>,
{
/// Performs the conversion from an owned `Self` to [Cid].
fn to_cid(self) -> Result<Cid, Error<Unit>> {
let ipld: Ipld = self.into();
let bytes = DagCborCodec.encode(&ipld)?;
let hash = Code::Sha3_256.digest(&bytes);
Ok(Cid::new_v1(DAG_CBOR, hash))
}
}

/// Trait for [DagCbor]-related encode/decode for references.
///
/// [DagCbor]: DagCborCodec
pub trait DagCborRef
where
Self: Sized,
for<'a> Ipld: From<&'a Self>,
{
/// Performs the conversion from a referenced `Self` to [Cid].
fn to_cid(&self) -> Result<Cid, Error<Unit>> {
let ipld: Ipld = self.into();
let bytes = DagCborCodec.encode(&ipld)?;
let hash = Code::Sha3_256.digest(&bytes);
Ok(Cid::new_v1(DAG_CBOR, hash))
}
}
47 changes: 47 additions & 0 deletions homestar-core/src/ipld/dag_json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Traits related to [Ipld] and [DagJson] encoding/decoding.
//!
//! [DagJson]: DagJsonCodec

use crate::{workflow::Error, Unit};
use libipld::{codec::Decode, json::DagJsonCodec, prelude::Codec, Ipld};
use std::io::Cursor;

/// Trait for serializing and deserializing types to and from JSON.
pub trait DagJson
where
Self: TryFrom<Ipld> + Clone,
Ipld: From<Self>,
{
/// Serialize `Self` type to JSON bytes.
fn to_json(&self) -> Result<Vec<u8>, Error<Unit>> {
let ipld: Ipld = self.to_owned().into();
Ok(DagJsonCodec.encode(&ipld)?)
}

/// Serialize `Self` type to JSON [String].
fn to_json_string(&self) -> Result<String, Error<Unit>> {
let encoded = self.to_json()?;
// JSON spec requires UTF-8 support
let s = std::str::from_utf8(&encoded)?;
Ok(s.to_string())
}

/// Deserialize `Self` type from JSON bytes.
fn from_json(data: &[u8]) -> Result<Self, Error<Unit>> {
let ipld = Ipld::decode(DagJsonCodec, &mut Cursor::new(data))?;
let from_ipld = Self::try_from(ipld).map_err(|_err| {
// re-decode with an unwrap, without a clone, as we know the data is
// valid JSON.
Error::<Unit>::UnexpectedIpldTypeError(
Ipld::decode(DagJsonCodec, &mut Cursor::new(data)).unwrap(),
)
})?;
Ok(from_ipld)
}

/// Deserialize `Self` type from JSON [String].
fn from_json_string(json: String) -> Result<Self, Error<Unit>> {
let data = json.as_bytes();
Self::from_json(data)
}
}
122 changes: 122 additions & 0 deletions homestar-core/src/ipld/link.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! Typed cid for custom links.
//!
//! Extracted from [libipld::Link] to allow for custom de/serialization on
//! custom types.

use libipld::{
codec::{Codec, Decode, Encode},
error, Cid,
};
use serde::{Deserialize, Serialize};
use std::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
io::{Read, Seek, Write},
marker::PhantomData,
ops::Deref,
};

/// Typed cid.
#[derive(Debug, Serialize, Deserialize)]
#[repr(transparent)]
pub struct Link<T> {
cid: Cid,
_marker: PhantomData<T>,
}

impl<T> Link<T> {
/// Creates a new `Link`.
pub fn new(cid: Cid) -> Self {
Self {
cid,
_marker: PhantomData,
}
}

/// Returns a reference to the cid.
pub fn cid(&self) -> &Cid {
&self.cid
}
}

impl<T> fmt::Display for Link<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.cid.fmt(f)
}
}

impl<T> Clone for Link<T> {
fn clone(&self) -> Self {
Self {
cid: self.cid,
_marker: self._marker,
}
}
}

impl<T> Copy for Link<T> {}

impl<T> PartialEq for Link<T> {
fn eq(&self, other: &Self) -> bool {
self.cid.eq(other.cid())
}
}

impl<T> Eq for Link<T> {}

impl<T> PartialOrd for Link<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cid.cmp(other.cid()))
}
}

impl<T> Ord for Link<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.cid.cmp(other.cid())
}
}

impl<T> Hash for Link<T> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
Hash::hash(self.cid(), hasher)
}
}

impl<C: Codec, T> Encode<C> for Link<T>
where
Cid: Encode<C>,
{
fn encode<W: Write>(&self, c: C, w: &mut W) -> error::Result<()> {
self.cid().encode(c, w)
}
}

impl<C: Codec, T> Decode<C> for Link<T>
where
Cid: Decode<C>,
{
fn decode<R: Read + Seek>(c: C, r: &mut R) -> error::Result<Self> {
Ok(Self::new(Cid::decode(c, r)?))
}
}

impl<T> Deref for Link<T> {
type Target = Cid;

fn deref(&self) -> &Self::Target {
self.cid()
}
}

impl<T> AsRef<Cid> for Link<T> {
fn as_ref(&self) -> &Cid {
self.cid()
}
}

impl<T> From<Cid> for Link<T> {
fn from(cid: Cid) -> Self {
Self::new(cid)
}
}
9 changes: 9 additions & 0 deletions homestar-core/src/ipld/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! [libipld::Ipld] customization and extensions.

mod dag_cbor;
mod dag_json;
mod link;

pub use dag_cbor::*;
pub use dag_json::*;
pub use link::*;
3 changes: 2 additions & 1 deletion homestar-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
//! [Ucan invocation]: <https://github.com/ucan-wg/invocation>

pub mod consts;
pub mod ipld;
pub mod macros;
#[cfg(any(test, feature = "test_utils"))]
#[cfg_attr(docsrs, doc(cfg(feature = "test_utils")))]
pub mod test_utils;
mod unit;

pub mod workflow;

pub use consts::*;
pub use unit::*;
pub use workflow::Workflow;
2 changes: 1 addition & 1 deletion homestar-core/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ macro_rules! bail {
};
}

/// /// Return early with an error if a condition is not satisfied.
/// Return early with an error if a condition is not satisfied.
///
/// Analogously to `assert!`, `ensure!` takes a condition and exits the function
/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error`
Expand Down
15 changes: 9 additions & 6 deletions homestar-core/src/test_utils/workflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
//!
//! [workflow]: crate::workflow

use crate::workflow::{
pointer::{Await, AwaitResult},
prf::UcanPrf,
Ability, Input, Instruction, InstructionResult, Nonce, Pointer, Receipt,
use crate::{
ipld::DagCbor,
workflow::{
pointer::{Await, AwaitResult},
prf::UcanPrf,
Ability, Input, Instruction, InstructionResult, Nonce, Pointer, Receipt,
},
};
use libipld::{
cid::Cid,
Expand Down Expand Up @@ -55,7 +58,7 @@ where
);

let promise = Await::new(
Pointer::new(Cid::try_from(instr.clone()).unwrap()),
Pointer::new(instr.clone().to_cid().unwrap()),
AwaitResult::Ok,
);

Expand All @@ -72,7 +75,7 @@ where
);

let another_promise = Await::new(
Pointer::new(Cid::try_from(dep_instr1.clone()).unwrap()),
Pointer::new(dep_instr1.clone().to_cid().unwrap()),
AwaitResult::Ok,
);

Expand Down
3 changes: 2 additions & 1 deletion homestar-core/src/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ use crate::workflow::{
Error, Input,
};
use libipld::Ipld;
use serde::{Deserialize, Serialize};

/// Unit type, which allows only one value (and thusly holds
/// no information). Essentially a wrapper over `()`, but one we control.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Unit;

impl From<Unit> for Ipld {
Expand Down
Loading

0 comments on commit fc53a42

Please sign in to comment.