diff --git a/server/swimos_agent/src/agent_lifecycle/item_event/map/mod.rs b/server/swimos_agent/src/agent_lifecycle/item_event/map/mod.rs index 87926fe0c..e9fc97b10 100644 --- a/server/swimos_agent/src/agent_lifecycle/item_event/map/mod.rs +++ b/server/swimos_agent/src/agent_lifecycle/item_event/map/mod.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::cmp::Ordering; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use std::{cmp::Ordering, collections::HashMap}; use frunk::{Coprod, Coproduct}; use futures::future::Either; @@ -30,58 +30,52 @@ use crate::lanes::map::{ on_update::{OnUpdate, OnUpdateShared}, MapLaneLifecycle, }, - MapLane, MapLaneEvent, + MapLaneEvent, }; -use crate::stores::MapStore; +use crate::map_storage::MapOpsWithEntry; use super::{HLeaf, HTree, ItemEvent, ItemEventShared}; #[cfg(test)] mod tests; -pub type MapLeaf = MapBranch; -pub type MapBranch = MapLikeBranch, LC, L, R>; - -pub type MapStoreLeaf = MapStoreBranch; -pub type MapStoreBranch = - MapLikeBranch, LC, L, R>; - -pub type MapLifecycleHandler<'a, Context, K, V, LC> = Coprod!( - >::OnUpdateHandler<'a>, - >::OnRemoveHandler<'a>, - >::OnClearHandler<'a>, +pub type MapLifecycleHandler<'a, Context, K, V, M, LC> = Coprod!( + >::OnUpdateHandler<'a>, + >::OnRemoveHandler<'a>, + >::OnClearHandler<'a>, ); -pub type MapLifecycleHandlerShared<'a, Context, Shared, K, V, LC> = Coprod!( - >::OnUpdateHandler<'a>, - >::OnRemoveHandler<'a>, - >::OnClearHandler<'a>, +pub type MapLifecycleHandlerShared<'a, Context, Shared, K, V, M, LC> = Coprod!( + >::OnUpdateHandler<'a>, + >::OnRemoveHandler<'a>, + >::OnClearHandler<'a>, ); -type MapBranchHandler<'a, Context, K, V, LC, L, R> = Either< +type MapBranchHandler<'a, Context, K, V, M, LC, L, R> = Either< >::ItemEventHandler<'a>, Either< - MapLifecycleHandler<'a, Context, K, V, LC>, + MapLifecycleHandler<'a, Context, K, V, M, LC>, >::ItemEventHandler<'a>, >, >; -type MapBranchHandlerShared<'a, Context, Shared, K, V, LC, L, R> = Either< +type MapBranchHandlerShared<'a, Context, Shared, K, V, M, LC, L, R> = Either< >::ItemEventHandler<'a>, Either< - MapLifecycleHandlerShared<'a, Context, Shared, K, V, LC>, + MapLifecycleHandlerShared<'a, Context, Shared, K, V, M, LC>, >::ItemEventHandler<'a>, >, >; -fn map_handler<'a, Context, K, V, LC>( - event: MapLaneEvent, +fn map_handler<'a, Context, K, V, M, LC>( + event: MapLaneEvent, lifecycle: &'a LC, - map: &HashMap, -) -> MapLifecycleHandler<'a, Context, K, V, LC> + map: &M, +) -> MapLifecycleHandler<'a, Context, K, V, M, LC> where K: Hash + Eq, - LC: MapLaneLifecycle, + LC: MapLaneLifecycle, + M: MapOpsWithEntry, { match event { MapLaneEvent::Update(k, old) => { @@ -97,16 +91,17 @@ where } } -fn map_handler_shared<'a, Context, Shared, K, V, LC>( +fn map_handler_shared<'a, Context, Shared, K, V, M, LC>( shared: &'a Shared, handler_context: HandlerContext, - event: MapLaneEvent, + event: MapLaneEvent, lifecycle: &'a LC, - map: &HashMap, -) -> MapLifecycleHandlerShared<'a, Context, Shared, K, V, LC> + map: &M, +) -> MapLifecycleHandlerShared<'a, Context, Shared, K, V, M, LC> where K: Hash + Eq, - LC: MapLaneLifecycleShared, + LC: MapLaneLifecycleShared, + M: MapOpsWithEntry, { match event { MapLaneEvent::Update(k, old) => { @@ -126,10 +121,10 @@ where } } -type KeyValue = fn((K, V)) -> (K, V); +type MapKeyValue = fn((&M, K, V)) -> (K, V); /// Map lane lifecycle as a branch node of an [`HTree`]. -pub struct MapLikeBranch { - _type: PhantomData>, +pub struct MapLikeBranch { + _type: PhantomData>, label: &'static str, projection: fn(&Context) -> &Item, lifecycle: LC, @@ -137,8 +132,8 @@ pub struct MapLikeBranch { right: R, } -impl Clone - for MapLikeBranch +impl Clone + for MapLikeBranch { fn clone(&self) -> Self { Self { @@ -152,15 +147,15 @@ impl Clone } } -impl HTree - for MapLikeBranch +impl HTree + for MapLikeBranch { fn label(&self) -> Option<&'static str> { Some(self.label) } } -impl Debug for MapLikeBranch +impl Debug for MapLikeBranch where LC: Debug, L: Debug, @@ -177,13 +172,13 @@ where } } -impl MapLikeBranch { +impl MapLikeBranch { pub fn leaf(label: &'static str, projection: fn(&Context) -> &Item, lifecycle: LC) -> Self { MapLikeBranch::new(label, projection, lifecycle, HLeaf, HLeaf) } } -impl MapLikeBranch +impl MapLikeBranch where L: HTree, R: HTree, @@ -212,16 +207,17 @@ where } } -impl ItemEvent - for MapLikeBranch +impl ItemEvent + for MapLikeBranch where K: Clone + Eq + Hash, - Item: MapItem, - LC: MapLaneLifecycle, + Item: MapItem, + LC: MapLaneLifecycle, L: HTree + ItemEvent, R: HTree + ItemEvent, + M: MapOpsWithEntry, { - type ItemEventHandler<'a> = MapBranchHandler<'a, Context, K, V, LC, L, R> + type ItemEventHandler<'a> = MapBranchHandler<'a, Context, K, V, M, LC, L, R> where Self: 'a; @@ -253,16 +249,17 @@ where } } -impl ItemEventShared - for MapLikeBranch +impl ItemEventShared + for MapLikeBranch where K: Clone + Eq + Hash, - Item: MapItem, - LC: MapLaneLifecycleShared, + Item: MapItem, + LC: MapLaneLifecycleShared, L: HTree + ItemEventShared, R: HTree + ItemEventShared, + M: MapOpsWithEntry, { - type ItemEventHandler<'a> = MapBranchHandlerShared<'a, Context, Shared, K, V, LC, L, R> + type ItemEventHandler<'a> = MapBranchHandlerShared<'a, Context, Shared, K, V, M, LC, L, R> where Self: 'a, Shared: 'a; diff --git a/server/swimos_agent/src/agent_lifecycle/item_event/map/tests.rs b/server/swimos_agent/src/agent_lifecycle/item_event/map/tests.rs index ac89becfc..978309bcd 100644 --- a/server/swimos_agent/src/agent_lifecycle/item_event/map/tests.rs +++ b/server/swimos_agent/src/agent_lifecycle/item_event/map/tests.rs @@ -20,7 +20,7 @@ use swimos_model::Text; use swimos_utilities::routing::RouteUri; use crate::{ - agent_lifecycle::item_event::{tests::run_handler, HLeaf, ItemEvent, MapBranch, MapLeaf}, + agent_lifecycle::item_event::{tests::run_handler, HLeaf, ItemEvent}, event_handler::{ActionContext, HandlerAction, StepResult}, lanes::map::{ lifecycle::{on_clear::OnClear, on_remove::OnRemove, on_update::OnUpdate}, @@ -29,6 +29,12 @@ use crate::{ meta::AgentMetadata, }; +use super::MapLikeBranch; + +type MapLeaf = MapBranch; +type MapBranch = + MapLikeBranch, LC, L, R>; + struct TestAgent { first: MapLane, second: MapLane, @@ -248,7 +254,7 @@ impl FakeLifecycle { } } -impl OnUpdate for FakeLifecycle +impl OnUpdate, TestAgent> for FakeLifecycle where K: Clone + Send + 'static, V: Clone + Send + 'static, @@ -268,7 +274,7 @@ where } } -impl OnRemove for FakeLifecycle +impl OnRemove, TestAgent> for FakeLifecycle where K: Clone + Send + 'static, V: Clone + Send + 'static, @@ -287,7 +293,7 @@ where } } -impl OnClear for FakeLifecycle +impl OnClear, TestAgent> for FakeLifecycle where K: Clone + Send + 'static, V: Clone + Send + 'static, diff --git a/server/swimos_agent/src/agent_lifecycle/item_event/mod.rs b/server/swimos_agent/src/agent_lifecycle/item_event/mod.rs index 1963840d6..0ac0d2396 100644 --- a/server/swimos_agent/src/agent_lifecycle/item_event/mod.rs +++ b/server/swimos_agent/src/agent_lifecycle/item_event/mod.rs @@ -42,10 +42,7 @@ pub use demand_map::{ DemandMapBranch, DemandMapLeaf, DemandMapLifecycleHandler, DemandMapLifecycleHandlerShared, }; pub use http::{HttpBranch, HttpLeaf}; -pub use map::{ - MapBranch, MapLeaf, MapLifecycleHandler, MapLifecycleHandlerShared, MapLikeBranch, - MapStoreBranch, MapStoreLeaf, -}; +pub use map::{MapLifecycleHandler, MapLifecycleHandlerShared, MapLikeBranch}; pub use value::{ ValueBranch, ValueLeaf, ValueLifecycleHandler, ValueLifecycleHandlerShared, ValueLikeBranch, ValueStoreBranch, ValueStoreLeaf, diff --git a/server/swimos_agent/src/agent_lifecycle/utility/mod.rs b/server/swimos_agent/src/agent_lifecycle/utility/mod.rs index 2b0f3005e..e60144578 100644 --- a/server/swimos_agent/src/agent_lifecycle/utility/mod.rs +++ b/server/swimos_agent/src/agent_lifecycle/utility/mod.rs @@ -234,13 +234,13 @@ impl HandlerContext { ) -> impl HandlerAction + Send + 'a where Agent: 'static, - Item: InspectableMapLikeItem + 'static, + Item: InspectableMapLikeItem + 'static, K: Send + Clone + Eq + Hash + 'static, V: Borrow + 'static, B: ?Sized + 'static, F: FnOnce(Option<&B>) -> U + Send + 'a, { - Item::with_entry_handler::(item, key, f) + Item::with_entry_handler::(item, key, f) } /// Create an event handler that will transform the value in an entry of a map lane or store of the agent. @@ -326,12 +326,12 @@ impl HandlerContext { /// /// #Arguments /// * `item` - Projection to the map-like item. - pub fn get_map( + pub fn get_map( &self, item: fn(&Agent) -> &Item, - ) -> impl HandlerAction> + Send + 'static + ) -> impl HandlerAction + Send + 'static where - Item: MapLikeItem, + Item: MapLikeItem, K: Send + Clone + Eq + Hash + 'static, V: Send + Clone + 'static, { diff --git a/server/swimos_agent/src/agent_model/init/mod.rs b/server/swimos_agent/src/agent_model/init/mod.rs index 17857df34..79db51d48 100644 --- a/server/swimos_agent/src/agent_model/init/mod.rs +++ b/server/swimos_agent/src/agent_model/init/mod.rs @@ -33,6 +33,7 @@ use tokio_util::codec::{Decoder, FramedRead, FramedWrite}; use crate::item::{MapItem, ValueItem}; use crate::lanes::{MapLane, ValueLane}; +use crate::map_storage::MapOps; use crate::stores::value::ValueStore; use crate::stores::MapStore; @@ -163,23 +164,23 @@ impl ValueStoreInitializer { } /// [`ItemInitializer`] to construct the state of a map lane. -pub struct MapLaneInitializer { - projection: fn(&Agent) -> &MapLane, +pub struct MapLaneInitializer> { + projection: fn(&Agent) -> &MapLane, } -impl MapLaneInitializer { - pub fn new(projection: fn(&Agent) -> &MapLane) -> Self { +impl MapLaneInitializer { + pub fn new(projection: fn(&Agent) -> &MapLane) -> Self { MapLaneInitializer { projection } } } /// [`ItemInitializer`] to construct the state of a map store. -pub struct MapStoreInitializer { - projection: fn(&Agent) -> &MapStore, +pub struct MapStoreInitializer> { + projection: fn(&Agent) -> &MapStore, } -impl MapStoreInitializer { - pub fn new(projection: fn(&Agent) -> &MapStore) -> Self { +impl MapStoreInitializer { + pub fn new(projection: fn(&Agent) -> &MapStore) -> Self { MapStoreInitializer { projection } } } @@ -238,7 +239,7 @@ where } } -async fn map_like_init( +async fn map_like_init( mut stream: BoxStream<'_, Result, FrameIoError>>, projection: F, ) -> Result, FrameIoError> @@ -246,8 +247,9 @@ where Agent: 'static, K: Eq + Hash + Ord + Clone + RecognizerReadable + Send + 'static, V: RecognizerReadable + Send + 'static, - Item: MapItem + 'static, + Item: MapItem + 'static, F: Fn(&Agent) -> &Item + Send + 'static, + M: MapOps, { let mut key_decoder = RecognizerDecoder::new(K::make_recognizer()); let mut value_decoder = RecognizerDecoder::new(V::make_recognizer()); @@ -293,20 +295,20 @@ where } } } - let map_init = map.into_iter().collect::>(); - let f = move |agent: &Agent| projection(agent).init(map_init); + let f = move |agent: &Agent| projection(agent).init(M::from_entries(map)); let f_init: InitFn = Box::new(f); Ok(f_init) } -impl ItemInitializer> - for MapLaneInitializer +impl ItemInitializer> + for MapLaneInitializer where Agent: 'static, K: RecognizerReadable + Hash + Eq + Ord + Clone + Send + 'static, K::Rec: Send, V: RecognizerReadable + Send + 'static, V::Rec: Send, + M: MapOps + 'static, { fn initialize( self: Box, @@ -317,14 +319,15 @@ where } } -impl ItemInitializer> - for MapStoreInitializer +impl ItemInitializer> + for MapStoreInitializer where Agent: 'static, K: RecognizerReadable + Hash + Eq + Ord + Clone + Send + 'static, K::Rec: Send, V: RecognizerReadable + Send + 'static, V::Rec: Send, + M: MapOps + 'static, { fn initialize( self: Box, diff --git a/server/swimos_agent/src/downlink_lifecycle/map/on_remove.rs b/server/swimos_agent/src/downlink_lifecycle/map/on_remove.rs index eda2ab407..b824e5c9a 100644 --- a/server/swimos_agent/src/downlink_lifecycle/map/on_remove.rs +++ b/server/swimos_agent/src/downlink_lifecycle/map/on_remove.rs @@ -119,9 +119,9 @@ where impl OnDownlinkRemoveShared for FnHandler where - F: for<'a> MapRemoveFn<'a, Context, Shared, K, V> + Send, + F: for<'a> MapRemoveFn<'a, Context, Shared, K, V, HashMap> + Send, { - type OnRemoveHandler<'a> = >::Handler + type OnRemoveHandler<'a> = >>::Handler where Self: 'a, Shared: 'a; diff --git a/server/swimos_agent/src/downlink_lifecycle/map/on_update.rs b/server/swimos_agent/src/downlink_lifecycle/map/on_update.rs index ceaef0bd6..3e84ea4cc 100644 --- a/server/swimos_agent/src/downlink_lifecycle/map/on_update.rs +++ b/server/swimos_agent/src/downlink_lifecycle/map/on_update.rs @@ -192,9 +192,9 @@ where impl OnDownlinkUpdateShared for FnHandler where - F: for<'a> MapUpdateFn<'a, Context, Shared, K, V> + Send, + F: for<'a> MapUpdateFn<'a, Context, Shared, K, V, HashMap> + Send, { - type OnUpdateHandler<'a> = >::Handler + type OnUpdateHandler<'a> = >>::Handler where Self: 'a, Shared: 'a; @@ -218,9 +218,9 @@ impl OnDownlinkUpdateShared where B: ?Sized, V: Borrow, - F: for<'a> MapUpdateBorrowFn<'a, Context, Shared, K, V, B> + Send, + F: for<'a> MapUpdateBorrowFn<'a, Context, Shared, K, V, HashMap, B> + Send, { - type OnUpdateHandler<'a> = >::Handler + type OnUpdateHandler<'a> = , B>>::Handler where Self: 'a, Shared: 'a; diff --git a/server/swimos_agent/src/event_handler/handler_fn.rs b/server/swimos_agent/src/event_handler/handler_fn.rs index 85fce1754..4fa337ae6 100644 --- a/server/swimos_agent/src/event_handler/handler_fn.rs +++ b/server/swimos_agent/src/event_handler/handler_fn.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; - use crate::{ agent_lifecycle::HandlerContext, lanes::http::{lifecycle::HttpRequestContext, Response, UnitResponse}, @@ -199,23 +197,23 @@ where } } -pub trait MapRemoveFn<'a, Context, Shared, K, V> { +pub trait MapRemoveFn<'a, Context, Shared, K, V, M> { type Handler: EventHandler + 'a; fn make_handler( &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: V, ) -> Self::Handler; } -impl<'a, Context, Shared, K, V, F, H> MapRemoveFn<'a, Context, Shared, K, V> for F +impl<'a, Context, Shared, K, V, M, F, H> MapRemoveFn<'a, Context, Shared, K, V, M> for F where H: EventHandler + 'a, - F: Fn(&'a Shared, HandlerContext, &HashMap, K, V) -> H + 'a, + F: Fn(&'a Shared, HandlerContext, &M, K, V) -> H + 'a, Shared: 'a, { type Handler = H; @@ -224,7 +222,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: V, ) -> Self::Handler { @@ -232,24 +230,24 @@ where } } -pub trait MapUpdateFn<'a, Context, Shared, K, V> { +pub trait MapUpdateFn<'a, Context, Shared, K, V, M> { type Handler: EventHandler + 'a; fn make_handler( &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, ) -> Self::Handler; } -impl<'a, Context, Shared, K, V, F, H> MapUpdateFn<'a, Context, Shared, K, V> for F +impl<'a, Context, Shared, K, V, M, F, H> MapUpdateFn<'a, Context, Shared, K, V, M> for F where H: EventHandler + 'a, - F: Fn(&'a Shared, HandlerContext, &HashMap, K, Option, &V) -> H + 'a, + F: Fn(&'a Shared, HandlerContext, &M, K, Option, &V) -> H + 'a, Shared: 'a, { type Handler = H; @@ -258,7 +256,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, @@ -267,25 +265,25 @@ where } } -pub trait MapUpdateBorrowFn<'a, Context, Shared, K, V, B: ?Sized> { +pub trait MapUpdateBorrowFn<'a, Context, Shared, K, V, M, B: ?Sized> { type Handler: EventHandler + 'a; fn make_handler( &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &B, ) -> Self::Handler; } -impl<'a, Context, Shared, K, V, B, F, H> MapUpdateBorrowFn<'a, Context, Shared, K, V, B> for F +impl<'a, Context, Shared, K, V, M, B, F, H> MapUpdateBorrowFn<'a, Context, Shared, K, V, M, B> for F where B: ?Sized, H: EventHandler + 'a, - F: Fn(&'a Shared, HandlerContext, &HashMap, K, Option, &B) -> H + 'a, + F: Fn(&'a Shared, HandlerContext, &M, K, Option, &B) -> H + 'a, Shared: 'a, { type Handler = H; @@ -294,7 +292,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &B, diff --git a/server/swimos_agent/src/event_queue/mod.rs b/server/swimos_agent/src/event_queue/mod.rs index af75d068d..ed9beba22 100644 --- a/server/swimos_agent/src/event_queue/mod.rs +++ b/server/swimos_agent/src/event_queue/mod.rs @@ -18,7 +18,7 @@ use std::hash::Hash; use swimos_agent_protocol::MapOperation; use swimos_agent_protocol::StoreResponse; -use crate::map_storage::MapEventQueue; +use crate::map_storage::{MapEventQueue, MapOps}; /// Keeps track of what changes to the state of the map need to be reported as events. #[derive(Debug)] @@ -119,9 +119,14 @@ where type Output<'a> = StoreResponse> where Self: 'a, + K: 'a, V: 'a; - fn pop<'a>(&mut self, content: &'a HashMap) -> Option> { + fn pop<'a, M>(&mut self, content: &'a M) -> Option> + where + K: 'a, + M: MapOps, + { loop { let action = EventQueue::pop(self)?; if let Some(op) = to_operation(content, action) { @@ -133,9 +138,9 @@ where pub type Action = MapOperation; -pub fn to_operation(content: &HashMap, action: Action) -> Option> +pub fn to_operation(content: &M, action: Action) -> Option> where - K: Eq + Hash + Clone, + M: MapOps, { match action { MapOperation::Update { key, .. } => content diff --git a/server/swimos_agent/src/item.rs b/server/swimos_agent/src/item.rs index 64974a080..1ef94b06b 100644 --- a/server/swimos_agent/src/item.rs +++ b/server/swimos_agent/src/item.rs @@ -35,19 +35,19 @@ pub trait ValueItem: AgentItem { fn init(&self, value: T); } -pub trait MapItem: AgentItem { - fn init(&self, map: HashMap); +pub trait MapItem>: AgentItem { + fn init(&self, map: M); fn read_with_prev(&self, f: F) -> R where - F: FnOnce(Option>, &HashMap) -> R; + F: FnOnce(Option>, &M) -> R; } -pub trait MapLikeItem { +pub trait MapLikeItem> { type GetHandler: HandlerAction> + Send + 'static where C: AgentDescription + 'static; - type GetMapHandler: HandlerAction> + Send + 'static + type GetMapHandler: HandlerAction + Send + 'static where C: AgentDescription + 'static; @@ -61,25 +61,21 @@ pub trait MapLikeItem { ) -> Self::GetMapHandler; } -pub trait InspectableMapLikeItem { - type WithEntryHandler<'a, C, F, B, U>: HandlerAction + Send + 'a +pub trait InspectableMapLikeItem, B: ?Sized + 'static> { + type WithEntryHandler<'a, C, F, U>: HandlerAction + Send + 'a where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a; - fn with_entry_handler<'a, C, F, B, U>( + fn with_entry_handler<'a, C, F, U>( projection: fn(&C) -> &Self, key: K, f: F, - ) -> Self::WithEntryHandler<'a, C, F, B, U> + ) -> Self::WithEntryHandler<'a, C, F, U> where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a; } diff --git a/server/swimos_agent/src/lanes/join/map/mod.rs b/server/swimos_agent/src/lanes/join/map/mod.rs index 132f82e13..b1d315a26 100644 --- a/server/swimos_agent/src/lanes/join/map/mod.rs +++ b/server/swimos_agent/src/lanes/join/map/mod.rs @@ -882,30 +882,27 @@ where } } -impl InspectableMapLikeItem for JoinMapLane +impl InspectableMapLikeItem for JoinMapLane where L: Send + 'static, K: Clone + Eq + Hash + Send + 'static, - V: Send + 'static, + V: Borrow + Send + 'static, + B: ?Sized + 'static, { - type WithEntryHandler<'a, C, F, B, U> = JoinMapLaneWithEntry + type WithEntryHandler<'a, C, F, U> = JoinMapLaneWithEntry where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a; - fn with_entry_handler<'a, C, F, B, U>( + fn with_entry_handler<'a, C, F, U>( projection: fn(&C) -> &Self, key: K, f: F, - ) -> Self::WithEntryHandler<'a, C, F, B, U> + ) -> Self::WithEntryHandler<'a, C, F, U> where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a, { JoinMapLaneWithEntry::new(projection, key, f) diff --git a/server/swimos_agent/src/lanes/join/value/mod.rs b/server/swimos_agent/src/lanes/join/value/mod.rs index af66c2de2..f014ca443 100644 --- a/server/swimos_agent/src/lanes/join/value/mod.rs +++ b/server/swimos_agent/src/lanes/join/value/mod.rs @@ -656,29 +656,26 @@ where } } -impl InspectableMapLikeItem for JoinValueLane +impl InspectableMapLikeItem for JoinValueLane where K: Clone + Eq + Hash + Send + 'static, - V: Send + 'static, + V: Borrow + Send + 'static, + B: ?Sized + 'static, { - type WithEntryHandler<'a, C, F, B, U> = JoinValueLaneWithEntry + type WithEntryHandler<'a, C, F, U> = JoinValueLaneWithEntry where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a; - fn with_entry_handler<'a, C, F, B, U>( + fn with_entry_handler<'a, C, F, U>( projection: fn(&C) -> &Self, key: K, f: F, - ) -> Self::WithEntryHandler<'a, C, F, B, U> + ) -> Self::WithEntryHandler<'a, C, F, U> where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a, { JoinValueLaneWithEntry::new(projection, key, f) diff --git a/server/swimos_agent/src/lanes/map/event.rs b/server/swimos_agent/src/lanes/map/event.rs index 0cb4a50b8..b08589483 100644 --- a/server/swimos_agent/src/lanes/map/event.rs +++ b/server/swimos_agent/src/lanes/map/event.rs @@ -17,16 +17,17 @@ use std::hash::Hash; /// Enumeration of the possible inputs to a map lane event handler. #[derive(Debug, Clone)] -pub enum MapLaneEvent { - Clear(HashMap), +pub enum MapLaneEvent> { + Clear(M), Update(K, Option), Remove(K, V), } -impl PartialEq for MapLaneEvent +impl PartialEq for MapLaneEvent where K: Eq + Hash, V: PartialEq, + M: PartialEq, { fn eq(&self, other: &Self) -> bool { match (self, other) { @@ -38,10 +39,11 @@ where } } -impl Eq for MapLaneEvent +impl Eq for MapLaneEvent where K: Eq + Hash, V: Eq, + M: Eq, { fn assert_receiver_is_total_eq(&self) {} } diff --git a/server/swimos_agent/src/lanes/map/lifecycle/mod.rs b/server/swimos_agent/src/lanes/map/lifecycle/mod.rs index 00ec012cc..281fc39ec 100644 --- a/server/swimos_agent/src/lanes/map/lifecycle/mod.rs +++ b/server/swimos_agent/src/lanes/map/lifecycle/mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{borrow::Borrow, collections::HashMap, marker::PhantomData}; +use std::{borrow::Borrow, marker::PhantomData}; use swimos_utilities::handlers::{BorrowHandler, FnHandler, NoHandler}; @@ -34,35 +34,40 @@ pub mod on_update; /// * `K` - The type of the map keys. /// * `V` - The type of the map values. /// * `Context` - The context within which the event handlers execute (providing access to the agent lanes). -pub trait MapLaneLifecycle: - OnUpdate + OnRemove + OnClear +pub trait MapLaneLifecycle: + OnUpdate + OnRemove + OnClear { } -impl MapLaneLifecycle for L where - L: OnUpdate + OnRemove + OnClear +impl MapLaneLifecycle for L where + L: OnUpdate + OnRemove + OnClear { } -pub trait MapLaneLifecycleShared: - OnUpdateShared - + OnRemoveShared - + OnClearShared +pub trait MapLaneLifecycleShared: + OnUpdateShared + + OnRemoveShared + + OnClearShared { } -impl MapLaneLifecycleShared for L where - L: OnUpdateShared - + OnRemoveShared - + OnClearShared +impl MapLaneLifecycleShared for L where + L: OnUpdateShared + + OnRemoveShared + + OnClearShared { } +type LifecycleType = fn(Context, Shared, K, V, M); + /// A lifecycle for a map lane with some shared state (shard with other lifecycles in the same agent). /// /// # Type Parameters /// * `Context` - The context for the event handlers (providing access to the agent lanes). /// * `Shared` - The shared state to which the lifecycle has access. +/// * `K` - The key type of the map. +/// * `V` - The value type of the map. +/// * `M` - The map type (for example `HashMap`). /// * `FUpd` - The `on_update` event handler. /// * `FRem` - The `on_remove` event handler. /// * `FClr` - The `on_clear` event handler. @@ -71,18 +76,19 @@ pub struct StatefulMapLaneLifecycle< Shared, K, V, + M, FUpd = NoHandler, FRem = NoHandler, FClr = NoHandler, > { - _value_type: PhantomData, + _value_type: PhantomData>, on_update: FUpd, on_remove: FRem, on_clear: FClr, } -impl Clone - for StatefulMapLaneLifecycle +impl Clone + for StatefulMapLaneLifecycle { fn clone(&self) -> Self { Self { @@ -94,7 +100,7 @@ impl Clone } } -impl Default for StatefulMapLaneLifecycle { +impl Default for StatefulMapLaneLifecycle { fn default() -> Self { Self { _value_type: Default::default(), @@ -105,17 +111,17 @@ impl Default for StatefulMapLaneLifecycle - StatefulMapLaneLifecycle +impl + StatefulMapLaneLifecycle { pub fn on_update( self, f: F, - ) -> StatefulMapLaneLifecycle, FRem, FClr> + ) -> StatefulMapLaneLifecycle, FRem, FClr> where B: ?Sized, V: Borrow, - BorrowHandler: for<'a> OnUpdateShared, + BorrowHandler: for<'a> OnUpdateShared, { StatefulMapLaneLifecycle { _value_type: PhantomData, @@ -128,9 +134,9 @@ impl pub fn on_remove( self, f: F, - ) -> StatefulMapLaneLifecycle, FClr> + ) -> StatefulMapLaneLifecycle, FClr> where - FnHandler: OnRemoveShared, + FnHandler: OnRemoveShared, { StatefulMapLaneLifecycle { _value_type: PhantomData, @@ -143,9 +149,9 @@ impl pub fn on_clear( self, f: F, - ) -> StatefulMapLaneLifecycle> + ) -> StatefulMapLaneLifecycle> where - FnHandler: OnClearShared, + FnHandler: OnClearShared, { StatefulMapLaneLifecycle { _value_type: PhantomData, @@ -156,10 +162,10 @@ impl } } -impl OnUpdateShared - for StatefulMapLaneLifecycle +impl OnUpdateShared + for StatefulMapLaneLifecycle where - FUpd: OnUpdateShared, + FUpd: OnUpdateShared, FRem: Send, FClr: Send, { @@ -172,7 +178,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, @@ -182,11 +188,11 @@ where } } -impl OnRemoveShared - for StatefulMapLaneLifecycle +impl OnRemoveShared + for StatefulMapLaneLifecycle where FUpd: Send, - FRem: OnRemoveShared, + FRem: OnRemoveShared, FClr: Send, { type OnRemoveHandler<'a> = FRem::OnRemoveHandler<'a> @@ -198,7 +204,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: V, ) -> Self::OnRemoveHandler<'a> { @@ -207,12 +213,12 @@ where } } -impl OnClearShared - for StatefulMapLaneLifecycle +impl OnClearShared + for StatefulMapLaneLifecycle where FUpd: Send, FRem: Send, - FClr: OnClearShared, + FClr: OnClearShared, { type OnClearHandler<'a> = FClr::OnClearHandler<'a> where @@ -223,7 +229,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - before: HashMap, + before: M, ) -> Self::OnClearHandler<'a> { self.on_clear.on_clear(shared, handler_context, before) } diff --git a/server/swimos_agent/src/lanes/map/lifecycle/on_clear.rs b/server/swimos_agent/src/lanes/map/lifecycle/on_clear.rs index 11438d511..58bfd977b 100644 --- a/server/swimos_agent/src/lanes/map/lifecycle/on_clear.rs +++ b/server/swimos_agent/src/lanes/map/lifecycle/on_clear.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; - use swimos_utilities::handlers::{FnHandler, NoHandler}; use crate::{ @@ -22,19 +20,19 @@ use crate::{ }; /// Lifecycle event for the `on_clear` event of a map lane. -pub trait OnClear: Send { +pub trait OnClear: Send { type OnClearHandler<'a>: EventHandler + 'a where Self: 'a; /// # Arguments /// * `before` - The contents of the map before it was cleared. - fn on_clear(&self, before: HashMap) -> Self::OnClearHandler<'_>; + fn on_clear(&self, before: M) -> Self::OnClearHandler<'_>; } /// Lifecycle event for the `on_clear` event of a map lane where the event handler /// has shared state with other handlers for the same agent. -pub trait OnClearShared: Send { +pub trait OnClearShared: Send { type OnClearHandler<'a>: EventHandler + 'a where Self: 'a, @@ -48,21 +46,21 @@ pub trait OnClearShared: Send { &'a self, shared: &'a Shared, handler_context: HandlerContext, - before: HashMap, + before: M, ) -> Self::OnClearHandler<'a>; } -impl OnClear for NoHandler { +impl OnClear for NoHandler { type OnClearHandler<'a> = UnitHandler where Self: 'a; - fn on_clear(&self, _before: HashMap) -> Self::OnClearHandler<'_> { + fn on_clear(&self, _before: M) -> Self::OnClearHandler<'_> { UnitHandler::default() } } -impl OnClearShared for NoHandler { +impl OnClearShared for NoHandler { type OnClearHandler<'a> = UnitHandler where Self: 'a, @@ -72,32 +70,32 @@ impl OnClearShared for NoHandler { &'a self, _shared: &'a Shared, _handler_context: HandlerContext, - _before: HashMap, + _before: M, ) -> Self::OnClearHandler<'a> { UnitHandler::default() } } -impl OnClear for FnHandler +impl OnClear for FnHandler where - F: Fn(HashMap) -> H + Send, + F: Fn(M) -> H + Send, H: EventHandler + 'static, { type OnClearHandler<'a> = H where Self: 'a; - fn on_clear(&self, before: HashMap) -> Self::OnClearHandler<'_> { + fn on_clear(&self, before: M) -> Self::OnClearHandler<'_> { let FnHandler(f) = self; f(before) } } -impl OnClearShared for FnHandler +impl OnClearShared for FnHandler where - F: for<'a> TakeFn<'a, Context, Shared, HashMap> + Send, + F: for<'a> TakeFn<'a, Context, Shared, M> + Send, { - type OnClearHandler<'a> = >>::Handler + type OnClearHandler<'a> = >::Handler where Self: 'a, Shared: 'a; @@ -106,7 +104,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - before: HashMap, + before: M, ) -> Self::OnClearHandler<'a> { let FnHandler(f) = self; f.make_handler(shared, handler_context, before) diff --git a/server/swimos_agent/src/lanes/map/lifecycle/on_remove.rs b/server/swimos_agent/src/lanes/map/lifecycle/on_remove.rs index dc06090b1..f32993d6f 100644 --- a/server/swimos_agent/src/lanes/map/lifecycle/on_remove.rs +++ b/server/swimos_agent/src/lanes/map/lifecycle/on_remove.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; - use swimos_utilities::handlers::{FnHandler, NoHandler}; use crate::{ @@ -22,7 +20,7 @@ use crate::{ }; /// Lifecycle event for the `on_remove` event of a map lane. -pub trait OnRemove: Send { +pub trait OnRemove: Send { type OnRemoveHandler<'a>: EventHandler + 'a where Self: 'a; @@ -31,17 +29,12 @@ pub trait OnRemove: Send { /// * `map` - The current contents of the map. /// * `key` - The key that was removed. /// * `prev_value` - The value that was removed. - fn on_remove<'a>( - &'a self, - map: &HashMap, - key: K, - prev_value: V, - ) -> Self::OnRemoveHandler<'a>; + fn on_remove<'a>(&'a self, map: &M, key: K, prev_value: V) -> Self::OnRemoveHandler<'a>; } /// Lifecycle event for the `on_remove` event of a map lane where the event handler /// has shared state with other handlers for the same agent. -pub trait OnRemoveShared: Send { +pub trait OnRemoveShared: Send { type OnRemoveHandler<'a>: EventHandler + 'a where Self: 'a, @@ -57,28 +50,23 @@ pub trait OnRemoveShared: Send { &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: V, ) -> Self::OnRemoveHandler<'a>; } -impl OnRemove for NoHandler { +impl OnRemove for NoHandler { type OnRemoveHandler<'a> = UnitHandler where Self: 'a; - fn on_remove<'a>( - &'a self, - _map: &HashMap, - _key: K, - _prev_value: V, - ) -> Self::OnRemoveHandler<'a> { + fn on_remove<'a>(&'a self, _map: &M, _key: K, _prev_value: V) -> Self::OnRemoveHandler<'a> { UnitHandler::default() } } -impl OnRemoveShared for NoHandler { +impl OnRemoveShared for NoHandler { type OnRemoveHandler<'a> = UnitHandler where Self: 'a, @@ -88,7 +76,7 @@ impl OnRemoveShared for NoHandler &'a self, _shared: &'a Shared, _handler_context: HandlerContext, - _map: &HashMap, + _map: &M, _key: K, _prev_value: V, ) -> Self::OnRemoveHandler<'a> { @@ -96,31 +84,26 @@ impl OnRemoveShared for NoHandler } } -impl OnRemove for FnHandler +impl OnRemove for FnHandler where - F: Fn(&HashMap, K, V) -> H + Send, + F: Fn(&M, K, V) -> H + Send, H: EventHandler + 'static, { type OnRemoveHandler<'a> = H where Self: 'a; - fn on_remove<'a>( - &'a self, - map: &HashMap, - key: K, - prev_value: V, - ) -> Self::OnRemoveHandler<'a> { + fn on_remove<'a>(&'a self, map: &M, key: K, prev_value: V) -> Self::OnRemoveHandler<'a> { let FnHandler(f) = self; f(map, key, prev_value) } } -impl OnRemoveShared for FnHandler +impl OnRemoveShared for FnHandler where - F: for<'a> MapRemoveFn<'a, Context, Shared, K, V> + Send, + F: for<'a> MapRemoveFn<'a, Context, Shared, K, V, M> + Send, { - type OnRemoveHandler<'a> = >::Handler + type OnRemoveHandler<'a> = >::Handler where Self: 'a, Shared: 'a; @@ -129,7 +112,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: V, ) -> Self::OnRemoveHandler<'a> { diff --git a/server/swimos_agent/src/lanes/map/lifecycle/on_update.rs b/server/swimos_agent/src/lanes/map/lifecycle/on_update.rs index f6ea55505..761b44903 100644 --- a/server/swimos_agent/src/lanes/map/lifecycle/on_update.rs +++ b/server/swimos_agent/src/lanes/map/lifecycle/on_update.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{borrow::Borrow, collections::HashMap}; +use std::borrow::Borrow; use swimos_utilities::handlers::{BorrowHandler, FnHandler, NoHandler}; @@ -22,7 +22,7 @@ use crate::{ }; /// Lifecycle event for the `on_update` event of a map lane. -pub trait OnUpdate: Send { +pub trait OnUpdate: Send { type OnUpdateHandler<'a>: EventHandler + 'a where Self: 'a; @@ -34,7 +34,7 @@ pub trait OnUpdate: Send { /// * `new_value` - The updated value. fn on_update<'a>( &'a self, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, @@ -43,7 +43,7 @@ pub trait OnUpdate: Send { /// Lifecycle event for the `on_update` event of a map lane where the event handler /// has shared state with other handlers for the same agent. -pub trait OnUpdateShared: Send { +pub trait OnUpdateShared: Send { type OnUpdateHandler<'a>: EventHandler + 'a where Self: 'a, @@ -60,21 +60,21 @@ pub trait OnUpdateShared: Send { &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, ) -> Self::OnUpdateHandler<'a>; } -impl OnUpdate for NoHandler { +impl OnUpdate for NoHandler { type OnUpdateHandler<'a> = UnitHandler where Self: 'a; fn on_update<'a>( &'a self, - _map: &HashMap, + _map: &M, _key: K, _prev_value: Option, _new_value: &V, @@ -83,7 +83,7 @@ impl OnUpdate for NoHandler { } } -impl OnUpdateShared for NoHandler { +impl OnUpdateShared for NoHandler { type OnUpdateHandler<'a> = UnitHandler where Self: 'a, @@ -93,7 +93,7 @@ impl OnUpdateShared for NoHandler &'a self, _shared: &'a Shared, _handler_context: HandlerContext, - _map: &HashMap, + _map: &M, _key: K, _prev_value: Option, _new_value: &V, @@ -102,9 +102,9 @@ impl OnUpdateShared for NoHandler } } -impl OnUpdate for FnHandler +impl OnUpdate for FnHandler where - F: Fn(&HashMap, K, Option, &V) -> H + Send, + F: Fn(&M, K, Option, &V) -> H + Send, H: EventHandler + 'static, { type OnUpdateHandler<'a> = H @@ -113,7 +113,7 @@ where fn on_update<'a>( &'a self, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, @@ -123,11 +123,11 @@ where } } -impl OnUpdateShared for FnHandler +impl OnUpdateShared for FnHandler where - F: for<'a> MapUpdateFn<'a, Context, Shared, K, V> + Send, + F: for<'a> MapUpdateFn<'a, Context, Shared, K, V, M> + Send, { - type OnUpdateHandler<'a> = >::Handler + type OnUpdateHandler<'a> = >::Handler where Self: 'a, Shared: 'a; @@ -136,7 +136,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, @@ -146,11 +146,11 @@ where } } -impl OnUpdate for BorrowHandler +impl OnUpdate for BorrowHandler where B: ?Sized, V: Borrow, - F: Fn(&HashMap, K, Option, &B) -> H + Send, + F: Fn(&M, K, Option, &B) -> H + Send, H: EventHandler + 'static, { type OnUpdateHandler<'a> = H @@ -159,7 +159,7 @@ where fn on_update<'a>( &'a self, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, @@ -169,13 +169,14 @@ where } } -impl OnUpdateShared for BorrowHandler +impl OnUpdateShared + for BorrowHandler where B: ?Sized, V: Borrow, - F: for<'a> MapUpdateBorrowFn<'a, Context, Shared, K, V, B> + Send, + F: for<'a> MapUpdateBorrowFn<'a, Context, Shared, K, V, M, B> + Send, { - type OnUpdateHandler<'a> = >::Handler + type OnUpdateHandler<'a> = >::Handler where Self: 'a, Shared: 'a; @@ -184,7 +185,7 @@ where &'a self, shared: &'a Shared, handler_context: HandlerContext, - map: &HashMap, + map: &M, key: K, prev_value: Option, new_value: &V, diff --git a/server/swimos_agent/src/lanes/map/mod.rs b/server/swimos_agent/src/lanes/map/mod.rs index 328960d5b..19ca8c392 100644 --- a/server/swimos_agent/src/lanes/map/mod.rs +++ b/server/swimos_agent/src/lanes/map/mod.rs @@ -43,7 +43,7 @@ use crate::{ HandlerTrans, Modification, StepResult, }, item::{AgentItem, InspectableMapLikeItem, MapItem, MapLikeItem, MutableMapLikeItem}, - map_storage::{MapStoreInner, TransformEntryResult}, + map_storage::{MapOps, MapOpsWithEntry, MapStoreInner, TransformEntryResult}, meta::AgentMetadata, }; @@ -53,26 +53,23 @@ pub use event::MapLaneEvent; use super::{LaneItem, ProjTransform}; -type Inner = MapStoreInner>; +type Inner = MapStoreInner, M>; /// Model of a value lane. This maintains a sate consisting of a hash-map from keys to values. It generates an /// event whenever the map is updated (updating the value for a key, removing a key or clearing the map). -/// -/// TODO: This could be parameterized over the type of the hash (and potentially over the kind of the map, -/// potentially allowing a choice between hash and ordered maps). #[derive(Debug)] -pub struct MapLane { +pub struct MapLane> { id: u64, - inner: RefCell>, + inner: RefCell>, } assert_impl_all!(MapLane<(), ()>: Send); -impl MapLane { +impl MapLane { /// # Arguments /// * `id` - The ID of the lane. This should be unique within an agent. /// * `init` - The initial contents of the map. - pub fn new(id: u64, init: HashMap) -> Self { + pub fn new(id: u64, init: M) -> Self { MapLane { id, inner: RefCell::new(Inner::new(init)), @@ -80,31 +77,33 @@ impl MapLane { } } -impl AgentItem for MapLane { +impl AgentItem for MapLane { fn id(&self) -> u64 { self.id } } -impl MapItem for MapLane +impl MapItem for MapLane where K: Eq + Hash + Clone, + M: MapOps, { - fn init(&self, map: HashMap) { + fn init(&self, map: M) { self.inner.borrow_mut().init(map) } fn read_with_prev(&self, f: F) -> R where - F: FnOnce(Option>, &HashMap) -> R, + F: FnOnce(Option>, &M) -> R, { self.inner.borrow_mut().read_with_prev(f) } } -impl MapLane +impl MapLane where K: Clone + Eq + Hash, + M: MapOps, { /// Update the value associated with a key. pub(crate) fn update(&self, key: K, value: V) { @@ -135,6 +134,7 @@ where K: Borrow, Q: Hash + Eq, F: FnOnce(Option<&V>) -> R, + M: MapOpsWithEntry, { self.inner.borrow().with_entry(key, f) } @@ -142,7 +142,7 @@ where /// Read the complete state of the map. pub fn get_map(&self, f: F) -> R where - F: FnOnce(&HashMap) -> R, + F: FnOnce(&M) -> R, { self.inner.borrow().get_map(f) } @@ -154,15 +154,13 @@ where } } -impl MapLane -where - K: Eq + Hash, -{ +impl MapLane { pub fn with_entry(&self, key: &K, f: F) -> U where B: ?Sized, V: Borrow, F: FnOnce(Option<&B>) -> U, + M: MapOpsWithEntry, { self.inner.borrow().with_entry(key, f) } @@ -170,10 +168,11 @@ where const INFALLIBLE_SER: &str = "Serializing lane responses to recon should be infallible."; -impl LaneItem for MapLane +impl LaneItem for MapLane where K: Clone + Eq + Hash + StructuralWritable, V: StructuralWritable, + M: MapOps, { fn write_to_buffer(&self, buffer: &mut BytesMut) -> WriteResult { let mut encoder = MapLaneResponseEncoder::default(); @@ -192,13 +191,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will update the value of an entry in the map. -pub struct MapLaneUpdate { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneUpdate> { + projection: for<'a> fn(&'a C) -> &'a MapLane, key_value: Option<(K, V)>, } -impl MapLaneUpdate { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K, value: V) -> Self { +impl MapLaneUpdate { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K, value: V) -> Self { MapLaneUpdate { projection, key_value: Some((key, value)), @@ -206,10 +205,11 @@ impl MapLaneUpdate { } } -impl HandlerAction for MapLaneUpdate +impl HandlerAction for MapLaneUpdate where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -251,13 +251,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will remove an entry from the map. -pub struct MapLaneRemove { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneRemove> { + projection: for<'a> fn(&'a C) -> &'a MapLane, key: Option, } -impl MapLaneRemove { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K) -> Self { +impl MapLaneRemove { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K) -> Self { MapLaneRemove { projection, key: Some(key), @@ -265,10 +265,11 @@ impl MapLaneRemove { } } -impl HandlerAction for MapLaneRemove +impl HandlerAction for MapLaneRemove where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -304,13 +305,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will clear the map. -pub struct MapLaneClear { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneClear> { + projection: for<'a> fn(&'a C) -> &'a MapLane, done: bool, } -impl MapLaneClear { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane) -> Self { +impl MapLaneClear { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane) -> Self { MapLaneClear { projection, done: false, @@ -318,10 +319,11 @@ impl MapLaneClear { } } -impl HandlerAction for MapLaneClear +impl HandlerAction for MapLaneClear where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -358,14 +360,14 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will get an entry from the map. -pub struct MapLaneGet { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneGet> { + projection: for<'a> fn(&'a C) -> &'a MapLane, key: K, done: bool, } -impl MapLaneGet { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K) -> Self { +impl MapLaneGet { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K) -> Self { MapLaneGet { projection, key, @@ -374,11 +376,12 @@ impl MapLaneGet { } } -impl HandlerAction for MapLaneGet +impl HandlerAction for MapLaneGet where C: AgentDescription, K: Clone + Eq + Hash, V: Clone, + M: MapOpsWithEntry, { type Completion = Option; @@ -414,13 +417,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will read the entire state of a map lane. -pub struct MapLaneGetMap { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneGetMap> { + projection: for<'a> fn(&'a C) -> &'a MapLane, done: bool, } -impl MapLaneGetMap { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane) -> Self { +impl MapLaneGetMap { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane) -> Self { MapLaneGetMap { projection, done: false, @@ -428,13 +431,14 @@ impl MapLaneGetMap { } } -impl HandlerAction for MapLaneGetMap +impl HandlerAction for MapLaneGetMap where C: AgentDescription, K: Clone + Eq + Hash, V: Clone, + M: MapOps + Clone, { - type Completion = HashMap; + type Completion = M; fn step( &mut self, @@ -463,13 +467,35 @@ where } } -impl HandlerAction for MapLaneWithEntry +/// An [event handler](crate::event_handler::EventHandler)`] that will alter an entry in the map. +pub struct MapLaneWithEntry> { + projection: for<'a> fn(&'a C) -> &'a MapLane, + key_and_f: Option<(K, F)>, + _type: PhantomData, +} + +impl MapLaneWithEntry { + /// #Arguments + /// * `projection` - Projection from the agent context to the lane. + /// * `key` - Key of the entry. + /// * `f` - The closure to apply to the entry. + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K, f: F) -> Self { + MapLaneWithEntry { + projection, + key_and_f: Some((key, f)), + _type: PhantomData, + } + } +} + +impl HandlerAction for MapLaneWithEntry where C: AgentDescription, K: Eq + Hash, B: ?Sized, V: Borrow, F: FnOnce(Option<&B>) -> U, + M: MapOpsWithEntry, { type Completion = U; @@ -504,35 +530,14 @@ where } } -/// An [event handler](crate::event_handler::EventHandler)`] that will alter an entry in the map. -pub struct MapLaneWithEntry { - projection: for<'a> fn(&'a C) -> &'a MapLane, - key_and_f: Option<(K, F)>, - _type: PhantomData, -} - -impl MapLaneWithEntry { - /// #Arguments - /// * `projection` - Projection from the agent context to the lane. - /// * `key` - Key of the entry. - /// * `f` - The closure to apply to the entry. - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K, f: F) -> Self { - MapLaneWithEntry { - projection, - key_and_f: Some((key, f)), - _type: PhantomData, - } - } -} - /// An [event handler](crate::event_handler::EventHandler)`] that will request a sync from the lane. -pub struct MapLaneSync { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneSync> { + projection: for<'a> fn(&'a C) -> &'a MapLane, id: Option, } -impl MapLaneSync { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, id: Uuid) -> Self { +impl MapLaneSync { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, id: Uuid) -> Self { MapLaneSync { projection, id: Some(id), @@ -540,10 +545,11 @@ impl MapLaneSync { } } -impl HandlerAction for MapLaneSync +impl HandlerAction for MapLaneSync where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -578,15 +584,15 @@ where } } -type MapLaneHandler = Coprod!( - MapLaneUpdate, - MapLaneRemove, - MapLaneClear, - MapLaneDropOrTake, +type MapLaneHandler> = Coprod!( + MapLaneUpdate, + MapLaneRemove, + MapLaneClear, + MapLaneDropOrTake, ); -impl HandlerTrans> for ProjTransform> { - type Out = MapLaneHandler; +impl HandlerTrans> for ProjTransform> { + type Out = MapLaneHandler; fn transform(self, input: MapMessage) -> Self::Out { let ProjTransform { projection } = self; @@ -697,31 +703,32 @@ where } } -pub type DecodeAndApply = - AndThen, MapLaneHandler, ProjTransform>>; +pub type DecodeAndApply> = + AndThen, MapLaneHandler, ProjTransform>>; /// Create an event handler that will decode an incoming map message and apply the value into a map lane. -pub fn decode_and_apply( +pub fn decode_and_apply( message: MapMessage, - projection: fn(&C) -> &MapLane, -) -> DecodeAndApply + projection: fn(&C) -> &MapLane, +) -> DecodeAndApply where C: AgentDescription, K: Form + Clone + Eq + Hash, V: RecognizerReadable, + M: MapOps, { let decode: DecodeMapMessage = DecodeMapMessage::new(message); decode.and_then(ProjTransform::new(projection)) } /// An (event handler)[`crate::event_handler::EventHandler`] that will alter an entry in the map. -pub struct MapLaneTransformEntry { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneTransformEntry> { + projection: for<'a> fn(&'a C) -> &'a MapLane, key_and_f: Option<(K, F)>, } -impl MapLaneTransformEntry { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K, f: F) -> Self { +impl MapLaneTransformEntry { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, key: K, f: F) -> Self { MapLaneTransformEntry { projection, key_and_f: Some((key, f)), @@ -729,11 +736,12 @@ impl MapLaneTransformEntry { } } -impl HandlerAction for MapLaneTransformEntry +impl HandlerAction for MapLaneTransformEntry where C: AgentDescription, K: Clone + Eq + Hash, F: FnOnce(Option<&V>) -> Option, + M: MapOps, { type Completion = (); @@ -778,12 +786,13 @@ where } } -impl MapLikeItem for MapLane +impl MapLikeItem for MapLane where K: Clone + Eq + Hash + Send + 'static, V: Clone + 'static, + M: MapOpsWithEntry + Clone + 'static, { - type GetHandler = MapLaneGet + type GetHandler = MapLaneGet where C: AgentDescription + 'static; @@ -794,7 +803,7 @@ where MapLaneGet::new(projection, key) } - type GetMapHandler = MapLaneGetMap + type GetMapHandler = MapLaneGetMap where C: AgentDescription + 'static; @@ -805,49 +814,48 @@ where } } -impl InspectableMapLikeItem for MapLane +impl InspectableMapLikeItem for MapLane where K: Eq + Hash + Send + 'static, - V: 'static, + V: Borrow + 'static, + B: ?Sized + 'static, + M: MapOpsWithEntry, { - type WithEntryHandler<'a, C, F, B, U> = MapLaneWithEntry + type WithEntryHandler<'a, C, F, U> = MapLaneWithEntry where Self: 'static, C: AgentDescription + 'a, - B: ?Sized +'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a; - fn with_entry_handler<'a, C, F, B, U>( + fn with_entry_handler<'a, C, F, U>( projection: fn(&C) -> &Self, key: K, f: F, - ) -> Self::WithEntryHandler<'a, C, F, B, U> + ) -> Self::WithEntryHandler<'a, C, F, U> where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a, { MapLaneWithEntry::new(projection, key, f) } } -impl MutableMapLikeItem for MapLane +impl MutableMapLikeItem for MapLane where K: Clone + Eq + Hash + Send + 'static, V: Send + 'static, + M: MapOps + 'static, { - type UpdateHandler = MapLaneUpdate + type UpdateHandler = MapLaneUpdate where C: AgentDescription + 'static; - type RemoveHandler = MapLaneRemove + type RemoveHandler = MapLaneRemove where C: AgentDescription + 'static; - type ClearHandler = MapLaneClear + type ClearHandler = MapLaneClear where C: AgentDescription + 'static; @@ -872,7 +880,7 @@ where MapLaneClear::new(projection) } - type TransformEntryHandler<'a, C, F> = MapLaneTransformEntry + type TransformEntryHandler<'a, C, F> = MapLaneTransformEntry where Self: 'static, C: AgentDescription + 'a, @@ -920,11 +928,12 @@ impl MapLaneSelectUpdate { } } -impl HandlerAction for MapLaneSelectUpdate +impl HandlerAction for MapLaneSelectUpdate where C: AgentDescription, K: Clone + Eq + Hash, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { type Completion = (); @@ -1003,10 +1012,11 @@ impl MapLaneSelectRemove { } } -impl HandlerAction for MapLaneSelectRemove +impl HandlerAction for MapLaneSelectRemove where K: Clone + Eq + Hash, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { type Completion = (); @@ -1078,11 +1088,12 @@ impl MapLaneSelectClear { } } -impl HandlerAction for MapLaneSelectClear +impl HandlerAction for MapLaneSelectClear where C: AgentDescription, K: Clone + Eq + Hash, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { type Completion = (); @@ -1140,12 +1151,13 @@ pub enum DecodeAndSelectApply { Done, } -impl HandlerAction for DecodeAndSelectApply +impl HandlerAction for DecodeAndSelectApply where C: AgentDescription, K: Form + Clone + Eq + Hash, V: RecognizerReadable, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { type Completion = (); @@ -1267,14 +1279,15 @@ where } /// Create an event handler that will decode an incoming map message and apply the value into a map lane. -pub fn decode_and_select_apply( +pub fn decode_and_select_apply( message: MapMessage, projection: F, ) -> DecodeAndSelectApply where K: Clone + Eq + Hash + RecognizerReadable, V: RecognizerReadable, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { let decode: DecodeMapMessage = DecodeMapMessage::new(message); DecodeAndSelectApply::Decoding(decode, projection) @@ -1301,11 +1314,12 @@ impl MapLaneSelectSync { } } -impl HandlerAction for MapLaneSelectSync +impl HandlerAction for MapLaneSelectSync where C: AgentDescription, K: Clone + Eq + Hash, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { type Completion = (); @@ -1347,14 +1361,14 @@ where } } -struct MapLaneRemoveMultiple { - projection: for<'a> fn(&'a C) -> &'a MapLane, +struct MapLaneRemoveMultiple> { + projection: for<'a> fn(&'a C) -> &'a MapLane, keys: Option>, - current: Option>, + current: Option>, } -impl MapLaneRemoveMultiple { - fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, keys: VecDeque) -> Self { +impl MapLaneRemoveMultiple { + fn new(projection: for<'a> fn(&'a C) -> &'a MapLane, keys: VecDeque) -> Self { MapLaneRemoveMultiple { projection, keys: Some(keys), @@ -1363,10 +1377,11 @@ impl MapLaneRemoveMultiple { } } -impl HandlerAction for MapLaneRemoveMultiple +impl HandlerAction for MapLaneRemoveMultiple where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -1424,23 +1439,23 @@ enum DropOrTake { Take, } -enum DropOrTakeState { +enum DropOrTakeState { Init, - Removing(MapLaneRemoveMultiple), + Removing(MapLaneRemoveMultiple), } /// An [event handler](crate::event_handler::EventHandler)`] that will either retain or drop the first `n` elements /// from a map (ordering the keys by the ordering of their Recon model representations). -pub struct MapLaneDropOrTake { - projection: for<'a> fn(&'a C) -> &'a MapLane, +pub struct MapLaneDropOrTake> { + projection: for<'a> fn(&'a C) -> &'a MapLane, kind: DropOrTake, number: u64, - state: DropOrTakeState, + state: DropOrTakeState, } -impl MapLaneDropOrTake { +impl MapLaneDropOrTake { fn new( - projection: for<'a> fn(&'a C) -> &'a MapLane, + projection: for<'a> fn(&'a C) -> &'a MapLane, kind: DropOrTake, number: u64, ) -> Self { @@ -1453,38 +1468,37 @@ impl MapLaneDropOrTake { } } -fn drop_or_take(map: &HashMap, kind: DropOrTake, number: usize) -> VecDeque +fn drop_or_take(map: &M, kind: DropOrTake, number: usize) -> VecDeque where K: StructuralWritable + Clone + Eq + Hash, + M: MapOps, +{ + if M::ORDERED_KEYS { + to_deque(kind, number, map.keys()) + } else { + let mut keys_with_recon = map.keys().map(|k| (k.structure(), k)).collect::>(); + keys_with_recon.sort_by(|(k1, _), (k2, _)| k1.cmp(k2)); + let it = keys_with_recon.into_iter().map(|(_, k)| k); + to_deque(kind, number, it) + } +} + +fn to_deque<'a, I, K>(kind: DropOrTake, number: usize, it: I) -> VecDeque +where + K: Clone + 'a, + I: Iterator, { - let mut keys_with_recon = map.keys().map(|k| (k.structure(), k)).collect::>(); - keys_with_recon.sort_by(|(k1, _), (k2, _)| k1.cmp(k2)); - let mut to_remove: VecDeque = VecDeque::new(); match kind { - DropOrTake::Drop => { - to_remove.extend( - keys_with_recon - .into_iter() - .take(number) - .map(|(_, k1)| k1.clone()), - ); - } - DropOrTake::Take => { - to_remove.extend( - keys_with_recon - .into_iter() - .skip(number) - .map(|(_, k1)| k1.clone()), - ); - } + DropOrTake::Drop => it.take(number).cloned().collect(), + DropOrTake::Take => it.skip(number).cloned().collect(), } - to_remove } -impl HandlerAction for MapLaneDropOrTake +impl HandlerAction for MapLaneDropOrTake where C: AgentDescription, K: StructuralWritable + Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -1561,10 +1575,11 @@ impl MapLaneSelectRemoveMultiple { } } -impl HandlerAction for MapLaneSelectRemoveMultiple +impl HandlerAction for MapLaneSelectRemoveMultiple where K: Clone + Eq + Hash, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { type Completion = (); @@ -1635,11 +1650,12 @@ impl MapLaneSelectDropOrTake { } } -impl HandlerAction for MapLaneSelectDropOrTake +impl HandlerAction for MapLaneSelectDropOrTake where C: AgentDescription, K: StructuralWritable + Clone + Eq + Hash, - F: SelectorFn>, + F: SelectorFn>, + M: MapOps, { type Completion = (); diff --git a/server/swimos_agent/src/lanes/map/tests.rs b/server/swimos_agent/src/lanes/map/tests.rs index 12947d368..81ffa0190 100644 --- a/server/swimos_agent/src/lanes/map/tests.rs +++ b/server/swimos_agent/src/lanes/map/tests.rs @@ -13,7 +13,7 @@ // limitations under the License. use std::borrow::Cow; -use std::collections::{HashMap, VecDeque}; +use std::collections::{BTreeMap, HashMap, VecDeque}; use std::fmt::Debug; use bytes::BytesMut; @@ -1122,35 +1122,75 @@ fn drop_take_choose_keys() { .collect::>(); assert_eq!( - drop_or_take(&map, super::DropOrTake::Drop, 2), + drop_or_take(&map, DropOrTake::Drop, 2), VecDeque::from(vec![1, 2]) ); assert_eq!( - drop_or_take(&map, super::DropOrTake::Take, 3), + drop_or_take(&map, DropOrTake::Take, 3), VecDeque::from(vec![4]) ); assert_eq!( - drop_or_take(&map, super::DropOrTake::Drop, 4), + drop_or_take(&map, DropOrTake::Drop, 4), VecDeque::from(vec![1, 2, 3, 4]) ); assert_eq!( - drop_or_take(&map, super::DropOrTake::Take, 4), + drop_or_take(&map, DropOrTake::Take, 4), VecDeque::from(vec![]) ); assert_eq!( - drop_or_take(&map, super::DropOrTake::Drop, 10), + drop_or_take(&map, DropOrTake::Drop, 10), VecDeque::from(vec![1, 2, 3, 4]) ); assert_eq!( - drop_or_take(&map, super::DropOrTake::Take, 10), + drop_or_take(&map, DropOrTake::Take, 10), VecDeque::from(vec![]) ); assert_eq!( - drop_or_take(&map, super::DropOrTake::Drop, 0), + drop_or_take(&map, DropOrTake::Drop, 0), VecDeque::from(vec![]) ); assert_eq!( - drop_or_take(&map, super::DropOrTake::Take, 0), + drop_or_take(&map, DropOrTake::Take, 0), + VecDeque::from(vec![1, 2, 3, 4]) + ); +} + +#[test] +fn drop_take_choose_keys_ordered() { + let map = [(1, 2), (2, 4), (3, 6), (4, 8)] + .into_iter() + .collect::>(); + + assert_eq!( + drop_or_take(&map, DropOrTake::Drop, 2), + VecDeque::from(vec![1, 2]) + ); + assert_eq!( + drop_or_take(&map, DropOrTake::Take, 3), + VecDeque::from(vec![4]) + ); + assert_eq!( + drop_or_take(&map, DropOrTake::Drop, 4), + VecDeque::from(vec![1, 2, 3, 4]) + ); + assert_eq!( + drop_or_take(&map, DropOrTake::Take, 4), + VecDeque::from(vec![]) + ); + assert_eq!( + drop_or_take(&map, DropOrTake::Drop, 10), + VecDeque::from(vec![1, 2, 3, 4]) + ); + assert_eq!( + drop_or_take(&map, DropOrTake::Take, 10), + VecDeque::from(vec![]) + ); + assert_eq!( + drop_or_take(&map, DropOrTake::Drop, 0), + VecDeque::from(vec![]) + ); + assert_eq!( + drop_or_take(&map, DropOrTake::Take, 0), VecDeque::from(vec![1, 2, 3, 4]) ); } diff --git a/server/swimos_agent/src/lanes/queues/mod.rs b/server/swimos_agent/src/lanes/queues/mod.rs index b4d3351de..658eb99db 100644 --- a/server/swimos_agent/src/lanes/queues/mod.rs +++ b/server/swimos_agent/src/lanes/queues/mod.rs @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{HashMap, VecDeque}; +use std::collections::VecDeque; use std::hash::Hash; use swimos_agent_protocol::{LaneResponse, MapOperation}; use uuid::Uuid; use crate::event_queue::{to_operation, EventQueue}; -use crate::map_storage::MapEventQueue; +use crate::map_storage::{MapEventQueue, MapOps}; /// For a sync operation on a map lane, keeps track of which keys are synced for a given remote. #[derive(Debug)] @@ -202,7 +202,11 @@ where self.push_operation(action) } - fn pop<'a>(&mut self, content: &'a HashMap) -> Option> { + fn pop<'a, M>(&mut self, content: &'a M) -> Option> + where + K: 'a, + M: MapOps, + { loop { match WriteQueues::pop(self)? { ToWrite::Event(action) => { diff --git a/server/swimos_agent/src/lib.rs b/server/swimos_agent/src/lib.rs index 035d8b5c1..181f5e252 100644 --- a/server/swimos_agent/src/lib.rs +++ b/server/swimos_agent/src/lib.rs @@ -82,6 +82,9 @@ pub use item::AgentItem; #[doc(hidden)] pub use meta::AgentMetadata; +#[doc(hidden)] +pub use map_storage::MapBacking; + #[doc(hidden)] pub mod model { pub use swimos_agent_protocol::{MapMessage, MapOperation}; diff --git a/server/swimos_agent/src/map_storage/mod.rs b/server/swimos_agent/src/map_storage/mod.rs index 411580c84..dfc3bd514 100644 --- a/server/swimos_agent/src/map_storage/mod.rs +++ b/server/swimos_agent/src/map_storage/mod.rs @@ -12,7 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{borrow::Borrow, collections::HashMap, hash::Hash}; +use std::{ + borrow::Borrow, + collections::{BTreeMap, HashMap}, + hash::{BuildHasher, Hash}, + ops::Index, +}; use swimos_agent_protocol::MapOperation; @@ -20,9 +25,9 @@ use crate::lanes::map::MapLaneEvent; /// Common backing store used by both map stores and map lanes. #[derive(Debug)] -pub struct MapStoreInner { - content: HashMap, - previous: Option>, +pub struct MapStoreInner> { + content: M, + previous: Option>, queue: Q, } @@ -37,11 +42,14 @@ pub trait MapEventQueue: Default { fn push(&mut self, action: MapOperation); - fn pop<'a>(&mut self, content: &'a HashMap) -> Option>; + fn pop<'a, M>(&mut self, content: &'a M) -> Option> + where + K: 'a, + M: MapOps; } -impl MapStoreInner { - pub fn new(content: HashMap) -> Self { +impl MapStoreInner { + pub fn new(content: M) -> Self { MapStoreInner { content, previous: Default::default(), @@ -56,15 +64,217 @@ pub enum TransformEntryResult { Remove, } -impl MapStoreInner +impl MapStoreInner { + pub fn init(&mut self, map: M) { + self.content = map; + } +} + +impl MapStoreInner where - K: Eq + Hash + Clone, Q: MapEventQueue, + M: MapOps, { - pub fn init(&mut self, map: HashMap) { - self.content = map; + pub fn queue(&mut self) -> &mut Q { + &mut self.queue + } + + pub fn pop_operation(&mut self) -> Option> { + let MapStoreInner { content, queue, .. } = self; + queue.pop(content) + } +} + +pub trait MapBacking { + type KeyType; + type ValueType; +} + +/// Operations that a map implementation (e.g. [`HashMap`], [`BTreeMap`]) must support to be +/// used as a backing store for a [`MapStoreInner`] (which underlies all map stores and map lanes). +pub trait MapOps: MapBacking { + /// Attempt to get the value associated with a key. + /// + /// # Arguments + /// * `key` - The key. + fn get(&self, key: &K) -> Option<&V>; + + /// Insert a new entry into the map. If such an entry already existed, the old value + /// will be returned. + /// + /// # Arguments + /// * `key` - The key. + /// * `value` - The new value. + fn insert(&mut self, key: K, value: V) -> Option; + + /// Remove an entry from the map. If the entry existed, it's previous value will be returned. + /// # Arguments + /// * `key` - The key of the entry to remove. + fn remove(&mut self, key: &K) -> Option; + + /// Clear the map, returning the previous contents. + fn take(&mut self) -> Self; + + /// Get an iterator over the keys of the map. If [`MapOps::ORDERED_KEYS`] is true, this must + /// return the keys in their intrinsic order (which must be consistent with the order of their + /// Recon representation, if the key type implements [`swimos_form::Form`]). + fn keys<'a>(&'a self) -> impl Iterator + where + K: 'a; + + /// If this is true, the keys returned by [`MapOps::keys`] will be in order. + const ORDERED_KEYS: bool; + + /// Build an instance of the map from an iterator of key-value pairs. + /// + /// # Arguments + /// * `it` - The key value pairs to insert into the map. + fn from_entries(it: I) -> Self + where + I: IntoIterator; +} + +/// Extension to [`MapOps`] that allows entries of the map to be inspected with a borrowed form +/// of the key. +pub trait MapOpsWithEntry: + MapOps + for<'a> Index<&'a BK, Output = V> +{ + /// Compute a value based on an entry of the map. + /// + /// # Arguments + /// * `key` - Borrowed form of the key. + /// * `f` - A function to apply to a borrow of the value associated with the key. + fn with_item(&self, key: &BK, f: F) -> R + where + V: Borrow, + F: FnOnce(Option<&BV>) -> R; +} + +impl MapBacking for HashMap { + type KeyType = K; + + type ValueType = V; +} + +impl MapOps for HashMap +where + K: Hash + Eq, + S: BuildHasher + Default, +{ + fn get(&self, key: &K) -> Option<&V> { + HashMap::get(self, key) + } + + fn insert(&mut self, key: K, value: V) -> Option { + HashMap::insert(self, key, value) + } + + fn remove(&mut self, key: &K) -> Option { + HashMap::remove(self, key) } + fn take(&mut self) -> Self { + std::mem::take(self) + } + + fn keys<'a>(&'a self) -> impl Iterator + where + K: 'a, + { + HashMap::keys(self) + } + + fn from_entries(it: I) -> Self + where + I: IntoIterator, + { + it.into_iter().collect() + } + + const ORDERED_KEYS: bool = false; +} + +impl MapBacking for BTreeMap { + type KeyType = K; + + type ValueType = V; +} + +impl MapOps for BTreeMap +where + K: Ord, +{ + fn get(&self, key: &K) -> Option<&V> { + BTreeMap::get(self, key) + } + + fn insert(&mut self, key: K, value: V) -> Option { + BTreeMap::insert(self, key, value) + } + + fn remove(&mut self, key: &K) -> Option { + BTreeMap::remove(self, key) + } + + fn take(&mut self) -> Self { + std::mem::take(self) + } + + fn keys<'a>(&'a self) -> impl Iterator + where + K: 'a, + { + BTreeMap::keys(self) + } + + fn from_entries(it: I) -> Self + where + I: IntoIterator, + { + it.into_iter().collect() + } + + const ORDERED_KEYS: bool = true; +} + +impl MapOpsWithEntry for HashMap +where + K: Borrow, + BK: ?Sized, + K: Hash + Eq, + BK: Hash + Eq, +{ + fn with_item(&self, key: &BK, f: F) -> R + where + V: Borrow, + F: FnOnce(Option<&BV>) -> R, + { + f(self.get(key).map(Borrow::borrow)) + } +} + +impl MapOpsWithEntry for BTreeMap +where + K: Borrow, + BK: ?Sized, + K: Ord, + BK: Ord, +{ + fn with_item(&self, key: &BK, f: F) -> R + where + V: Borrow, + F: FnOnce(Option<&BV>) -> R, + { + f(self.get(key).map(Borrow::borrow)) + } +} + +impl MapStoreInner +where + K: Clone, + Q: MapEventQueue, + M: MapOps, +{ pub fn update(&mut self, key: K, value: V) { let MapStoreInner { content, @@ -123,20 +333,26 @@ where queue.push(MapOperation::Remove { key: key.clone() }); } } +} +impl MapStoreInner +where + Q: MapEventQueue, + M: MapOps, +{ pub fn clear(&mut self) { let MapStoreInner { content, previous, queue, } = self; - *previous = Some(MapLaneEvent::Clear(std::mem::take(content))); + *previous = Some(MapLaneEvent::Clear(content.take())); queue.push(MapOperation::Clear); } pub fn get_map(&self, f: F) -> R where - F: FnOnce(&HashMap) -> R, + F: FnOnce(&M) -> R, { let MapStoreInner { content, .. } = self; f(content) @@ -144,38 +360,26 @@ where pub fn read_with_prev(&mut self, f: F) -> R where - F: FnOnce(Option>, &HashMap) -> R, + F: FnOnce(Option>, &M) -> R, { let MapStoreInner { content, previous, .. } = self; f(previous.take(), content) } - - pub fn queue(&mut self) -> &mut Q { - &mut self.queue - } - - pub fn pop_operation(&mut self) -> Option> { - let MapStoreInner { content, queue, .. } = self; - queue.pop(content) - } } -impl MapStoreInner -where - K: Eq + Hash, -{ - pub fn with_entry(&self, key: &B1, f: F) -> R +impl MapStoreInner { + pub fn with_entry(&self, key: &BK, f: F) -> R where - B1: ?Sized, - B2: ?Sized, - K: Borrow, - V: Borrow, - B1: Hash + Eq, - F: FnOnce(Option<&B2>) -> R, + BK: ?Sized, + BV: ?Sized, + K: Borrow, + V: Borrow, + F: FnOnce(Option<&BV>) -> R, + M: MapOpsWithEntry, { let MapStoreInner { content, .. } = self; - f(content.get(key).map(Borrow::borrow)) + MapOpsWithEntry::with_item(content, key, f) } } diff --git a/server/swimos_agent/src/stores/map/mod.rs b/server/swimos_agent/src/stores/map/mod.rs index c1cd97b15..0ccc6669b 100644 --- a/server/swimos_agent/src/stores/map/mod.rs +++ b/server/swimos_agent/src/stores/map/mod.rs @@ -29,7 +29,8 @@ use crate::agent_model::{AgentDescription, WriteResult}; use crate::event_handler::{ActionContext, HandlerAction, Modification, StepResult}; use crate::event_queue::EventQueue; use crate::item::{AgentItem, InspectableMapLikeItem, MapItem, MapLikeItem, MutableMapLikeItem}; -use crate::map_storage::{MapStoreInner, TransformEntryResult}; +use crate::lanes::map::MapLaneEvent; +use crate::map_storage::{MapOps, MapOpsWithEntry, MapStoreInner, TransformEntryResult}; use crate::meta::AgentMetadata; use super::StoreItem; @@ -37,24 +38,24 @@ use super::StoreItem; #[cfg(test)] mod tests; -type Inner = MapStoreInner>; +type Inner = MapStoreInner, M>; /// Adding a [`MapStore`] to an agent provides additional state that is not exposed as a lane. /// If persistence is enabled (and the store is not marked as transient) the state of the store /// will be persisted in the same way as the state of a lane. #[derive(Debug)] -pub struct MapStore { +pub struct MapStore> { id: u64, - inner: RefCell>, + inner: RefCell>, } assert_impl_all!(MapStore<(), ()>: Send); -impl MapStore { +impl MapStore { /// # Arguments /// * `id` - The ID of the store. This should be unique within an agent. /// * `init` - The initial contents of the map. - pub fn new(id: u64, init: HashMap) -> Self { + pub fn new(id: u64, init: M) -> Self { MapStore { id, inner: RefCell::new(Inner::new(init)), @@ -62,31 +63,33 @@ impl MapStore { } } -impl AgentItem for MapStore { +impl AgentItem for MapStore { fn id(&self) -> u64 { self.id } } -impl MapItem for MapStore +impl MapItem for MapStore where - K: Eq + Hash + Clone, + K: Hash + Eq + Clone, + M: MapOps, { - fn init(&self, map: HashMap) { + fn init(&self, map: M) { self.inner.borrow_mut().init(map) } fn read_with_prev(&self, f: F) -> R where - F: FnOnce(Option>, &HashMap) -> R, + F: FnOnce(Option>, &M) -> R, { self.inner.borrow_mut().read_with_prev(f) } } -impl MapStore +impl MapStore where K: Clone + Eq + Hash, + M: MapOps, { /// Update the value associated with a key. pub fn update(&self, key: K, value: V) { @@ -111,37 +114,36 @@ where self.inner.borrow_mut().clear() } - /// Read a value from the map, if it exists. - pub fn get(&self, key: &Q, f: F) -> R + /// Read the complete state of the map. + pub fn get_map(&self, f: F) -> R where - K: Borrow + Eq + Hash, - Q: Eq + Hash, - F: FnOnce(Option<&V>) -> R, + F: FnOnce(&M) -> R, { - self.inner.borrow().with_entry(key, f) + self.inner.borrow().get_map(f) } +} - /// Read the complete state of the map. - pub fn get_map(&self, f: F) -> R +impl MapStore { + /// Read a value from the map, if it exists. + pub fn get(&self, key: &Q, f: F) -> R where - F: FnOnce(&HashMap) -> R, + K: Borrow, + F: FnOnce(Option<&V>) -> R, + M: MapOpsWithEntry, { - self.inner.borrow().get_map(f) + self.inner.borrow().with_entry(key, f) } } -impl MapStore -where - K: Eq + Hash, -{ +impl MapStore { pub fn with_entry(&self, key: &B1, f: F) -> U where B1: ?Sized, B2: ?Sized, K: Borrow, - B1: Eq + Hash, V: Borrow, F: FnOnce(Option<&B2>) -> U, + M: MapOpsWithEntry, { self.inner.borrow().with_entry(key, f) } @@ -149,10 +151,11 @@ where const INFALLIBLE_SER: &str = "Serializing store responses to recon should be infallible."; -impl StoreItem for MapStore +impl StoreItem for MapStore where K: Clone + Eq + Hash + StructuralWritable + 'static, V: StructuralWritable + 'static, + M: MapOps, { fn write_to_buffer(&self, buffer: &mut BytesMut) -> WriteResult { let mut encoder = MapStoreResponseEncoder::default(); @@ -171,13 +174,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will update the value of an entry in the map. -pub struct MapStoreUpdate { - projection: for<'a> fn(&'a C) -> &'a MapStore, +pub struct MapStoreUpdate> { + projection: for<'a> fn(&'a C) -> &'a MapStore, key_value: Option<(K, V)>, } -impl MapStoreUpdate { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K, value: V) -> Self { +impl MapStoreUpdate { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K, value: V) -> Self { MapStoreUpdate { projection, key_value: Some((key, value)), @@ -185,10 +188,11 @@ impl MapStoreUpdate { } } -impl HandlerAction for MapStoreUpdate +impl HandlerAction for MapStoreUpdate where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -230,13 +234,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will remove an entry from the map. -pub struct MapStoreRemove { - projection: for<'a> fn(&'a C) -> &'a MapStore, +pub struct MapStoreRemove> { + projection: for<'a> fn(&'a C) -> &'a MapStore, key: Option, } -impl MapStoreRemove { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K) -> Self { +impl MapStoreRemove { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K) -> Self { MapStoreRemove { projection, key: Some(key), @@ -244,10 +248,11 @@ impl MapStoreRemove { } } -impl HandlerAction for MapStoreRemove +impl HandlerAction for MapStoreRemove where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -283,13 +288,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will clear the map. -pub struct MapStoreClear { - projection: for<'a> fn(&'a C) -> &'a MapStore, +pub struct MapStoreClear> { + projection: for<'a> fn(&'a C) -> &'a MapStore, done: bool, } -impl MapStoreClear { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore) -> Self { +impl MapStoreClear { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore) -> Self { MapStoreClear { projection, done: false, @@ -297,10 +302,11 @@ impl MapStoreClear { } } -impl HandlerAction for MapStoreClear +impl HandlerAction for MapStoreClear where C: AgentDescription, K: Clone + Eq + Hash, + M: MapOps, { type Completion = (); @@ -337,14 +343,14 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will get an entry from the map. -pub struct MapStoreGet { - projection: for<'a> fn(&'a C) -> &'a MapStore, +pub struct MapStoreGet> { + projection: for<'a> fn(&'a C) -> &'a MapStore, key: K, done: bool, } -impl MapStoreGet { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K) -> Self { +impl MapStoreGet { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K) -> Self { MapStoreGet { projection, key, @@ -353,11 +359,11 @@ impl MapStoreGet { } } -impl HandlerAction for MapStoreGet +impl HandlerAction for MapStoreGet where C: AgentDescription, - K: Clone + Eq + Hash, V: Clone, + M: MapOpsWithEntry, { type Completion = Option; @@ -375,7 +381,7 @@ where if !*done { *done = true; let store = projection(context); - StepResult::done(store.get(key, |v| v.cloned())) + StepResult::done(MapStore::get(store, key, |v| v.cloned())) } else { StepResult::after_done() } @@ -393,13 +399,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that will read the entire state of a map store. -pub struct MapStoreGetMap { - projection: for<'a> fn(&'a C) -> &'a MapStore, +pub struct MapStoreGetMap> { + projection: for<'a> fn(&'a C) -> &'a MapStore, done: bool, } -impl MapStoreGetMap { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore) -> Self { +impl MapStoreGetMap { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore) -> Self { MapStoreGetMap { projection, done: false, @@ -407,13 +413,14 @@ impl MapStoreGetMap { } } -impl HandlerAction for MapStoreGetMap +impl HandlerAction for MapStoreGetMap where C: AgentDescription, K: Clone + Eq + Hash, V: Clone, + M: MapOps + Clone, { - type Completion = HashMap; + type Completion = M; fn step( &mut self, @@ -443,13 +450,13 @@ where } /// An [event handler](crate::event_handler::EventHandler)`] that may alter an entry in the map. -pub struct MapStoreTransformEntry { - projection: for<'a> fn(&'a C) -> &'a MapStore, +pub struct MapStoreTransformEntry> { + projection: for<'a> fn(&'a C) -> &'a MapStore, key_and_f: Option<(K, F)>, } -impl MapStoreTransformEntry { - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K, f: F) -> Self { +impl MapStoreTransformEntry { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K, f: F) -> Self { MapStoreTransformEntry { projection, key_and_f: Some((key, f)), @@ -457,11 +464,12 @@ impl MapStoreTransformEntry { } } -impl HandlerAction for MapStoreTransformEntry +impl HandlerAction for MapStoreTransformEntry where C: AgentDescription, K: Clone + Eq + Hash, F: FnOnce(Option<&V>) -> Option, + M: MapOps, { type Completion = (); @@ -511,18 +519,18 @@ where /// A [handler action][`HandlerAction`] that will produce a value by applying a closure to a reference to /// and entry in the store. -pub struct MapStoreWithEntry { - projection: for<'a> fn(&'a C) -> &'a MapStore, +pub struct MapStoreWithEntry> { + projection: for<'a> fn(&'a C) -> &'a MapStore, key_and_f: Option<(K, F)>, _type: PhantomData, } -impl MapStoreWithEntry { +impl MapStoreWithEntry { /// #Arguments /// * `projection` - Projection from the agent context to the store. /// * `key` - Key of the entry. /// * `f` - The closure to apply to the entry. - pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K, f: F) -> Self { + pub fn new(projection: for<'a> fn(&'a C) -> &'a MapStore, key: K, f: F) -> Self { MapStoreWithEntry { projection, key_and_f: Some((key, f)), @@ -531,13 +539,13 @@ impl MapStoreWithEntry { } } -impl HandlerAction for MapStoreWithEntry +impl HandlerAction for MapStoreWithEntry where C: AgentDescription, - K: Eq + Hash, B: ?Sized, V: Borrow, F: FnOnce(Option<&B>) -> U, + M: MapOpsWithEntry, { type Completion = U; @@ -571,12 +579,13 @@ where .finish() } } -impl MapLikeItem for MapStore +impl MapLikeItem for MapStore where K: Clone + Eq + Hash + Send + 'static, V: Clone + 'static, + M: MapOpsWithEntry + Clone + 'static, { - type GetHandler = MapStoreGet + type GetHandler = MapStoreGet where C: AgentDescription + 'static; @@ -587,7 +596,7 @@ where MapStoreGet::new(projection, key) } - type GetMapHandler = MapStoreGetMap + type GetMapHandler = MapStoreGetMap where C: AgentDescription + 'static; @@ -598,49 +607,48 @@ where } } -impl InspectableMapLikeItem for MapStore +impl InspectableMapLikeItem for MapStore where - K: Eq + Hash + Send + 'static, - V: 'static, + K: Send + 'static, + V: Borrow + 'static, + B: ?Sized + 'static, + M: MapOpsWithEntry, { - type WithEntryHandler<'a, C, F, B, U> = MapStoreWithEntry + type WithEntryHandler<'a, C, F, U> = MapStoreWithEntry where Self: 'static, C: AgentDescription + 'a, - B: ?Sized +'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a; - fn with_entry_handler<'a, C, F, B, U>( + fn with_entry_handler<'a, C, F, U>( projection: fn(&C) -> &Self, key: K, f: F, - ) -> Self::WithEntryHandler<'a, C, F, B, U> + ) -> Self::WithEntryHandler<'a, C, F, U> where Self: 'static, C: AgentDescription + 'a, - B: ?Sized + 'static, - V: Borrow, F: FnOnce(Option<&B>) -> U + Send + 'a, { MapStoreWithEntry::new(projection, key, f) } } -impl MutableMapLikeItem for MapStore +impl MutableMapLikeItem for MapStore where K: Clone + Eq + Hash + Send + 'static, V: Send + 'static, + M: MapOps + 'static, { - type UpdateHandler = MapStoreUpdate + type UpdateHandler = MapStoreUpdate where C: AgentDescription + 'static; - type RemoveHandler = MapStoreRemove + type RemoveHandler = MapStoreRemove where C: AgentDescription + 'static; - type ClearHandler = MapStoreClear + type ClearHandler = MapStoreClear where C: AgentDescription + 'static; @@ -665,7 +673,7 @@ where MapStoreClear::new(projection) } - type TransformEntryHandler<'a, C, F> = MapStoreTransformEntry + type TransformEntryHandler<'a, C, F> = MapStoreTransformEntry where Self: 'static, C: AgentDescription + 'a, diff --git a/server/swimos_agent/src/tests.rs b/server/swimos_agent/src/tests.rs index 5a4ecef2b..d51016d49 100644 --- a/server/swimos_agent/src/tests.rs +++ b/server/swimos_agent/src/tests.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; use std::hash::Hash; use std::{collections::HashMap, sync::Arc}; @@ -2276,3 +2276,296 @@ fn on_delete_handler() { ow => panic!("Events not as expected: {:?}", ow), } } + +#[derive(AgentLaneModel)] +#[agent(root(crate))] +struct OrderedMapsAgent { + lane: MapLane>, + store: MapStore>, +} + +const OV1: u64 = 8374749; +const OV2: u64 = 128374747; + +impl OrderedMapsAgent { + fn with_data() -> Self { + let content = init_ordered(); + OrderedMapsAgent { + lane: MapLane::new(0, content.clone()), + store: MapStore::new(1, content), + } + } +} + +fn init_ordered() -> BTreeMap { + let mut content = BTreeMap::new(); + content.insert(K1, OV1); + content.insert(K2, OV2); + content +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum OrderedMapEvent { + Clear(BTreeMap), + Remove(BTreeMap, i32, u64), + Update(BTreeMap, i32, Option), +} + +#[derive(Default, Debug, Clone)] +struct OrderedMapLifecycle { + events: Arc>>, +} + +impl OrderedMapLifecycle { + fn push(&self, event: OrderedMapEvent) { + self.events.lock().push(event) + } + + fn take(&self) -> Vec { + let mut guard = self.events.lock(); + std::mem::take(&mut *guard) + } +} + +#[test] +fn on_update_handler_lane_ordered() { + #[derive(Default, Clone)] + struct TestLifecycle(OrderedMapLifecycle); + + #[lifecycle(OrderedMapsAgent, agent_root(crate))] + impl TestLifecycle { + #[on_update(lane)] + fn my_on_update( + &self, + context: HandlerContext, + map: &BTreeMap, + key: i32, + prev: Option, + _new_value: &u64, + ) -> impl EventHandler + '_ { + let map_state = map.clone(); + context.effect(move || { + self.0.push(OrderedMapEvent::Update(map_state, key, prev)); + }) + } + } + + let agent = OrderedMapsAgent::default(); + let template = TestLifecycle::default(); + + let lifecycle = template.clone().into_lifecycle(); + + let v = 657383; + agent.lane.update(K2, v); + let handler = lifecycle + .item_event(&agent, "lane") + .expect("Expected handler for lane."); + run_handler(&agent, handler); + + let events = template.0.take(); + + let mut expected_map = BTreeMap::new(); + expected_map.insert(K2, v); + assert_eq!( + events, + vec![OrderedMapEvent::Update(expected_map, K2, None)] + ); +} + +#[test] +fn on_update_handler_store_ordered() { + #[derive(Default, Clone)] + struct TestLifecycle(OrderedMapLifecycle); + + #[lifecycle(OrderedMapsAgent, agent_root(crate))] + impl TestLifecycle { + #[on_update(store)] + fn my_on_update( + &self, + context: HandlerContext, + map: &BTreeMap, + key: i32, + prev: Option, + _new_value: &u64, + ) -> impl EventHandler + '_ { + let map_state = map.clone(); + context.effect(move || { + self.0.push(OrderedMapEvent::Update(map_state, key, prev)); + }) + } + } + + let agent = OrderedMapsAgent::default(); + let template = TestLifecycle::default(); + + let lifecycle = template.clone().into_lifecycle(); + + let v = 93747; + agent.store.update(K2, v); + let handler = lifecycle + .item_event(&agent, "store") + .expect("Expected handler for lane."); + run_handler(&agent, handler); + + let events = template.0.take(); + + let mut expected_map = BTreeMap::new(); + expected_map.insert(K2, v); + assert_eq!( + events, + vec![OrderedMapEvent::Update(expected_map, K2, None)] + ); +} + +#[test] +fn on_remove_handler_lane_ordered() { + #[derive(Default, Clone)] + struct TestLifecycle(OrderedMapLifecycle); + + #[lifecycle(OrderedMapsAgent, agent_root(crate))] + impl TestLifecycle { + #[on_remove(lane)] + fn my_on_remove( + &self, + context: HandlerContext, + map: &BTreeMap, + key: i32, + prev: u64, + ) -> impl EventHandler + '_ { + let map_state = map.clone(); + context.effect(move || { + self.0.push(OrderedMapEvent::Remove(map_state, key, prev)); + }) + } + } + + let agent = OrderedMapsAgent::with_data(); + let template = TestLifecycle::default(); + + let lifecycle = template.clone().into_lifecycle(); + + agent.lane.remove(&K2); + let handler = lifecycle + .item_event(&agent, "lane") + .expect("Expected handler for lane."); + run_handler(&agent, handler); + + let events = template.0.take(); + + let mut expected_map = BTreeMap::new(); + expected_map.insert(K1, OV1); + assert_eq!(events, vec![OrderedMapEvent::Remove(expected_map, K2, OV2)]); +} + +#[test] +fn on_remove_handler_store_ordered() { + #[derive(Default, Clone)] + struct TestLifecycle(OrderedMapLifecycle); + + #[lifecycle(OrderedMapsAgent, agent_root(crate))] + impl TestLifecycle { + #[on_remove(store)] + fn my_on_remove( + &self, + context: HandlerContext, + map: &BTreeMap, + key: i32, + prev: u64, + ) -> impl EventHandler + '_ { + let map_state = map.clone(); + context.effect(move || { + self.0.push(OrderedMapEvent::Remove(map_state, key, prev)); + }) + } + } + + let agent = OrderedMapsAgent::with_data(); + let template = TestLifecycle::default(); + + let lifecycle = template.clone().into_lifecycle(); + + agent.store.remove(&K2); + let handler = lifecycle + .item_event(&agent, "store") + .expect("Expected handler for lane."); + run_handler(&agent, handler); + + let events = template.0.take(); + + let mut expected_map = BTreeMap::new(); + expected_map.insert(K1, OV1); + expected_map.insert(K1, OV1); + assert_eq!(events, vec![OrderedMapEvent::Remove(expected_map, K2, OV2)]); +} + +#[test] +fn on_clear_handler_lane_ordered() { + #[derive(Default, Clone)] + struct TestLifecycle(OrderedMapLifecycle); + + #[lifecycle(OrderedMapsAgent, agent_root(crate))] + impl TestLifecycle { + #[on_clear(lane)] + fn my_on_clear( + &self, + context: HandlerContext, + map: BTreeMap, + ) -> impl EventHandler + '_ { + context.effect(move || { + self.0.push(OrderedMapEvent::Clear(map)); + }) + } + } + + let agent = OrderedMapsAgent::with_data(); + let template = TestLifecycle::default(); + + let lifecycle = template.clone().into_lifecycle(); + + agent.lane.clear(); + let handler = lifecycle + .item_event(&agent, "lane") + .expect("Expected handler for lane."); + run_handler(&agent, handler); + + let events = template.0.take(); + + let expected_map = init_ordered(); + assert_eq!(events, vec![OrderedMapEvent::Clear(expected_map)]); +} + +#[test] +fn on_clear_handler_store_ordered() { + #[derive(Default, Clone)] + struct TestLifecycle(OrderedMapLifecycle); + + #[lifecycle(OrderedMapsAgent, agent_root(crate))] + impl TestLifecycle { + #[on_clear(store)] + fn my_on_clear( + &self, + context: HandlerContext, + map: BTreeMap, + ) -> impl EventHandler + '_ { + context.effect(move || { + self.0.push(OrderedMapEvent::Clear(map)); + }) + } + } + + let agent = OrderedMapsAgent::with_data(); + let template = TestLifecycle::default(); + + let lifecycle = template.clone().into_lifecycle(); + + agent.store.clear(); + let handler = lifecycle + .item_event(&agent, "store") + .expect("Expected handler for lane."); + run_handler(&agent, handler); + + let events = template.0.take(); + + let expected_map = init_ordered(); + assert_eq!(events, vec![OrderedMapEvent::Clear(expected_map)]); +} diff --git a/server/swimos_agent_derive/src/agent_lifecycle/mod.rs b/server/swimos_agent_derive/src/agent_lifecycle/mod.rs index 0ed5ff38b..2cad981af 100644 --- a/server/swimos_agent_derive/src/agent_lifecycle/mod.rs +++ b/server/swimos_agent_derive/src/agent_lifecycle/mod.rs @@ -170,7 +170,7 @@ impl<'a> LaneLifecycleBuilder<'a> { .. }) => { let mut builder: syn::Expr = parse_quote! { - <#root::lanes::map::lifecycle::StatefulMapLaneLifecycle::<#agent_type, #lifecycle_type, _, _> as ::core::default::Default>::default() + <#root::lanes::map::lifecycle::StatefulMapLaneLifecycle::<#agent_type, #lifecycle_type, _, _, _> as ::core::default::Default>::default() }; if let Some(handler) = on_update { builder = parse_quote! { diff --git a/server/swimos_agent_derive/src/agent_lifecycle/model.rs b/server/swimos_agent_derive/src/agent_lifecycle/model.rs index 0f02c3322..103a0f0eb 100644 --- a/server/swimos_agent_derive/src/agent_lifecycle/model.rs +++ b/server/swimos_agent_derive/src/agent_lifecycle/model.rs @@ -347,42 +347,33 @@ fn validate_method_as<'a>( Validation::valid(acc) }) } - HandlerKind::Update => Validation::join( - acc, - validate_typed_sig(sig, 4, true).and_then(|t| hash_map_type_params(sig, t)), - ) - .and_then(|(mut acc, (k, v))| { - for target in targets { - if let Err(e) = acc.add_on_update(target, k, v, &sig.ident) { - return Validation::Validated(acc, Errors::of(e)); + HandlerKind::Update => Validation::join(acc, validate_typed_sig(sig, 4, true)) + .and_then(|(mut acc, map_t)| { + for target in targets { + if let Err(e) = acc.add_on_update(target, map_t, &sig.ident) { + return Validation::Validated(acc, Errors::of(e)); + } } - } - Validation::valid(acc) - }), - HandlerKind::Remove => Validation::join( - acc, - validate_typed_sig(sig, 3, true).and_then(|t| hash_map_type_params(sig, t)), - ) - .and_then(|(mut acc, (k, v))| { - for target in targets { - if let Err(e) = acc.add_on_remove(target, k, v, &sig.ident) { - return Validation::Validated(acc, Errors::of(e)); + Validation::valid(acc) + }), + HandlerKind::Remove => Validation::join(acc, validate_typed_sig(sig, 3, true)) + .and_then(|(mut acc, map_t)| { + for target in targets { + if let Err(e) = acc.add_on_remove(target, map_t, &sig.ident) { + return Validation::Validated(acc, Errors::of(e)); + } } - } - Validation::valid(acc) - }), - HandlerKind::Clear => Validation::join( - acc, - validate_typed_sig(sig, 1, false).and_then(|t| hash_map_type_params(sig, t)), - ) - .and_then(|(mut acc, (k, v))| { - for target in targets { - if let Err(e) = acc.add_on_clear(target, k, v, &sig.ident) { - return Validation::Validated(acc, Errors::of(e)); + Validation::valid(acc) + }), + HandlerKind::Clear => Validation::join(acc, validate_typed_sig(sig, 1, false)) + .and_then(|(mut acc, map_t)| { + for target in targets { + if let Err(e) = acc.add_on_clear(target, map_t, &sig.ident) { + return Validation::Validated(acc, Errors::of(e)); + } } - } - Validation::valid(acc) - }), + Validation::valid(acc) + }), HandlerKind::JoinMap => Validation::join(acc, validate_join_map_lifecycle_sig(sig)) .and_then(|(mut acc, (_l, k, v))| { for target in targets { @@ -847,36 +838,11 @@ fn peel_ref_type<'a>( } } -const HASH_MAP: &str = "HashMap"; const OPTION: &str = "Option"; const HASH_SET: &str = "HashSet"; const RESPONSE: &str = "Response"; const UNIT_RESPONSE: &str = "UnitResponse"; -fn hash_map_type_params<'a>( - sig: &Signature, - map_type: &'a Type, -) -> Validation<(&'a Type, &'a Type), Errors> { - match map_type { - Type::Path(TypePath { qself: None, path }) => match path.segments.last() { - Some(PathSegment { - ident, - arguments: - PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }), - }) if ident == HASH_MAP && (args.len() == 2 || args.len() == 3) => { - match (&args[0], &args[1]) { - (GenericArgument::Type(key_type), GenericArgument::Type(value_type)) => { - Validation::valid((key_type, value_type)) - } - _ => Validation::fail(syn::Error::new_spanned(sig, BAD_SIGNATURE)), - } - } - _ => Validation::fail(syn::Error::new_spanned(sig, BAD_SIGNATURE)), - }, - _ => Validation::fail(syn::Error::new_spanned(sig, BAD_SIGNATURE)), - } -} - fn extract_option_type_param<'a>( sig: &Signature, parameterized: &'a Type, @@ -1545,8 +1511,7 @@ impl<'a> AgentLifecycleDescriptorBuilder<'a> { pub fn add_on_update( &mut self, name: String, - key_type: &'a Type, - value_type: &'a Type, + map_type: &'a Type, method: &'a Ident, ) -> Result<(), syn::Error> { let AgentLifecycleDescriptorBuilder { @@ -1585,9 +1550,8 @@ impl<'a> AgentLifecycleDescriptorBuilder<'a> { method, format!("Lane '{}' has both map and HTTP lane event handlers.", name), )), - Some(ItemLifecycle::Map(desc)) => desc.add_on_update(key_type, value_type, method), + Some(ItemLifecycle::Map(desc)) => desc.add_on_update(map_type, method), _ => { - let map_type = (key_type, value_type); lane_lifecycles.insert( name.clone(), ItemLifecycle::Map(MapLifecycleDescriptor::new_on_update( @@ -1602,8 +1566,7 @@ impl<'a> AgentLifecycleDescriptorBuilder<'a> { pub fn add_on_remove( &mut self, name: String, - key_type: &'a Type, - value_type: &'a Type, + map_type: &'a Type, method: &'a Ident, ) -> Result<(), syn::Error> { let AgentLifecycleDescriptorBuilder { @@ -1642,9 +1605,8 @@ impl<'a> AgentLifecycleDescriptorBuilder<'a> { method, format!("Lane '{}' has both map and HTTP lane event handlers.", name), )), - Some(ItemLifecycle::Map(desc)) => desc.add_on_remove(key_type, value_type, method), + Some(ItemLifecycle::Map(desc)) => desc.add_on_remove(map_type, method), _ => { - let map_type = (key_type, value_type); lane_lifecycles.insert( name.clone(), ItemLifecycle::Map(MapLifecycleDescriptor::new_on_remove( @@ -1659,8 +1621,7 @@ impl<'a> AgentLifecycleDescriptorBuilder<'a> { pub fn add_on_clear( &mut self, name: String, - key_type: &'a Type, - value_type: &'a Type, + map_type: &'a Type, method: &'a Ident, ) -> Result<(), syn::Error> { let AgentLifecycleDescriptorBuilder { @@ -1699,9 +1660,8 @@ impl<'a> AgentLifecycleDescriptorBuilder<'a> { method, format!("Lane '{}' has both map and HTTP lane event handlers.", name), )), - Some(ItemLifecycle::Map(desc)) => desc.add_on_clear(key_type, value_type, method), + Some(ItemLifecycle::Map(desc)) => desc.add_on_clear(map_type, method), _ => { - let map_type = (key_type, value_type); lane_lifecycles.insert( name.clone(), ItemLifecycle::Map(MapLifecycleDescriptor::new_on_clear( @@ -2214,10 +2174,29 @@ pub enum JoinLifecycle<'a> { JoinValue(&'a Ident), } +#[derive(Hash, PartialEq, Eq)] +pub enum MapType<'a> { + Combined(&'a Type), + Extracted(&'a Type, &'a Type), +} + +impl<'a> From<&'a Type> for MapType<'a> { + fn from(t: &'a Type) -> Self { + MapType::Combined(t) + } +} + +impl<'a> From<(&'a Type, &'a Type)> for MapType<'a> { + fn from(kv: (&'a Type, &'a Type)) -> Self { + let (k, v) = kv; + MapType::Extracted(k, v) + } +} + pub struct MapLifecycleDescriptor<'a> { - name: String, //The name of the lane. - primary_lane_type: (&'a Type, &'a Type), //First observed type of the lane. - alternative_lane_types: HashSet<(&'a Type, &'a Type)>, //Further types observed for the lane. + name: String, //The name of the lane. + primary_lane_type: MapType<'a>, //First observed type of the lane. + alternative_lane_types: HashSet>, //Further types observed for the lane. pub on_update: Option<&'a Ident>, pub on_remove: Option<&'a Ident>, pub on_clear: Option<&'a Ident>, @@ -2335,14 +2314,10 @@ impl<'a> HttpLifecycleDescriptor<'a> { } impl<'a> MapLifecycleDescriptor<'a> { - pub fn new_on_update( - name: String, - map_type: (&'a Type, &'a Type), - on_update: &'a Ident, - ) -> Self { + pub fn new_on_update(name: String, map_type: &'a Type, on_update: &'a Ident) -> Self { MapLifecycleDescriptor { name, - primary_lane_type: map_type, + primary_lane_type: MapType::from(map_type), alternative_lane_types: Default::default(), on_update: Some(on_update), on_remove: None, @@ -2351,14 +2326,10 @@ impl<'a> MapLifecycleDescriptor<'a> { } } - pub fn new_on_remove( - name: String, - map_type: (&'a Type, &'a Type), - on_remove: &'a Ident, - ) -> Self { + pub fn new_on_remove(name: String, map_type: &'a Type, on_remove: &'a Ident) -> Self { MapLifecycleDescriptor { name, - primary_lane_type: map_type, + primary_lane_type: MapType::from(map_type), alternative_lane_types: Default::default(), on_update: None, on_remove: Some(on_remove), @@ -2367,10 +2338,10 @@ impl<'a> MapLifecycleDescriptor<'a> { } } - pub fn new_on_clear(name: String, map_type: (&'a Type, &'a Type), on_clear: &'a Ident) -> Self { + pub fn new_on_clear(name: String, map_type: &'a Type, on_clear: &'a Ident) -> Self { MapLifecycleDescriptor { name, - primary_lane_type: map_type, + primary_lane_type: MapType::from(map_type), alternative_lane_types: Default::default(), on_update: None, on_remove: None, @@ -2386,7 +2357,7 @@ impl<'a> MapLifecycleDescriptor<'a> { ) -> Self { MapLifecycleDescriptor { name, - primary_lane_type: map_type, + primary_lane_type: MapType::from(map_type), alternative_lane_types: Default::default(), on_update: None, on_remove: None, @@ -2402,7 +2373,7 @@ impl<'a> MapLifecycleDescriptor<'a> { ) -> Self { MapLifecycleDescriptor { name, - primary_lane_type: map_type, + primary_lane_type: MapType::from(map_type), alternative_lane_types: Default::default(), on_update: None, on_remove: None, @@ -2413,8 +2384,7 @@ impl<'a> MapLifecycleDescriptor<'a> { pub fn add_on_update( &mut self, - key_type: &'a Type, - value_type: &'a Type, + map_type: &'a Type, method: &'a Ident, ) -> Result<(), syn::Error> { let MapLifecycleDescriptor { @@ -2424,15 +2394,15 @@ impl<'a> MapLifecycleDescriptor<'a> { on_update, .. } = self; - let map_type = (key_type, value_type); if on_update.is_some() { Err(syn::Error::new_spanned( method, format!("Duplicate on_update handler for '{}'.", name), )) } else { - if map_type != *primary_lane_type { - alternative_lane_types.insert(map_type); + let map_t = MapType::from(map_type); + if map_t != *primary_lane_type { + alternative_lane_types.insert(map_t); } *on_update = Some(method); Ok(()) @@ -2441,8 +2411,7 @@ impl<'a> MapLifecycleDescriptor<'a> { pub fn add_on_remove( &mut self, - key_type: &'a Type, - value_type: &'a Type, + map_type: &'a Type, method: &'a Ident, ) -> Result<(), syn::Error> { let MapLifecycleDescriptor { @@ -2452,15 +2421,15 @@ impl<'a> MapLifecycleDescriptor<'a> { on_remove, .. } = self; - let map_type = (key_type, value_type); if on_remove.is_some() { Err(syn::Error::new_spanned( method, format!("Duplicate on_remove handler for '{}'.", name), )) } else { - if map_type != *primary_lane_type { - alternative_lane_types.insert(map_type); + let map_t = MapType::from(map_type); + if map_t != *primary_lane_type { + alternative_lane_types.insert(map_t); } *on_remove = Some(method); Ok(()) @@ -2469,8 +2438,7 @@ impl<'a> MapLifecycleDescriptor<'a> { pub fn add_on_clear( &mut self, - key_type: &'a Type, - value_type: &'a Type, + map_type: &'a Type, method: &'a Ident, ) -> Result<(), syn::Error> { let MapLifecycleDescriptor { @@ -2480,15 +2448,15 @@ impl<'a> MapLifecycleDescriptor<'a> { on_clear, .. } = self; - let map_type = (key_type, value_type); if on_clear.is_some() { Err(syn::Error::new_spanned( method, format!("Duplicate on_clear handler for '{}'.", name), )) } else { - if map_type != *primary_lane_type { - alternative_lane_types.insert(map_type); + let map_t = MapType::from(map_type); + if map_t != *primary_lane_type { + alternative_lane_types.insert(map_t); } *on_clear = Some(method); Ok(()) @@ -2512,8 +2480,9 @@ impl<'a> MapLifecycleDescriptor<'a> { match join_lifecycle { JoinLifecycle::None => { *join_lifecycle = JoinLifecycle::JoinValue(method); - if map_type != *primary_lane_type { - alternative_lane_types.insert(map_type); + let map_t = MapType::from(map_type); + if map_t != *primary_lane_type { + alternative_lane_types.insert(map_t); } Ok(()) } @@ -2541,8 +2510,9 @@ impl<'a> MapLifecycleDescriptor<'a> { match join_lifecycle { JoinLifecycle::None => { *join_lifecycle = JoinLifecycle::JoinMap(method); - if map_type != *primary_lane_type { - alternative_lane_types.insert(map_type); + let map_t = MapType::from(map_type); + if map_t != *primary_lane_type { + alternative_lane_types.insert(map_t); } Ok(()) } diff --git a/server/swimos_agent_derive/src/lane_model_derive/mod.rs b/server/swimos_agent_derive/src/lane_model_derive/mod.rs index b116dca0f..d29ef8412 100644 --- a/server/swimos_agent_derive/src/lane_model_derive/mod.rs +++ b/server/swimos_agent_derive/src/lane_model_derive/mod.rs @@ -345,7 +345,7 @@ impl<'a> OrdinalWarpLaneModel<'a> { pub fn map_like(&self) -> bool { matches!( &self.model.kind, - WarpLaneSpec::Map(_, _) + WarpLaneSpec::Map(_, _, _) | WarpLaneSpec::DemandMap(_, _) | WarpLaneSpec::JoinValue(_, _) | WarpLaneSpec::JoinMap(_, _, _) @@ -391,7 +391,7 @@ impl<'a> OrdinalItemModel<'a> { fn category(&self) -> ItemCategory { match &self.model.kind { - ItemSpec::Map(_, _, _) + ItemSpec::Map(_, _, _, _) | ItemSpec::JoinValue(_, _) | ItemSpec::JoinMap(_, _, _) | ItemSpec::DemandMap(_, _) => ItemCategory::MapLike, @@ -430,10 +430,10 @@ impl<'a> FieldInitializer<'a> { ItemSpec::Value(ItemKind::Store, _) => { quote!(#name: #root::stores::ValueStore::new(#ordinal, ::core::default::Default::default())) } - ItemSpec::Map(ItemKind::Lane, _, _) => { + ItemSpec::Map(ItemKind::Lane, _, _, _) => { quote!(#name: #root::lanes::MapLane::new(#ordinal, ::core::default::Default::default())) } - ItemSpec::Map(ItemKind::Store, _, _) => { + ItemSpec::Map(ItemKind::Store, _, _, _) => { quote!(#name: #root::stores::MapStore::new(#ordinal, ::core::default::Default::default())) } ItemSpec::JoinValue(_, _) => { @@ -470,8 +470,12 @@ impl<'a> HandlerType<'a> { WarpLaneSpec::Value(t) => { quote!(#root::lanes::value::DecodeAndSet<#agent_name, #t>) } - WarpLaneSpec::Map(k, v) => { - quote!(#root::lanes::map::DecodeAndApply<#agent_name, #k, #v>) + WarpLaneSpec::Map(k, v, m) => { + if let Some(map_t) = m { + quote!(#root::lanes::map::DecodeAndApply<#agent_name, #k, #v, #map_t>) + } else { + quote!(#root::lanes::map::DecodeAndApply<#agent_name, #k, #v>) + } } WarpLaneSpec::Demand(_) | WarpLaneSpec::DemandMap(_, _) @@ -503,8 +507,12 @@ impl<'a> SyncHandlerType<'a> { WarpLaneSpec::Value(t) => { quote!(#root::lanes::value::ValueLaneSync<#agent_name, #t>) } - WarpLaneSpec::Map(k, v) => { - quote!(#root::lanes::map::MapLaneSync<#agent_name, #k, #v>) + WarpLaneSpec::Map(k, v, m) => { + if let Some(map_t) = m { + quote!(#root::lanes::map::MapLaneSync<#agent_name, #k, #v, #map_t>) + } else { + quote!(#root::lanes::map::MapLaneSync<#agent_name, #k, #v>) + } } WarpLaneSpec::JoinValue(k, v) => { quote!(#root::lanes::join_value::JoinValueLaneSync<#agent_name, #k, #v>) @@ -568,8 +576,12 @@ impl<'a> WarpLaneHandlerMatch<'a> { WarpLaneSpec::Value(ty) => { quote!(#root::lanes::value::decode_and_set::<#agent_name, #ty>(body, |agent: &#agent_name| &agent.#name)) } - WarpLaneSpec::Map(k, v) => { - quote!(#root::lanes::map::decode_and_apply::<#agent_name, #k, #v>(body, |agent: &#agent_name| &agent.#name)) + WarpLaneSpec::Map(k, v, m) => { + if let Some(map_t) = m { + quote!(#root::lanes::map::decode_and_apply::<#agent_name, #k, #v, #map_t>(body, |agent: &#agent_name| &agent.#name)) + } else { + quote!(#root::lanes::map::decode_and_apply::<#agent_name, #k, #v, _>(body, |agent: &#agent_name| &agent.#name)) + } } WarpLaneSpec::Demand(_) | WarpLaneSpec::DemandMap(_, _) @@ -672,8 +684,12 @@ impl<'a> SyncHandlerMatch<'a> { WarpLaneSpec::Value(ty) => { quote!(#root::lanes::value::ValueLaneSync::<#agent_name, #ty>::new(|agent: &#agent_name| &agent.#name, id)) } - WarpLaneSpec::Map(k, v) => { - quote!(#root::lanes::map::MapLaneSync::<#agent_name, #k, #v>::new(|agent: &#agent_name| &agent.#name, id)) + WarpLaneSpec::Map(k, v, m) => { + if let Some(map_t) = m { + quote!(#root::lanes::map::MapLaneSync::<#agent_name, #k, #v, #map_t>::new(|agent: &#agent_name| &agent.#name, id)) + } else { + quote!(#root::lanes::map::MapLaneSync::<#agent_name, #k, #v>::new(|agent: &#agent_name| &agent.#name, id)) + } } WarpLaneSpec::JoinValue(k, v) => { quote!(#root::lanes::join_value::JoinValueLaneSync::<#agent_name, #k, #v>::new(|agent: &#agent_name| &agent.#name, id)) @@ -764,7 +780,7 @@ struct MapItemInitMatch<'a> { impl<'a> MapItemInitMatch<'a> { pub fn new(item: &OrdinalItemModel<'a>) -> Self { let init_kind = match &item.model.kind { - ItemSpec::Map(ItemKind::Lane, _, _) => InitKind::MapLane, + ItemSpec::Map(ItemKind::Lane, _, _, _) => InitKind::MapLane, _ => InitKind::MapStore, }; MapItemInitMatch { @@ -822,10 +838,10 @@ impl<'a> LaneSpecInsert<'a> { ItemSpec::Value(ItemKind::Store, _) => { quote!(#root::agent_model::ItemDescriptor::Store { kind: #root::agent_model::StoreKind::Value, flags: #flags }) } - ItemSpec::Map(ItemKind::Lane, _, _) => { + ItemSpec::Map(ItemKind::Lane, _, _, _) => { quote!(#root::agent_model::ItemDescriptor::WarpLane { kind: #root::agent_model::WarpLaneKind::Map, flags: #flags }) } - ItemSpec::Map(ItemKind::Store, _, _) => { + ItemSpec::Map(ItemKind::Store, _, _, _) => { quote!(#root::agent_model::ItemDescriptor::Store { kind: #root::agent_model::StoreKind::Map, flags: #flags }) } ItemSpec::JoinValue(_, _) => { diff --git a/server/swimos_agent_derive/src/lane_model_derive/model.rs b/server/swimos_agent_derive/src/lane_model_derive/model.rs index b6e5e1976..53b82f185 100644 --- a/server/swimos_agent_derive/src/lane_model_derive/model.rs +++ b/server/swimos_agent_derive/src/lane_model_derive/model.rs @@ -101,7 +101,7 @@ pub enum ItemSpec<'a> { Demand(&'a Type), DemandMap(&'a Type, &'a Type), Value(ItemKind, &'a Type), - Map(ItemKind, &'a Type, &'a Type), + Map(ItemKind, &'a Type, &'a Type, Option<&'a Type>), Supply(&'a Type), JoinValue(&'a Type, &'a Type), JoinMap(&'a Type, &'a Type, &'a Type), @@ -115,7 +115,7 @@ impl<'a> ItemSpec<'a> { ItemSpec::Demand(t) => Some(WarpLaneSpec::Demand(t)), ItemSpec::DemandMap(k, v) => Some(WarpLaneSpec::DemandMap(k, v)), ItemSpec::Value(ItemKind::Lane, t) => Some(WarpLaneSpec::Value(t)), - ItemSpec::Map(ItemKind::Lane, k, v) => Some(WarpLaneSpec::Map(k, v)), + ItemSpec::Map(ItemKind::Lane, k, v, m) => Some(WarpLaneSpec::Map(k, v, *m)), ItemSpec::JoinValue(k, v) => Some(WarpLaneSpec::JoinValue(k, v)), ItemSpec::JoinMap(l, k, v) => Some(WarpLaneSpec::JoinMap(l, k, v)), ItemSpec::Supply(t) => Some(WarpLaneSpec::Supply(t)), @@ -134,7 +134,7 @@ impl<'a> ItemSpec<'a> { pub fn item_kind(&self) -> ItemKind { match self { ItemSpec::Value(k, _) => *k, - ItemSpec::Map(k, _, _) => *k, + ItemSpec::Map(k, _, _, _) => *k, ItemSpec::Command(_) => ItemKind::Lane, ItemSpec::JoinValue(_, _) => ItemKind::Lane, ItemSpec::JoinMap(_, _, _) => ItemKind::Lane, @@ -154,7 +154,7 @@ pub enum WarpLaneSpec<'a> { DemandMap(&'a Type, &'a Type), Value(&'a Type), Supply(&'a Type), - Map(&'a Type, &'a Type), + Map(&'a Type, &'a Type, Option<&'a Type>), JoinValue(&'a Type, &'a Type), JoinMap(&'a Type, &'a Type, &'a Type), } @@ -423,19 +423,19 @@ fn extract_lane_model(field: &Field) -> Validation, Errors Validation::fail(Errors::of(e)), }, - MAP_LANE_NAME => match two_params(arguments) { - Ok((param1, param2)) => Validation::valid(ItemModel::new( + MAP_LANE_NAME => match two_plus_one_params(arguments) { + Ok((param1, param2, param3)) => Validation::valid(ItemModel::new( fld_name, - ItemSpec::Map(ItemKind::Lane, param1, param2), + ItemSpec::Map(ItemKind::Lane, param1, param2, param3), lane_flags, transform, )), Err(e) => Validation::fail(Errors::of(e)), }, - MAP_STORE_NAME => match two_params(arguments) { - Ok((param1, param2)) => Validation::valid(ItemModel::new( + MAP_STORE_NAME => match two_plus_one_params(arguments) { + Ok((param1, param2, param3)) => Validation::valid(ItemModel::new( fld_name, - ItemSpec::Map(ItemKind::Store, param1, param2), + ItemSpec::Map(ItemKind::Store, param1, param2, param3), lane_flags, transform, )), @@ -571,14 +571,34 @@ fn http_params(args: &PathArguments, simple: bool) -> Result, s } fn two_params(args: &PathArguments) -> Result<(&Type, &Type), syn::Error> { - extract_params::<2>(args).map(|[p1, p2]| (p1, p2)) + let maybe_params = extract_params::<2>(args)?; + maybe_params + .ok_or_else(|| syn::Error::new_spanned(args, BAD_PARAMS)) + .map(|[p1, p2]| (p1, p2)) } fn three_params(args: &PathArguments) -> Result<(&Type, &Type, &Type), syn::Error> { - extract_params::<3>(args).map(|[p1, p2, p3]| (p1, p2, p3)) + let maybe_params = extract_params::<3>(args)?; + maybe_params + .ok_or_else(|| syn::Error::new_spanned(args, BAD_PARAMS)) + .map(|[p1, p2, p3]| (p1, p2, p3)) } -fn extract_params(args: &PathArguments) -> Result<[&Type; N], syn::Error> { +fn two_plus_one_params(args: &PathArguments) -> Result<(&Type, &Type, Option<&Type>), syn::Error> { + let params = extract_params_vec(args)?; + match <[&Type; 2]>::try_from(params) { + Ok([p1, p2]) => Ok((p1, p2, None)), + Err(params) => { + if let Ok([p1, p2, p3]) = <[&Type; 3]>::try_from(params) { + Ok((p1, p2, Some(p3))) + } else { + Err(syn::Error::new_spanned(args, BAD_PARAMS)) + } + } + } +} + +fn extract_params_vec(args: &PathArguments) -> Result, syn::Error> { if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) = args { let mut good = vec![]; let it = args.iter(); @@ -589,10 +609,13 @@ fn extract_params(args: &PathArguments) -> Result<[&Type; N], sy return Err(syn::Error::new_spanned(args, BAD_PARAMS)); } } - Ok(good - .try_into() - .map_err(|_| syn::Error::new_spanned(args, BAD_PARAMS))?) + Ok(good) } else { Err(syn::Error::new_spanned(args, BAD_PARAMS)) } } + +fn extract_params(args: &PathArguments) -> Result, syn::Error> { + let params = extract_params_vec(args)?; + Ok(<[&Type; N]>::try_from(params).ok()) +} diff --git a/swimos/tests/deriveagentlanemodel.rs b/swimos/tests/deriveagentlanemodel.rs index fac3a1355..ce72d55a4 100644 --- a/swimos/tests/deriveagentlanemodel.rs +++ b/swimos/tests/deriveagentlanemodel.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::fmt::Write; use swimos::agent::agent_model::ItemFlags; @@ -844,3 +844,43 @@ fn agent_level_transient_flag() { transient_store(3, "fourth", StoreKind::Value), ]); } + +#[test] +fn map_lane_explicit_backing() { + #[derive(AgentLaneModel)] + struct MapLaneExplicit { + lane: MapLane>, + } + + check_agent::(vec![persistent_lane(0, "lane", WarpLaneKind::Map)]); +} + +#[test] +fn map_store_explicit_backing() { + #[derive(AgentLaneModel)] + struct MapStoreExplicit { + store: MapStore>, + } + + check_agent::(vec![persistent_store(0, "store", StoreKind::Map)]); +} + +#[test] +fn map_lane_ordered_map() { + #[derive(AgentLaneModel)] + struct MapLaneOrdered { + lane: MapLane>, + } + + check_agent::(vec![persistent_lane(0, "lane", WarpLaneKind::Map)]); +} + +#[test] +fn map_store_ordered_map() { + #[derive(AgentLaneModel)] + struct MapStoreOrdered { + store: MapStore>, + } + + check_agent::(vec![persistent_store(0, "store", StoreKind::Map)]); +}