|
1 | | -use crate::{BlobCacher, BlobFetcher, BlobFetcherConfig}; |
2 | | -use reth::transaction_pool::TransactionPool; |
3 | | -use url::Url; |
| 1 | +use crate::{ |
| 2 | + AsyncBlobSource, BlobCacher, BlobFetcher, BlobFetcherConfig, BlobSource, |
| 3 | + sources::{BeaconBlobSource, BlobExplorerSource, PylonBlobSource}, |
| 4 | +}; |
4 | 5 |
|
5 | 6 | /// Errors that can occur while building the [`BlobFetcher`] with a |
6 | 7 | /// [`BlobFetcherBuilder`]. |
7 | | -#[derive(Debug, thiserror::Error)] |
| 8 | +#[derive(Debug, Clone, Copy, thiserror::Error)] |
8 | 9 | pub enum BuilderError { |
9 | | - /// The transaction pool was not provided. |
10 | | - #[error("transaction pool is required")] |
11 | | - MissingPool, |
12 | | - /// The explorer URL was not provided or could not be parsed. |
13 | | - #[error("explorer URL is required and must be valid")] |
14 | | - MissingExplorerUrl, |
15 | 10 | /// The URL provided was invalid. |
16 | 11 | #[error("invalid URL provided")] |
17 | 12 | Url(#[from] url::ParseError), |
18 | | - /// The client was not provided. |
19 | | - #[error("client is required")] |
20 | | - MissingClient, |
21 | | - /// The client failed to build. |
22 | | - #[error("failed to build client: {0}")] |
23 | | - Client(#[from] reqwest::Error), |
24 | | - /// The slot calculator was not provided. |
25 | | - #[error("slot calculator is required")] |
26 | | - MissingSlotCalculator, |
27 | 13 | } |
28 | 14 |
|
29 | 15 | /// Builder for the [`BlobFetcher`]. |
30 | | -#[derive(Debug, Default, Clone)] |
31 | | -pub struct BlobFetcherBuilder<Pool> { |
32 | | - pool: Option<Pool>, |
33 | | - explorer_url: Option<String>, |
34 | | - client: Option<reqwest::Client>, |
35 | | - cl_url: Option<String>, |
36 | | - pylon_url: Option<String>, |
| 16 | +/// |
| 17 | +/// Add synchronous and asynchronous blob sources, then call [`build`] to |
| 18 | +/// produce a [`BlobFetcher`] or [`build_cache`] for a [`BlobCacher`]. |
| 19 | +/// |
| 20 | +/// [`build`]: BlobFetcherBuilder::build |
| 21 | +/// [`build_cache`]: BlobFetcherBuilder::build_cache |
| 22 | +#[derive(Default)] |
| 23 | +pub struct BlobFetcherBuilder { |
| 24 | + sync_sources: Vec<Box<dyn BlobSource>>, |
| 25 | + async_sources: Vec<Box<dyn AsyncBlobSource>>, |
37 | 26 | } |
38 | 27 |
|
39 | | -impl<Pool> BlobFetcherBuilder<Pool> { |
40 | | - /// Set the transaction pool to use for the extractor. |
41 | | - pub fn with_pool<P2>(self, pool: P2) -> BlobFetcherBuilder<P2> { |
42 | | - BlobFetcherBuilder { |
43 | | - pool: Some(pool), |
44 | | - explorer_url: self.explorer_url, |
45 | | - client: self.client, |
46 | | - cl_url: self.cl_url, |
47 | | - pylon_url: self.pylon_url, |
48 | | - } |
49 | | - } |
50 | | - |
51 | | - /// Set the transaction pool to use a mock test pool. |
52 | | - #[cfg(feature = "test-utils")] |
53 | | - pub fn with_test_pool(self) -> BlobFetcherBuilder<reth_transaction_pool::test_utils::TestPool> { |
54 | | - self.with_pool(reth_transaction_pool::test_utils::testing_pool()) |
55 | | - } |
56 | | - |
57 | | - /// Set the configuration for the CL url, pylon url, from the provided |
58 | | - /// [`BlobFetcherConfig`]. |
59 | | - pub fn with_config(self, config: &BlobFetcherConfig) -> Result<Self, BuilderError> { |
60 | | - let this = self.with_explorer_url(config.blob_explorer_url()); |
61 | | - let this = |
62 | | - if let Some(cl_url) = config.cl_url() { this.with_cl_url(cl_url)? } else { this }; |
63 | | - |
64 | | - if let Some(pylon_url) = config.pylon_url() { |
65 | | - this.with_pylon_url(pylon_url) |
66 | | - } else { |
67 | | - Ok(this) |
68 | | - } |
| 28 | +impl core::fmt::Debug for BlobFetcherBuilder { |
| 29 | + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 30 | + f.debug_struct("BlobFetcherBuilder") |
| 31 | + .field("sync_sources", &self.sync_sources.len()) |
| 32 | + .field("async_sources", &self.async_sources.len()) |
| 33 | + .finish() |
69 | 34 | } |
| 35 | +} |
70 | 36 |
|
71 | | - /// Set the blob explorer URL to use for the extractor. This will be used |
72 | | - /// to construct a [`foundry_blob_explorers::Client`]. |
73 | | - pub fn with_explorer_url(mut self, explorer_url: &str) -> Self { |
74 | | - self.explorer_url = Some(explorer_url.to_string()); |
| 37 | +impl BlobFetcherBuilder { |
| 38 | + /// Adds a synchronous blob source. |
| 39 | + pub fn with_source(mut self, source: impl BlobSource + 'static) -> Self { |
| 40 | + self.sync_sources.push(Box::new(source)); |
75 | 41 | self |
76 | 42 | } |
77 | 43 |
|
78 | | - /// Set the [`reqwest::Client`] to use for the extractor. This client will |
79 | | - /// be used to make requests to the blob explorer, and the CL and Pylon URLs |
80 | | - /// if provided. |
81 | | - pub fn with_client(mut self, client: reqwest::Client) -> Self { |
82 | | - self.client = Some(client); |
| 44 | + /// Adds an asynchronous blob source. |
| 45 | + pub fn with_async_source(mut self, source: impl AsyncBlobSource + 'static) -> Self { |
| 46 | + self.async_sources.push(Box::new(source)); |
83 | 47 | self |
84 | 48 | } |
85 | 49 |
|
86 | | - /// Set the [`reqwest::Client`] via a [reqwest::ClientBuilder]. This |
87 | | - /// function will immediately build the client and return an error if it |
88 | | - /// fails. |
| 50 | + /// Configures standard remote sources from a [`BlobFetcherConfig`]. |
89 | 51 | /// |
90 | | - /// This client will be used to make requests to the blob explorer, and the |
91 | | - /// CL and Pylon URLs if provided. |
92 | | - pub fn with_client_builder(self, client: reqwest::ClientBuilder) -> Result<Self, BuilderError> { |
93 | | - client.build().map(|client| self.with_client(client)).map_err(Into::into) |
94 | | - } |
95 | | - |
96 | | - /// Set the CL URL to use for the extractor. |
97 | | - pub fn with_cl_url(mut self, cl_url: &str) -> Result<Self, BuilderError> { |
98 | | - self.cl_url = Some(cl_url.to_string()); |
99 | | - Ok(self) |
100 | | - } |
101 | | - |
102 | | - /// Set the Pylon URL to use for the extractor. |
103 | | - pub fn with_pylon_url(mut self, pylon_url: &str) -> Result<Self, BuilderError> { |
104 | | - self.pylon_url = Some(pylon_url.to_string()); |
105 | | - Ok(self) |
| 52 | + /// This constructs a [`BlobExplorerSource`], and optionally a |
| 53 | + /// [`BeaconBlobSource`] and [`PylonBlobSource`] depending on whether |
| 54 | + /// the config provides CL and Pylon URLs. |
| 55 | + pub fn with_config( |
| 56 | + self, |
| 57 | + config: &BlobFetcherConfig, |
| 58 | + client: reqwest::Client, |
| 59 | + ) -> Result<Self, BuilderError> { |
| 60 | + let explorer = foundry_blob_explorers::Client::new_with_client( |
| 61 | + config.blob_explorer_url(), |
| 62 | + client.clone(), |
| 63 | + ); |
| 64 | + let this = self.with_async_source(BlobExplorerSource::new(explorer)); |
| 65 | + |
| 66 | + let this = match config.cl_url() { |
| 67 | + Some(cl) => { |
| 68 | + let url = url::Url::parse(cl)?; |
| 69 | + this.with_async_source(BeaconBlobSource::new(client.clone(), url)) |
| 70 | + } |
| 71 | + None => this, |
| 72 | + }; |
| 73 | + |
| 74 | + match config.pylon_url() { |
| 75 | + Some(pylon) => { |
| 76 | + let url = url::Url::parse(pylon)?; |
| 77 | + Ok(this.with_async_source(PylonBlobSource::new(client, url))) |
| 78 | + } |
| 79 | + None => Ok(this), |
| 80 | + } |
106 | 81 | } |
107 | | -} |
108 | | - |
109 | | -impl<Pool: TransactionPool> BlobFetcherBuilder<Pool> { |
110 | | - /// Build the [`BlobFetcher`] with the provided parameters. |
111 | | - pub fn build(self) -> Result<BlobFetcher<Pool>, BuilderError> { |
112 | | - let pool = self.pool.ok_or(BuilderError::MissingPool)?; |
113 | | - |
114 | | - let explorer_url = self.explorer_url.ok_or(BuilderError::MissingExplorerUrl)?; |
115 | | - |
116 | | - let cl_url = self.cl_url.map(parse_url).transpose()?; |
117 | 82 |
|
118 | | - let pylon_url = self.pylon_url.map(parse_url).transpose()?; |
119 | | - |
120 | | - let client = self.client.ok_or(BuilderError::MissingClient)?; |
121 | | - |
122 | | - let explorer = |
123 | | - foundry_blob_explorers::Client::new_with_client(explorer_url, client.clone()); |
124 | | - |
125 | | - Ok(BlobFetcher::new(pool, explorer, client, cl_url, pylon_url)) |
| 83 | + /// Build the [`BlobFetcher`]. |
| 84 | + pub fn build(self) -> BlobFetcher { |
| 85 | + BlobFetcher::new(self.sync_sources, self.async_sources) |
126 | 86 | } |
127 | 87 |
|
128 | | - /// Build a [`BlobCacher`] with the provided parameters. |
129 | | - pub fn build_cache(self) -> Result<BlobCacher<Pool>, BuilderError> |
130 | | - where |
131 | | - Pool: 'static, |
132 | | - { |
133 | | - let fetcher = self.build()?; |
134 | | - Ok(BlobCacher::new(fetcher)) |
| 88 | + /// Build a [`BlobCacher`] wrapping the constructed [`BlobFetcher`]. |
| 89 | + pub fn build_cache(self) -> BlobCacher { |
| 90 | + BlobCacher::new(self.build()) |
135 | 91 | } |
136 | 92 | } |
137 | | - |
138 | | -fn parse_url(url: String) -> Result<Url, BuilderError> { |
139 | | - Url::parse(url.as_ref()).map_err(BuilderError::Url) |
140 | | -} |
0 commit comments