1use crate::commands::auth::AuthCommand;
2use crate::commands::builder::CommandBuilder;
3use crate::commands::hello::{HelloCommand, HelloResponse};
4use crate::commands::Command;
5use crate::network::buffer::Network;
6use crate::network::future::Future;
7use crate::network::handler::{ConnectionError, Credentials};
8use crate::network::protocol::{Protocol, Resp3};
9use crate::network::timeout::{Timeout, TimeoutError};
10use crate::subscription::client::{Error, Subscription};
11use crate::subscription::messages::ToPushMessage;
12use alloc::string::String;
13use bytes::Bytes;
14use core::fmt::{Debug, Formatter};
15use embedded_io_async::{Read, Write};
16use embedded_time::duration::Microseconds;
17use embedded_time::Clock;
18use futures_util::future::{select, Either};
19use futures_util::pin_mut;
20
21#[derive(Debug, Eq, PartialEq, Clone)]
23pub enum CommandErrors {
24 Timeout,
27 EncodingCommandFailed,
29 ProtocolViolation,
36 InvalidFuture,
41 TcpError,
43 TimerError,
45 CommandResponseViolation,
47 ErrorResponse(String),
49 MemoryFull,
52}
53
54pub struct Client<'a, T: Read + Write, C: Clock, P: Protocol>
58where
59 HelloCommand: Command<<P as Protocol>::FrameType>,
60{
61 pub(crate) network: Network<T, P>,
62 pub(crate) clock: Option<&'a C>,
63
64 pub(crate) timeout_duration: Microseconds,
66
67 pub(crate) hello_response: Option<<HelloCommand as Command<<P as Protocol>::FrameType>>::Response>,
69}
70
71impl<'a, T: Read + Write, C: Clock, P: Protocol> Client<'a, T, C, P>
72where
73 AuthCommand: Command<<P as Protocol>::FrameType>,
74 HelloCommand: Command<<P as Protocol>::FrameType>,
75{
76 pub async fn send<Cmd>(&self, command: Cmd) -> Result<Cmd::Response, CommandErrors>
78 where
79 Cmd: Command<P::FrameType>,
80 {
81 self.send_pending(command).await?.wait().await
82 }
83
84 pub(crate) async fn send_pending<'b, Cmd>(
86 &'b self,
87 command: Cmd,
88 ) -> Result<Future<'b, T, C, P, Cmd>, CommandErrors>
89 where
90 Cmd: Command<P::FrameType>,
91 {
92 let id = self.network.send(command.encode()).await?;
93
94 Ok(Future::new(
95 id,
96 command,
97 self.network.get_protocol(),
98 &self.network,
99 Timeout::new(self.clock, self.timeout_duration)?,
100 ))
101 }
102
103 pub async fn subscribe<const L: usize>(
108 self,
109 channels: [Bytes; L],
110 ) -> Result<Subscription<'a, T, C, P, L>, Error>
111 where
112 <P as Protocol>::FrameType: ToPushMessage,
113 <P as Protocol>::FrameType: From<CommandBuilder>,
114 {
115 Subscription::new(self, channels).subscribe().await
116 }
117
118 pub(crate) async fn auth(&self, credentials: Option<Credentials>) -> Result<(), ConnectionError> {
120 if let Some(credentials) = credentials.as_ref() {
121 self.send(AuthCommand::from(credentials)).await.map_err(auth_error)?;
122 }
123
124 Ok(())
125 }
126
127 pub(crate) async fn init(
129 &self,
130 credentials: Option<Credentials>,
131 ) -> Result<Option<<HelloCommand as Command<<P as Protocol>::FrameType>>::Response>, ConnectionError>
132 {
133 self.auth(credentials).await?;
134 if self.network.get_protocol().requires_hello() {
135 return Ok(Some(self.send(HelloCommand {}).await.map_err(hello_error)?));
136 }
137
138 Ok(None)
139 }
140
141 pub async fn close(&self) {
143 if !self.network.remaining_dropped_futures() {
144 return;
145 }
146
147 let timer = match Timeout::new(self.clock, self.timeout_duration) {
148 Ok(timer) => timer,
149 Err(_) => {
150 return;
151 }
152 };
153
154 while self.network.remaining_dropped_futures() {
155 self.network.handle_dropped_futures();
156
157 if !self.network.remaining_dropped_futures() {
158 return;
159 }
160
161 if timer.is_enabled() {
162 let receive = self.network.receive_chunk();
163 let expired = timer.wait();
164 pin_mut!(receive, expired);
165
166 match select(receive, expired).await {
167 Either::Left((Ok(bytes), _)) if bytes > 0 => {}
168 Either::Left((Err(_), _)) | Either::Right(_) => return,
169 Either::Left((Ok(_), _)) => return,
170 }
171 } else {
172 match self.network.receive_chunk().await {
173 Ok(bytes) if bytes > 0 => {}
174 _ => return,
175 }
176 }
177 }
178 }
179}
180
181impl<T: Read + Write, C: Clock> Client<'_, T, C, Resp3> {
182 pub fn get_hello_response(&self) -> &HelloResponse {
185 self.hello_response.as_ref().unwrap()
186 }
187}
188
189impl From<TimeoutError> for CommandErrors {
190 fn from(_: TimeoutError) -> Self {
191 CommandErrors::TimerError
192 }
193}
194
195fn auth_error(error: CommandErrors) -> ConnectionError {
196 ConnectionError::AuthenticationError(error)
197}
198
199#[allow(dead_code)]
200fn hello_error(error: CommandErrors) -> ConnectionError {
201 ConnectionError::ProtocolSwitchError(error)
202}
203
204impl<T: Read + Write, C: Clock, P: Protocol> Debug for Client<'_, T, C, P>
205where
206 HelloCommand: Command<<P as Protocol>::FrameType>,
207{
208 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
209 f.debug_struct("Client")
210 .field("network", &self.network)
211 .field("timeout_duration", &self.timeout_duration)
212 .finish()
213 }
214}