@@ -105,9 +105,13 @@ impl BoostedHexInfo {
105105 Some ( self . multipliers [ 0 ] )
106106 }
107107 }
108+
109+ fn matches_device_type ( & self , device_type : & BoostedHexDeviceType ) -> bool {
110+ self . device_type == * device_type || self . device_type == BoostedHexDeviceType :: All
111+ }
108112}
109113
110- #[ derive( Debug , Clone ) ]
114+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
111115pub enum BoostedHexDeviceType {
112116 All ,
113117 CbrsIndoor ,
@@ -154,11 +158,6 @@ impl TryFrom<i32> for BoostedHexDeviceType {
154158 }
155159}
156160
157- #[ derive( Debug , Clone , Default ) ]
158- pub struct BoostedHexes {
159- hexes : HashMap < Cell , BoostedHexInfo > ,
160- }
161-
162161#[ derive( PartialEq , Debug , Clone ) ]
163162pub struct BoostedHex {
164163 pub location : Cell ,
@@ -180,27 +179,33 @@ impl TryFrom<BoostedHexProto> for BoostedHex {
180179 }
181180}
182181
182+ #[ derive( Debug , Clone , Default ) ]
183+ pub struct BoostedHexes {
184+ hexes : HashMap < Cell , Vec < BoostedHexInfo > > ,
185+ }
186+
183187impl BoostedHexes {
184188 pub fn new ( hexes : Vec < BoostedHexInfo > ) -> Self {
185- let hexes = hexes
186- . into_iter ( )
187- . map ( |info| ( info . location , info ) )
188- . collect ( ) ;
189- Self { hexes }
189+ let mut me = Self :: default ( ) ;
190+ for hex in hexes {
191+ me . insert ( hex ) ;
192+ }
193+ me
190194 }
191195
192196 pub async fn get_all (
193197 hex_service_client : & impl HexBoostingInfoResolver < Error = ClientError > ,
194198 ) -> anyhow:: Result < Self > {
195- let mut map = HashMap :: new ( ) ;
196199 let mut stream = hex_service_client
197200 . clone ( )
198201 . stream_boosted_hexes_info ( )
199202 . await ?;
203+
204+ let mut me = Self :: default ( ) ;
200205 while let Some ( info) = stream. next ( ) . await {
201- map . insert ( info . location , info) ;
206+ me . insert ( info) ;
202207 }
203- Ok ( Self { hexes : map } )
208+ Ok ( me )
204209 }
205210
206211 pub fn is_boosted ( & self , location : & Cell ) -> bool {
@@ -211,37 +216,50 @@ impl BoostedHexes {
211216 hex_service_client : & impl HexBoostingInfoResolver < Error = ClientError > ,
212217 timestamp : DateTime < Utc > ,
213218 ) -> anyhow:: Result < Self > {
214- let mut map = HashMap :: new ( ) ;
215219 let mut stream = hex_service_client
216220 . clone ( )
217221 . stream_modified_boosted_hexes_info ( timestamp)
218222 . await ?;
223+
224+ let mut me = Self :: default ( ) ;
219225 while let Some ( info) = stream. next ( ) . await {
220- map . insert ( info . location , info) ;
226+ me . insert ( info) ;
221227 }
222- Ok ( Self { hexes : map } )
228+ Ok ( me )
223229 }
224230
225- pub fn get_current_multiplier ( & self , location : Cell , ts : DateTime < Utc > ) -> Option < NonZeroU32 > {
226- self . hexes
227- . get ( & location)
228- . and_then ( |info| info. current_multiplier ( ts) )
231+ pub fn get_current_multiplier (
232+ & self ,
233+ location : Cell ,
234+ device_type : BoostedHexDeviceType ,
235+ ts : DateTime < Utc > ,
236+ ) -> Option < NonZeroU32 > {
237+ let current_multiplier = self
238+ . hexes
239+ . get ( & location) ?
240+ . iter ( )
241+ . filter ( |info| info. matches_device_type ( & device_type) )
242+ . flat_map ( |info| info. current_multiplier ( ts) )
243+ . map ( |x| x. get ( ) )
244+ . sum :: < u32 > ( ) ;
245+
246+ NonZeroU32 :: new ( current_multiplier)
229247 }
230248
231249 pub fn count ( & self ) -> usize {
232250 self . hexes . len ( )
233251 }
234252
235253 pub fn iter_hexes ( & self ) -> impl Iterator < Item = & BoostedHexInfo > {
236- self . hexes . values ( )
254+ self . hexes . values ( ) . flatten ( )
237255 }
238256
239- pub fn get ( & self , location : & Cell ) -> Option < & BoostedHexInfo > {
257+ pub fn get ( & self , location : & Cell ) -> Option < & Vec < BoostedHexInfo > > {
240258 self . hexes . get ( location)
241259 }
242260
243261 pub fn insert ( & mut self , info : BoostedHexInfo ) {
244- self . hexes . insert ( info. location , info) ;
262+ self . hexes . entry ( info. location ) . or_default ( ) . push ( info) ;
245263 }
246264}
247265
@@ -379,6 +397,66 @@ mod tests {
379397 const BOOST_HEX_PUBKEY : & str = "J9JiLTpjaShxL8eMvUs8txVw6TZ36E38SiJ89NxnMbLU" ;
380398 const BOOST_HEX_CONFIG_PUBKEY : & str = "BZM1QTud72B2cpTW7PhEnFmRX7ZWzvY7DpPpNJJuDrWG" ;
381399
400+ #[ test]
401+ fn boosted_hexes_accumulate_multipliers ( ) -> anyhow:: Result < ( ) > {
402+ let cell = Cell :: from_raw ( 631252734740306943 ) ?;
403+ let now = Utc :: now ( ) ;
404+
405+ let hexes = vec ! [
406+ BoostedHexInfo {
407+ location: cell,
408+ start_ts: None ,
409+ end_ts: None ,
410+ period_length: Duration :: seconds( 2592000 ) ,
411+ multipliers: vec![ NonZeroU32 :: new( 2 ) . unwrap( ) ] ,
412+ boosted_hex_pubkey: Pubkey :: from_str( BOOST_HEX_PUBKEY ) ?,
413+ boost_config_pubkey: Pubkey :: from_str( BOOST_HEX_CONFIG_PUBKEY ) ?,
414+ version: 0 ,
415+ device_type: BoostedHexDeviceType :: All ,
416+ } ,
417+ BoostedHexInfo {
418+ location: cell,
419+ start_ts: None ,
420+ end_ts: None ,
421+ period_length: Duration :: seconds( 2592000 ) ,
422+ multipliers: vec![ NonZeroU32 :: new( 3 ) . unwrap( ) ] ,
423+ boosted_hex_pubkey: Pubkey :: from_str( BOOST_HEX_PUBKEY ) ?,
424+ boost_config_pubkey: Pubkey :: from_str( BOOST_HEX_CONFIG_PUBKEY ) ?,
425+ version: 0 ,
426+ device_type: BoostedHexDeviceType :: CbrsIndoor ,
427+ } ,
428+ // Expired boosts should not be considered
429+ BoostedHexInfo {
430+ location: cell,
431+ start_ts: Some ( now - Duration :: days( 60 ) ) ,
432+ end_ts: Some ( now - Duration :: days( 30 ) ) ,
433+ period_length: Duration :: seconds( 2592000 ) ,
434+ multipliers: vec![ NonZeroU32 :: new( 999 ) . unwrap( ) ] ,
435+ boosted_hex_pubkey: Pubkey :: from_str( BOOST_HEX_PUBKEY ) ?,
436+ boost_config_pubkey: Pubkey :: from_str( BOOST_HEX_CONFIG_PUBKEY ) ?,
437+ version: 0 ,
438+ device_type: BoostedHexDeviceType :: All ,
439+ } ,
440+ ] ;
441+
442+ let boosted_hexes = BoostedHexes :: new ( hexes) ;
443+ let boosts = boosted_hexes. get ( & cell) . expect ( "boosts for test cell" ) ;
444+ assert_eq ! ( boosts. len( ) , 3 , "a hex can be boosted multiple times" ) ;
445+
446+ assert_eq ! (
447+ boosted_hexes. get_current_multiplier( cell, BoostedHexDeviceType :: CbrsIndoor , now) ,
448+ NonZeroU32 :: new( 5 ) ,
449+ "Specific boosts stack with ::ALL"
450+ ) ;
451+ assert_eq ! (
452+ boosted_hexes. get_current_multiplier( cell, BoostedHexDeviceType :: WifiIndoor , now) ,
453+ NonZeroU32 :: new( 2 ) ,
454+ "Missing boosts still return ::ALL"
455+ ) ;
456+
457+ Ok ( ( ) )
458+ }
459+
382460 #[ test]
383461 fn boosted_hex_from_proto_valid_not_started ( ) -> anyhow:: Result < ( ) > {
384462 let proto = BoostedHexInfoProto {
0 commit comments