-
Notifications
You must be signed in to change notification settings - Fork 10
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
feat: add Codec trait #5
Conversation
So, one annoying thing about this as it doesn't support "views". I.e., types I can only "encode" into IPLD, but can't "decode" IPLD into (e.g., types that borrow their values). But I guess I can probably live with that? One option would be to add separate encode/decode traits, but I'm not entirely sure how to combine them. |
} | ||
|
||
/// Trait for returning the links of a serialized IPLD data. | ||
pub trait Links { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not Links<T>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I wonder if we want a relationship with Codec<T>
? Are there any codecs where (a) we support links but (b) don't actually support enumerating them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a separate trait, so it doesn't have to be <T>
. The idea is that you can extract links out of the encoded data. If it would be <T>
, then it could/would be part of the Codec
trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, makes sense. You don't care about T.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm.
- It's kind of odd that
Links
doesn't have the code. But I don't have a great solution. - It would be kind of nice to have
Codec<T>: Links
. That way if I take a codec asC: Codec<T>
I also, getlinks
.
src/codec.rs
Outdated
/// Decode a slice into the desired type. | ||
fn decode(bytes: &[u8]) -> Result<T, Self::Error>; | ||
/// Encode a type into bytes. | ||
fn encode(obj: &T) -> Result<Vec<u8>, Self::Error>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... can we define encode_to_vec
(with a default implementation) and encode(obj, writer)
, or something like that? Having to allocate is annoying.
This comment was marked as off-topic.
This comment was marked as off-topic.
So... I'm not 100% sure this is a good idea but... GATs may be the answer here: use std::marker::PhantomData;
// stubs
struct Cid;
trait Serialize {}
trait Deserialize {}
trait Codec {
const CODE: u64;
type Error;
// encoder/decoder can be implemented independently.
type Encoder<T>;
type Decoder<T>;
fn encode<T>(value: &T) -> Result<Vec<u8>, Self::Error>
where
Self::Encoder<T>: Encode<T, Error = Self::Error>,
{
Self::Encoder::<T>::encode(value)
}
fn decode<T>(bytes: &[u8]) -> Result<T, Self::Error>
where
Self::Decoder<T>: Decode<T, Error = Self::Error>,
{
Self::Decoder::<T>::decode(bytes)
}
// links can go on the codec itself!
fn links(bytes: &[u8]) -> Result<Vec<Cid>, Self::Error>;
}
trait Encode<T> {
type Error;
fn encode(value: &T) -> Result<Vec<u8>, Self::Error>;
}
trait Decode<T> {
type Error;
fn decode(bytes: &[u8]) -> Result<T, Self::Error>;
}
struct CborCoder<T>(PhantomData<T>);
impl<T> Encode<T> for CborCoder<T>
where
T: Serialize,
{
type Error = ();
fn encode(_: &T) -> Result<Vec<u8>, Self::Error> {
todo!("implement")
}
}
impl<T> Decode<T> for CborCoder<T>
where
T: Deserialize,
{
type Error = ();
fn decode(_: &[u8]) -> Result<T, Self::Error> {
todo!("implement")
}
}
struct DagCbor;
impl Codec for DagCbor {
const CODE: u64 = 0x71;
type Error = ();
type Encoder<T> = CborCoder<T>;
type Decoder<T> = CborCoder<T>;
fn links(bytes: &[u8]) -> Result<Vec<Cid>, Self::Error> {
todo!()
}
} |
Ah, actually, there's no reason |
Yeah, actually, this works pretty great because we can create a single helper struct for serde-based encoding: use std::marker::PhantomData;
struct Cid;
trait Serialize {}
trait Deserialize {}
trait Codec {
const CODE: u64;
type Error;
type Encoding<T>;
fn encode<T>(value: &T) -> Result<Vec<u8>, Self::Error>
where
Self::Encoding<T>: Encode<T, Error = Self::Error>,
{
Self::Encoding::<T>::encode(value)
}
fn decode<T>(bytes: &[u8]) -> Result<T, Self::Error>
where
Self::Encoding<T>: Decode<T, Error = Self::Error>,
{
Self::Encoding::<T>::decode(bytes)
}
fn links(bytes: &[u8]) -> Result<Vec<Cid>, Self::Error>;
}
trait Encode<T> {
type Error;
fn encode(value: &T) -> Result<Vec<u8>, Self::Error>;
}
trait Decode<T> {
type Error;
fn decode(bytes: &[u8]) -> Result<T, Self::Error>;
}
struct SerdeEncoding<T>(PhantomData<T>);
impl<T> Encode<T> for SerdeEncoding<T>
where
T: Serialize,
{
type Error = ();
fn encode(_: &T) -> Result<Vec<u8>, Self::Error> {
todo!("implement")
}
}
impl<T> Decode<T> for SerdeEncoding<T>
where
T: Deserialize,
{
type Error = ();
fn decode(_: &[u8]) -> Result<T, Self::Error> {
todo!("implement")
}
}
struct DagCbor;
impl Codec for DagCbor {
const CODE: u64 = 0x71;
type Error = ();
type Encoding<T> = SerdeEncoding<T>;
fn links(bytes: &[u8]) -> Result<Vec<Cid>, Self::Error> {
todo!()
}
} |
The downside is that this slightly complicates generic use of codecs. I.e., with your current fn do_thing<C, T>(value: T)
where
C: Codec<T>,
{
...
} With the GAT approach, I need to write: fn do_thing<C, T>(value: T)
where
C: Codec,
C::Encoding<T>: Encoding<T>,
{
...
} Assuming I have some trait Encoding<T>: Encode<T> + Decode<T> {}
impl<C, T> Encoding<T> for C where C: Encode<T> + Decode<T> {} |
Thanks @Stebalien for looking into GATs. For me that's exactly what I wanted to try out, but I didn't find how to do it. Nonetheless, to me it seems like doing the I've pushed to branches to
So I would go with the " |
Yeah, |
But I have no idea what's going on with that error, it seems like a rust bug. |
Ok, I've pushed new commits which address:
I've implemented that new version of the |
With the `Codec` trait it's now possible to have a unified API to encode and decode data across different codecs. Th accompanying `Links` trait allows to extract all the links (CIDs) from some encoded IPLD data.
This way a `Codec` must always implement the `Links` trait as well.
Return in iterator instead of a vector.
Instead of passing in slices and returning vectors, pass in readers and writers instead. Add default implementation for a slice/vector based API.
The last push was just a proper rebase (no idea why the automatic didn't work as expected). I'll now squash merge it. |
/// easily exchanged or combined. | ||
pub trait Codec<T>: Links { | ||
/// The multicodec code of the IPLD codec. | ||
const CODE: u64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're going to want NAME
eventually too, it's pretty helpful. This trait is almost the same as the interfaces we landed on in JS, although we ended up splitting encode and decode operations into two parts and combining them into a single BlockCodec
: https://github.com/multiformats/js-multiformats/blob/master/src/codecs/interface.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want the name. People should really use the constants as identifiers and not strings that might change.
Splitting encode and decode: I'd keep it simple for now, if the need of splitting ever occurs (which I currently doubt), we can do it then.
With the
Codec
trait it's now possible to have a unified API to encode and decode data across different codecs.Th accompanying
Links
trait allows to extract all the links (CIDs) from some encoded IPLD data.For examples of its implementation see: