firehose_rs/firehose_v2/request.rs
1// Copyright 2024-, Semiotic AI, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::fmt::Display;
5
6use crate::BlockNumber;
7
8use super::{
9 single_block_request::{BlockHashAndNumber, Reference},
10 Response, SingleBlockRequest,
11};
12
13impl SingleBlockRequest {
14 /// Create a Firehose [`SingleBlockRequest`] for the given *block number*.
15 ///
16 /// We're leaving this method here for backwards compatibility.
17 pub fn new(num: u64) -> SingleBlockRequest {
18 SingleBlockRequest::new_by_block_number(num)
19 }
20
21 /// Create a Firehose [`SingleBlockRequest`] for the given *block number*.
22 pub fn new_by_block_number(num: u64) -> SingleBlockRequest {
23 SingleBlockRequest {
24 reference: Some(Reference::BlockNumber(BlockNumber { num })),
25 ..Default::default()
26 }
27 }
28
29 /// Create a Firehose [`SingleBlockRequest`] for the given *block hash*.
30 pub fn new_by_block_hash_and_number(hash: String, num: u64) -> SingleBlockRequest {
31 SingleBlockRequest {
32 reference: Some(Reference::BlockHashAndNumber(BlockHashAndNumber {
33 hash,
34 num,
35 })),
36 ..Default::default()
37 }
38 }
39}
40
41/// Work with block numbers or slots in a unified way.
42///
43/// This trait provides a common interface for accessing block identifiers,
44/// which can either be a block number (for execution layer blocks) or a slot
45/// (for consensus layer blocks). By implementing this trait, types can expose
46/// their block number or slot in a standardized manner, enabling generic handling
47/// of different block types in streaming and processing workflows.
48///
49/// # Requirements
50///
51/// Types implementing this trait must also implement [`Clone`], [`Send`], and
52/// `'static` to ensure compatibility with asynchronous and concurrent contexts.
53///
54/// # Provided Method
55///
56/// * `number_or_slot`: Returns the block number or slot as a `u64`.
57///
58/// # Example
59///
60/// ```rust,ignore
61/// use firehose_client::HasNumberOrSlot;
62///
63/// #[derive(Clone)]
64/// struct ExecutionBlock {
65/// block_number: u64,
66/// }
67///
68/// #[derive(Clone)]
69/// struct ConsensusBlock {
70/// slot: u64,
71/// }
72///
73/// impl HasNumberOrSlot for ExecutionBlock {
74/// fn number_or_slot(&self) -> u64 {
75/// self.block_number
76/// }
77/// }
78///
79/// impl HasNumberOrSlot for ConsensusBlock {
80/// fn number_or_slot(&self) -> u64 {
81/// self.slot
82/// }
83/// }
84///
85/// fn process_block<T: HasNumberOrSlot>(block: &T) {
86/// println!("Processing block with identifier: {}", block.number_or_slot());
87/// }
88///
89/// let execution_block = ExecutionBlock { block_number: 42 };
90/// let consensus_block = ConsensusBlock { slot: 24 };
91///
92/// process_block(&execution_block);
93/// process_block(&consensus_block);
94/// ```
95///
96/// # Use Case
97///
98/// This trait is particularly useful in scenarios where both execution and
99/// consensus layer blocks need to be processed generically, such as in blockchain
100/// indexing or synchronization applications.
101///
102/// [`Clone`]: std::clone::Clone
103/// [`Send`]: std::marker::Send
104///
105pub trait HasNumberOrSlot: Clone + Send + 'static {
106 /// Return the block number or slot.
107 ///
108 /// This value uniquely identifies the block within its respective layer,
109 /// either as a block number (execution layer) or a slot (consensus layer).
110 fn number_or_slot(&self) -> u64;
111}
112
113/// Convert protocol buffer messages into domain-specific block types.
114///
115/// This trait is intended to simplify the deserialization and conversion process
116/// when streaming data from a Firehose gRPC service. Implementations of this trait
117/// provide a uniform way to transform a `firehose_protos::Response` message into
118/// a concrete type.
119///
120/// # Example
121///
122/// ```rust,ignore
123/// use firehose_client::FromResponse;
124/// use firehose_protos::Response;
125///
126/// struct MyBlock;
127///
128/// impl FromResponse for MyBlock {
129/// fn from_response(msg: Response) -> Result<Self, ClientError> {
130/// // Perform conversion logic here.
131/// Ok(MyBlock)
132/// }
133/// }
134/// ```
135///
136/// # Errors
137///
138/// Implementations should return a `ProtosError` if the conversion fails. This can
139/// occur due to invalid data, missing fields, or other deserialization issues.
140///
141/// # Usage
142///
143/// The `FromResponse` trait is typically used in conjunction with generic streaming
144/// methods, such as `stream_blocks`, allowing these methods to work with
145/// different block types by specifying the type parameter:
146///
147/// ```rust, ignore
148/// let stream = client
149/// .stream_blocks_generic::<FirehoseBeaconBlock>(start, total)
150/// .await?;
151/// ```
152///
153pub trait FromResponse: Sized
154where
155 Self::Error: Display + Send,
156{
157 type Error;
158
159 /// Convert a `crate::Response` into the implementing type.
160 ///
161 /// # Parameters
162 ///
163 /// * `msg`: The `Response` message received from the Firehose stream.
164 ///
165 /// # Returns
166 ///
167 /// A `Result` containing the converted type on success, or a `ClientError`
168 /// if the conversion fails.
169 fn from_response(msg: Response) -> Result<Self, Self::Error>;
170}