Skip to main content

async_nats/jetstream/
message.rs

1// Copyright 2020-2022 The NATS Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//! A wrapped `crate::Message` with `JetStream` related methods.
15use super::context::Context;
16use crate::datetime::{self, DateTime};
17use crate::header::{IntoHeaderName, IntoHeaderValue};
18use crate::subject::ToSubject;
19use crate::{error, header, message, Error, HeaderValue};
20use crate::{subject::Subject, HeaderMap};
21use bytes::Bytes;
22use futures_util::future::TryFutureExt;
23use futures_util::StreamExt;
24use std::fmt::Display;
25use std::{mem, time::Duration};
26
27/// A message received directly from the stream, without leveraging a consumer.
28#[derive(Debug, Clone)]
29pub struct StreamMessage {
30    pub subject: Subject,
31    pub sequence: u64,
32    pub headers: HeaderMap,
33    pub payload: Bytes,
34    pub time: DateTime,
35}
36
37/// An outbound message to be published.
38/// Does not contain status or description which are valid only for inbound messages.
39pub struct OutboundMessage {
40    pub subject: Subject,
41    pub payload: Bytes,
42    pub headers: Option<HeaderMap>,
43}
44
45impl OutboundMessage {
46    pub fn new(subject: Subject, payload: Bytes, headers: Option<HeaderMap>) -> Self {
47        Self {
48            subject,
49            payload,
50            headers,
51        }
52    }
53}
54
55impl From<OutboundMessage> for message::OutboundMessage {
56    fn from(message: OutboundMessage) -> Self {
57        message::OutboundMessage {
58            subject: message.subject,
59            payload: message.payload,
60            headers: message.headers,
61            reply: None,
62        }
63    }
64}
65
66/// Used for building customized `publish` message.
67#[derive(Default, Clone, Debug)]
68pub struct PublishMessage {
69    pub(crate) payload: Bytes,
70    pub(crate) headers: Option<header::HeaderMap>,
71}
72impl PublishMessage {
73    /// Creates a new custom Publish struct to be used with.
74    pub fn build() -> Self {
75        Default::default()
76    }
77
78    /// Sets the payload for the message.
79    pub fn payload(mut self, payload: Bytes) -> Self {
80        self.payload = payload;
81        self
82    }
83    /// Adds headers to the message.
84    pub fn headers(mut self, headers: HeaderMap) -> Self {
85        self.headers = Some(headers);
86        self
87    }
88    /// A shorthand to add a single header.
89    pub fn header<N: IntoHeaderName, V: IntoHeaderValue>(mut self, name: N, value: V) -> Self {
90        self.headers
91            .get_or_insert(header::HeaderMap::new())
92            .insert(name, value);
93        self
94    }
95    /// Sets the `Nats-Msg-Id` header, that is used by stream deduplicate window.
96    pub fn message_id<T: AsRef<str>>(self, id: T) -> Self {
97        self.header(header::NATS_MESSAGE_ID, id.as_ref())
98    }
99    /// Sets expected last message ID.
100    /// It sets the `Nats-Expected-Last-Msg-Id` header with provided value.
101    pub fn expected_last_message_id<T: AsRef<str>>(self, last_message_id: T) -> Self {
102        self.header(
103            header::NATS_EXPECTED_LAST_MESSAGE_ID,
104            last_message_id.as_ref(),
105        )
106    }
107    /// Sets the last expected stream sequence.
108    /// It sets the `Nats-Expected-Last-Sequence` header with provided value.
109    pub fn expected_last_sequence(self, last_sequence: u64) -> Self {
110        self.header(
111            header::NATS_EXPECTED_LAST_SEQUENCE,
112            HeaderValue::from(last_sequence),
113        )
114    }
115    /// Sets the last expected stream sequence for a subject this message will be published to.
116    /// It sets the `Nats-Expected-Last-Subject-Sequence` header with provided value.
117    pub fn expected_last_subject_sequence(self, subject_sequence: u64) -> Self {
118        self.header(
119            header::NATS_EXPECTED_LAST_SUBJECT_SEQUENCE,
120            HeaderValue::from(subject_sequence),
121        )
122    }
123    /// Sets the expected stream name.
124    /// It sets the `Nats-Expected-Stream` header with provided value.
125    pub fn expected_stream<T: AsRef<str>>(self, stream: T) -> Self {
126        self.header(
127            header::NATS_EXPECTED_STREAM,
128            HeaderValue::from(stream.as_ref()),
129        )
130    }
131
132    #[cfg(feature = "server_2_11")]
133    /// Sets TTL for a single message.
134    /// It sets the `Nats-TTL` header with provided value.
135    pub fn ttl(self, ttl: Duration) -> Self {
136        self.header(header::NATS_MESSAGE_TTL, ttl.as_secs().to_string())
137    }
138
139    /// Creates an [crate::jetstream::message::OutboundMessage] that can be sent using
140    /// [crate::jetstream::context::traits::Publisher::publish_message].
141    pub fn outbound_message<S: ToSubject>(self, subject: S) -> OutboundMessage {
142        OutboundMessage {
143            subject: subject.to_subject(),
144            payload: self.payload,
145            headers: self.headers,
146        }
147    }
148}
149
150#[derive(Clone, Debug)]
151pub struct Message {
152    pub message: crate::Message,
153    pub context: Context,
154}
155
156impl TryFrom<crate::Message> for StreamMessage {
157    type Error = StreamMessageError;
158
159    fn try_from(message: crate::Message) -> Result<Self, Self::Error> {
160        let headers = message.headers.ok_or_else(|| {
161            StreamMessageError::with_source(StreamMessageErrorKind::MissingHeader, "no headers")
162        })?;
163
164        let sequence = headers
165            .get_last(header::NATS_SEQUENCE)
166            .ok_or_else(|| {
167                StreamMessageError::with_source(StreamMessageErrorKind::MissingHeader, "sequence")
168            })
169            .and_then(|seq| {
170                seq.as_str().parse().map_err(|err| {
171                    StreamMessageError::with_source(
172                        StreamMessageErrorKind::ParseError,
173                        format!("could not parse sequence header: {err}"),
174                    )
175                })
176            })?;
177
178        let time = headers
179            .get_last(header::NATS_TIME_STAMP)
180            .ok_or_else(|| {
181                StreamMessageError::with_source(StreamMessageErrorKind::MissingHeader, "timestamp")
182            })
183            .and_then(|time| {
184                datetime::parse_rfc3339(time.as_str()).map_err(|err| {
185                    StreamMessageError::with_source(
186                        StreamMessageErrorKind::ParseError,
187                        format!("could not parse timestamp header: {err}"),
188                    )
189                })
190            })?;
191
192        let subject = headers
193            .get_last(header::NATS_SUBJECT)
194            .ok_or_else(|| {
195                StreamMessageError::with_source(StreamMessageErrorKind::MissingHeader, "subject")
196            })?
197            .as_str()
198            .into();
199
200        Ok(StreamMessage {
201            subject,
202            sequence,
203            headers,
204            payload: message.payload,
205            time,
206        })
207    }
208}
209
210#[derive(Debug, Clone, PartialEq)]
211pub enum StreamMessageErrorKind {
212    MissingHeader,
213    ParseError,
214}
215
216/// Error returned when library is unable to parse message got directly from the stream.
217pub type StreamMessageError = error::Error<StreamMessageErrorKind>;
218
219impl Display for StreamMessageErrorKind {
220    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221        match self {
222            StreamMessageErrorKind::MissingHeader => write!(f, "missing message header"),
223            StreamMessageErrorKind::ParseError => write!(f, "parse error"),
224        }
225    }
226}
227
228impl std::ops::Deref for Message {
229    type Target = crate::Message;
230
231    fn deref(&self) -> &Self::Target {
232        &self.message
233    }
234}
235
236impl From<Message> for crate::Message {
237    fn from(source: Message) -> crate::Message {
238        source.message
239    }
240}
241
242impl Message {
243    /// Splits [Message] into [Acker] and [crate::Message].
244    /// This can help reduce memory footprint if [Message] can be dropped before acking,
245    /// for example when it's transformed into another structure and acked later
246    pub fn split(mut self) -> (crate::Message, Acker) {
247        let reply = mem::take(&mut self.message.reply);
248        (
249            self.message,
250            Acker {
251                context: self.context,
252                reply,
253            },
254        )
255    }
256    /// Acknowledges a message delivery by sending `+ACK` to the server.
257    ///
258    /// If [AckPolicy][crate::jetstream::consumer::AckPolicy] is set to `All` or `Explicit`, messages has to be acked.
259    /// Otherwise redeliveries will occur and [Consumer][crate::jetstream::consumer::Consumer] will not be able to advance.
260    ///
261    /// # Examples
262    ///
263    /// ```no_run
264    /// # #[tokio::main]
265    /// # async fn main() -> Result<(), async_nats::Error> {
266    /// use async_nats::jetstream::consumer::PullConsumer;
267    /// use futures_util::StreamExt;
268    /// let client = async_nats::connect("localhost:4222").await?;
269    /// let jetstream = async_nats::jetstream::new(client);
270    ///
271    /// let consumer: PullConsumer = jetstream
272    ///     .get_stream("events")
273    ///     .await?
274    ///     .get_consumer("pull")
275    ///     .await?;
276    ///
277    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
278    ///
279    /// while let Some(message) = messages.next().await {
280    ///     message?.ack().await?;
281    /// }
282    /// # Ok(())
283    /// # }
284    /// ```
285    pub async fn ack(&self) -> Result<(), Error> {
286        if let Some(ref reply) = self.reply {
287            self.context
288                .client
289                .publish(reply.clone(), "".into())
290                .map_err(Error::from)
291                .await
292        } else {
293            Err(Box::new(std::io::Error::other(
294                "No reply subject, not a JetStream message",
295            )))
296        }
297    }
298
299    /// Acknowledges a message delivery by sending a chosen [AckKind] variant to the server.
300    ///
301    /// # Examples
302    ///
303    /// ```no_run
304    /// # #[tokio::main]
305    /// # async fn main() -> Result<(), async_nats::Error> {
306    /// use async_nats::jetstream::consumer::PullConsumer;
307    /// use async_nats::jetstream::AckKind;
308    /// use futures_util::StreamExt;
309    /// let client = async_nats::connect("localhost:4222").await?;
310    /// let jetstream = async_nats::jetstream::new(client);
311    ///
312    /// let consumer: PullConsumer = jetstream
313    ///     .get_stream("events")
314    ///     .await?
315    ///     .get_consumer("pull")
316    ///     .await?;
317    ///
318    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
319    ///
320    /// while let Some(message) = messages.next().await {
321    ///     message?.ack_with(AckKind::Nak(None)).await?;
322    /// }
323    /// # Ok(())
324    /// # }
325    /// ```
326    pub async fn ack_with(&self, kind: AckKind) -> Result<(), Error> {
327        if let Some(ref reply) = self.reply {
328            self.context
329                .client
330                .publish(reply.to_owned(), kind.into())
331                .map_err(Error::from)
332                .await
333        } else {
334            Err(Box::new(std::io::Error::other(
335                "No reply subject, not a JetStream message",
336            )))
337        }
338    }
339
340    /// Acknowledges a message delivery by sending a chosen [AckKind] to the server
341    /// and awaits for confirmation for the server that it received the message.
342    /// Useful if user wants to ensure `exactly once` semantics.
343    ///
344    /// If [AckPolicy][crate::jetstream::consumer::AckPolicy] is set to `All` or `Explicit`, messages has to be acked.
345    /// Otherwise redeliveries will occur and [Consumer][crate::jetstream::consumer::Consumer] will not be able to advance.
346    ///
347    /// # Examples
348    ///
349    /// ```no_run
350    /// # #[tokio::main]
351    /// # async fn main() -> Result<(), async_nats::Error> {
352    /// use async_nats::jetstream::AckKind;
353    /// use futures_util::StreamExt;
354    /// let client = async_nats::connect("localhost:4222").await?;
355    /// let jetstream = async_nats::jetstream::new(client);
356    ///
357    /// let consumer = jetstream
358    ///     .get_stream("events")
359    ///     .await?
360    ///     .get_consumer("pull")
361    ///     .await?;
362    ///
363    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
364    ///
365    /// while let Some(message) = messages.next().await {
366    ///     message?.double_ack_with(AckKind::Ack).await?;
367    /// }
368    /// # Ok(())
369    /// # }
370    /// ```
371    pub async fn double_ack_with(&self, ack_kind: AckKind) -> Result<(), Error> {
372        if let Some(ref reply) = self.reply {
373            let inbox = self.context.client.new_inbox();
374            let mut subscription = self.context.client.subscribe(inbox.clone()).await?;
375            self.context
376                .client
377                .publish_with_reply(reply.clone(), inbox, ack_kind.into())
378                .await?;
379            match tokio::time::timeout(self.context.timeout, subscription.next())
380                .await
381                .map_err(|_| {
382                    std::io::Error::new(
383                        std::io::ErrorKind::TimedOut,
384                        "double ack response timed out",
385                    )
386                })? {
387                Some(_) => Ok(()),
388                None => Err(Box::new(std::io::Error::other("subscription dropped"))),
389            }
390        } else {
391            Err(Box::new(std::io::Error::other(
392                "No reply subject, not a JetStream message",
393            )))
394        }
395    }
396
397    /// Acknowledges a message delivery by sending `+ACK` to the server
398    /// and awaits for confirmation for the server that it received the message.
399    /// Useful if user wants to ensure `exactly once` semantics.
400    ///
401    /// If [AckPolicy][crate::jetstream::consumer::AckPolicy] is set to `All` or `Explicit`, messages has to be acked.
402    /// Otherwise redeliveries will occur and [Consumer][crate::jetstream::consumer::Consumer] will not be able to advance.
403    ///
404    /// # Examples
405    ///
406    /// ```no_run
407    /// # #[tokio::main]
408    /// # async fn main() -> Result<(), async_nats::Error> {
409    /// use futures_util::StreamExt;
410    /// let client = async_nats::connect("localhost:4222").await?;
411    /// let jetstream = async_nats::jetstream::new(client);
412    ///
413    /// let consumer = jetstream
414    ///     .get_stream("events")
415    ///     .await?
416    ///     .get_consumer("pull")
417    ///     .await?;
418    ///
419    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
420    ///
421    /// while let Some(message) = messages.next().await {
422    ///     message?.double_ack().await?;
423    /// }
424    /// # Ok(())
425    /// # }
426    /// ```
427    pub async fn double_ack(&self) -> Result<(), Error> {
428        self.double_ack_with(AckKind::Ack).await
429    }
430
431    /// Returns the `JetStream` message ID
432    /// if this is a `JetStream` message.
433    #[allow(clippy::mixed_read_write_in_expression)]
434    pub fn info(&self) -> Result<Info<'_>, Error> {
435        const PREFIX: &str = "$JS.ACK.";
436        const SKIP: usize = PREFIX.len();
437
438        let mut reply: &str = self.reply.as_ref().ok_or_else(|| {
439            std::io::Error::new(std::io::ErrorKind::NotFound, "did not found reply subject")
440        })?;
441
442        if !reply.starts_with(PREFIX) {
443            return Err(Box::new(std::io::Error::other(
444                "did not found proper prefix",
445            )));
446        }
447
448        reply = &reply[SKIP..];
449
450        let mut split = reply.split('.');
451
452        // we should avoid allocating to prevent
453        // large performance degradations in
454        // parsing this.
455        let mut tokens: [Option<&str>; 10] = [None; 10];
456        let mut n_tokens = 0;
457        for each_token in &mut tokens {
458            if let Some(token) = split.next() {
459                *each_token = Some(token);
460                n_tokens += 1;
461            }
462        }
463
464        let mut token_index = 0;
465
466        macro_rules! try_parse {
467            () => {
468                match str::parse(try_parse!(str)) {
469                    Ok(parsed) => parsed,
470                    Err(e) => {
471                        return Err(Box::new(e));
472                    }
473                }
474            };
475            (str) => {
476                if let Some(next) = tokens[token_index].take() {
477                    #[allow(unused)]
478                    {
479                        // this isn't actually unused, but it's
480                        // difficult for the compiler to infer this.
481                        token_index += 1;
482                    }
483                    next
484                } else {
485                    return Err(Box::new(std::io::Error::other("too few tokens")));
486                }
487            };
488        }
489
490        // now we can try to parse the tokens to
491        // individual types. We use an if-else
492        // chain instead of a match because it
493        // produces more optimal code usually,
494        // and we want to try the 9 (11 - the first 2)
495        // case first because we expect it to
496        // be the most common. We use >= to be
497        // future-proof.
498        if n_tokens >= 9 {
499            Ok(Info {
500                domain: {
501                    let domain: &str = try_parse!(str);
502                    if domain == "_" {
503                        None
504                    } else {
505                        Some(domain)
506                    }
507                },
508                acc_hash: Some(try_parse!(str)),
509                stream: try_parse!(str),
510                consumer: try_parse!(str),
511                delivered: try_parse!(),
512                stream_sequence: try_parse!(),
513                consumer_sequence: try_parse!(),
514                published: {
515                    let nanos: i128 = try_parse!();
516                    datetime::from_nanos(nanos)?
517                },
518                pending: try_parse!(),
519                token: if n_tokens >= 9 {
520                    Some(try_parse!(str))
521                } else {
522                    None
523                },
524            })
525        } else if n_tokens == 7 {
526            // we expect this to be increasingly rare, as older
527            // servers are phased out.
528            Ok(Info {
529                domain: None,
530                acc_hash: None,
531                stream: try_parse!(str),
532                consumer: try_parse!(str),
533                delivered: try_parse!(),
534                stream_sequence: try_parse!(),
535                consumer_sequence: try_parse!(),
536                published: {
537                    let nanos: i128 = try_parse!();
538                    datetime::from_nanos(nanos)?
539                },
540                pending: try_parse!(),
541                token: None,
542            })
543        } else {
544            Err(Box::new(std::io::Error::other("bad token number")))
545        }
546    }
547}
548
549/// A lightweight struct useful for decoupling message contents and the ability to ack it.
550pub struct Acker {
551    context: Context,
552    reply: Option<Subject>,
553}
554
555// TODO(tp): This should be async trait to avoid duplication of code. Will be refactored into one when async traits are available.
556// The async-trait crate is not a solution here, as it would mean we're allocating at every ack.
557// Creating separate function to ack just to avoid one duplication is not worth it either.
558impl Acker {
559    pub fn new(context: Context, reply: Option<Subject>) -> Self {
560        Self { context, reply }
561    }
562    /// Acknowledges a message delivery by sending `+ACK` to the server.
563    ///
564    /// If [AckPolicy][crate::jetstream::consumer::AckPolicy] is set to `All` or `Explicit`, messages has to be acked.
565    /// Otherwise redeliveries will occur and [Consumer][crate::jetstream::consumer::Consumer] will not be able to advance.
566    ///
567    /// # Examples
568    ///
569    /// ```no_run
570    /// # #[tokio::main]
571    /// # async fn main() -> Result<(), async_nats::Error> {
572    /// use async_nats::jetstream::{consumer::PullConsumer, Message};
573    /// use futures_util::StreamExt;
574    /// let client = async_nats::connect("localhost:4222").await?;
575    /// let jetstream = async_nats::jetstream::new(client);
576    ///
577    /// let consumer: PullConsumer = jetstream
578    ///     .get_stream("events")
579    ///     .await?
580    ///     .get_consumer("pull")
581    ///     .await?;
582    ///
583    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
584    ///
585    /// while let Some(message) = messages.next().await {
586    ///     let (message, acker) = message.map(Message::split)?;
587    ///     // Do something with the message. Ownership can be taken over `Message`
588    ///     // while retaining ability to ack later.
589    ///     println!("message: {:?}", message);
590    ///     // Ack it. `Message` may be dropped already.
591    ///     acker.ack().await?;
592    /// }
593    /// # Ok(())
594    /// # }
595    /// ```
596    pub async fn ack(&self) -> Result<(), Error> {
597        if let Some(ref reply) = self.reply {
598            self.context
599                .client
600                .publish(reply.to_owned(), "".into())
601                .map_err(Error::from)
602                .await
603        } else {
604            Err(Box::new(std::io::Error::other(
605                "No reply subject, not a JetStream message",
606            )))
607        }
608    }
609
610    /// Acknowledges a message delivery by sending a chosen [AckKind] variant to the server.
611    ///
612    /// # Examples
613    ///
614    /// ```no_run
615    /// # #[tokio::main]
616    /// # async fn main() -> Result<(), async_nats::Error> {
617    /// use async_nats::jetstream::{consumer::PullConsumer, AckKind, Message};
618    /// use futures_util::StreamExt;
619    /// let client = async_nats::connect("localhost:4222").await?;
620    /// let jetstream = async_nats::jetstream::new(client);
621    ///
622    /// let consumer: PullConsumer = jetstream
623    ///     .get_stream("events")
624    ///     .await?
625    ///     .get_consumer("pull")
626    ///     .await?;
627    ///
628    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
629    ///
630    /// while let Some(message) = messages.next().await {
631    ///     let (message, acker) = message.map(Message::split)?;
632    ///     // Do something with the message. Ownership can be taken over `Message`.
633    ///     // while retaining ability to ack later.
634    ///     println!("message: {:?}", message);
635    ///     // Ack it. `Message` may be dropped already.
636    ///     acker.ack_with(AckKind::Nak(None)).await?;
637    /// }
638    /// # Ok(())
639    /// # }
640    /// ```
641    pub async fn ack_with(&self, kind: AckKind) -> Result<(), Error> {
642        if let Some(ref reply) = self.reply {
643            self.context
644                .client
645                .publish(reply.to_owned(), kind.into())
646                .map_err(Error::from)
647                .await
648        } else {
649            Err(Box::new(std::io::Error::other(
650                "No reply subject, not a JetStream message",
651            )))
652        }
653    }
654
655    /// Acknowledges a message delivery by sending the chosen [AckKind] to the server
656    /// and awaits for confirmation for the server that it received the message.
657    /// Useful if user wants to ensure `exactly once` semantics.
658    ///
659    /// If [AckPolicy][crate::jetstream::consumer::AckPolicy] is set to `All` or `Explicit`, messages has to be acked.
660    /// Otherwise redeliveries will occur and [Consumer][crate::jetstream::consumer::Consumer] will not be able to advance.
661    ///
662    /// # Examples
663    ///
664    /// ```no_run
665    /// # #[tokio::main]
666    /// # async fn main() -> Result<(), async_nats::Error> {
667    /// use async_nats::jetstream::{AckKind, Message};
668    /// use futures_util::StreamExt;
669    /// let client = async_nats::connect("localhost:4222").await?;
670    /// let jetstream = async_nats::jetstream::new(client);
671    ///
672    /// let consumer = jetstream
673    ///     .get_stream("events")
674    ///     .await?
675    ///     .get_consumer("pull")
676    ///     .await?;
677    ///
678    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
679    ///
680    /// while let Some(message) = messages.next().await {
681    ///     let (message, acker) = message.map(Message::split)?;
682    ///     // Do something with the message. Ownership can be taken over `Message`.
683    ///     // while retaining ability to ack later.
684    ///     println!("message: {:?}", message);
685    ///     // Ack it. `Message` may be dropped already.
686    ///     acker.double_ack_with(AckKind::Ack).await?;
687    /// }
688    /// # Ok(())
689    /// # }
690    /// ```
691    pub async fn double_ack_with(&self, ack_kind: AckKind) -> Result<(), Error> {
692        if let Some(ref reply) = self.reply {
693            let inbox = self.context.client.new_inbox();
694            let mut subscription = self.context.client.subscribe(inbox.to_owned()).await?;
695            self.context
696                .client
697                .publish_with_reply(reply.to_owned(), inbox, ack_kind.into())
698                .await?;
699            match tokio::time::timeout(self.context.timeout, subscription.next())
700                .await
701                .map_err(|_| {
702                    std::io::Error::new(
703                        std::io::ErrorKind::TimedOut,
704                        "double ack response timed out",
705                    )
706                })? {
707                Some(_) => Ok(()),
708                None => Err(Box::new(std::io::Error::other("subscription dropped"))),
709            }
710        } else {
711            Err(Box::new(std::io::Error::other(
712                "No reply subject, not a JetStream message",
713            )))
714        }
715    }
716
717    /// Acknowledges a message delivery by sending `+ACK` to the server
718    /// and awaits for confirmation for the server that it received the message.
719    /// Useful if user wants to ensure `exactly once` semantics.
720    ///
721    /// If [AckPolicy][crate::jetstream::consumer::AckPolicy] is set to `All` or `Explicit`, messages has to be acked.
722    /// Otherwise redeliveries will occur and [Consumer][crate::jetstream::consumer::Consumer] will not be able to advance.
723    ///
724    /// # Examples
725    ///
726    /// ```no_run
727    /// # #[tokio::main]
728    /// # async fn main() -> Result<(), async_nats::Error> {
729    /// use async_nats::jetstream::Message;
730    /// use futures_util::StreamExt;
731    /// let client = async_nats::connect("localhost:4222").await?;
732    /// let jetstream = async_nats::jetstream::new(client);
733    ///
734    /// let consumer = jetstream
735    ///     .get_stream("events")
736    ///     .await?
737    ///     .get_consumer("pull")
738    ///     .await?;
739    ///
740    /// let mut messages = consumer.fetch().max_messages(100).messages().await?;
741    ///
742    /// while let Some(message) = messages.next().await {
743    ///     let (message, acker) = message.map(Message::split)?;
744    ///     // Do something with the message. Ownership can be taken over `Message`.
745    ///     // while retaining ability to ack later.
746    ///     println!("message: {:?}", message);
747    ///     // Ack it. `Message` may be dropped already.
748    ///     acker.double_ack().await?;
749    /// }
750    /// # Ok(())
751    /// # }
752    /// ```
753    pub async fn double_ack(&self) -> Result<(), Error> {
754        self.double_ack_with(AckKind::Ack).await
755    }
756}
757/// The kinds of response used for acknowledging a processed message.
758#[derive(Debug, Clone, Copy)]
759pub enum AckKind {
760    /// Acknowledges a message was completely handled.
761    Ack,
762    /// Signals that the message will not be processed now
763    /// and processing can move onto the next message, NAK'd
764    /// message will be retried.
765    Nak(Option<Duration>),
766    /// When sent before the AckWait period indicates that
767    /// work is ongoing and the period should be extended by
768    /// another equal to AckWait.
769    Progress,
770    /// Acknowledges the message was handled and requests
771    /// delivery of the next message to the reply subject.
772    /// Only applies to Pull-mode.
773    Next,
774    /// Instructs the server to stop redelivery of a message
775    /// without acknowledging it as successfully processed.
776    Term,
777}
778
779impl From<AckKind> for Bytes {
780    fn from(kind: AckKind) -> Self {
781        use AckKind::*;
782        match kind {
783            Ack => Bytes::from_static(b"+ACK"),
784            Nak(maybe_duration) => match maybe_duration {
785                None => Bytes::from_static(b"-NAK"),
786                Some(duration) => format!("-NAK {{\"delay\":{}}}", duration.as_nanos()).into(),
787            },
788            Progress => Bytes::from_static(b"+WPI"),
789            Next => Bytes::from_static(b"+NXT"),
790            Term => Bytes::from_static(b"+TERM"),
791        }
792    }
793}
794
795/// Information about a received message
796#[derive(Debug, Clone)]
797pub struct Info<'a> {
798    /// Optional domain, present in servers post-ADR-15
799    pub domain: Option<&'a str>,
800    /// Optional account hash, present in servers post-ADR-15
801    pub acc_hash: Option<&'a str>,
802    /// The stream name
803    pub stream: &'a str,
804    /// The consumer name
805    pub consumer: &'a str,
806    /// The stream sequence number associated with this message
807    pub stream_sequence: u64,
808    /// The consumer sequence number associated with this message
809    pub consumer_sequence: u64,
810    /// The number of delivery attempts for this message
811    pub delivered: i64,
812    /// the number of messages known by the server to be pending to this consumer
813    pub pending: u64,
814    /// the time that this message was received by the server from its publisher
815    pub published: DateTime,
816    /// Optional token, present in servers post-ADR-15
817    pub token: Option<&'a str>,
818}