1use core::fmt;
13use core::hash::{Hash, Hasher};
14use core::marker::PhantomData;
15
16use serde::{Deserialize, Deserializer, Serialize, Serializer};
17
18use crate::id::cid::Cid;
19
20pub struct Link<T: ?Sized> {
26 cid: Cid,
27 _target: PhantomData<fn() -> T>,
28}
29
30impl<T: ?Sized> Link<T> {
31 #[must_use]
37 pub const fn new(cid: Cid) -> Self {
38 Self {
39 cid,
40 _target: PhantomData,
41 }
42 }
43
44 #[must_use]
46 pub const fn cid(&self) -> &Cid {
47 &self.cid
48 }
49
50 #[must_use]
52 pub const fn into_cid(self) -> Cid {
53 self.cid
54 }
55
56 #[must_use]
60 pub const fn transmute<U: ?Sized>(self) -> Link<U> {
61 Link::new(self.cid)
62 }
63}
64
65impl<T: ?Sized> Clone for Link<T> {
68 fn clone(&self) -> Self {
69 Self::new(self.cid.clone())
70 }
71}
72
73impl<T: ?Sized> PartialEq for Link<T> {
74 fn eq(&self, other: &Self) -> bool {
75 self.cid == other.cid
76 }
77}
78
79impl<T: ?Sized> Eq for Link<T> {}
80
81impl<T: ?Sized> PartialOrd for Link<T> {
82 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
83 Some(self.cmp(other))
84 }
85}
86
87impl<T: ?Sized> Ord for Link<T> {
88 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
89 self.cid.cmp(&other.cid)
90 }
91}
92
93impl<T: ?Sized> Hash for Link<T> {
94 fn hash<H: Hasher>(&self, state: &mut H) {
95 self.cid.hash(state);
96 }
97}
98
99impl<T: ?Sized> fmt::Debug for Link<T> {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "Link<{}>({})", core::any::type_name::<T>(), self.cid)
102 }
103}
104
105impl<T: ?Sized> fmt::Display for Link<T> {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 fmt::Display::fmt(&self.cid, f)
108 }
109}
110
111impl<T: ?Sized> Serialize for Link<T> {
112 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
113 self.cid.serialize(s)
114 }
115}
116
117impl<'de, T: ?Sized> Deserialize<'de> for Link<T> {
118 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
119 Cid::deserialize(d).map(Self::new)
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::id::cid::CODEC_DAG_CBOR;
127 use crate::id::multihash::Multihash;
128
129 struct TestNode;
132 struct TestEdge;
133
134 #[test]
135 fn link_distinguishes_target_types_at_compile_time() {
136 let cid = Cid::new(CODEC_DAG_CBOR, Multihash::sha2_256(b"x"));
137 let node_link: Link<TestNode> = Link::new(cid.clone());
138 let edge_link: Link<TestEdge> = Link::new(cid);
139 let node_link2 = node_link.clone();
143 assert_eq!(node_link, node_link2);
144 assert_eq!(edge_link.cid(), node_link2.cid());
145 }
146
147 #[test]
148 fn link_round_trip_cid_equality() {
149 let cid = Cid::new(CODEC_DAG_CBOR, Multihash::sha2_256(b"round"));
150 let link: Link<TestNode> = Link::new(cid.clone());
151 assert_eq!(link.cid(), &cid);
152 let taken = link.into_cid();
153 assert_eq!(taken, cid);
154 }
155}