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}