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)]
77pub struct Selection<T> {
78 /// Filters where matching values should be included in the response
79 /// Default::default() means include everything
80 #[serde(default, flatten)]
81 pub include: T,
82 /// Filters where matching values should be excluded from the response
83 /// None means exclude nothing, Some(Default::default()) means exclude everything
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub exclude: Option<T>,
86}
87
88impl<T> From<Selection<T>> for AnyOf<Selection<T>> {
89 fn from(selection: Selection<T>) -> AnyOf<Selection<T>> {
90 AnyOf::new(selection)
91 }
92}
93
94impl<T> Selection<T> {
95 /// Create a new selection with the given filter.
96 ///
97 /// # Arguments
98 /// * `filter` - The filter to include in the selection
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// use hypersync_net_types::{LogSelection, LogFilter};
104 ///
105 /// // Create a selection that includes the filter
106 ///
107 /// let selection = LogSelection::new(
108 /// LogFilter::all().and_address(["0xdadB0d80178819F2319190D340ce9A924f783711"])?,
109 /// );
110 ///
111 /// Ok::<(), anyhow::Error>(())
112 /// ```
113 pub fn new(filter: T) -> Self {
114 Self {
115 include: filter,
116 exclude: None,
117 }
118 }
119
120 /// Add a filter to exclude from the selection
121 ///
122 /// # Arguments
123 /// * `filter` - The filter to exclude from the selection
124 ///
125 /// # Examples
126 ///
127 /// ```
128 /// use hypersync_net_types::{LogSelection, LogFilter};
129 ///
130 /// // Create a selection with a filter that matches any log. (or your own filter)
131 /// let all = LogSelection::new(LogFilter::all());
132 ///
133 /// // Create a selection that excludes only logs from a specific address
134 /// let selection = all.and_not(
135 /// LogFilter::all().and_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?,
136 /// );
137 ///
138 /// Ok::<(), anyhow::Error>(())
139 /// ```
140 pub fn and_not(mut self, filter: T) -> Self {
141 self.exclude = Some(filter);
142 self
143 }
144
145 /// Combine this selection with another selection using logical OR.
146 ///
147 /// This creates an `AnyOf` type that will match if either this selection
148 /// or the provided selection matches.
149 ///
150 /// # Arguments
151 /// * `selection` - Another selection to combine with this one
152 ///
153 /// # Returns
154 /// An `AnyOf<Self>` that represents the logical OR of both selections
155 ///
156 /// # Examples
157 ///
158 /// ```
159 /// use hypersync_net_types::{LogSelection, LogFilter};
160 ///
161 /// // Create two different selections
162 /// let selection1 = LogSelection::new(LogFilter::all())
163 /// .and_not(LogFilter::all().and_address(["0xdadB0d80178819F2319190D340ce9A924f783711"])?);
164 /// let selection2 = LogSelection::new(
165 /// LogFilter::all().and_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?,
166 /// );
167 ///
168 /// // Combine them with OR - matches logs from either address
169 /// let combined = selection1.or(selection2);
170 ///
171 /// Ok::<(), anyhow::Error>(())
172 /// ```
173 pub fn or(self, selection: Self) -> AnyOf<Self> {
174 AnyOf::new(self).or(selection)
175 }
176}
177
178impl<T> From<T> for Selection<T> {
179 fn from(include: T) -> Self {
180 Self {
181 include,
182 exclude: None,
183 }
184 }
185}
186
187pub trait CapnpBuilder<O: capnp::traits::Owned> {
188 fn populate_builder<'a>(&self, builder: &mut O::Builder<'a>) -> Result<(), capnp::Error>;
189}
190
191pub trait CapnpReader<O: capnp::traits::Owned> {
192 fn from_reader<'a>(reader: O::Reader<'a>) -> Result<Self, capnp::Error>
193 where
194 Self: Sized;
195}
196
197impl<O, T> CapnpBuilder<hypersync_net_types_capnp::selection::Owned<O>> for Selection<T>
198where
199 O: capnp::traits::Owned,
200 T: CapnpBuilder<O>,
201{
202 fn populate_builder<'a>(
203 &self,
204 builder: &mut hypersync_net_types_capnp::selection::Builder<'a, O>,
205 ) -> Result<(), capnp::Error> {
206 {
207 let mut include_builder = builder.reborrow().init_include();
208 self.include.populate_builder(&mut include_builder)?;
209 } // include borrow ends
210
211 if let Some(exclude) = &self.exclude {
212 let mut exclude_builder = builder.reborrow().init_exclude();
213 exclude.populate_builder(&mut exclude_builder)?;
214 } // exclude borrow ends
215
216 Ok(())
217 }
218}
219
220impl<O, T> CapnpReader<hypersync_net_types_capnp::selection::Owned<O>> for Selection<T>
221where
222 O: capnp::traits::Owned,
223 T: CapnpReader<O>,
224{
225 fn from_reader<'a>(
226 reader: hypersync_net_types_capnp::selection::Reader<'a, O>,
227 ) -> Result<Self, capnp::Error> {
228 let include = T::from_reader(reader.get_include()?)?;
229 let exclude = if reader.has_exclude() {
230 Some(T::from_reader(reader.get_exclude()?)?)
231 } else {
232 None
233 };
234 Ok(Self { include, exclude })
235 }
236}