hypersync_net_types/
lib.rs

1//! # HyperSync Network Types
2//!
3//! Core network types and query builders for the HyperSync protocol.
4//!
5//! This crate provides the fundamental types for constructing queries and handling
6//! responses when communicating with HyperSync servers. It supports both JSON
7//! and Cap'n Proto serialization formats for efficient network communication.
8//!
9//! ## Features
10//!
11//! - **Query builder API**: Fluent interface for building complex blockchain queries
12//! - **Type-safe filtering**: Strongly-typed filters for blocks, transactions, logs, and traces
13//! - **Field selection**: Choose exactly which data fields to retrieve
14//! - **Multiple serialization**: Support for JSON and Cap'n Proto protocols
15//! - **Validation**: Built-in query validation and optimization
16//!
17//! ## Key Types
18//!
19//! - [`Query`] - Main query builder for specifying blockchain data to retrieve
20//! - [`BlockFilter`] - Filter blocks by number, hash, or other criteria
21//! - [`TransactionFilter`] - Filter transactions by from/to addresses, value, etc.
22//! - [`LogFilter`] - Filter event logs by contract address, topics, etc.
23//! - [`TraceFilter`] - Filter execution traces by various criteria
24//!
25//! ## Example
26//!
27//! ```
28//! use hypersync_net_types::{Query, LogFilter, LogField};
29//!
30//! // Build a query for ERC20 transfer events
31//! let mut query = Query::new().from_block(19000000);
32//! query.to_block = Some(19001000);
33//! query = query.where_logs(
34//!     LogFilter::all()
35//!         .and_topic0(["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"])?
36//! ).select_log_fields([
37//!     LogField::Address,
38//!     LogField::Topic1,
39//!     LogField::Topic2,
40//!     LogField::Data,
41//! ]);
42//!
43//! println!("Query: {:?}", query);
44//! # Ok::<(), anyhow::Error>(())
45//! ```
46
47// Module declarations
48pub mod block;
49pub mod log;
50pub mod query;
51pub mod request;
52pub mod response;
53pub mod trace;
54pub mod transaction;
55pub mod types;
56
57// Cap'n Proto generated code
58mod __generated__;
59pub use __generated__::hypersync_net_types_capnp;
60
61// Re-export types from modules for backward compatibility and convenience
62pub use block::{BlockField, BlockFilter, BlockSelection};
63pub use log::{LogField, LogFilter, LogSelection};
64pub use query::{FieldSelection, JoinMode, Query};
65pub use response::{ArchiveHeight, ChainId, RollbackGuard};
66pub use trace::{TraceField, TraceFilter, TraceSelection};
67pub use transaction::{
68    AuthorizationSelection, TransactionField, TransactionFilter, TransactionSelection,
69};
70pub use types::Sighash;
71
72use serde::{Deserialize, Serialize};
73
74use crate::types::AnyOf;
75
76#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
77#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
78pub struct Selection<T> {
79    /// Filters where matching values should be included in the response
80    /// Default::default() means include everything
81    #[serde(default, flatten)]
82    pub include: T,
83    /// Filters where matching values should be excluded from the response
84    /// None means exclude nothing, Some(Default::default()) means exclude everything
85    #[serde(default, skip_serializing_if = "Option::is_none")]
86    pub exclude: Option<T>,
87}
88
89impl<T> From<Selection<T>> for AnyOf<Selection<T>> {
90    fn from(selection: Selection<T>) -> AnyOf<Selection<T>> {
91        AnyOf::new(selection)
92    }
93}
94
95impl<T> Selection<T> {
96    /// Create a new selection with the given filter.
97    ///
98    /// # Arguments
99    /// * `filter` - The filter to include in the selection
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use hypersync_net_types::{LogSelection, LogFilter};
105    ///
106    /// // Create a selection that includes the filter
107    ///
108    /// let selection = LogSelection::new(
109    ///     LogFilter::all().and_address(["0xdadB0d80178819F2319190D340ce9A924f783711"])?,
110    /// );
111    ///
112    /// Ok::<(), anyhow::Error>(())
113    /// ```
114    pub fn new(filter: T) -> Self {
115        Self {
116            include: filter,
117            exclude: None,
118        }
119    }
120
121    /// Add a filter to exclude from the selection
122    ///
123    /// # Arguments
124    /// * `filter` - The filter to exclude from the selection
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use hypersync_net_types::{LogSelection, LogFilter};
130    ///
131    /// // Create a selection with a filter that matches any log. (or your own filter)
132    /// let all = LogSelection::new(LogFilter::all());
133    ///
134    /// // Create a selection that excludes only logs from a specific address
135    /// let selection = all.and_not(
136    ///     LogFilter::all().and_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?,
137    /// );
138    ///
139    /// Ok::<(), anyhow::Error>(())
140    /// ```
141    pub fn and_not(mut self, filter: T) -> Self {
142        self.exclude = Some(filter);
143        self
144    }
145
146    /// Combine this selection with another selection using logical OR.
147    ///
148    /// This creates an `AnyOf` type that will match if either this selection
149    /// or the provided selection matches.
150    ///
151    /// # Arguments
152    /// * `selection` - Another selection to combine with this one
153    ///
154    /// # Returns
155    /// An `AnyOf<Self>` that represents the logical OR of both selections
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// use hypersync_net_types::{LogSelection, LogFilter};
161    ///
162    /// // Create two different selections
163    /// let selection1 = LogSelection::new(LogFilter::all())
164    ///     .and_not(LogFilter::all().and_address(["0xdadB0d80178819F2319190D340ce9A924f783711"])?);
165    /// let selection2 = LogSelection::new(
166    ///     LogFilter::all().and_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?,
167    /// );
168    ///
169    /// // Combine them with OR - matches logs from either address
170    /// let combined = selection1.or(selection2);
171    ///
172    /// Ok::<(), anyhow::Error>(())
173    /// ```
174    pub fn or(self, selection: Self) -> AnyOf<Self> {
175        AnyOf::new(self).or(selection)
176    }
177}
178
179impl<T> From<T> for Selection<T> {
180    fn from(include: T) -> Self {
181        Self {
182            include,
183            exclude: None,
184        }
185    }
186}
187
188pub trait CapnpBuilder<O: capnp::traits::Owned> {
189    fn populate_builder<'a>(&self, builder: &mut O::Builder<'a>) -> Result<(), capnp::Error>;
190}
191
192pub trait CapnpReader<O: capnp::traits::Owned> {
193    fn from_reader<'a>(reader: O::Reader<'a>) -> Result<Self, capnp::Error>
194    where
195        Self: Sized;
196}
197
198impl<O, T> CapnpBuilder<hypersync_net_types_capnp::selection::Owned<O>> for Selection<T>
199where
200    O: capnp::traits::Owned,
201    T: CapnpBuilder<O>,
202{
203    fn populate_builder<'a>(
204        &self,
205        builder: &mut hypersync_net_types_capnp::selection::Builder<'a, O>,
206    ) -> Result<(), capnp::Error> {
207        {
208            let mut include_builder = builder.reborrow().init_include();
209            self.include.populate_builder(&mut include_builder)?;
210        } // include borrow ends
211
212        if let Some(exclude) = &self.exclude {
213            let mut exclude_builder = builder.reborrow().init_exclude();
214            exclude.populate_builder(&mut exclude_builder)?;
215        } // exclude borrow ends
216
217        Ok(())
218    }
219}
220
221impl<O, T> CapnpReader<hypersync_net_types_capnp::selection::Owned<O>> for Selection<T>
222where
223    O: capnp::traits::Owned,
224    T: CapnpReader<O>,
225{
226    fn from_reader<'a>(
227        reader: hypersync_net_types_capnp::selection::Reader<'a, O>,
228    ) -> Result<Self, capnp::Error> {
229        let include = T::from_reader(reader.get_include()?)?;
230        let exclude = if reader.has_exclude() {
231            Some(T::from_reader(reader.get_exclude()?)?)
232        } else {
233            None
234        };
235        Ok(Self { include, exclude })
236    }
237}