tokio_stomp_rs_rs/
lib.rs

1//! tokio-stomp - A library for asynchronous streaming of STOMP messages
2
3use custom_debug_derive::Debug as CustomDebug;
4use frame::Frame;
5
6pub mod client;
7mod frame;
8
9pub(crate) type Result<T> = anyhow::Result<T>;
10
11/// A representation of a STOMP frame
12#[derive(Debug)]
13pub struct Message<T> {
14    /// The message content
15    pub content: T,
16    /// Headers present in the frame which were not required by the content
17    pub extra_headers: Vec<(Vec<u8>, Vec<u8>)>,
18}
19
20fn pretty_bytes(b: &Option<Vec<u8>>, f: &mut std::fmt::Formatter) -> std::fmt::Result {
21    if let Some(v) = b {
22        write!(f, "{}", String::from_utf8_lossy(v))
23    } else {
24        write!(f, "None")
25    }
26}
27
28/// A STOMP message sent from the server
29/// See the [Spec](https://stomp.github.io/stomp-specification-1.2.html) for more information
30#[derive(CustomDebug, Clone)]
31pub enum FromServer {
32    #[doc(hidden)] // The user shouldn't need to know about this one
33    Connected {
34        version: String,
35        session: Option<String>,
36        server: Option<String>,
37        heartbeat: Option<String>,
38    },
39    /// Conveys messages from subscriptions to the client
40    Message {
41        destination: String,
42        message_id: String,
43        subscription: String,
44        #[debug(with = "pretty_bytes")]
45        body: Option<Vec<u8>>,
46    },
47    /// Sent from the server to the client once a server has successfully
48    /// processed a client frame that requests a receipt
49    Receipt { receipt_id: String },
50    /// Something went wrong. After sending an Error, the server will close the connection
51    Error {
52        message: Option<String>,
53        #[debug(with = "pretty_bytes")]
54        body: Option<Vec<u8>>,
55    },
56}
57
58// TODO tidy this lot up with traits?
59impl Message<FromServer> {
60    // fn to_frame<'a>(&'a self) -> Frame<'a> {
61    //     unimplemented!()
62    // }
63
64    // TODO make this undead
65    fn from_frame<'a>(frame: Frame<'a>) -> Result<Message<FromServer>> {
66        frame.to_server_msg()
67    }
68}
69
70/// A STOMP message sent by the client.
71/// See the [Spec](https://stomp.github.io/stomp-specification-1.2.html) for more information
72#[derive(Debug, Clone)]
73pub enum ToServer {
74    #[doc(hidden)] // The user shouldn't need to know about this one
75    Connect {
76        accept_version: String,
77        host: String,
78        login: Option<String>,
79        passcode: Option<String>,
80        heartbeat: Option<(u32, u32)>,
81    },
82    /// Send a message to a destination in the messaging system
83    Send {
84        destination: String,
85        transaction: Option<String>,
86        body: Option<Vec<u8>>,
87    },
88    /// Register to listen to a given destination
89    Subscribe {
90        destination: String,
91        id: String,
92        ack: Option<AckMode>,
93    },
94    /// Remove an existing subscription
95    Unsubscribe { id: String },
96    /// Acknowledge consumption of a message from a subscription using
97    /// 'client' or 'client-individual' acknowledgment.
98    Ack {
99        // TODO ack and nack should be automatic?
100        id: String,
101        transaction: Option<String>,
102    },
103    /// Notify the server that the client did not consume the message
104    Nack {
105        id: String,
106        transaction: Option<String>,
107    },
108    /// Start a transaction
109    Begin { transaction: String },
110    /// Commit an in-progress transaction
111    Commit { transaction: String },
112    /// Roll back an in-progress transaction
113    Abort { transaction: String },
114    /// Gracefully disconnect from the server
115    /// Clients MUST NOT send any more frames after the DISCONNECT frame is sent.
116    Disconnect { receipt: Option<String> },
117}
118
119#[derive(Debug, Clone, Copy)]
120pub enum AckMode {
121    Auto,
122    Client,
123    ClientIndividual,
124}
125
126impl Message<ToServer> {
127    fn to_frame<'a>(&'a self) -> Frame<'a> {
128        self.content.to_frame()
129    }
130
131    #[allow(dead_code)]
132    fn from_frame<'a>(frame: Frame<'a>) -> Result<Message<ToServer>> {
133        frame.to_client_msg()
134    }
135}
136
137impl From<ToServer> for Message<ToServer> {
138    fn from(content: ToServer) -> Message<ToServer> {
139        Message {
140            content,
141            extra_headers: vec![],
142        }
143    }
144}