WIP: represent CID as enum and not as newtype struct

Changing from newtype struct to enum should hopefully make things
more robust.
diff --git a/Cargo.toml b/Cargo.toml
index d4f38e4..5879751 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -63,3 +63,6 @@
 [[bench]]
 name = "store"
 harness = false
+
+[patch.crates-io]
+cid = { git = "https://github.com/ipld/rust-cid", branch = "cid-as-enum" }
diff --git a/core/src/serde/de.rs b/core/src/serde/de.rs
index 6f0655a..c20d4a8 100644
--- a/core/src/serde/de.rs
+++ b/core/src/serde/de.rs
@@ -4,7 +4,7 @@
 use cid::serde::{BytesToCidVisitor, CID_SERDE_PRIVATE_IDENTIFIER};
 use cid::Cid;
 use serde::{
-    de::{self, IntoDeserializer},
+    de::{self, IntoDeserializer, VariantAccess},
     forward_to_deserialize_any,
 };
 
@@ -170,15 +170,24 @@
                 Ok(Ipld::Map(values))
             }
 
-            /// Newtype structs are only used to deserialize CIDs.
+            /// Enums are only used to deserialize CIDs.
             #[inline]
-            fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+            fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
             where
-                D: de::Deserializer<'de>,
+                A: de::EnumAccess<'de>,
             {
-                deserializer
-                    .deserialize_bytes(BytesToCidVisitor)
-                    .map(Ipld::Link)
+                match data.variant() {
+                    // Make sure that we only deserialize a CID when we clearly intended to.
+                    Ok((CID_SERDE_PRIVATE_IDENTIFIER, value)) => {
+                        // It's not really a tuple, we use the `tuple_variant` call in order to be
+                        // able to pass in a custom visitor.
+                        let cid = value.tuple_variant(1, BytesToCidVisitor)?;
+                        Ok(Ipld::Link(cid))
+                    }
+                    _ => Err(de::Error::custom(
+                        "invalid type: enum, expected any valid IPLD kind",
+                    )),
+                }
             }
         }
 
@@ -207,6 +216,26 @@
     };
 }
 
+/// Dummy deserializer to deserialize an existing string.
+///
+/// From https://github.com/honsunrise/path-value/blob/d5eb3283f68b82e73cbc627889c32d32d484a009/src/value/de.rs#L141-L162
+struct StrDeserializer<'a>(&'a str);
+
+impl<'de, 'a: 'de> de::Deserializer<'de> for StrDeserializer<'a> {
+    type Error = SerdeError;
+
+    #[inline]
+    fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
+        visitor.visit_borrowed_str(self.0)
+    }
+
+    forward_to_deserialize_any! {
+        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+        bytes byte_buf map struct unit enum newtype_struct
+        identifier ignored_any unit_struct tuple_struct tuple option
+    }
+}
+
 /// A Deserializer for CIDs.
 ///
 /// A separate deserializer is needed to make sure we always deserialize only CIDs as `Ipld::Link`
@@ -231,6 +260,60 @@
     }
 }
 
+impl<'de> de::EnumAccess<'de> for CidDeserializer {
+    type Error = SerdeError;
+    // We just implement `VariantAccess` for `CidDeserializer`.
+    type Variant = Self;
+
+    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+    where
+        V: de::DeserializeSeed<'de>,
+    {
+        // This is the Serde way of saying `let value = CID_SERDE_PRIVATE_IDENTIFIER;`.
+        let key = seed.deserialize(StrDeserializer(CID_SERDE_PRIVATE_IDENTIFIER))?;
+        // The `CidDeserializer` already contains the CID, hence return itself.
+        Ok((key, self))
+    }
+}
+
+impl<'de> de::VariantAccess<'de> for CidDeserializer {
+    type Error = SerdeError;
+
+    fn unit_variant(self) -> Result<(), Self::Error> {
+        unreachable!();
+    }
+
+    fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
+    where
+        T: de::DeserializeSeed<'de>,
+    {
+        unreachable!();
+    }
+
+    fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        if len == 1 {
+            // This is not how tuple variants usually work. This is a hack in order to get a CID out.
+            visitor.visit_bytes(&self.0.to_bytes())
+        } else {
+            error("CidDeserializer only support deserializing CIDs")
+        }
+    }
+
+    fn struct_variant<V>(
+        self,
+        _fields: &'static [&'static str],
+        _visitor: V,
+    ) -> Result<V::Value, Self::Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        unreachable!();
+    }
+}
+
 /// Deserialize from an [`Ipld`] enum into a Rust type.
 ///
 /// The deserialization will return an error if you try to deserialize into an integer type that
@@ -254,7 +337,7 @@
             Self::Bytes(bytes) => visitor.visit_bytes(&bytes),
             Self::List(list) => visit_seq(list, visitor),
             Self::Map(map) => visit_map(map, visitor),
-            Self::Link(cid) => visitor.visit_newtype_struct(CidDeserializer(cid)),
+            Self::Link(cid) => visitor.visit_enum(CidDeserializer(cid)),
         }
     }
 
@@ -464,31 +547,30 @@
 
     fn deserialize_newtype_struct<V: de::Visitor<'de>>(
         self,
-        name: &str,
+        _name: &str,
         visitor: V,
     ) -> Result<V::Value, Self::Error> {
-        if name == CID_SERDE_PRIVATE_IDENTIFIER {
-            match self {
-                Ipld::Link(cid) => visitor.visit_newtype_struct(CidDeserializer(cid)),
-                _ => error(format!(
-                    "Only `Ipld::Link`s can be deserialized to CIDs, input was `{:#?}`",
-                    self
-                )),
-            }
-        } else {
-            visitor.visit_newtype_struct(self)
-        }
+        visitor.visit_newtype_struct(self)
     }
 
     // Heavily based on
     // https://github.com/serde-rs/json/blob/95f67a09399d546d9ecadeb747a845a77ff309b2/src/value/de.rs#L249
     fn deserialize_enum<V: de::Visitor<'de>>(
         self,
-        _name: &str,
-        _variants: &[&str],
+        name: &str,
+        variants: &[&str],
         visitor: V,
     ) -> Result<V::Value, Self::Error> {
-        let (variant, value) = match self {
+        if name == CID_SERDE_PRIVATE_IDENTIFIER && variants == [CID_SERDE_PRIVATE_IDENTIFIER] {
+            match self {
+                Ipld::Link(cid) => visitor.visit_enum(CidDeserializer(cid)),
+                _ => error(format!(
+                    "Only `Ipld::Link`s can be deserialized to CIDs, input was `{:#?}`",
+                    self
+                )),
+            }
+        } else {
+            let (variant, value) = match self {
             Ipld::Map(map) => {
                 let mut iter = map.into_iter();
                 let (variant, value) = match iter.next() {
@@ -514,7 +596,8 @@
             )),
         };
 
-        visitor.visit_enum(EnumDeserializer { variant, value })
+            visitor.visit_enum(EnumDeserializer { variant, value })
+        }
     }
 
     // Heavily based on
diff --git a/core/src/serde/mod.rs b/core/src/serde/mod.rs
index e502fe7..5f7c9fd 100644
--- a/core/src/serde/mod.rs
+++ b/core/src/serde/mod.rs
@@ -17,7 +17,7 @@
     use cid::serde::CID_SERDE_PRIVATE_IDENTIFIER;
     use cid::Cid;
     use serde::{de::DeserializeOwned, Deserialize, Serialize};
-    use serde_test::{assert_tokens, Token};
+    use serde_test::{assert_ser_tokens, Token};
 
     use crate::ipld::Ipld;
     use crate::serde::{from_ipld, to_ipld};
@@ -61,7 +61,9 @@
     fn test_tokens() {
         let person = Person::default();
 
-        assert_tokens(
+        // The `serde_test` deserializer doesn't deserialize enums as one would expect (it doesn't
+        // call `visit_enum`), hence only the serializer is tested.
+        assert_ser_tokens(
             &person,
             &[
                 Token::Struct {
@@ -80,14 +82,17 @@
                 Token::Str("is_cool"),
                 Token::Bool(true),
                 Token::Str("link"),
-                Token::NewtypeStruct {
+                Token::TupleVariant {
                     name: CID_SERDE_PRIVATE_IDENTIFIER,
+                    variant: CID_SERDE_PRIVATE_IDENTIFIER,
+                    len: 1,
                 },
                 Token::Bytes(&[
                     0x01, 0x71, 0x12, 0x20, 0x35, 0x4d, 0x45, 0x5f, 0xf3, 0xa6, 0x41, 0xb8, 0xca,
                     0xc2, 0x5c, 0x38, 0xa7, 0x7e, 0x64, 0xaa, 0x73, 0x5d, 0xc8, 0xa4, 0x89, 0x66,
                     0xa6, 0xf, 0x1a, 0x78, 0xca, 0xa1, 0x72, 0xa4, 0x88, 0x5e,
                 ]),
+                Token::TupleVariantEnd,
                 Token::StructEnd,
             ],
         );
diff --git a/core/src/serde/ser.rs b/core/src/serde/ser.rs
index 94b6038..4ffedc9 100644
--- a/core/src/serde/ser.rs
+++ b/core/src/serde/ser.rs
@@ -209,21 +209,13 @@
     #[inline]
     fn serialize_newtype_struct<T: ?Sized>(
         self,
-        name: &'static str,
+        _name: &'static str,
         value: &T,
     ) -> Result<Self::Ok, Self::Error>
     where
         T: ser::Serialize,
     {
-        let ipld = value.serialize(self);
-        if name == CID_SERDE_PRIVATE_IDENTIFIER {
-            if let Ok(Ipld::Bytes(bytes)) = ipld {
-                let cid = Cid::try_from(bytes)
-                    .map_err(|err| ser::Error::custom(format!("Invalid CID: {}", err)))?;
-                return Ok(Self::Ok::Link(cid));
-            }
-        }
-        ipld
+        value.serialize(self)
     }
 
     fn serialize_newtype_variant<T: ?Sized>(
@@ -398,7 +390,15 @@
         Ok(())
     }
 
-    fn end(self) -> Result<Self::Ok, Self::Error> {
+    fn end(mut self) -> Result<Self::Ok, Self::Error> {
+        if self.name == CID_SERDE_PRIVATE_IDENTIFIER && self.vec.len() == 1 {
+            if let Ipld::Bytes(bytes) = self.vec.pop().unwrap() {
+                let cid = Cid::try_from(bytes)
+                    .map_err(|err| ser::Error::custom(format!("Invalid CID: {}", err)))?;
+                return Ok(Self::Ok::Link(cid));
+            }
+        }
+
         let map = BTreeMap::from([(self.name, Self::Ok::List(self.vec))]);
         Ok(Self::Ok::Map(map))
     }
diff --git a/core/tests/serde_deserialize.rs b/core/tests/serde_deserialize.rs
index 3e67ce2..6a1b45a 100644
--- a/core/tests/serde_deserialize.rs
+++ b/core/tests/serde_deserialize.rs
@@ -5,9 +5,10 @@
 use alloc::collections::BTreeMap;
 use core::convert::TryFrom;
 
+use serde::Deserialize;
 use serde_test::{assert_de_tokens, Token};
 
-use libipld_core::cid::{serde::CID_SERDE_PRIVATE_IDENTIFIER, Cid};
+use libipld_core::cid::Cid;
 use libipld_core::ipld::Ipld;
 
 #[test]
@@ -124,25 +125,6 @@
 }
 
 #[test]
-fn ipld_deserialize_link() {
-    let cid = Cid::try_from("bafkreie74tgmnxqwojhtumgh5dzfj46gi4mynlfr7dmm7duwzyvnpw7h7m").unwrap();
-    let ipld = Ipld::Link(cid);
-    assert_de_tokens(
-        &ipld,
-        &[
-            Token::NewtypeStruct {
-                name: CID_SERDE_PRIVATE_IDENTIFIER,
-            },
-            Token::Bytes(&[
-                1, 85, 18, 32, 159, 228, 204, 198, 222, 22, 114, 79, 58, 48, 199, 232, 242, 84,
-                243, 198, 71, 25, 134, 172, 177, 248, 216, 207, 142, 150, 206, 42, 215, 219, 231,
-                251,
-            ]),
-        ],
-    );
-}
-
-#[test]
 #[should_panic(expected = "assertion failed")]
 fn ipld_deserialize_link_not_as_bytes() {
     let cid = Cid::try_from("bafkreie74tgmnxqwojhtumgh5dzfj46gi4mynlfr7dmm7duwzyvnpw7h7m").unwrap();
@@ -155,3 +137,70 @@
         ])],
     );
 }
+
+#[test]
+fn ipld_deserialize_ipld_null() {
+    let ipld = Ipld::Null;
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_bool() {
+    let ipld = Ipld::Bool(true);
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_integer() {
+    let ipld = Ipld::Integer(31);
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_float() {
+    let ipld = Ipld::Float(211.421);
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_string() {
+    let ipld = Ipld::String("hello".into());
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_bytes() {
+    let ipld = Ipld::Bytes(vec![0x68, 0x65, 0x6c, 0x6c, 0x6f]);
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_list() {
+    let ipld = Ipld::List(vec![Ipld::Bool(false), Ipld::Float(22.7)]);
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_map() {
+    let ipld = Ipld::Map(BTreeMap::from([
+        ("hello".to_string(), Ipld::Bool(true)),
+        ("world!".to_string(), Ipld::Bool(false)),
+    ]));
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
+
+#[test]
+fn ipld_deserialize_ipld_link() {
+    let cid = Cid::try_from("bafkreie74tgmnxqwojhtumgh5dzfj46gi4mynlfr7dmm7duwzyvnpw7h7m").unwrap();
+    let ipld = Ipld::Link(cid);
+    let deserialized = Ipld::deserialize(ipld.clone()).unwrap();
+    assert_eq!(deserialized, ipld);
+}
diff --git a/core/tests/serde_serialize.rs b/core/tests/serde_serialize.rs
index 1e9c17b..83a6017 100644
--- a/core/tests/serde_serialize.rs
+++ b/core/tests/serde_serialize.rs
@@ -95,14 +95,17 @@
     assert_ser_tokens(
         &ipld,
         &[
-            Token::NewtypeStruct {
+            Token::TupleVariant {
                 name: CID_SERDE_PRIVATE_IDENTIFIER,
+                variant: CID_SERDE_PRIVATE_IDENTIFIER,
+                len: 1,
             },
             Token::Bytes(&[
                 1, 85, 18, 32, 159, 228, 204, 198, 222, 22, 114, 79, 58, 48, 199, 232, 242, 84,
                 243, 198, 71, 25, 134, 172, 177, 248, 216, 207, 142, 150, 206, 42, 215, 219, 231,
                 251,
             ]),
+            Token::TupleVariantEnd,
         ],
     );
 }