@@ -34,50 +34,27 @@ struct EventsParams {
3434 topics : Option < String > ,
3535}
3636
37- /// Topics an SSE client asked for: a `Copy` bitmask over [`Topic`]
38- /// discriminants, tested once per received event.
39- ///
40- /// The filter lives here, in the sole consumer that needs it, rather than in
41- /// the [`EventBus`]: the bus stays a plain fan-out, and any other subscriber
42- /// pays nothing for a feature only this endpoint uses.
43- #[ derive( Clone , Copy ) ]
44- struct TopicSet ( u16 ) ;
45-
46- impl TopicSet {
47- fn contains ( self , topic : Topic ) -> bool {
48- self . 0 & ( 1 << topic as u16 ) != 0
49- }
50- }
51-
52- impl FromIterator < Topic > for TopicSet {
53- fn from_iter < I : IntoIterator < Item = Topic > > ( iter : I ) -> Self {
54- iter. into_iter ( ) . fold ( TopicSet ( 0 ) , |set, topic| {
55- TopicSet ( set. 0 | ( 1 << topic as u16 ) )
56- } )
57- }
58- }
59-
6037async fn get_events (
6138 Extension ( events) : Extension < EventBus > ,
6239 Query ( params) : Query < EventsParams > ,
6340) -> Response {
6441 // `topics` is required, matching the Beacon API: a missing or empty value
6542 // is a 400, as is any unknown topic name. See docs/rpc.md.
66- let topics = match params. topics . as_deref ( ) {
43+ //
44+ // The parsed selection lives here, in the sole consumer that filters, not
45+ // in the bus: a plain `Vec` since there are only a handful of topics.
46+ let topics: Vec < Topic > = match params. topics . as_deref ( ) {
6747 None | Some ( "" ) => {
6848 return (
6949 StatusCode :: BAD_REQUEST ,
7050 "missing required query parameter: topics" ,
7151 )
7252 . into_response ( ) ;
7353 }
74- Some ( list) => {
75- let parsed: Result < TopicSet , _ > = list. split ( ',' ) . map ( Topic :: from_str) . collect ( ) ;
76- match parsed {
77- Ok ( topics) => topics,
78- Err ( err) => return ( StatusCode :: BAD_REQUEST , err. to_string ( ) ) . into_response ( ) ,
79- }
80- }
54+ Some ( list) => match list. split ( ',' ) . map ( Topic :: from_str) . collect ( ) {
55+ Ok ( topics) => topics,
56+ Err ( err) => return ( StatusCode :: BAD_REQUEST , err. to_string ( ) ) . into_response ( ) ,
57+ } ,
8158 } ;
8259
8360 Sse :: new ( event_stream ( events. subscribe ( ) , topics) )
@@ -89,13 +66,13 @@ async fn get_events(
8966/// events the client's `topics` filter excludes.
9067fn event_stream (
9168 rx : broadcast:: Receiver < ChainEvent > ,
92- topics : TopicSet ,
69+ topics : Vec < Topic > ,
9370) -> impl Stream < Item = Result < Event , Infallible > > {
9471 unfold ( ( rx, topics) , |( mut rx, topics) | async move {
9572 loop {
9673 match rx. recv ( ) . await {
9774 // Not in the client's `?topics=` set: skip without a frame.
98- Ok ( ev) if !topics. contains ( ev. topic ( ) ) => continue ,
75+ Ok ( ev) if !topics. contains ( & ev. topic ( ) ) => continue ,
9976 Ok ( ev) => {
10077 let frame = Event :: default ( )
10178 . event ( ev. topic ( ) . as_str ( ) )
@@ -143,40 +120,15 @@ mod tests {
143120 body:: Body ,
144121 http:: { Request , StatusCode } ,
145122 } ;
146- use ethlambda_blockchain:: { ChainEvent , EventBus , Topic } ;
123+ use ethlambda_blockchain:: { ChainEvent , EventBus } ;
147124 use ethlambda_storage:: { Store , backend:: InMemoryBackend } ;
148125 use futures_util:: StreamExt ;
149126 use http_body_util:: BodyExt ;
150127 use std:: sync:: Arc ;
151128 use tower:: ServiceExt ;
152129
153- use super :: TopicSet ;
154130 use crate :: test_utils:: create_test_state;
155131
156- #[ test]
157- fn topic_set_collects_and_contains ( ) {
158- let set: TopicSet = [ Topic :: Head , Topic :: FinalizedCheckpoint ]
159- . into_iter ( )
160- . collect ( ) ;
161- assert ! ( set. contains( Topic :: Head ) ) ;
162- assert ! ( set. contains( Topic :: FinalizedCheckpoint ) ) ;
163- assert ! ( !set. contains( Topic :: Block ) ) ;
164- assert ! ( !set. contains( Topic :: JustifiedCheckpoint ) ) ;
165-
166- // The empty set matches nothing; a fully-populated set matches all.
167- let all = [
168- Topic :: Head ,
169- Topic :: Block ,
170- Topic :: JustifiedCheckpoint ,
171- Topic :: FinalizedCheckpoint ,
172- ] ;
173- let full: TopicSet = all. into_iter ( ) . collect ( ) ;
174- for topic in all {
175- assert ! ( !TopicSet ( 0 ) . contains( topic) ) ;
176- assert ! ( full. contains( topic) ) ;
177- }
178- }
179-
180132 async fn events_response ( events : & EventBus , uri : & str ) -> axum:: response:: Response {
181133 let store = Store :: from_anchor_state ( Arc :: new ( InMemoryBackend :: new ( ) ) , create_test_state ( ) ) ;
182134 let app = crate :: test_utils:: test_api_router ( store) . layer ( Extension ( events. clone ( ) ) ) ;
0 commit comments