embedded_redis/subscription/
client.rs1use crate::commands::builder::CommandBuilder;
2use crate::commands::hello::HelloCommand;
3use crate::commands::Command;
4use crate::network::protocol::Protocol;
5use crate::network::timeout::Timeout;
6use crate::network::{Client, CommandErrors};
7use crate::subscription::messages::{DecodeError, Message as PushMessage, ToPushMessage};
8use bytes::Bytes;
9use embedded_io_async::{Read, Write};
10use embedded_time::Clock;
11use futures_util::future::{select, Either};
12use futures_util::pin_mut;
13
14#[derive(Debug, Eq, PartialEq, Clone)]
16pub enum Error {
17 CommandError(CommandErrors),
19 ClockError,
21 TcpError,
23 DecodeError,
25 Timeout,
27}
28
29#[derive(Debug, Clone)]
31pub struct Message {
32 pub channel: Bytes,
34
35 pub payload: Bytes,
37}
38
39#[derive(Debug)]
43pub struct Subscription<'a, T: Read + Write, C: Clock, P: Protocol, const L: usize>
44where
45 HelloCommand: Command<<P as Protocol>::FrameType>,
46 <P as Protocol>::FrameType: From<CommandBuilder>,
47 <P as Protocol>::FrameType: ToPushMessage,
48{
49 client: Client<'a, T, C, P>,
50
51 channels: [Bytes; L],
53
54 subscribed: bool,
56}
57
58impl<'a, T, C, P, const L: usize> Subscription<'a, T, C, P, L>
59where
60 T: Read + Write,
61 C: Clock,
62 P: Protocol,
63 HelloCommand: Command<<P as Protocol>::FrameType>,
64 <P as Protocol>::FrameType: From<CommandBuilder>,
65 <P as Protocol>::FrameType: ToPushMessage,
66{
67 pub fn new(client: Client<'a, T, C, P>, topics: [Bytes; L]) -> Self {
68 Self {
69 client,
70 channels: topics,
71 subscribed: false,
72 }
73 }
74
75 pub async fn receive(&mut self) -> Result<Option<Message>, Error> {
77 loop {
78 let message = self.receive_message().await?;
79
80 if message.is_none() {
81 continue;
82 }
83
84 if let PushMessage::Publish(channel, payload) = message.unwrap() {
85 return Ok(Some(Message { channel, payload }));
86 }
87 }
88 }
89
90 pub(crate) async fn subscribe(mut self) -> Result<Self, Error> {
92 let mut cmd = CommandBuilder::new("SUBSCRIBE");
93 for topic in &self.channels {
94 cmd = cmd.arg(topic);
95 }
96
97 self.client.network.send_frame(cmd.into()).await.map_err(Error::CommandError)?;
98 self.wait_for_confirmation(|message| message == PushMessage::SubConfirmation(self.channels.len()))
99 .await?;
100
101 self.subscribed = true;
102 Ok(self)
103 }
104
105 pub async fn unsubscribe(mut self) -> Result<(), Error> {
109 self.close().await
110 }
111
112 pub(crate) async fn close(&mut self) -> Result<(), Error> {
114 self.subscribed = false;
115 let cmd = CommandBuilder::new("UNSUBSCRIBE");
116
117 self.client.network.send_frame(cmd.into()).await.map_err(Error::CommandError)?;
118 self.wait_for_confirmation(|message| message == PushMessage::UnSubConfirmation(0))
119 .await?;
120
121 Ok(())
122 }
123
124 async fn wait_for_confirmation<F: Fn(PushMessage) -> bool>(
126 &self,
127 is_confirmation: F,
128 ) -> Result<(), Error> {
129 let timeout =
130 Timeout::new(self.client.clock, self.client.timeout_duration).map_err(|_| Error::ClockError)?;
131
132 loop {
133 if timeout.is_enabled() {
134 let receive = self.receive_message();
135 let expired = timeout.wait();
136 pin_mut!(receive, expired);
137
138 match select(receive, expired).await {
139 Either::Left((message, _)) => {
140 if let Some(message) = message? {
141 if is_confirmation(message) {
142 return Ok(());
143 }
144 }
145 }
146 Either::Right((result, _)) => {
147 result.map_err(|_| Error::ClockError)?;
148 return Err(Error::Timeout);
149 }
150 }
151 } else if let Some(message) = self.receive_message().await? {
152 if is_confirmation(message) {
153 return Ok(());
154 }
155 }
156 }
157 }
158
159 async fn receive_message(&self) -> Result<Option<PushMessage>, Error> {
161 let mut frame = self.client.network.take_next_frame();
162
163 if frame.is_none() {
164 let received = self.client.network.receive_chunk().await.map_err(|_| Error::TcpError)?;
165 if received == 0 {
166 return Err(Error::TcpError);
167 }
168 frame = self.client.network.take_next_frame();
169 }
170
171 if frame.is_none() {
172 return Ok(None);
173 }
174
175 match frame.unwrap().decode_push() {
176 Ok(message) => Ok(Some(message)),
177 Err(error) => match error {
178 DecodeError::ProtocolViolation => Err(Error::DecodeError),
179 DecodeError::IntegerOverflow => Err(Error::DecodeError),
180 },
181 }
182 }
183
184 #[cfg(test)]
186 pub(crate) fn set_unsubscribed(&mut self) {
187 self.subscribed = false;
188 }
189}