Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: to/from json, traits for cid #142

Merged
merged 1 commit into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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