//! A composed codec that covers the well-known codecs that cover the entire IPLD data model. use ipld_core::{ cid::Cid, codec::{Codec, Links}, ipld::Ipld, }; use std::io::{BufRead, Write}; use thiserror::Error; #[cfg(feature = "dag_cbor")] use serde_ipld_dagcbor::codec::DagCborCodec; #[cfg(feature = "dag_json")] use serde_ipld_dagjson::codec::DagJsonCodec; #[cfg(feature = "test_utils")] use proptest::prelude::*; /// A composed codec that covers the well-known codecs that cover the entire IPLD data model. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum TotalCodec { /// A tag representing [`DagCborCodec`]. #[default] DagCbor, /// A tag representing [`DagJsonCodec`]. DagJson, } impl Links for TotalCodec { type LinksError = LinksError; fn links(&self, bytes: &[u8]) -> Result, Self::LinksError> { match self { TotalCodec::DagCbor => Ok(DagCborCodec.links(bytes)?.collect::>().into_iter()), // FIXME gross TotalCodec::DagJson => Ok(DagJsonCodec.links(bytes)?.collect::>().into_iter()), // FIXME gross } } } /// The union of errors for the [`TotalCodec`] variants. #[derive(Debug, Error)] pub enum LinksError { /// [`DagCborCodec`] errors. #[error("dag_cbor links error: {0}")] DagCborLinksError(#[from] serde_ipld_dagcbor::error::CodecError), /// [`DagJsonCodec`] errors. #[error("dag_json links error: {0}")] DagJsonLinksError(#[from] serde_ipld_dagjson::error::CodecError), } impl serde::Deserialize<'de>> Codec for TotalCodec { type Error = (); // FIXME fn to_code(&self) -> u64 { match self { TotalCodec::DagCbor => Codec::::to_code(&DagCborCodec), TotalCodec::DagJson => Codec::::to_code(&DagJsonCodec), } } fn try_from_code(code: u64) -> Option { if code == Codec::::to_code(&DagCborCodec) { return Some(TotalCodec::DagCbor); } if code == Codec::::to_code(&DagJsonCodec) { return Some(TotalCodec::DagJson); } None } fn encode(&self, writer: W, data: &T) -> Result<(), Self::Error> { match self { TotalCodec::DagCbor => DagCborCodec.encode(writer, data).map_err(|_| ()), TotalCodec::DagJson => DagJsonCodec.encode(writer, data).map_err(|_| ()), } } fn decode(&self, reader: R) -> Result { match self { TotalCodec::DagCbor => DagCborCodec.decode(reader).map_err(|_| ()), TotalCodec::DagJson => DagJsonCodec.decode(reader).map_err(|_| ()), } } } #[cfg(feature = "dag_cbor")] impl From for TotalCodec { fn from(_: DagCborCodec) -> Self { TotalCodec::DagCbor } } #[cfg(feature = "dag_json")] impl From for TotalCodec { fn from(_: DagJsonCodec) -> Self { TotalCodec::DagJson } } #[cfg(feature = "dag_cbor")] impl TryFrom for DagCborCodec { type Error = &'static str; fn try_from(value: TotalCodec) -> Result { match value { TotalCodec::DagCbor => Ok(DagCborCodec), _ => Err("not a DagCborCodec"), } } } #[cfg(feature = "dag_json")] impl TryFrom for DagJsonCodec { type Error = &'static str; fn try_from(value: TotalCodec) -> Result { match value { TotalCodec::DagJson => Ok(DagJsonCodec), _ => Err("not a DagJsonCodec"), } } } #[cfg(feature = "test_utils")] impl Arbitrary for TotalCodec { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { prop_oneof![Just(TotalCodec::DagCbor), Just(TotalCodec::DagJson)].boxed() } }