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}