thegraph_core/allocation_id.rs
1use alloy::primitives::Address;
2
3use crate::collection_id::CollectionId;
4
5/// A unique identifier for an allocation: the allocation's Ethereum address.
6///
7/// This is a "new-type" wrapper around [`Address`] to provide type safety.
8///
9/// ## Formatting
10///
11/// The `AllocationId` type implements the following formatting traits:
12///
13/// - Use [`std::fmt::Display`] for formatting the `AllocationId` as an [EIP-55] checksum string.
14/// - Use [`std::fmt::LowerHex`] (or [`std::fmt::UpperHex`]) for formatting the `AllocationId` as
15/// a hexadecimal string.
16///
17/// See the [`Display`], [`LowerHex`], and [`UpperHex`] trait implementations for usage examples.
18///
19/// ## Generating test data
20///
21/// The `AllocationId` type implements the [`fake`] crate's [`fake::Dummy`] trait, allowing you to
22/// generate random `AllocationId` values for testing.
23///
24/// Note that the `fake` feature must be enabled to use this functionality.
25///
26/// See the [`Dummy`] trait impl for usage examples.
27///
28/// [EIP-55]: https://eips.ethereum.org/EIPS/eip-55
29/// [`Display`]: #impl-Display-for-AllocationId
30/// [`LowerHex`]: #impl-LowerHex-for-AllocationId
31/// [`UpperHex`]: #impl-UpperHex-for-AllocationId
32/// [`Dummy`]: #impl-Dummy<Faker>-for-AllocationId
33#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct AllocationId(Address);
35
36impl AllocationId {
37 /// Create a new [`AllocationId`].
38 pub const fn new(address: Address) -> Self {
39 AllocationId(address)
40 }
41
42 /// Return the internal representation.
43 pub fn into_inner(self) -> Address {
44 self.0
45 }
46}
47
48impl std::fmt::Display for AllocationId {
49 /// Formats the `AllocationId` using its [EIP-55](https://eips.ethereum.org/EIPS/eip-55)
50 /// checksum representation.
51 ///
52 /// See [`LowerHex`] (and [`UpperHex`]) for formatting the `AllocationId` as a hexadecimal
53 /// string.
54 ///
55 /// [`LowerHex`]: struct.AllocationId.html#impl-LowerHex-for-AllocationId
56 /// [`UpperHex`]: struct.AllocationId.html#impl-UpperHex-for-AllocationId
57 ///
58 /// ```rust
59 /// # use thegraph_core::{allocation_id, AllocationId};
60 /// const ID: AllocationId = allocation_id!("0002c67268fb8c8917f36f865a0cbdf5292fa68d");
61 ///
62 /// // Note the uppercase and lowercase hex characters in the checksum
63 /// assert_eq!(format!("{}", ID), "0x0002c67268FB8C8917F36F865a0CbdF5292FA68d");
64 /// ```
65 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
66 std::fmt::Display::fmt(&self.0, f)
67 }
68}
69
70impl std::fmt::Debug for AllocationId {
71 /// Formats the `AllocationId` using its raw lower-case hexadecimal representation.
72 ///
73 /// It is advised to use the [`LowerHex`] (and [`UpperHex`]) format trait implementation over
74 /// the [`Debug`](std::fmt::Debug) implementation to format the `AllocationId` as a lower-case
75 /// hexadecimal string.
76 ///
77 /// This implementation matches `alloy_primitives::Address`'s `Debug` implementation.
78 ///
79 /// [`LowerHex`]: struct.AllocationId.html#impl-LowerHex-for-AllocationId
80 /// [`UpperHex`]: struct.AllocationId.html#impl-UpperHex-for-AllocationId
81 ///
82 /// ```rust
83 /// # use thegraph_core::{allocation_id, AllocationId};
84 /// const ID: AllocationId = allocation_id!("0002c67268fb8c8917f36f865a0cbdf5292fa68d");
85 ///
86 /// assert_eq!(format!("{:?}", ID), "0x0002c67268fb8c8917f36f865a0cbdf5292fa68d");
87 /// ```
88 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
89 std::fmt::Debug::fmt(&self.0, f)
90 }
91}
92
93impl std::fmt::LowerHex for AllocationId {
94 /// Lowercase hex representation of the `AllocationId`.
95 ///
96 /// Note that the alternate flag, `#`, adds a `0x` in front of the output.
97 ///
98 /// ```rust
99 /// # use thegraph_core::{allocation_id, AllocationId};
100 /// const ID: AllocationId = allocation_id!("0002c67268fb8c8917f36f865a0cbdf5292fa68d");
101 ///
102 /// // Lower hex
103 /// assert_eq!(format!("{:x}", ID), "0002c67268fb8c8917f36f865a0cbdf5292fa68d");
104 ///
105 /// // Lower hex with alternate flag
106 /// assert_eq!(format!("{:#x}", ID), "0x0002c67268fb8c8917f36f865a0cbdf5292fa68d");
107 /// ```
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 std::fmt::LowerHex::fmt(&self.0, f)
110 }
111}
112
113impl std::fmt::UpperHex for AllocationId {
114 /// Uppercase hex representation of the `AllocationId`.
115 ///
116 /// Note that the alternate flag, `#`, adds a `0x` in front of the output.
117 ///
118 /// ```rust
119 /// # use thegraph_core::{allocation_id, AllocationId};
120 /// const ID: AllocationId = allocation_id!("0002c67268fb8c8917f36f865a0cbdf5292fa68d");
121 ///
122 /// // Upper hex
123 /// assert_eq!(format!("{:X}", ID), "0002C67268FB8C8917F36F865A0CBDF5292FA68D");
124 ///
125 /// // Upper hex with alternate flag
126 /// assert_eq!(format!("{:#X}", ID), "0x0002C67268FB8C8917F36F865A0CBDF5292FA68D");
127 /// ```
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 std::fmt::UpperHex::fmt(&self.0, f)
130 }
131}
132
133impl From<Address> for AllocationId {
134 fn from(address: Address) -> Self {
135 AllocationId(address)
136 }
137}
138
139/// Convert a [`CollectionId`] into an [`AllocationId`] by truncating to the last 20 bytes.
140///
141/// ```rust
142/// use thegraph_core::{
143/// alloy::primitives::Address,
144/// alloy::primitives::address,
145/// collection_id, CollectionId,
146/// allocation_id, AllocationId
147/// };
148///
149/// let collection_id: CollectionId = collection_id!("0000000000000000000000003e1f9c2ab4c7f1b3d7e839ebe6ae451c8a0b1d24");
150/// let allocation_id: AllocationId = collection_id.into();
151/// assert_eq!(format!("{:?}", allocation_id), "0x3e1f9c2ab4c7f1b3d7e839ebe6ae451c8a0b1d24");
152///
153/// let allocation_id2: AllocationId = AllocationId::from(collection_id!("0000000000000000000000003e1f9c2ab4c7f1b3d7e839ebe6ae451c8a0b1d24"));
154/// assert_eq!(format!("{:?}", allocation_id2), "0x3e1f9c2ab4c7f1b3d7e839ebe6ae451c8a0b1d24");
155/// ```
156impl From<CollectionId> for AllocationId {
157 fn from(collection_id: CollectionId) -> Self {
158 let bytes = collection_id.as_ref();
159 let addr_bytes: [u8; 20] = bytes[12..].try_into().expect("slice should be 20 bytes");
160 AllocationId(Address::from(addr_bytes))
161 }
162}
163
164impl std::str::FromStr for AllocationId {
165 type Err = <Address as std::str::FromStr>::Err;
166
167 fn from_str(s: &str) -> Result<Self, Self::Err> {
168 let address = std::str::FromStr::from_str(s)?;
169 Ok(AllocationId(address))
170 }
171}
172
173impl PartialEq<Address> for AllocationId {
174 fn eq(&self, other: &Address) -> bool {
175 self.0.eq(other)
176 }
177}
178
179impl AsRef<Address> for AllocationId {
180 fn as_ref(&self) -> &Address {
181 &self.0
182 }
183}
184
185impl std::ops::Deref for AllocationId {
186 type Target = Address;
187
188 fn deref(&self) -> &Self::Target {
189 &self.0
190 }
191}
192
193#[cfg(feature = "serde")]
194impl<'de> serde::Deserialize<'de> for AllocationId {
195 fn deserialize<D>(deserializer: D) -> Result<AllocationId, D::Error>
196 where
197 D: serde::Deserializer<'de>,
198 {
199 let address = Address::deserialize(deserializer)?;
200 Ok(AllocationId(address))
201 }
202}
203
204#[cfg(feature = "serde")]
205impl serde::Serialize for AllocationId {
206 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
207 where
208 S: serde::Serializer,
209 {
210 self.0.serialize(serializer)
211 }
212}
213
214#[cfg(feature = "fake")]
215/// To use the [`fake`] crate to generate random [`AllocationId`] values, **the `fake` feature must
216/// be enabled.**
217///
218/// ```rust
219/// # use thegraph_core::AllocationId;
220/// # use fake::Fake;
221/// let allocation_id = fake::Faker.fake::<AllocationId>();
222///
223/// println!("AllocationId: {:#x}", allocation_id);
224/// ```
225impl fake::Dummy<fake::Faker> for AllocationId {
226 fn dummy_with_rng<R: fake::Rng + ?Sized>(_: &fake::Faker, rng: &mut R) -> Self {
227 use crate::fake_impl::alloy::Alloy;
228 Self(Address::dummy_with_rng(&Alloy, rng))
229 }
230}
231
232/// Converts a sequence of string literals containing hex-encoded data into a new [`AllocationId`]
233/// at compile time.
234///
235/// To create an `AllocationId` from a string literal (no `0x` prefix) at compile time:
236///
237/// ```rust
238/// use thegraph_core::{allocation_id, AllocationId};
239///
240/// const ALLOCATION_ID: AllocationId = allocation_id!("0002c67268fb8c8917f36f865a0cbdf5292fa68d");
241/// ```
242///
243/// If no argument is provided, the macro will create an `AllocationId` with the zero address:
244///
245/// ```rust
246/// use thegraph_core::{
247/// alloy::primitives::Address,
248/// allocation_id, AllocationId
249/// };
250///
251/// const ALLOCATION_ID: AllocationId = allocation_id!();
252///
253/// assert_eq!(ALLOCATION_ID, Address::ZERO);
254/// ```
255#[macro_export]
256#[doc(hidden)]
257macro_rules! __allocation_id {
258 () => {
259 $crate::AllocationId::new($crate::alloy::primitives::Address::ZERO)
260 };
261 ($value:tt) => {
262 $crate::AllocationId::new($crate::alloy::primitives::address!($value))
263 };
264}