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}