1use crate::types::args::EzArgs;
2use crate::types::event::EzMsg;
3use crate::types::ship_ids::{EzShipId, EzShipIds};
4use crate::types::state::EzState;
5use crate::{AttrValue, Attribute, BoxedShip, Error, EzCptIds, EzEvent, Result, Sender, State};
6use displaydoc::Display;
7use hashlink::LinkedHashMap;
8use std::fmt::Debug;
9use tracing::warn;
10
11pub trait Ship<CID, CA, CS, CM>: Debug
20where
21 CID: EzCptIds,
22 CA: EzArgs,
23 CS: EzState,
24 CM: EzMsg,
25{
26 #[allow(unused_variables)]
29 fn on_event(
30 &mut self,
31 event: EzEvent<CID, CM>,
32 state: &mut State<CS>,
33 ) -> Vec<EzEvent<CID, CM>> {
34 vec![]
35 }
36
37 #[allow(unused_variables)]
39 fn init(&mut self, state: &mut State<CS>) -> Vec<EzEvent<CID, CM>> {
40 vec![]
41 }
42
43 #[allow(unused_variables)]
45 fn tick(&mut self, state: &mut State<CS>) -> Vec<EzEvent<CID, CM>> {
46 vec![]
47 }
48
49 fn set_tx(&mut self, msg_tx: Sender<EzEvent<CID, CM>>);
51}
52
53#[derive(Debug, Display, Eq, PartialEq, Clone, PartialOrd)]
55pub enum PortError {
56 ShipAlreadyRegistered,
58 ShipNotFound,
60}
61
62#[derive(Debug)]
65pub struct Port<CID, SID, CA, CS, CM>
66where
67 SID: EzShipIds,
68 CA: EzArgs,
69 CS: EzState,
70 CM: EzMsg,
71{
72 ships: LinkedHashMap<EzShipId<SID>, BoxedShip<CID, CA, CS, CM>>,
73}
74
75impl<CID, SID, CA, CS, CM> Default for Port<CID, SID, CA, CS, CM>
76where
77 CID: EzCptIds,
78 SID: EzShipIds,
79 CA: EzArgs,
80 CS: EzState,
81 CM: EzMsg,
82{
83 fn default() -> Self {
84 Self {
85 ships: LinkedHashMap::new(),
86 }
87 }
88}
89
90impl<CID, SID, CA, CS, CM> Port<CID, SID, CA, CS, CM>
92where
93 CID: EzCptIds,
94 SID: EzShipIds,
95 CA: EzArgs,
96 CS: EzState,
97 CM: EzMsg,
98{
99 pub fn init(&mut self, tx: &Sender<EzEvent<CID, CM>>) {
101 for (_, ship) in &mut self.ships {
102 ship.set_tx(tx.clone());
103 }
104 }
105 pub fn register(&mut self, id: SID, ship: BoxedShip<CID, CA, CS, CM>) -> Result<()> {
110 self.register_internal(id.into(), ship)
111 }
112
113 pub fn unregister(&mut self, id: &SID) -> Result<()> {
118 self.unregister_internal(&id.clone().into())
119 }
120
121 pub fn registered(&self, id: &SID) -> bool {
123 self.registered_internal(&id.clone().into())
124 }
125
126 pub fn query_ship(&self, _: &EzShipId<SID>, _: Attribute) -> Result<Option<AttrValue>> {
131 Ok(None)
132 }
133
134 pub fn set_ship_attr(&mut self, _: &EzShipId<SID>, _: Attribute, _: AttrValue) -> Result<()> {
139 Ok(())
140 }
141}
142impl<CID, SID, CA, CS, CM> Port<CID, SID, CA, CS, CM>
144where
145 CID: EzCptIds,
146 SID: EzShipIds,
147 CA: EzArgs,
148 CS: EzState,
149 CM: EzMsg,
150{
151 pub(crate) fn forward(
152 &mut self,
153 id: &EzShipId<SID>,
154 event: EzEvent<CID, CM>,
155 state: &mut State<CS>,
156 ) -> Vec<EzEvent<CID, CM>> {
157 match self.ships.get_mut(id) {
158 None => {
159 warn!("Forwarding to missing ship {:?}", id);
160 vec![]
161 }
162 Some(port) => port.on_event(event, state),
163 }
164 }
165 pub(crate) fn init_ships(&mut self, state: &mut State<CS>) -> Vec<EzEvent<CID, CM>> {
166 self.ships
167 .iter_mut()
168 .flat_map(|(_, s)| s.init(state))
169 .collect()
170 }
171
172 pub(crate) fn tick(&mut self, state: &mut State<CS>) -> Vec<EzEvent<CID, CM>> {
174 self.ships
175 .iter_mut()
176 .flat_map(|(_, ship)| ship.tick(state))
177 .collect()
178 }
179
180 pub(crate) fn register_internal(
181 &mut self,
182 ez_id: EzShipId<SID>,
183 ship: BoxedShip<CID, CA, CS, CM>,
184 ) -> Result<()> {
185 if self.registered_internal(&ez_id) {
186 Err(PortError::ShipAlreadyRegistered.into())
187 } else {
188 self.ships.insert(ez_id, ship);
189 Ok(())
190 }
191 }
192
193 pub(crate) fn unregister_internal(&mut self, ez_id: &EzShipId<SID>) -> Result<()> {
194 if !self.registered_internal(ez_id) {
195 return Err(PortError::ShipNotFound.into());
196 }
197 self.ships.remove(ez_id);
198 Ok(())
199 }
200
201 pub(crate) fn registered_internal(&self, ez_id: &EzShipId<SID>) -> bool {
202 self.ships.contains_key(ez_id)
203 }
204}
205
206impl From<PortError> for Error {
207 fn from(value: PortError) -> Self {
208 Error::Port(value)
209 }
210}