rubin/net/client/
mod.rs

1//! Client protocol for interacting with the Server
2//!
3//! The client connects to a running server and can make network requests to
4//! retrieve items from the store.
5//!
6//! # Usage
7//!
8//! ```no_run
9//! use rubin::net::client::RubinClient;
10//!
11//! #[tokio::main]
12//! async fn main() -> std::io::Result<()> {
13//!     let client = RubinClient::new("127.0.0.1", 9876);
14//!
15//!     let result = client.insert_string("user:1000", "value").await?;
16//!
17//!     assert_eq!(&result, "OK");
18//!
19//!     let value = client.get_string("user:1000").await?;
20//!
21//!     assert_eq!(&result, "value");
22//!
23//!     Ok(())
24//! }
25//! ```
26
27use tokio::{
28    io::{AsyncReadExt, AsyncWriteExt},
29    net::TcpStream,
30};
31
32use crate::net::parser::{create_request, parse_response, Operation};
33
34use std::io::Result;
35
36/// Client protocol for interacting with the Rubin Server
37pub struct RubinClient {
38    /// Address of the server
39    pub address: String,
40}
41
42impl RubinClient {
43    /// Creates a new client, storing the address
44    ///
45    /// # Example
46    ///
47    /// ```no_run
48    /// use rubin::net::client::RubinClient;
49    ///
50    /// #[tokio::main]
51    /// async fn main() -> std::io::Result<()> {
52    ///     let client = RubinClient::new("127.0.0.1", 9876);
53    ///
54    ///     Ok(())
55    /// }
56    /// ```
57    pub fn new(addr: &str, port: usize) -> Self {
58        let address = format!("{}:{}", addr, port);
59        Self { address }
60    }
61
62    /// Sends a request to the server to insert a key-value pair into the string store
63    ///
64    /// # Example
65    ///
66    /// ```no_run
67    /// use rubin::net::client::RubinClient;
68    ///
69    /// #[tokio::main]
70    /// async fn main() -> std::io::Result<()> {
71    ///     let client = RubinClient::new("127.0.0.1", 9876);
72    ///     client.insert_string("username", "rubinuser").await?;
73    ///
74    ///     Ok(())
75    /// }
76    /// ```
77    pub async fn insert_string(&self, key: &str, value: &str) -> Result<String> {
78        let msg = create_request(
79            Operation::StringSet,
80            vec![key.to_string(), value.to_string()],
81        );
82        self.request(&msg).await
83    }
84
85    /// Sends a request to the server to retrieve a value from the string store
86    /// with the given key
87    ///
88    /// # Example
89    ///
90    /// ```no_run
91    /// use rubin::net::client::RubinClient;
92    ///
93    /// #[tokio::main]
94    /// async fn main() -> std::io::Result<()> {
95    ///     let client = RubinClient::new("127.0.0.1", 9876);
96    ///     let result = client.get_string("username").await?;
97    ///
98    ///     Ok(())
99    /// }
100    /// ```
101    pub async fn get_string(&self, key: &str) -> Result<String> {
102        let msg = create_request(Operation::StringGet, vec![key.to_string()]);
103        self.request(&msg).await
104    }
105
106    /// Sends a request to the server to remove a value form the string store denoted by the given
107    /// key.
108    ///
109    /// # Example
110    ///
111    /// ```rust,no_run
112    /// use rubin::net::client::RubinClient;
113    ///
114    /// #[tokio::main]
115    /// async fn main() -> std::io::Result<()> {
116    ///     let client = RubinClient::new("127.0.0.1", 9876);
117    ///     let result = client.remove_string("user:1000").await?;
118    ///
119    ///     Ok(())
120    /// }
121    /// ```
122    pub async fn remove_string(&self, key: &str) -> Result<String> {
123        let msg = create_request(Operation::StringRemove, vec![key.to_string()]);
124
125        self.request(&msg).await
126    }
127
128    /// Sends a request to the server to increment a value in the counter store
129    ///
130    /// # Example
131    ///
132    /// ```rust,no_run
133    /// use rubin::net::client::RubinClient;
134    ///
135    /// #[tokio::main]
136    /// async fn main() -> std::io::Result<()> {
137    ///     let client = RubinClient::new("127.0.0.1", 9876);
138    ///     let result = client.incr("view-counter").await?;
139    ///
140    ///     Ok(())
141    /// }
142    /// ```
143    pub async fn incr(&self, key: &str) -> Result<String> {
144        let msg = create_request(Operation::Incr, vec![key.to_string()]);
145
146        self.request(&msg).await
147    }
148
149    /// Sends a request to the server to decrement a value in the counter store
150    ///
151    /// # Example
152    ///
153    /// ```rust,no_run
154    /// use rubin::net::client::RubinClient;
155    ///
156    /// #[tokio::main]
157    /// async fn main() -> std::io::Result<()> {
158    ///     let client = RubinClient::new("127.0.0.1", 9876);
159    ///     let result = client.decr("view-counter").await?;
160    ///
161    ///     Ok(())
162    /// }
163    /// ```
164    pub async fn decr(&self, key: &str) -> Result<String> {
165        let msg = create_request(Operation::Decr, vec![key.to_string()]);
166
167        self.request(&msg).await
168    }
169
170    /// Sends a request to the server to clear all keys and values from the string store
171    ///
172    /// # Example
173    ///
174    /// ```rust,no_run
175    /// use rubin::net::client::RubinClient;
176    ///
177    /// #[tokio::main]
178    /// async fn main() -> std::io::Result<()> {
179    ///     let client = RubinClient::new("127.0.0.1", 9876);
180    ///     let result = client.clear_strings().await?;
181    ///
182    ///     Ok(())
183    /// }
184    /// ```
185    pub async fn clear_strings(&self) -> Result<String> {
186        let msg = create_request(Operation::StringClear, vec!["noop".to_string()]);
187
188        self.request(&msg).await
189    }
190
191    /// Sends a request to the server to dump the store out to disk.
192    /// The folder MUST exist on the host.
193    ///
194    /// # Example
195    ///
196    /// ```rust,no_run
197    /// use rubin::net::client::RubinClient;
198    ///
199    /// #[tokio::main]
200    /// async fn main() -> std::io::Result<()> {
201    ///     let client = RubinClient::new("127.0.0.1", 9876);
202    ///     let result = client.dump_store("/path/on/server/to/dump.json").await?;
203    ///
204    ///     Ok(())
205    /// }
206    /// ```
207    pub async fn dump_store(&self, filepath: &str) -> Result<String> {
208        let msg = create_request(Operation::Dump, vec![filepath.to_string()]);
209
210        self.request(&msg).await
211    }
212
213    /// Sends a request to server and parses the response
214    pub async fn request(&self, msg: &str) -> Result<String> {
215        let response = self.send(msg).await?;
216        let contents = parse_response(&response);
217
218        Ok(contents)
219    }
220
221    /// Sends a request to the server, returning the raw response
222    async fn send(&self, msg: &str) -> Result<String> {
223        let mut client = TcpStream::connect(&self.address).await?;
224        client.write_all(msg.as_bytes()).await?;
225
226        let mut buffer = [0; 4096];
227        let n_bytes = client.read(&mut buffer).await?;
228        if n_bytes == 0 {
229            return Ok(String::from(""));
230        }
231
232        let response = String::from_utf8_lossy(&buffer[..n_bytes]);
233
234        Ok(response.to_string())
235    }
236}