-
Notifications
You must be signed in to change notification settings - Fork 6
Implement Query Builder API with Docs #88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughReplaces JSON query literals with a typed, fluent Query/FieldSelection builder across examples; renames Cap’n Proto root from Query → Request (adds queryId and shouldCache) and updates client wiring to build request payloads; adds builder-style filters and TryFrom for FixedSizeData. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor App
participant Examples as examples/*/main.rs
participant NetTypes as net_types (Query/Request)
participant Client as hypersync-client
participant Server as Hypersync Service
App->>Examples: Build Query via fluent API
Examples->>NetTypes: Query::select_*(), .where_*(...), .from_block(...)
Examples->>Client: client.get(Request or Query)
Client->>NetTypes: request::Builder::build_full_query_from_query(...) or build_query_id_from_query(...)
Client->>Server: Send Request { body: query | queryId, shouldCache?, blockRange }
Server-->>Client: Response (results)
Client-->>App: Streamed results
sequenceDiagram
autonumber
participant NetTypes as net_types::Request
participant Client as hypersync-client
participant Server as Hypersync Service
NetTypes->>Client: QueryId creation (canonicalize & hash)
Client->>Server: Send Request { queryId, blockRange }
Server-->>Client: Cached response path
Client-->>NetTypes: Deserialize result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
e5d1a95 to
da5d20a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
hypersync-net-types/src/query.rs (2)
173-177: Typo in documentation: “Weather” → “Whether”.Fix the comment on include_all_blocks.
589-616: u64 ⇄ usize conversions can truncate on 32-bit; make fallible.Reading from capnp uses
as usize; this can truncate values >usize::MAXon 32‑bit targets. Prefer fallible conversion and error out. Example patch (applies similarly to tx/log/trace counts):- let value = max_blocks_reader.get_value(); - Some(value as usize) + let value = max_blocks_reader.get_value(); + let v = usize::try_from(value) + .map_err(|_| capnp::Error::failed("max_num_blocks exceeds usize".to_string()))?; + Some(v)Writing is fine (
usize→u64), but stays symmetric after the read fix.Also applies to: 882-897
examples/call_watch/src/main.rs (1)
47-60: Correct the transfer log text.Typo: the message prints “DAU transfer” instead of “DAI transfer,” which is confusing for users reading the output.
♻️ Duplicate comments (2)
examples/wallet/Cargo.toml (1)
13-13: Same version‑skew note as call_watchAlign anyhow version via workspace to match other examples (e.g., all_erc20). Keeps builds lean.
examples/reverse_wallet/Cargo.toml (1)
13-13: Consolidate anyhow versionPin through [workspace.dependencies] and reference here with
{ workspace = true }to avoid multiple anyhow versions.
🧹 Nitpick comments (10)
examples/watch/Cargo.toml (1)
13-13: Confirmed usage; inconsistent anyhow versions across examples—refactor recommended.Verification shows anyhow is actively used in
examples/watch/src/main.rs(returnsanyhow::Result<()>). However, anyhow versions are inconsistent: five examples pin1.0.89, butexamples/all_erc20pins1.0.100. While patch-level differences are compatible, this inconsistency is a maintenance concern.Recommended refactor:
- Align all examples to the same version, or
- Switch to a caret range (
anyhow = "1") for automatic patch upgrades, or- Promote anyhow to
[workspace.dependencies]and useanyhow.workspace = trueacross all examples for consistency.hypersync-format/src/types/fixed_size_data.rs (1)
131-137: Ergonomic addition; minor idiomatic polish + add tests for TryFrom pathsThe impls are correct and consistent with FromStr. Optionally tighten style by using
s.parse::<Self>()(clearer, no UFCS), and add unit tests covering both&strandStringconversions.Apply this small polish:
impl<const N: usize> TryFrom<&str> for FixedSizeData<N> { type Error = Error; fn try_from(s: &str) -> StdResult<Self, Self::Error> { - std::str::FromStr::from_str(s) + s.parse::<Self>() } } impl<const N: usize> TryFrom<String> for FixedSizeData<N> { type Error = Error; fn try_from(s: String) -> StdResult<Self, Self::Error> { - std::str::FromStr::from_str(&s) + s.parse::<Self>() } }Add focused tests:
@@ fn test_display() { let data = FSD4::from(hex!("42feed00")); // Check that Display prints the 0x-prefixed hex assert_eq!(data.to_string(), "0x42feed00"); } + + #[test] + fn test_try_from_str_valid() { + let data = FSD4::try_from("0x00420000").expect("valid 4-byte hex via &str"); + assert_eq!(data, FSD4::from(hex!("00420000"))); + } + + #[test] + fn test_try_from_string_valid() { + let s = String::from("0x42000000"); + let data = FSD4::try_from(s).expect("valid 4-byte hex via String"); + assert_eq!(data, FSD4::from(hex!("42000000"))); + } + + #[test] + fn test_try_from_str_missing_prefix_err() { + let data = FSD4::try_from("00420000"); + assert!(data.is_err()); + } }Please run the test suite to confirm the new cases pass and no trait coherence warnings appear.
Also applies to: 139-145
examples/all_erc20/Cargo.toml (1)
14-15: Normalize anyhow across examplesThis crate uses anyhow = "1.0.100" while others use 1.0.89. Suggest centralizing in [workspace.dependencies] and referencing from each example.
I can draft the workspace stanza if you want.
hypersync-client/src/lib.rs (1)
507-509: Nit: remove duplicate content-type headerYou set
"content-type: application/x-capnp"twice in this function (earlier and again before.body(full_query_bytes)). Drop the second call.Apply:
- let res = req - .header("content-type", "application/x-capnp") + let res = req .body(full_query_bytes) .send()hypersync-net-types/src/block.rs (1)
21-143: Nice builder API with contextual errors
any(),and_hash_any, andand_miner_address_anyare clear and consistent with other filters. Docs are helpful.Optional: add single‑value helpers (
and_hash,and_miner_address) that forward to the*_anyforms for common cases.examples/call_watch/Cargo.toml (1)
13-13: Unifyanyhowunderworkspace.dependenciesAdd to your root Cargo.toml:
[workspace.dependencies] anyhow = "1.0.100"Then in each member’s Cargo.toml replace any
anyhow = "…"(e.g."1.0.89","1.0.100"or"1") with:anyhow = { workspace = true }This ensures a single
anyhowversion and avoids duplicate builds.hypersync-net-types/src/query.rs (4)
280-289: Builder semantics:match_*_anyreplace previous selections.Current methods overwrite existing vectors, which may surprise users chaining multiple calls. Either:
- Document that they replace prior selections, or
- Change to extend existing selections (or add
add_*_anyvariants that append).Happy to provide a patch for either approach.
Also applies to: 323-332, 369-378, 421-429
907-914: Avoid initializing empty capnp lists/structs; align with serde “skip if default”.
- You always
init_field_selection()even when empty; and callinit_*with length 0 for lists. This sets non-null pointers, sohas_*()becomes true though logically empty.- Guard inits to reduce payload and keep presence semantics clean.
Example refactor:
- { - let mut field_selection = self.reborrow().init_field_selection(); - query.field_selection.populate_builder(&mut field_selection)?; - } + if !(query.field_selection.block.is_empty() + && query.field_selection.transaction.is_empty() + && query.field_selection.log.is_empty() + && query.field_selection.trace.is_empty()) + { + let mut fs = self.reborrow().init_field_selection(); + query.field_selection.populate_builder(&mut fs)?; + } - { - let mut logs_list = self.reborrow().init_logs(query.logs.len() as u32); + if !query.logs.is_empty() { + let mut logs_list = self.reborrow().init_logs(query.logs.len() as u32); ... - } + }Apply similarly to transactions, traces, and blocks.
Also applies to: 917-952
969-974: Clarify error message when body is QueryId.Message reads as if QueryId “cannot be read … with QueryBody”. Suggest:
- "QueryId cannot be read from capnp request with QueryBody" + "expected request.body=Query, got QueryId"Small polish for debuggability.
409-416: Trace call type literals: please verify accepted values.Example uses
"create"and"suicide". Ensure these match yourTraceCallType(or equivalent) canonical names; many APIs usecreate/selfdestruct. Update docs for consistency if needed.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
hypersync-net-types/src/__generated__/hypersync_net_types_capnp.rsis excluded by!**/__generated__/**
📒 Files selected for processing (22)
examples/all_erc20/Cargo.toml(1 hunks)examples/all_erc20/src/main.rs(4 hunks)examples/call_decode_output/src/main.rs(2 hunks)examples/call_watch/Cargo.toml(1 hunks)examples/call_watch/src/main.rs(1 hunks)examples/reverse_wallet/Cargo.toml(1 hunks)examples/reverse_wallet/src/main.rs(2 hunks)examples/wallet/Cargo.toml(1 hunks)examples/wallet/src/main.rs(4 hunks)examples/watch/Cargo.toml(1 hunks)examples/watch/src/main.rs(2 hunks)hypersync-client/src/lib.rs(2 hunks)hypersync-format/src/types/fixed_size_data.rs(1 hunks)hypersync-net-types/benches/compression.rs(3 hunks)hypersync-net-types/hypersync_net_types.capnp(1 hunks)hypersync-net-types/src/block.rs(2 hunks)hypersync-net-types/src/lib.rs(3 hunks)hypersync-net-types/src/log.rs(3 hunks)hypersync-net-types/src/query.rs(6 hunks)hypersync-net-types/src/request.rs(1 hunks)hypersync-net-types/src/trace.rs(2 hunks)hypersync-net-types/src/transaction.rs(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (13)
hypersync-net-types/src/block.rs (3)
hypersync-net-types/src/log.rs (1)
any(25-27)hypersync-net-types/src/transaction.rs (3)
any(21-23)any(168-170)and_hash_any(475-490)hypersync-net-types/src/trace.rs (1)
any(52-54)
hypersync-net-types/src/log.rs (2)
hypersync-net-types/src/transaction.rs (3)
any(21-23)any(168-170)and_address_any(98-114)hypersync-net-types/src/trace.rs (2)
any(52-54)and_address_any(195-211)
hypersync-net-types/src/request.rs (2)
hypersync-net-types/src/query.rs (8)
new(228-230)new(708-710)from_reader(957-977)from_reader(1027-1068)from_block(232-235)to_block(237-240)from_capnp_query_body_reader(573-673)log(819-822)hypersync-net-types/src/lib.rs (4)
from(98-103)new(64-69)from_reader(111-113)from_reader(144-154)
examples/call_watch/src/main.rs (6)
hypersync-net-types/src/transaction.rs (1)
TransactionField(1188-1191)examples/call_decode_output/src/main.rs (1)
main(14-85)examples/reverse_wallet/src/main.rs (1)
main(12-86)examples/wallet/src/main.rs (1)
main(18-128)examples/watch/src/main.rs (1)
main(13-80)hypersync-client/src/decode_call.rs (1)
from_signatures(27-40)
hypersync-net-types/src/transaction.rs (3)
hypersync-net-types/src/log.rs (2)
any(25-27)and_address_any(66-82)hypersync-net-types/src/block.rs (2)
any(26-28)and_hash_any(67-82)hypersync-net-types/src/trace.rs (6)
any(52-54)and_address_any(195-211)and_from_address_any(94-110)and_to_address_any(147-163)and_sighash_any(345-361)and_type_any(300-307)
examples/all_erc20/src/main.rs (2)
hypersync-net-types/src/query.rs (3)
log(819-822)new(228-230)new(708-710)hypersync-net-types/src/log.rs (2)
LogField(491-494)any(25-27)
examples/watch/src/main.rs (3)
hypersync-net-types/src/log.rs (1)
LogField(491-494)examples/wallet/src/main.rs (1)
main(18-128)examples/all_erc20/src/main.rs (1)
main(17-113)
examples/wallet/src/main.rs (3)
examples/call_watch/src/main.rs (1)
main(13-82)examples/watch/src/main.rs (1)
main(13-80)examples/all_erc20/src/main.rs (1)
main(17-113)
examples/reverse_wallet/src/main.rs (3)
examples/call_decode_output/src/main.rs (1)
main(14-85)examples/call_watch/src/main.rs (1)
main(13-82)examples/wallet/src/main.rs (1)
main(18-128)
hypersync-net-types/src/trace.rs (2)
hypersync-net-types/src/log.rs (2)
any(25-27)and_address_any(66-82)hypersync-net-types/src/transaction.rs (7)
any(21-23)any(168-170)and_from_address_any(209-225)and_to_address_any(261-277)and_address_any(98-114)and_type_any(383-389)and_sighash_any(314-330)
hypersync-net-types/src/lib.rs (5)
hypersync-net-types/src/query.rs (4)
block(742-745)log(819-822)trace(870-873)transaction(780-783)hypersync-net-types/src/block.rs (1)
BlockField(391-394)hypersync-net-types/src/log.rs (1)
LogField(491-494)hypersync-net-types/src/trace.rs (1)
TraceField(756-759)hypersync-net-types/src/transaction.rs (1)
TransactionField(1188-1191)
examples/call_decode_output/src/main.rs (2)
hypersync-net-types/src/trace.rs (1)
TraceField(756-759)hypersync-net-types/src/lib.rs (1)
new(64-69)
hypersync-net-types/src/query.rs (2)
hypersync-net-types/src/lib.rs (3)
new(64-69)from_reader(111-113)from_reader(144-154)hypersync-net-types/src/request.rs (1)
from_reader(71-103)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: test_release
- GitHub Check: test_dev
🔇 Additional comments (4)
hypersync-client/src/lib.rs (1)
448-452: Cap’n Proto switch to request root looks correctUsing
request::Builderandbuild_query_id_from_query(query)?is consistent with the new schema. Confirm server semantics: for the queryId path you don’t setshouldCache; for the full query you pass it viabuild_full_query_from_query(query, should_cache). If the server expectsshouldCacheonly on full queries, this is fine.hypersync-net-types/benches/compression.rs (1)
330-330: Root switch to request::{Builder,Reader} is consistentEncoding/decoding now uses the new Request root and continues to round-trip via
Query::from_reader(...). Looks good.Also applies to: 351-352, 360-361
hypersync-net-types/hypersync_net_types.capnp (1)
239-250: Confirm struct ID continuity and add wire-compat testRenaming
Query→Requestwith explicit@0xbe854ee336b40054preserves on-wire compatibility; please add a CI test that decodes legacyQuerybytes intoRequest::Readerto validate the ID matches.hypersync-net-types/src/query.rs (1)
688-874: FieldSelection builder API: solid, deterministic, and ergonomic.Using BTreeSet ensures deterministic ordering; chaining methods read well. Nice work.
examples/call_watch/src/main.rs
Outdated
| .match_transactions_any([ | ||
| // The logs we want. We will also automatically get transactions and blocks relating to these logs (the query implicitly joins them). | ||
| // We want all DAI transfers so no address filter and only a filter for the first topic | ||
| hypersync_client::net_types::TransactionFilter::any() | ||
| .and_from_address_any([DAI_ADDRESS])? | ||
| .and_to_address_any([DAI_ADDRESS])?, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the DAI transaction filter logic.
Requiring both the from and to address to equal the DAI contract means only self-calls from the contract survive, so regular transfer/transferFrom calls (user → DAI) are filtered out. The stream will appear empty. Drop the from constraint (or replace it with the intended caller set) so you still capture real transfers.
- .match_transactions_any([
- hypersync_client::net_types::TransactionFilter::any()
- .and_from_address_any([DAI_ADDRESS])?
- .and_to_address_any([DAI_ADDRESS])?,
- ])
+ .match_transactions_any([
+ hypersync_client::net_types::TransactionFilter::any()
+ .and_to_address_any([DAI_ADDRESS])?,
+ ])📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .match_transactions_any([ | |
| // The logs we want. We will also automatically get transactions and blocks relating to these logs (the query implicitly joins them). | |
| // We want all DAI transfers so no address filter and only a filter for the first topic | |
| hypersync_client::net_types::TransactionFilter::any() | |
| .and_from_address_any([DAI_ADDRESS])? | |
| .and_to_address_any([DAI_ADDRESS])?, | |
| .match_transactions_any([ | |
| // The logs we want. We will also automatically get transactions and blocks relating to these logs (the query implicitly joins them). | |
| // We want all DAI transfers so no address filter and only a filter for the first topic | |
| hypersync_client::net_types::TransactionFilter::any() | |
| .and_to_address_any([DAI_ADDRESS])?, | |
| ]) |
🤖 Prompt for AI Agents
In examples/call_watch/src/main.rs around lines 23 to 28, the transaction filter
currently requires both from and to to equal the DAI contract which only matches
self-calls; remove the from-address constraint so normal transfers (user → DAI)
are captured. Specifically, delete the .and_from_address_any([DAI_ADDRESS])?
call (or replace it with the intended caller whitelist if you meant to restrict
callers), leaving the .and_to_address_any([DAI_ADDRESS])? in place and ensuring
the chain of builder calls and error handling remain valid.
examples/reverse_wallet/src/main.rs
Outdated
| .match_transactions_any([TransactionFilter::any() | ||
| .and_from_address_any([address])? | ||
| .and_to_address_any([address])?]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the transaction filter OR semantics.
Chaining .and_from_address_any([address])? and .and_to_address_any([address])? on the same TransactionFilter now requires every transaction to have both sender and recipient equal to address, which effectively filters out the transfers this example is supposed to surface. Please keep these predicates in separate filters so match_transactions_any retains its intended OR semantics.
- .match_transactions_any([TransactionFilter::any()
- .and_from_address_any([address])?
- .and_to_address_any([address])?]);
+ .match_transactions_any([
+ TransactionFilter::any().and_from_address_any([address])?,
+ TransactionFilter::any().and_to_address_any([address])?,
+ ]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .match_transactions_any([TransactionFilter::any() | |
| .and_from_address_any([address])? | |
| .and_to_address_any([address])?]); | |
| .match_transactions_any([ | |
| TransactionFilter::any().and_from_address_any([address])?, | |
| TransactionFilter::any().and_to_address_any([address])?, | |
| ]); |
🤖 Prompt for AI Agents
In examples/reverse_wallet/src/main.rs around lines 29 to 31, the code chains
.and_from_address_any([address])? and .and_to_address_any([address])? on the
same TransactionFilter which produces an AND (both sender and recipient must
match) instead of the intended OR; fix this by creating two separate
TransactionFilter instances (one with .and_from_address_any([address])? and
another with .and_to_address_any([address])?) and pass them as separate entries
to .match_transactions_any([...]) so the filters are evaluated with OR
semantics.
| /// ``` | ||
| /// use hypersync_net_types::{ | ||
| /// Query, FieldSelection, LogFilter, BlockFilter, TransactionFilter, | ||
| /// block::BlockField, log::LogField, transaction::TransactionField | ||
| /// }; | ||
| /// | ||
| /// // Simple log query for USDT transfers | ||
| /// let usdt_transfers = Query::new() | ||
| /// .from_block(18_000_000) | ||
| /// .to_block(18_001_000) | ||
| /// .select_fields( | ||
| /// FieldSelection::new() | ||
| /// .block([BlockField::Number, BlockField::Timestamp]) | ||
| /// .log([LogField::Address, LogField::Data, LogField::Topic0, LogField::Topic1, LogField::Topic2]) | ||
| /// .transaction([TransactionField::Hash, TransactionField::From, TransactionField::To]) | ||
| /// ) | ||
| /// .match_logs_any([ | ||
| /// LogFilter::any() | ||
| /// .and_address_any(["0xdac17f958d2ee523a2206206994597c13d831ec7"])? // USDT contract | ||
| /// .and_topic0_any(["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"])? // Transfer event | ||
| /// ]); | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| /// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Doctests will fail due to ? in code blocks; also one example mislabeled.
- Most examples use
?but aren’t wrapped in afn main() -> anyhow::Result<()> { ... }(hidden lines) or markedno_run/ignore. Rustdoc will compile these and fail. Wrap each example with hidden Result-returning main or addrust,no_runto the fence. - In the “Include all blocks” section, the snippet labeled “Normal mode - only blocks with matching logs” calls
.include_all_blocks(), which contradicts the comment. Remove that call or fix the label.
I can patch the fences (hidden wrapper) across these blocks if you want.
Also applies to: 75-102, 117-131, 254-279, 301-323, 345-369, 396-420, 545-567
🏁 Script executed:
#!/bin/bash
# Print the first 100 lines to inspect doc examples around lines 32-55
sed -n '1,100p' hypersync-net-types/src/query.rsLength of output: 4567
🏁 Script executed:
#!/bin/bash
# Print lines 75-140 to inspect the Include all blocks example and labels
sed -n '75,140p' hypersync-net-types/src/query.rsLength of output: 2891
🏁 Script executed:
#!/bin/bash
# Print lines 140-200 to inspect the “Include all blocks” example and labels
sed -n '140,200p' hypersync-net-types/src/query.rsLength of output: 3787
Fix doctest wrappers and correct typo in include_all_blocks doc
- Wrap each example using the
?operator in afn main() -> anyhow::Result<()> { … }(hidden with#) or mark the fence asrust,no_runso Rustdoc compiles without errors (applies to all code blocks throughout the file). - In the
Querystruct doc comment, change “Weather to include all blocks…” to “Whether to include all blocks…”.
🤖 Prompt for AI Agents
In hypersync-net-types/src/query.rs around lines 32 to 55, the doctest examples
use the `?` operator unwrapped and one docstring contains a typo ("Weather"
should be "Whether"); update all offending code blocks in this file by either
wrapping examples that use `?` in a hidden `# fn main() -> anyhow::Result<()> {
... }` wrapper or change the code fence to `rust,no_run` so rustdoc can compile
them, and correct the typo in the `Query` struct doc comment from "Weather to
include all blocks…" to "Whether to include all blocks…".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
hypersync-net-types/src/query.rs (1)
32-688: Docs still break doctests and contain unresolved nits.
All of theseQueryexamples continue to use?without hiding afn main() -> anyhow::Result<()> { … }, socargo test --docstill fails. Please wrap each fence (or mark itrust,no_run) the same way across this doc. Also, theinclude_all_blocksfield docs still say “Weather to include…” and the “Normal mode – only blocks with matching logs” example still calls.include_all_blocks(), contradicting the label. Those were raised earlier and need to be corrected.-/// ``` -/// use hypersync_net_types::{ Query, FieldSelection, LogFilter, BlockFilter, TransactionFilter }; +/// ``` +/// # fn main() -> anyhow::Result<()> { +/// use hypersync_net_types::{ Query, FieldSelection, LogFilter, BlockFilter, TransactionFilter }; /// let usdt_transfers = Query::new() /// .from_block(18_000_000) /// .where_logs([/* … */])?; -/// # Ok::<(), anyhow::Error>(()) +/// # Ok::<(), anyhow::Error>(()) +/// # } /// ``` @@ - /// Weather to include all blocks regardless of if they are related… + /// Whether to include all blocks regardless of whether they are related… @@ - /// // Normal mode - only blocks with matching logs + /// // Normal mode - only blocks with matching logs /// let query = Query::new() /// .from_block(18_000_000) -/// .include_all_blocks() /// .where_logs([/* … */]);examples/call_watch/src/main.rs (1)
26-28: Restore DAI transfer filter to OR semantics.
Chaining.and_from_address([DAI_ADDRESS])?with.and_to_address([DAI_ADDRESS])?forces both sender and recipient to be the DAI contract, so every real transfer (user → DAI or DAI → user) is filtered out and the example streams nothing. Drop thefromrestriction (or move it into a separate filter) so transfers are actually captured.hypersync_client::net_types::TransactionFilter::any() - .and_from_address([DAI_ADDRESS])? .and_to_address([DAI_ADDRESS])?,examples/reverse_wallet/src/main.rs (1)
29-31: Split sender/recipient filters so transfers aren’t dropped.
Combining.and_from_address([address])?and.and_to_address([address])?on the sameTransactionFilterdemands both ends equaladdress, killing every real transfer. Provide two filters somatch_transactions_anykeeps its OR behavior.- .where_transactions([TransactionFilter::any() - .and_from_address([address])? - .and_to_address([address])?]); + .where_transactions([ + TransactionFilter::any().and_from_address([address])?, + TransactionFilter::any().and_to_address([address])?, + ]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
examples/all_erc20/src/main.rs(4 hunks)examples/call_decode_output/src/main.rs(3 hunks)examples/call_watch/src/main.rs(1 hunks)examples/reverse_wallet/src/main.rs(2 hunks)examples/wallet/src/main.rs(4 hunks)examples/watch/src/main.rs(2 hunks)hypersync-net-types/src/block.rs(4 hunks)hypersync-net-types/src/lib.rs(3 hunks)hypersync-net-types/src/log.rs(5 hunks)hypersync-net-types/src/query.rs(6 hunks)hypersync-net-types/src/trace.rs(5 hunks)hypersync-net-types/src/transaction.rs(8 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
hypersync-net-types/src/block.rs (2)
hypersync-net-types/src/transaction.rs (3)
any(21-23)any(168-170)and_hash(475-490)hypersync-net-types/src/query.rs (3)
new(219-221)new(828-830)test_query_serde(1197-1248)
examples/all_erc20/src/main.rs (2)
hypersync-net-types/src/log.rs (2)
LogField(491-494)any(25-27)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)
examples/watch/src/main.rs (3)
hypersync-net-types/src/log.rs (2)
LogField(491-494)any(25-27)examples/all_erc20/src/main.rs (1)
main(17-111)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)
examples/wallet/src/main.rs (6)
hypersync-net-types/src/log.rs (2)
LogField(491-494)any(25-27)hypersync-net-types/src/transaction.rs (3)
TransactionField(1188-1191)any(21-23)any(168-170)examples/all_erc20/src/main.rs (1)
main(17-111)examples/call_watch/src/main.rs (1)
main(13-80)examples/watch/src/main.rs (1)
main(13-78)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)
hypersync-net-types/src/log.rs (4)
hypersync-net-types/src/block.rs (1)
any(26-28)hypersync-net-types/src/trace.rs (2)
any(52-54)and_address(195-211)hypersync-net-types/src/transaction.rs (3)
any(21-23)any(168-170)and_address(98-114)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)
hypersync-net-types/src/lib.rs (5)
hypersync-net-types/src/query.rs (6)
block(862-865)log(939-942)trace(990-993)transaction(900-903)new(219-221)new(828-830)hypersync-net-types/src/block.rs (1)
BlockField(391-394)hypersync-net-types/src/log.rs (1)
LogField(491-494)hypersync-net-types/src/trace.rs (1)
TraceField(756-759)hypersync-net-types/src/transaction.rs (1)
TransactionField(1188-1191)
hypersync-net-types/src/transaction.rs (4)
hypersync-net-types/src/block.rs (2)
any(26-28)and_hash(67-82)hypersync-net-types/src/log.rs (2)
any(25-27)and_address(66-82)hypersync-net-types/src/trace.rs (5)
any(52-54)and_address(195-211)and_from_address(94-110)and_to_address(147-163)and_sighash(345-361)hypersync-net-types/src/query.rs (3)
new(219-221)new(828-830)test_query_serde(1197-1248)
examples/reverse_wallet/src/main.rs (2)
hypersync-net-types/src/transaction.rs (2)
any(21-23)any(168-170)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)
examples/call_decode_output/src/main.rs (2)
hypersync-net-types/src/trace.rs (2)
TraceField(756-759)any(52-54)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)
examples/call_watch/src/main.rs (4)
hypersync-net-types/src/transaction.rs (3)
TransactionField(1188-1191)any(21-23)any(168-170)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)hypersync-client/src/lib.rs (1)
new(71-94)hypersync-client/src/decode_call.rs (1)
from_signatures(27-40)
hypersync-net-types/src/trace.rs (4)
hypersync-net-types/src/block.rs (2)
any(26-28)all(272-275)hypersync-net-types/src/log.rs (3)
any(25-27)and_address(66-82)all(426-429)hypersync-net-types/src/transaction.rs (8)
any(21-23)any(168-170)and_from_address(209-225)and_to_address(261-277)and_address(98-114)and_type(383-389)and_sighash(314-330)all(939-942)hypersync-net-types/src/query.rs (2)
new(219-221)new(828-830)
hypersync-net-types/src/query.rs (2)
hypersync-net-types/src/lib.rs (3)
new(64-69)from_reader(111-113)from_reader(144-154)hypersync-net-types/src/request.rs (1)
from_reader(71-103)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test_release
🔇 Additional comments (4)
examples/all_erc20/src/main.rs (1)
29-43: Builder migration matches prior JSON semantics.
from_block, the ERC20 transfer topic0 filter, and the explicit topic/data field selection line up with the old literal and keep the intent readable. Looks great.examples/watch/src/main.rs (1)
24-38: Clearer log filter composition looks good.The fluent
LogFilterchain mirrors the prior JSON while making the DAI address and transfer topic constraints explicit—no issues spotted.hypersync-net-types/src/block.rs (1)
67-142: Well-scoped builder helpers.
and_hash/and_miner_addressprovide the fluent ergonomics we need and wrap conversion failures with index-aware context, which will make misconfigured filters much easier to debug.hypersync-net-types/src/log.rs (1)
84-278: Topic builder handles sparse indices cleanly.The guard on topic index ≤3 plus the pre-fill of missing slots keeps serialization consistent while letting callers jump straight to topic3—nicely done.
| impl TraceFilter { | ||
| /// Create a trace filter that matches any trace. | ||
| /// | ||
| /// This creates an empty filter with no constraints, which will match all traces. | ||
| /// You can then use the builder methods to add specific filtering criteria. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Create a filter that matches any trace | ||
| /// let filter = TraceFilter::any(); | ||
| /// | ||
| /// // Chain with other filter methods | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_from_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?; | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn any() -> Self { | ||
| Default::default() | ||
| } | ||
|
|
||
| /// Filter traces by any of the provided "from" addresses. | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `Address`. | ||
| /// Common input types include string slices, byte arrays, and `Address` objects. | ||
| /// The "from" address typically represents the caller or originator of the trace. | ||
| /// | ||
| /// # Arguments | ||
| /// * `addresses` - An iterable of addresses to filter by | ||
| /// | ||
| /// # Returns | ||
| /// * `Ok(Self)` - The updated filter on success | ||
| /// * `Err(anyhow::Error)` - If any address fails to convert | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Filter by a single caller address | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_from_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?; | ||
| /// | ||
| /// // Filter by multiple caller addresses | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_from_address([ | ||
| /// "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", | ||
| /// "0xdac17f958d2ee523a2206206994597c13d831ec7", | ||
| /// ])?; | ||
| /// | ||
| /// // Using byte arrays | ||
| /// let caller_address = [ | ||
| /// 0xa0, 0xb8, 0x6a, 0x33, 0xe6, 0xc1, 0x1c, 0x8c, 0x0c, 0x5c, | ||
| /// 0x0b, 0x5e, 0x6a, 0xde, 0xe3, 0x0d, 0x1a, 0x23, 0x45, 0x67 | ||
| /// ]; | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_from_address([caller_address])?; | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn and_from_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self> | ||
| where | ||
| I: IntoIterator<Item = A>, | ||
| A: TryInto<Address>, | ||
| A::Error: std::error::Error + Send + Sync + 'static, | ||
| { | ||
| let mut converted_addresses: Vec<Address> = Vec::new(); | ||
| for (idx, address) in addresses.into_iter().enumerate() { | ||
| converted_addresses.push( | ||
| address | ||
| .try_into() | ||
| .with_context(|| format!("invalid from address at position {idx}"))?, | ||
| ); | ||
| } | ||
| self.from = converted_addresses; | ||
| Ok(self) | ||
| } | ||
|
|
||
| /// Filter traces by any of the provided "to" addresses. | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `Address`. | ||
| /// Common input types include string slices, byte arrays, and `Address` objects. | ||
| /// The "to" address typically represents the target or recipient of the trace. | ||
| /// | ||
| /// # Arguments | ||
| /// * `addresses` - An iterable of addresses to filter by | ||
| /// | ||
| /// # Returns | ||
| /// * `Ok(Self)` - The updated filter on success | ||
| /// * `Err(anyhow::Error)` - If any address fails to convert | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Filter by a single target address | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_to_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?; | ||
| /// | ||
| /// // Filter by multiple target addresses | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_to_address([ | ||
| /// "0xdac17f958d2ee523a2206206994597c13d831ec7", | ||
| /// "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", | ||
| /// ])?; | ||
| /// | ||
| /// // Chain with from address filtering | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_from_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])? | ||
| /// .and_to_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?; | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn and_to_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self> | ||
| where | ||
| I: IntoIterator<Item = A>, | ||
| A: TryInto<Address>, | ||
| A::Error: std::error::Error + Send + Sync + 'static, | ||
| { | ||
| let mut converted_addresses: Vec<Address> = Vec::new(); | ||
| for (idx, address) in addresses.into_iter().enumerate() { | ||
| converted_addresses.push( | ||
| address | ||
| .try_into() | ||
| .with_context(|| format!("invalid to address at position {idx}"))?, | ||
| ); | ||
| } | ||
| self.to = converted_addresses; | ||
| Ok(self) | ||
| } | ||
|
|
||
| /// Filter traces by any of the provided contract addresses. | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `Address`. | ||
| /// Common input types include string slices, byte arrays, and `Address` objects. | ||
| /// The address field typically represents the contract address involved in the trace. | ||
| /// | ||
| /// # Arguments | ||
| /// * `addresses` - An iterable of addresses to filter by | ||
| /// | ||
| /// # Returns | ||
| /// * `Ok(Self)` - The updated filter on success | ||
| /// * `Err(anyhow::Error)` - If any address fails to convert | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Filter by a single contract address | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?; | ||
| /// | ||
| /// // Filter by multiple contract addresses | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_address([ | ||
| /// "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", | ||
| /// "0xdac17f958d2ee523a2206206994597c13d831ec7", | ||
| /// ])?; | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn and_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self> | ||
| where | ||
| I: IntoIterator<Item = A>, | ||
| A: TryInto<Address>, | ||
| A::Error: std::error::Error + Send + Sync + 'static, | ||
| { | ||
| let mut converted_addresses: Vec<Address> = Vec::new(); | ||
| for (idx, address) in addresses.into_iter().enumerate() { | ||
| converted_addresses.push( | ||
| address | ||
| .try_into() | ||
| .with_context(|| format!("invalid address at position {idx}"))?, | ||
| ); | ||
| } | ||
| self.address = converted_addresses; | ||
| Ok(self) | ||
| } | ||
|
|
||
| /// Filter traces by any of the provided call types. | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `String`. | ||
| /// Common call types include "call", "staticcall", "delegatecall", "create", "create2", etc. | ||
| /// | ||
| /// # Arguments | ||
| /// * `call_types` - An iterable of call type strings to filter by | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Filter by specific call types | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_call_type(["call", "delegatecall"]); | ||
| /// | ||
| /// // Filter by contract creation traces | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_call_type(["create", "create2"]); | ||
| /// | ||
| /// // Chain with address filtering | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_from_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])? | ||
| /// .and_call_type(["call"]); | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn and_call_type<I, S>(mut self, call_types: I) -> Self | ||
| where | ||
| I: IntoIterator<Item = S>, | ||
| S: Into<String>, | ||
| { | ||
| self.call_type = call_types.into_iter().map(Into::into).collect(); | ||
| self | ||
| } | ||
|
|
||
| /// Filter traces by any of the provided reward types. | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `String`. | ||
| /// Common reward types include "block", "uncle", etc., typically used for mining rewards. | ||
| /// | ||
| /// # Arguments | ||
| /// * `reward_types` - An iterable of reward type strings to filter by | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Filter by block rewards | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_reward_type(["block"]); | ||
| /// | ||
| /// // Filter by both block and uncle rewards | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_reward_type(["block", "uncle"]); | ||
| /// ``` | ||
| pub fn and_reward_type<I, S>(mut self, reward_types: I) -> Self | ||
| where | ||
| I: IntoIterator<Item = S>, | ||
| S: Into<String>, | ||
| { | ||
| self.reward_type = reward_types.into_iter().map(Into::into).collect(); | ||
| self | ||
| } | ||
|
|
||
| /// Filter traces by any of the provided trace types. | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `String`. | ||
| /// Common trace types include "call", "create", "suicide", "reward", etc. | ||
| /// | ||
| /// # Arguments | ||
| /// * `types` - An iterable of trace type strings to filter by | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Filter by call traces | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_type(["call"]); | ||
| /// | ||
| /// // Filter by multiple trace types | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_type(["call", "create", "reward"]); | ||
| /// ``` | ||
| pub fn and_type<I, S>(mut self, types: I) -> Self | ||
| where | ||
| I: IntoIterator<Item = S>, | ||
| S: Into<String>, | ||
| { | ||
| self.type_ = types.into_iter().map(Into::into).collect(); | ||
| self | ||
| } | ||
|
|
||
| /// Filter traces by any of the provided function signature hashes (sighashes). | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `Sighash`. | ||
| /// Common input types include string slices, byte arrays, and `Sighash` objects. | ||
| /// Sighashes are the first 4 bytes of the keccak256 hash of a function signature. | ||
| /// | ||
| /// # Arguments | ||
| /// * `sighashes` - An iterable of sighash values to filter by | ||
| /// | ||
| /// # Returns | ||
| /// * `Ok(Self)` - The updated filter on success | ||
| /// * `Err(anyhow::Error)` - If any sighash fails to convert | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::TraceFilter; | ||
| /// | ||
| /// // Filter by transfer function signature | ||
| /// let transfer_sig = "0xa9059cbb"; // transfer(address,uint256) | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_sighash([transfer_sig])?; | ||
| /// | ||
| /// // Filter by multiple function signatures | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_sighash([ | ||
| /// "0xa9059cbb", // transfer(address,uint256) | ||
| /// "0x095ea7b3", // approve(address,uint256) | ||
| /// ])?; | ||
| /// | ||
| /// // Using byte arrays | ||
| /// let transfer_bytes = [0xa9, 0x05, 0x9c, 0xbb]; | ||
| /// let filter = TraceFilter::any() | ||
| /// .and_sighash([transfer_bytes])?; | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn and_sighash<I, S>(mut self, sighashes: I) -> anyhow::Result<Self> | ||
| where | ||
| I: IntoIterator<Item = S>, | ||
| S: TryInto<Sighash>, | ||
| S::Error: std::error::Error + Send + Sync + 'static, | ||
| { | ||
| let mut converted_sighashes: Vec<Sighash> = Vec::new(); | ||
| for (idx, sighash) in sighashes.into_iter().enumerate() { | ||
| converted_sighashes.push( | ||
| sighash | ||
| .try_into() | ||
| .with_context(|| format!("invalid sighash at position {idx}"))?, | ||
| ); | ||
| } | ||
| self.sighash = converted_sighashes; | ||
| Ok(self) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap fallible doctest snippets in Result-returning mains.
These examples still invoke ? inside the default doctest context, so cargo test --doc fails. Add hidden # fn main() -> anyhow::Result<()> { / # Ok(()) / # } lines (or mark the fences rust,no_run) around every snippet that uses ?, mirroring the fix needed in transaction.rs.
-/// ```
-/// use hypersync_net_types::TraceFilter;
+/// ```
+/// # fn main() -> anyhow::Result<()> {
+/// use hypersync_net_types::TraceFilter;
/// let filter = TraceFilter::any()
/// .and_from_address(["0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567"])?;
-/// # Ok::<(), anyhow::Error>(())
+/// # Ok::<(), anyhow::Error>(())
+/// # }
/// ```🤖 Prompt for AI Agents
In hypersync-net-types/src/trace.rs around lines 33 to 361, update every doctest
snippet that uses the ? operator so it becomes a Result-returning hidden main
(or mark the fence rust,no_run); specifically, wrap the snippet body with hidden
lines like # fn main() -> anyhow::Result<()> { at the start and # Ok(()) # } at
the end (keeping existing hidden Ok::<(), anyhow::Error>(()) lines if present)
so the examples compile under cargo test --doc without changing behavior.
| /// Filter authorizations by any of the provided chain IDs. | ||
| /// | ||
| /// # Arguments | ||
| /// * `chain_ids` - An iterable of chain IDs to filter by | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::AuthorizationSelection; | ||
| /// | ||
| /// // Filter by a single chain ID (Ethereum mainnet) | ||
| /// let selection = AuthorizationSelection::any() | ||
| /// .and_chain_id([1]); | ||
| /// | ||
| /// // Filter by multiple chain IDs | ||
| /// let selection = AuthorizationSelection::any() | ||
| /// .and_chain_id([ | ||
| /// 1, // Ethereum mainnet | ||
| /// 137, // Polygon | ||
| /// 42161, // Arbitrum One | ||
| /// ]); | ||
| /// | ||
| /// // Chain with address filter | ||
| /// let selection = AuthorizationSelection::any() | ||
| /// .and_chain_id([1, 137]) | ||
| /// .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?; | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn and_chain_id<I>(mut self, chain_ids: I) -> Self | ||
| where | ||
| I: IntoIterator<Item = u64>, | ||
| { | ||
| self.chain_id = chain_ids.into_iter().collect(); | ||
| self | ||
| } | ||
|
|
||
| /// Filter authorizations by any of the provided addresses. | ||
| /// | ||
| /// This method accepts any iterable of values that can be converted to `Address`. | ||
| /// Common input types include string slices, byte arrays, and `Address` objects. | ||
| /// | ||
| /// # Arguments | ||
| /// * `addresses` - An iterable of addresses to filter by | ||
| /// | ||
| /// # Returns | ||
| /// * `Ok(Self)` - The updated selection on success | ||
| /// * `Err(anyhow::Error)` - If any address fails to convert | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use hypersync_net_types::AuthorizationSelection; | ||
| /// | ||
| /// // Filter by a single address | ||
| /// let selection = AuthorizationSelection::any() | ||
| /// .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?; | ||
| /// | ||
| /// // Filter by multiple addresses | ||
| /// let selection = AuthorizationSelection::any() | ||
| /// .and_address([ | ||
| /// "0xdac17f958d2ee523a2206206994597c13d831ec7", // Address 1 | ||
| /// "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", // Address 2 | ||
| /// ])?; | ||
| /// | ||
| /// // Using byte arrays | ||
| /// let auth_address = [ | ||
| /// 0xda, 0xc1, 0x7f, 0x95, 0x8d, 0x2e, 0xe5, 0x23, 0xa2, 0x20, | ||
| /// 0x62, 0x06, 0x99, 0x45, 0x97, 0xc1, 0x3d, 0x83, 0x1e, 0xc7 | ||
| /// ]; | ||
| /// let selection = AuthorizationSelection::any() | ||
| /// .and_address([auth_address])?; | ||
| /// # Ok::<(), anyhow::Error>(()) | ||
| /// ``` | ||
| pub fn and_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self> | ||
| where | ||
| I: IntoIterator<Item = A>, | ||
| A: TryInto<Address>, | ||
| A::Error: std::error::Error + Send + Sync + 'static, | ||
| { | ||
| let mut converted_addresses: Vec<Address> = Vec::new(); | ||
| for (idx, address) in addresses.into_iter().enumerate() { | ||
| converted_addresses.push( | ||
| address | ||
| .try_into() | ||
| .with_context(|| format!("invalid authorization address at position {idx}"))?, | ||
| ); | ||
| } | ||
| self.address = converted_addresses; | ||
| Ok(self) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix doctest wrappers for fallible examples.
Every code fence here still uses ? inside the default doctest fn main() { … }, so cargo test --doc fails. Wrap the snippets in a hidden fn main() -> anyhow::Result<()> { … } (closing with # Ok(())\n# }) or mark the fences rust,no_run. Apply the same pattern to all fallible examples in this file.
-/// ```
-/// use hypersync_net_types::AuthorizationSelection;
+/// ```
+/// # fn main() -> anyhow::Result<()> {
+/// use hypersync_net_types::AuthorizationSelection;
/// let selection = AuthorizationSelection::any()
/// .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?;
-/// # Ok::<(), anyhow::Error>(())
+/// # Ok::<(), anyhow::Error>(())
+/// # }
/// ```📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// Filter authorizations by any of the provided chain IDs. | |
| /// | |
| /// # Arguments | |
| /// * `chain_ids` - An iterable of chain IDs to filter by | |
| /// | |
| /// # Examples | |
| /// | |
| /// ``` | |
| /// use hypersync_net_types::AuthorizationSelection; | |
| /// | |
| /// // Filter by a single chain ID (Ethereum mainnet) | |
| /// let selection = AuthorizationSelection::any() | |
| /// .and_chain_id([1]); | |
| /// | |
| /// // Filter by multiple chain IDs | |
| /// let selection = AuthorizationSelection::any() | |
| /// .and_chain_id([ | |
| /// 1, // Ethereum mainnet | |
| /// 137, // Polygon | |
| /// 42161, // Arbitrum One | |
| /// ]); | |
| /// | |
| /// // Chain with address filter | |
| /// let selection = AuthorizationSelection::any() | |
| /// .and_chain_id([1, 137]) | |
| /// .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?; | |
| /// # Ok::<(), anyhow::Error>(()) | |
| /// ``` | |
| pub fn and_chain_id<I>(mut self, chain_ids: I) -> Self | |
| where | |
| I: IntoIterator<Item = u64>, | |
| { | |
| self.chain_id = chain_ids.into_iter().collect(); | |
| self | |
| } | |
| /// Filter authorizations by any of the provided addresses. | |
| /// | |
| /// This method accepts any iterable of values that can be converted to `Address`. | |
| /// Common input types include string slices, byte arrays, and `Address` objects. | |
| /// | |
| /// # Arguments | |
| /// * `addresses` - An iterable of addresses to filter by | |
| /// | |
| /// # Returns | |
| /// * `Ok(Self)` - The updated selection on success | |
| /// * `Err(anyhow::Error)` - If any address fails to convert | |
| /// | |
| /// # Examples | |
| /// | |
| /// ``` | |
| /// use hypersync_net_types::AuthorizationSelection; | |
| /// | |
| /// // Filter by a single address | |
| /// let selection = AuthorizationSelection::any() | |
| /// .and_address(["0xdac17f958d2ee523a2206206994597c13d831ec7"])?; | |
| /// | |
| /// // Filter by multiple addresses | |
| /// let selection = AuthorizationSelection::any() | |
| /// .and_address([ | |
| /// "0xdac17f958d2ee523a2206206994597c13d831ec7", // Address 1 | |
| /// "0xa0b86a33e6c11c8c0c5c0b5e6adee30d1a234567", // Address 2 | |
| /// ])?; | |
| /// | |
| /// // Using byte arrays | |
| /// let auth_address = [ | |
| /// 0xda, 0xc1, 0x7f, 0x95, 0x8d, 0x2e, 0xe5, 0x23, 0xa2, 0x20, | |
| /// 0x62, 0x06, 0x99, 0x45, 0x97, 0xc1, 0x3d, 0x83, 0x1e, 0xc7 | |
| /// ]; | |
| /// let selection = AuthorizationSelection::any() | |
| /// .and_address([auth_address])?; | |
| /// # Ok::<(), anyhow::Error>(()) | |
| /// ``` | |
| pub fn and_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self> | |
| where | |
| I: IntoIterator<Item = A>, | |
| A: TryInto<Address>, | |
| A::Error: std::error::Error + Send + Sync + 'static, | |
| { | |
| let mut converted_addresses: Vec<Address> = Vec::new(); | |
| for (idx, address) in addresses.into_iter().enumerate() { | |
| converted_addresses.push( | |
| address | |
| .try_into() | |
| .with_context(|| format!("invalid authorization address at position {idx}"))?, | |
| ); | |
| } | |
| self.address = converted_addresses; | |
| Ok(self) | |
| } | |
| /// Filter authorizations by any of the provided chain IDs. | |
| /// | |
| /// # Arguments | |
| /// * `chain_ids` - An iterable of chain IDs to filter by | |
| /// | |
| /// # Examples | |
| /// | |
| /// |
Implement a new builder API for queries
Summary by CodeRabbit
New Features
Improvements
Refactor
Tests
Chores