├── .env
├── .github
├── dependabot.yml
└── workflows
│ └── ci.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── CHANGELOG.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── Makefile
├── README.md
├── examples
├── hello.rs
├── ping.rs
├── pub.rs
└── sub.rs
├── rust-toolchain.toml
├── src
├── bin
│ ├── cli.rs
│ └── server.rs
├── client
│ ├── cli.rs
│ ├── cmd.rs
│ ├── mod.rs
│ └── subscriber.rs
├── cmd
│ ├── get.rs
│ ├── mod.rs
│ ├── ping.rs
│ ├── publish.rs
│ ├── set.rs
│ ├── subscribe.rs
│ ├── unknown.rs
│ └── unsubscribe.rs
├── config.rs
├── connection
│ ├── connect.rs
│ ├── frame.rs
│ ├── mod.rs
│ └── parse.rs
├── consts.rs
├── error.rs
├── lib.rs
├── logger.rs
├── server
│ ├── handler.rs
│ ├── listener.rs
│ ├── mod.rs
│ └── shutdown.rs
└── storage
│ ├── db.rs
│ ├── mod.rs
│ ├── store.rs
│ └── traits.rs
└── tests
├── client.rs
└── server.rs
/.env:
--------------------------------------------------------------------------------
1 | LOG_LEVEL=INFO
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "cargo"
4 | # Look for `Cargo.toml` and `Cargo.lock` in the root directory
5 | directory: "/"
6 | # Check for updates every Monday
7 | schedule:
8 | interval: "weekly"
9 | open-pull-requests-limit: 10
10 | - package-ecosystem: "github-actions"
11 | directory: "/"
12 | # Check for updates every Monday
13 | schedule:
14 | interval: "weekly"
15 | open-pull-requests-limit: 10
16 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI # Continuous Integration
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | paths-ignore:
7 | - '**.md'
8 | pull_request:
9 | paths-ignore:
10 | - '**.md'
11 |
12 | env:
13 | RUST_TOOLCHAIN: stable
14 | TOOLCHAIN_PROFILE: minimal
15 |
16 | jobs:
17 | lints:
18 | name: Run cargo fmt and cargo clippy
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout sources
22 | uses: actions/checkout@v3
23 | - name: Install toolchain
24 | uses: actions-rs/toolchain@v1
25 | with:
26 | profile: ${{ env.TOOLCHAIN_PROFILE }}
27 | toolchain: ${{ env.RUST_TOOLCHAIN }}
28 | override: true
29 | components: rustfmt, clippy
30 | - name: Cache
31 | uses: Swatinem/rust-cache@v2
32 | - name: Run cargo fmt
33 | uses: actions-rs/cargo@v1
34 | with:
35 | command: fmt
36 | args: --all -- --check
37 | - name: Run cargo clippy
38 | uses: actions-rs/cargo@v1
39 | with:
40 | command: clippy
41 | args: -- -D warnings
42 | test:
43 | name: Run cargo test
44 | runs-on: ubuntu-latest
45 | steps:
46 | - name: Checkout sources
47 | uses: actions/checkout@v3
48 | - name: Install toolchain
49 | uses: actions-rs/toolchain@v1
50 | with:
51 | profile: ${{ env.TOOLCHAIN_PROFILE }}
52 | toolchain: ${{ env.RUST_TOOLCHAIN }}
53 | override: true
54 | - name: Cache
55 | uses: Swatinem/rust-cache@v2
56 | - name: Run cargo test
57 | uses: actions-rs/cargo@v1
58 | env:
59 | RUST_TEST_THREADS: 8
60 | with:
61 | command: test
62 | args: --all-features
63 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Rust template
2 | # Generated by Cargo
3 | # will have compiled files and executables
4 | debug/
5 | target/
6 |
7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
9 | #Cargo.lock
10 |
11 |
12 | # These are backup files generated by rustfmt
13 | **/*.rs.bk
14 |
15 | ### Example user template template
16 | ### Example user template
17 |
18 | # IntelliJ project files
19 | .idea
20 | *.iml
21 | out
22 | gen
23 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: v4.0.1
4 | hooks:
5 | - id: check-merge-conflict
6 | - id: check-toml
7 | - id: check-yaml
8 | - id: end-of-file-fixer
9 | - id: trailing-whitespace
10 | args: [ --markdown-linebreak-ext=md ]
11 |
12 | - repo: local
13 | hooks:
14 | - id: make-fmt
15 | name: make fmt
16 | entry: make fmt
17 | language: system
18 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "async-stream"
7 | version = "0.3.3"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
10 | dependencies = [
11 | "async-stream-impl",
12 | "futures-core",
13 | ]
14 |
15 | [[package]]
16 | name = "async-stream-impl"
17 | version = "0.3.3"
18 | source = "registry+https://github.com/rust-lang/crates.io-index"
19 | checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
20 | dependencies = [
21 | "proc-macro2",
22 | "quote",
23 | "syn",
24 | ]
25 |
26 | [[package]]
27 | name = "atoi"
28 | version = "2.0.0"
29 | source = "registry+https://github.com/rust-lang/crates.io-index"
30 | checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
31 | dependencies = [
32 | "num-traits",
33 | ]
34 |
35 | [[package]]
36 | name = "atty"
37 | version = "0.2.14"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
40 | dependencies = [
41 | "hermit-abi",
42 | "libc",
43 | "winapi",
44 | ]
45 |
46 | [[package]]
47 | name = "autocfg"
48 | version = "1.1.0"
49 | source = "registry+https://github.com/rust-lang/crates.io-index"
50 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
51 |
52 | [[package]]
53 | name = "bitflags"
54 | version = "1.3.2"
55 | source = "registry+https://github.com/rust-lang/crates.io-index"
56 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
57 |
58 | [[package]]
59 | name = "bytes"
60 | version = "1.3.0"
61 | source = "registry+https://github.com/rust-lang/crates.io-index"
62 | checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
63 |
64 | [[package]]
65 | name = "cfg-if"
66 | version = "1.0.0"
67 | source = "registry+https://github.com/rust-lang/crates.io-index"
68 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
69 |
70 | [[package]]
71 | name = "clap"
72 | version = "3.2.23"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 | checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
75 | dependencies = [
76 | "atty",
77 | "bitflags",
78 | "clap_derive",
79 | "clap_lex",
80 | "indexmap",
81 | "once_cell",
82 | "strsim",
83 | "termcolor",
84 | "textwrap",
85 | ]
86 |
87 | [[package]]
88 | name = "clap_derive"
89 | version = "3.2.18"
90 | source = "registry+https://github.com/rust-lang/crates.io-index"
91 | checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
92 | dependencies = [
93 | "heck",
94 | "proc-macro-error",
95 | "proc-macro2",
96 | "quote",
97 | "syn",
98 | ]
99 |
100 | [[package]]
101 | name = "clap_lex"
102 | version = "0.2.4"
103 | source = "registry+https://github.com/rust-lang/crates.io-index"
104 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
105 | dependencies = [
106 | "os_str_bytes",
107 | ]
108 |
109 | [[package]]
110 | name = "dotenv"
111 | version = "0.15.0"
112 | source = "registry+https://github.com/rust-lang/crates.io-index"
113 | checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
114 |
115 | [[package]]
116 | name = "futures-core"
117 | version = "0.3.25"
118 | source = "registry+https://github.com/rust-lang/crates.io-index"
119 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
120 |
121 | [[package]]
122 | name = "getrandom"
123 | version = "0.2.8"
124 | source = "registry+https://github.com/rust-lang/crates.io-index"
125 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
126 | dependencies = [
127 | "cfg-if",
128 | "libc",
129 | "wasi",
130 | ]
131 |
132 | [[package]]
133 | name = "hashbrown"
134 | version = "0.12.3"
135 | source = "registry+https://github.com/rust-lang/crates.io-index"
136 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
137 |
138 | [[package]]
139 | name = "heck"
140 | version = "0.4.0"
141 | source = "registry+https://github.com/rust-lang/crates.io-index"
142 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
143 |
144 | [[package]]
145 | name = "hermit-abi"
146 | version = "0.1.19"
147 | source = "registry+https://github.com/rust-lang/crates.io-index"
148 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
149 | dependencies = [
150 | "libc",
151 | ]
152 |
153 | [[package]]
154 | name = "indexmap"
155 | version = "1.9.2"
156 | source = "registry+https://github.com/rust-lang/crates.io-index"
157 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
158 | dependencies = [
159 | "autocfg",
160 | "hashbrown",
161 | ]
162 |
163 | [[package]]
164 | name = "libc"
165 | version = "0.2.137"
166 | source = "registry+https://github.com/rust-lang/crates.io-index"
167 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
168 |
169 | [[package]]
170 | name = "lock_api"
171 | version = "0.4.9"
172 | source = "registry+https://github.com/rust-lang/crates.io-index"
173 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
174 | dependencies = [
175 | "autocfg",
176 | "scopeguard",
177 | ]
178 |
179 | [[package]]
180 | name = "log"
181 | version = "0.4.17"
182 | source = "registry+https://github.com/rust-lang/crates.io-index"
183 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
184 | dependencies = [
185 | "cfg-if",
186 | ]
187 |
188 | [[package]]
189 | name = "memchr"
190 | version = "2.5.0"
191 | source = "registry+https://github.com/rust-lang/crates.io-index"
192 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
193 |
194 | [[package]]
195 | name = "mini-redis"
196 | version = "0.1.0"
197 | dependencies = [
198 | "async-stream",
199 | "atoi",
200 | "bytes",
201 | "clap",
202 | "dotenv",
203 | "log",
204 | "rand",
205 | "thiserror",
206 | "tokio",
207 | "tokio-stream",
208 | ]
209 |
210 | [[package]]
211 | name = "mio"
212 | version = "0.8.5"
213 | source = "registry+https://github.com/rust-lang/crates.io-index"
214 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
215 | dependencies = [
216 | "libc",
217 | "log",
218 | "wasi",
219 | "windows-sys",
220 | ]
221 |
222 | [[package]]
223 | name = "num-traits"
224 | version = "0.2.15"
225 | source = "registry+https://github.com/rust-lang/crates.io-index"
226 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
227 | dependencies = [
228 | "autocfg",
229 | ]
230 |
231 | [[package]]
232 | name = "num_cpus"
233 | version = "1.14.0"
234 | source = "registry+https://github.com/rust-lang/crates.io-index"
235 | checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
236 | dependencies = [
237 | "hermit-abi",
238 | "libc",
239 | ]
240 |
241 | [[package]]
242 | name = "once_cell"
243 | version = "1.16.0"
244 | source = "registry+https://github.com/rust-lang/crates.io-index"
245 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
246 |
247 | [[package]]
248 | name = "os_str_bytes"
249 | version = "6.4.1"
250 | source = "registry+https://github.com/rust-lang/crates.io-index"
251 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
252 |
253 | [[package]]
254 | name = "parking_lot"
255 | version = "0.12.1"
256 | source = "registry+https://github.com/rust-lang/crates.io-index"
257 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
258 | dependencies = [
259 | "lock_api",
260 | "parking_lot_core",
261 | ]
262 |
263 | [[package]]
264 | name = "parking_lot_core"
265 | version = "0.9.5"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
268 | dependencies = [
269 | "cfg-if",
270 | "libc",
271 | "redox_syscall",
272 | "smallvec",
273 | "windows-sys",
274 | ]
275 |
276 | [[package]]
277 | name = "pin-project-lite"
278 | version = "0.2.9"
279 | source = "registry+https://github.com/rust-lang/crates.io-index"
280 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
281 |
282 | [[package]]
283 | name = "ppv-lite86"
284 | version = "0.2.17"
285 | source = "registry+https://github.com/rust-lang/crates.io-index"
286 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
287 |
288 | [[package]]
289 | name = "proc-macro-error"
290 | version = "1.0.4"
291 | source = "registry+https://github.com/rust-lang/crates.io-index"
292 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
293 | dependencies = [
294 | "proc-macro-error-attr",
295 | "proc-macro2",
296 | "quote",
297 | "syn",
298 | "version_check",
299 | ]
300 |
301 | [[package]]
302 | name = "proc-macro-error-attr"
303 | version = "1.0.4"
304 | source = "registry+https://github.com/rust-lang/crates.io-index"
305 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
306 | dependencies = [
307 | "proc-macro2",
308 | "quote",
309 | "version_check",
310 | ]
311 |
312 | [[package]]
313 | name = "proc-macro2"
314 | version = "1.0.47"
315 | source = "registry+https://github.com/rust-lang/crates.io-index"
316 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
317 | dependencies = [
318 | "unicode-ident",
319 | ]
320 |
321 | [[package]]
322 | name = "quote"
323 | version = "1.0.21"
324 | source = "registry+https://github.com/rust-lang/crates.io-index"
325 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
326 | dependencies = [
327 | "proc-macro2",
328 | ]
329 |
330 | [[package]]
331 | name = "rand"
332 | version = "0.8.5"
333 | source = "registry+https://github.com/rust-lang/crates.io-index"
334 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
335 | dependencies = [
336 | "libc",
337 | "rand_chacha",
338 | "rand_core",
339 | ]
340 |
341 | [[package]]
342 | name = "rand_chacha"
343 | version = "0.3.1"
344 | source = "registry+https://github.com/rust-lang/crates.io-index"
345 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
346 | dependencies = [
347 | "ppv-lite86",
348 | "rand_core",
349 | ]
350 |
351 | [[package]]
352 | name = "rand_core"
353 | version = "0.6.4"
354 | source = "registry+https://github.com/rust-lang/crates.io-index"
355 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
356 | dependencies = [
357 | "getrandom",
358 | ]
359 |
360 | [[package]]
361 | name = "redox_syscall"
362 | version = "0.2.16"
363 | source = "registry+https://github.com/rust-lang/crates.io-index"
364 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
365 | dependencies = [
366 | "bitflags",
367 | ]
368 |
369 | [[package]]
370 | name = "scopeguard"
371 | version = "1.1.0"
372 | source = "registry+https://github.com/rust-lang/crates.io-index"
373 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
374 |
375 | [[package]]
376 | name = "signal-hook-registry"
377 | version = "1.4.0"
378 | source = "registry+https://github.com/rust-lang/crates.io-index"
379 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
380 | dependencies = [
381 | "libc",
382 | ]
383 |
384 | [[package]]
385 | name = "smallvec"
386 | version = "1.10.0"
387 | source = "registry+https://github.com/rust-lang/crates.io-index"
388 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
389 |
390 | [[package]]
391 | name = "socket2"
392 | version = "0.4.7"
393 | source = "registry+https://github.com/rust-lang/crates.io-index"
394 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
395 | dependencies = [
396 | "libc",
397 | "winapi",
398 | ]
399 |
400 | [[package]]
401 | name = "strsim"
402 | version = "0.10.0"
403 | source = "registry+https://github.com/rust-lang/crates.io-index"
404 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
405 |
406 | [[package]]
407 | name = "syn"
408 | version = "1.0.104"
409 | source = "registry+https://github.com/rust-lang/crates.io-index"
410 | checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce"
411 | dependencies = [
412 | "proc-macro2",
413 | "quote",
414 | "unicode-ident",
415 | ]
416 |
417 | [[package]]
418 | name = "termcolor"
419 | version = "1.1.3"
420 | source = "registry+https://github.com/rust-lang/crates.io-index"
421 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
422 | dependencies = [
423 | "winapi-util",
424 | ]
425 |
426 | [[package]]
427 | name = "textwrap"
428 | version = "0.16.0"
429 | source = "registry+https://github.com/rust-lang/crates.io-index"
430 | checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
431 |
432 | [[package]]
433 | name = "thiserror"
434 | version = "1.0.38"
435 | source = "registry+https://github.com/rust-lang/crates.io-index"
436 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
437 | dependencies = [
438 | "thiserror-impl",
439 | ]
440 |
441 | [[package]]
442 | name = "thiserror-impl"
443 | version = "1.0.38"
444 | source = "registry+https://github.com/rust-lang/crates.io-index"
445 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
446 | dependencies = [
447 | "proc-macro2",
448 | "quote",
449 | "syn",
450 | ]
451 |
452 | [[package]]
453 | name = "tokio"
454 | version = "1.23.0"
455 | source = "registry+https://github.com/rust-lang/crates.io-index"
456 | checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
457 | dependencies = [
458 | "autocfg",
459 | "bytes",
460 | "libc",
461 | "memchr",
462 | "mio",
463 | "num_cpus",
464 | "parking_lot",
465 | "pin-project-lite",
466 | "signal-hook-registry",
467 | "socket2",
468 | "tokio-macros",
469 | "windows-sys",
470 | ]
471 |
472 | [[package]]
473 | name = "tokio-macros"
474 | version = "1.8.0"
475 | source = "registry+https://github.com/rust-lang/crates.io-index"
476 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
477 | dependencies = [
478 | "proc-macro2",
479 | "quote",
480 | "syn",
481 | ]
482 |
483 | [[package]]
484 | name = "tokio-stream"
485 | version = "0.1.11"
486 | source = "registry+https://github.com/rust-lang/crates.io-index"
487 | checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
488 | dependencies = [
489 | "futures-core",
490 | "pin-project-lite",
491 | "tokio",
492 | ]
493 |
494 | [[package]]
495 | name = "unicode-ident"
496 | version = "1.0.5"
497 | source = "registry+https://github.com/rust-lang/crates.io-index"
498 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
499 |
500 | [[package]]
501 | name = "version_check"
502 | version = "0.9.4"
503 | source = "registry+https://github.com/rust-lang/crates.io-index"
504 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
505 |
506 | [[package]]
507 | name = "wasi"
508 | version = "0.11.0+wasi-snapshot-preview1"
509 | source = "registry+https://github.com/rust-lang/crates.io-index"
510 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
511 |
512 | [[package]]
513 | name = "winapi"
514 | version = "0.3.9"
515 | source = "registry+https://github.com/rust-lang/crates.io-index"
516 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
517 | dependencies = [
518 | "winapi-i686-pc-windows-gnu",
519 | "winapi-x86_64-pc-windows-gnu",
520 | ]
521 |
522 | [[package]]
523 | name = "winapi-i686-pc-windows-gnu"
524 | version = "0.4.0"
525 | source = "registry+https://github.com/rust-lang/crates.io-index"
526 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
527 |
528 | [[package]]
529 | name = "winapi-util"
530 | version = "0.1.5"
531 | source = "registry+https://github.com/rust-lang/crates.io-index"
532 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
533 | dependencies = [
534 | "winapi",
535 | ]
536 |
537 | [[package]]
538 | name = "winapi-x86_64-pc-windows-gnu"
539 | version = "0.4.0"
540 | source = "registry+https://github.com/rust-lang/crates.io-index"
541 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
542 |
543 | [[package]]
544 | name = "windows-sys"
545 | version = "0.42.0"
546 | source = "registry+https://github.com/rust-lang/crates.io-index"
547 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
548 | dependencies = [
549 | "windows_aarch64_gnullvm",
550 | "windows_aarch64_msvc",
551 | "windows_i686_gnu",
552 | "windows_i686_msvc",
553 | "windows_x86_64_gnu",
554 | "windows_x86_64_gnullvm",
555 | "windows_x86_64_msvc",
556 | ]
557 |
558 | [[package]]
559 | name = "windows_aarch64_gnullvm"
560 | version = "0.42.0"
561 | source = "registry+https://github.com/rust-lang/crates.io-index"
562 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
563 |
564 | [[package]]
565 | name = "windows_aarch64_msvc"
566 | version = "0.42.0"
567 | source = "registry+https://github.com/rust-lang/crates.io-index"
568 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
569 |
570 | [[package]]
571 | name = "windows_i686_gnu"
572 | version = "0.42.0"
573 | source = "registry+https://github.com/rust-lang/crates.io-index"
574 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
575 |
576 | [[package]]
577 | name = "windows_i686_msvc"
578 | version = "0.42.0"
579 | source = "registry+https://github.com/rust-lang/crates.io-index"
580 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
581 |
582 | [[package]]
583 | name = "windows_x86_64_gnu"
584 | version = "0.42.0"
585 | source = "registry+https://github.com/rust-lang/crates.io-index"
586 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
587 |
588 | [[package]]
589 | name = "windows_x86_64_gnullvm"
590 | version = "0.42.0"
591 | source = "registry+https://github.com/rust-lang/crates.io-index"
592 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
593 |
594 | [[package]]
595 | name = "windows_x86_64_msvc"
596 | version = "0.42.0"
597 | source = "registry+https://github.com/rust-lang/crates.io-index"
598 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
599 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "mini-redis"
3 | version = "0.1.0"
4 | edition = "2018"
5 | description = "A mini-redis learn from tokio."
6 | repository = "https://github.com/JasonkayZK/mini-redis"
7 | license-file = "LICENSE"
8 |
9 | [[bin]]
10 | name = "mini-redis-cli"
11 | path = "src/bin/cli.rs"
12 |
13 | [[bin]]
14 | name = "mini-redis-server"
15 | path = "src/bin/server.rs"
16 |
17 | [dependencies]
18 | async-stream = "0.3.0"
19 | atoi = "2.0.0"
20 | bytes = "1"
21 | rand = "0.8.5"
22 | clap = { version = "3.2.23", features = ["derive"] }
23 | tokio = { version = "1", features = ["full"] }
24 | tokio-stream = "0.1"
25 | thiserror = "1.0.38"
26 | log = "0.4"
27 | dotenv = "0.15"
28 |
29 | [dev-dependencies]
30 | # Enable test-utilities in dev mode only. This is mostly for tests.
31 | tokio = { version = "1", features = ["test-util"] }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 JasonkayZK <271226192@qq.com>
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: fmt clippy clean build pack all ci
2 |
3 | all: clean fmt clippy pack
4 |
5 | ci: fmt clippy
6 |
7 | fmt:
8 | cargo fmt --all --
9 |
10 | clippy:
11 | cargo clippy -- -D warnings
12 |
13 | clean:
14 | rm -rf ./target
15 |
16 | build:
17 | cargo build
18 |
19 | pack:
20 | cargo build --release
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mini-redis
2 |
3 | [](https://github.com/JasonkayZK/mini-redis/actions)
4 |
5 |
6 |
7 | ## **相关文章**
8 |
9 | 系列文章:
10 |
11 | - [《mini-redis项目-1-简介》](https://jasonkayzk.github.io/2022/12/05/mini-redis项目-1-简介/)
12 | - [《mini-redis项目-2-存储层》](https://jasonkayzk.github.io/2022/12/05/mini-redis项目-2-存储层/)
13 | - [《mini-redis项目-3-连接层》](https://jasonkayzk.github.io/2022/12/05/mini-redis项目-3-连接层/)
14 | - [《mini-redis项目-4-服务端》](https://jasonkayzk.github.io/2022/12/06/mini-redis项目-4-服务端/)
15 | - [《mini-redis项目-5-客户端》](https://jasonkayzk.github.io/2022/12/07/mini-redis项目-5-客户端/)
16 | - [《mini-redis项目-6-测试与示例》](https://jasonkayzk.github.io/2022/12/07/mini-redis项目-6-测试与示例/)
17 |
18 |
19 | ## **前言**
20 |
21 | tokio 官方文档如下:
22 |
23 | - https://tokio.rs/tokio/tutorial
24 |
25 | 项目的目录结构如下:
26 |
27 | ```bash
28 | $ tree ./src/
29 | .
30 | ├── bin
31 | │ ├── cli.rs
32 | │ └── server.rs
33 | ├── client
34 | │ ├── cli.rs
35 | │ ├── cmd.rs
36 | │ ├── mod.rs
37 | │ └── subscriber.rs
38 | ├── cmd
39 | │ ├── get.rs
40 | │ ├── mod.rs
41 | │ ├── ping.rs
42 | │ ├── publish.rs
43 | │ ├── set.rs
44 | │ ├── subscribe.rs
45 | │ ├── unknown.rs
46 | │ └── unsubscribe.rs
47 | ├── config.rs
48 | ├── connection
49 | │ ├── connect.rs
50 | │ ├── frame.rs
51 | │ ├── mod.rs
52 | │ └── parse.rs
53 | ├── consts.rs
54 | ├── error.rs
55 | ├── lib.rs
56 | ├── logger.rs
57 | ├── server
58 | │ ├── handler.rs
59 | │ ├── listener.rs
60 | │ ├── mod.rs
61 | │ └── shutdown.rs
62 | └── storage
63 | ├── db.rs
64 | ├── mod.rs
65 | ├── store.rs
66 | └── traits.rs
67 | ```
68 |
69 | 其中:
70 |
71 | - `bin` 目录:server 和 cli 的命令行入口可执行文件;
72 | - `client` 目录:客户端具体实现逻辑;
73 | - `server` 目录:服务端具体实现逻辑;
74 | - `cmd` 目录:mini-redis 相关命令实现;
75 | - `connection` 目录:客户端、服务端异步连接实现;
76 | - `storage` 目录:kv、subscribe 存储实现(本例中直接使用 HashMap 实现,实际生产环境多用 LSM-Tree);
77 | - `config.rs`:mini-redis 配置相关;
78 | - `consts.rs`:mini-redis 常量配置相关;
79 | - `error.rs`:mini-redis 错误定义;
80 | - `logger.rs`:mini-redis 日志配置;
81 | - `lib.rs`:mini-redis 库入口;
82 |
83 | 总体分为下面几个部分:
84 |
85 | - **存储实现;**
86 | - **连接实现;**
87 | - **具体命令实现**
88 | - **客户端、服务端实现;**
89 |
90 |
91 |
92 | ## **基本使用**
93 |
94 | 首先启动server:
95 |
96 | ```bash
97 | $ cargo run --bin mini-redis-server
98 |
99 | [ INFO]: mini_redis::server - mini-redis server started listen on: 0.0.0.0:6379
100 | [ INFO]: mini_redis::server::listener - server started, accepting inbound connections
101 | ```
102 |
103 | 随后可以使用 client:
104 |
105 | ```bash
106 | $ cargo run --bin mini-redis-cli
107 |
108 | mini-redis-cli 0.1.0
109 | Issue Redis commands
110 |
111 | USAGE:
112 | mini-redis-cli [OPTIONS]
113 |
114 | OPTIONS:
115 | -h, --help Print help information
116 | --hostname [default: 127.0.0.1]
117 | --port [default: 6379]
118 | -V, --version Print version information
119 |
120 | SUBCOMMANDS:
121 | get Get the value of key
122 | help Print this message or the help of the given subcommand(s)
123 | ping
124 | publish Publisher to send a message to a specific channel
125 | set Set key to hold the string value
126 | subscribe Subscribe a client to a specific channel or channels
127 | ```
128 |
129 |
130 |
131 | ping命令测试:
132 |
133 | ```bash
134 | $ cargo run --bin mini-redis-cli ping
135 | "PONG"
136 |
137 | $ cargo run --bin mini-redis-cli ping abc
138 | "abc"
139 | ```
140 |
141 |
142 |
143 | get/set 测试:
144 |
145 | ```bash
146 | $ cargo run --bin mini-redis-cli get foo
147 | (nil)
148 |
149 | $ cargo run --bin mini-redis-cli set foo 123
150 | OK
151 |
152 | $ cargo run --bin mini-redis-cli get foo
153 | "123"
154 | ```
155 |
156 | 过期键测试,设置 5s 过期:
157 |
158 | ```bash
159 | $ cargo run --bin mini-redis-cli set foo 123 5000
160 | ```
161 |
162 | 获取:
163 |
164 | ```bash
165 | $ cargo run --bin mini-redis-cli get foo
166 | "123"
167 |
168 | $ cargo run --bin mini-redis-cli get foo
169 | (nil)
170 | ```
171 |
172 | 5s后,获取不到 key 值了!
173 |
174 |
175 |
176 | pub/sub 测试;
177 |
178 | 启动三个 subscribe,订阅同一个 channel,ch1:
179 |
180 | ```bash
181 | $ cargo run --bin mini-redis-cli subscribe ch1
182 |
183 | $ cargo run --bin mini-redis-cli subscribe ch1
184 |
185 | $ cargo run --bin mini-redis-cli subscribe ch1
186 | ```
187 |
188 | 向 ch1 发布消息:
189 |
190 | ```bash
191 | $ cargo run --bin mini-redis-cli publish ch1 a-message
192 | Publish OK
193 | ```
194 |
195 | 其他订阅者均收到消息:
196 |
197 | ```
198 | got message from the channel: ch1; message = b"a-message"
199 | ```
200 |
201 |
202 |
203 | 错误命令测试:
204 |
205 | ```bash
206 | $ cargo run --bin mini-redis-cli ping get foo
207 |
208 | error: Found argument 'foo' which wasn't expected, or isn't valid in this context
209 | ```
210 |
--------------------------------------------------------------------------------
/examples/hello.rs:
--------------------------------------------------------------------------------
1 | //! Hello world client.
2 | //!
3 | //! A simple client that connects to a mini-redis server, sets key "hello" with value "world",
4 | //! and gets it from the server after
5 | //!
6 | //! You can test this out by running:
7 | //!
8 | //! cargo run --bin mini-redis-server
9 | //!
10 | //! And then in another terminal run:
11 | //!
12 | //! cargo run --example hello_world
13 |
14 | use mini_redis::client;
15 | use mini_redis::error::MiniRedisClientError;
16 |
17 | #[tokio::main]
18 | pub async fn main() -> Result<(), MiniRedisClientError> {
19 | // Open a connection to the mini-redis address.
20 | let mut client = client::connect("127.0.0.1:6379").await?;
21 |
22 | // Set the key "hello" with value "world"
23 | let result = client.set("hello", "world".into()).await?;
24 | println!("set value to the server success, result: {:?}", result);
25 |
26 | // Get key "hello"
27 | let result = client.get("hello").await?;
28 | println!("got value from the server success, result: {:?}", result);
29 |
30 | Ok(())
31 | }
32 |
--------------------------------------------------------------------------------
/examples/ping.rs:
--------------------------------------------------------------------------------
1 | //! Ping client.
2 | //!
3 | //! A simple client that connects to a mini-redis server, and say `ping`
4 | //!
5 | //! You can test this out by running:
6 | //!
7 | //! cargo run --bin mini-redis-server
8 | //!
9 | //! And then in another terminal run:
10 | //!
11 | //! cargo run --example ping
12 |
13 | use mini_redis::client;
14 | use mini_redis::error::MiniRedisConnectionError;
15 |
16 | #[tokio::main]
17 | pub async fn main() -> Result<(), MiniRedisConnectionError> {
18 | // Open a connection to the mini-redis address.
19 | let mut client = client::connect("127.0.0.1:6379").await?;
20 |
21 | let result = client.ping(None).await?;
22 | println!("empty ping response: {:?}", result);
23 |
24 | let result = client.ping(Some("hello".into())).await?;
25 | println!("bytes ping response: {:?}", result);
26 |
27 | Ok(())
28 | }
29 |
--------------------------------------------------------------------------------
/examples/pub.rs:
--------------------------------------------------------------------------------
1 | //! Publish to a redis channel example.
2 | //!
3 | //! A simple client that connects to a mini-redis server, and
4 | //! publishes a message on `foo` channel
5 | //!
6 | //! You can test this out by running:
7 | //!
8 | //! cargo run --bin mini-redis-server
9 | //!
10 | //! Then in another terminal run:
11 | //!
12 | //! cargo run --example sub
13 | //!
14 | //! And then in another terminal run:
15 | //!
16 | //! cargo run --example pub
17 |
18 | use mini_redis::client;
19 | use mini_redis::error::MiniRedisClientError;
20 |
21 | #[tokio::main]
22 | async fn main() -> Result<(), MiniRedisClientError> {
23 | // Open a connection to the mini-redis address.
24 | let mut client = client::connect("127.0.0.1:6379").await?;
25 |
26 | // publish message `bar` on channel foo
27 | let res = client.publish("foo", "bar".into()).await?;
28 |
29 | println!("pushed message success, res: {:?}", res);
30 |
31 | Ok(())
32 | }
33 |
--------------------------------------------------------------------------------
/examples/sub.rs:
--------------------------------------------------------------------------------
1 | //! Subscribe to a redis channel example.
2 | //!
3 | //! A simple client that connects to a mini-redis server, subscribes to "foo" and "bar" channels
4 | //! and awaits messages published on those channels
5 | //!
6 | //! You can test this out by running:
7 | //!
8 | //! cargo run --bin mini-redis-server
9 | //!
10 | //! Then in another terminal run:
11 | //!
12 | //! cargo run --example sub
13 | //!
14 | //! And then in another terminal run:
15 | //!
16 | //! cargo run --example pub
17 |
18 | use mini_redis::client;
19 | use mini_redis::error::MiniRedisClientError;
20 |
21 | #[tokio::main]
22 | pub async fn main() -> Result<(), MiniRedisClientError> {
23 | // Open a connection to the mini-redis address.
24 | let client = client::connect("127.0.0.1:6379").await?;
25 |
26 | // subscribe to channel foo
27 | let mut subscriber = client.subscribe(vec!["foo".into()]).await?;
28 |
29 | // await messages on channel foo
30 | if let Some(msg) = subscriber.next_message().await? {
31 | println!(
32 | "got message from the channel: {}; message = {:?}",
33 | msg.channel, msg.content
34 | );
35 | }
36 |
37 | Ok(())
38 | }
39 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "stable"
3 |
--------------------------------------------------------------------------------
/src/bin/cli.rs:
--------------------------------------------------------------------------------
1 | use clap::Parser;
2 | use dotenv::dotenv;
3 | use log::debug;
4 |
5 | use mini_redis::client::cmd::Command;
6 | use mini_redis::consts::DEFAULT_PORT;
7 | use mini_redis::error::{MiniRedisClientError, MiniRedisConnectionError};
8 | use mini_redis::{client, logger};
9 |
10 | #[derive(Parser, Debug)]
11 | #[clap(
12 | name = "mini-redis-cli",
13 | version,
14 | author,
15 | about = "Issue Redis commands"
16 | )]
17 | struct Cli {
18 | #[clap(subcommand)]
19 | command: Command,
20 |
21 | #[clap(name = "hostname", long, default_value = "127.0.0.1")]
22 | host: String,
23 |
24 | #[clap(long, default_value_t = DEFAULT_PORT)]
25 | port: u16,
26 | }
27 |
28 | /// Entry point for CLI tool.
29 | ///
30 | /// The `[tokio::main]` annotation signals that the Tokio runtime should be
31 | /// started when the function is called. The body of the function is executed
32 | /// within the newly spawned runtime.
33 | ///
34 | /// `flavor = "current_thread"` is used here to avoid spawning background
35 | /// threads. The CLI tool use case benefits more by being lighter instead of
36 | /// multi-threaded.
37 | #[tokio::main(flavor = "current_thread")]
38 | async fn main() -> Result<(), MiniRedisClientError> {
39 | dotenv().ok();
40 | logger::init();
41 |
42 | // Parse command line arguments
43 | let cli = Cli::parse();
44 | debug!("get cli: {:?}", cli);
45 |
46 | // Get the remote address to connect to
47 | let addr = format!("{}:{}", cli.host, cli.port);
48 |
49 | // Establish a connection
50 | let mut client = client::connect(&addr).await?;
51 |
52 | // Process the requested command
53 | match cli.command {
54 | Command::Ping { msg } => {
55 | let value = client.ping(msg).await?;
56 | if let Ok(string) = std::str::from_utf8(&value) {
57 | println!("\"{}\"", string);
58 | } else {
59 | println!("{:?}", value);
60 | }
61 | }
62 | Command::Get { key } => {
63 | if let Some(value) = client.get(&key).await? {
64 | if let Ok(string) = std::str::from_utf8(&value) {
65 | println!("\"{}\"", string);
66 | } else {
67 | println!("{:?}", value);
68 | }
69 | } else {
70 | println!("(nil)");
71 | }
72 | }
73 | Command::Set {
74 | key,
75 | value,
76 | expires: None,
77 | } => {
78 | client.set(&key, value).await?;
79 | println!("OK");
80 | }
81 | Command::Set {
82 | key,
83 | value,
84 | expires: Some(expires),
85 | } => {
86 | client.set_expires(&key, value, expires).await?;
87 | println!("OK");
88 | }
89 | Command::Publish { channel, message } => {
90 | client.publish(&channel, message).await?;
91 | println!("Publish OK");
92 | }
93 | Command::Subscribe { channels } => {
94 | if channels.is_empty() {
95 | return Err(MiniRedisConnectionError::InvalidArgument(
96 | "channel(s) must be provided".into(),
97 | )
98 | .into());
99 | }
100 | let mut subscriber = client.subscribe(channels).await?;
101 |
102 | // await messages on channels
103 | while let Some(msg) = subscriber.next_message().await? {
104 | println!(
105 | "got message from the channel: {}; message = {:?}",
106 | msg.channel, msg.content
107 | );
108 | }
109 | }
110 | }
111 |
112 | Ok(())
113 | }
114 |
--------------------------------------------------------------------------------
/src/bin/server.rs:
--------------------------------------------------------------------------------
1 | //! mini-redis server.
2 | //!
3 | //! This file is the entry point for the server implemented in the library. It
4 | //! performs command line parsing and passes the arguments on to
5 | //! `mini_redis::server`.
6 | //!
7 | //! The `clap` crate is used for parsing arguments.
8 |
9 | use clap::Parser;
10 | use dotenv::dotenv;
11 | use tokio::net::TcpListener;
12 | use tokio::signal;
13 |
14 | use mini_redis::consts::DEFAULT_PORT;
15 | use mini_redis::error::MiniRedisServerError;
16 | use mini_redis::{logger, server};
17 |
18 | #[derive(Parser, Debug)]
19 | #[clap(
20 | name = "mini-redis-server",
21 | version,
22 | author,
23 | about = "A mini redis server"
24 | )]
25 | struct Cli {
26 | #[clap(long)]
27 | port: Option,
28 | }
29 |
30 | #[tokio::main]
31 | pub async fn main() -> Result<(), MiniRedisServerError> {
32 | let cli = init();
33 | let port = cli.port.unwrap_or(DEFAULT_PORT);
34 |
35 | // Bind a TCP listener
36 | let listener = TcpListener::bind(&format!("0.0.0.0:{}", port)).await?;
37 |
38 | server::run(listener, signal::ctrl_c()).await;
39 |
40 | Ok(())
41 | }
42 |
43 | fn init() -> Cli {
44 | dotenv().ok();
45 | logger::init();
46 | Cli::parse()
47 | }
48 |
--------------------------------------------------------------------------------
/src/client/cli.rs:
--------------------------------------------------------------------------------
1 | //! Minimal Redis client implementation
2 | //!
3 | //! Provides an async connect and methods for issuing the supported commands.
4 |
5 | use crate::client::subscriber::Subscriber;
6 | use crate::cmd::get::Get;
7 | use bytes::Bytes;
8 | use log::{debug, error};
9 | use std::time::Duration;
10 |
11 | use crate::cmd::ping::Ping;
12 | use crate::cmd::publish::Publish;
13 | use crate::cmd::set::Set;
14 | use crate::cmd::subscribe::Subscribe;
15 | use crate::connection::connect::Connection;
16 | use crate::connection::frame::Frame;
17 | use crate::error::MiniRedisConnectionError;
18 |
19 | /// Established connection with a Redis server.
20 | ///
21 | /// Backed by a single `TcpStream`, `Client` provides basic network client
22 | /// functionality (no pooling, retrying, ...). Connections are established using
23 | /// the [`connect`](fn@connect) function.
24 | ///
25 | /// Requests are issued using the various methods of `Client`.
26 | pub struct Client {
27 | /// The TCP connection decorated with the redis protocol encoder / decoder
28 | /// implemented using a buffered `TcpStream`.
29 | ///
30 | /// When `Listener` receives an inbound connection, the `TcpStream` is
31 | /// passed to `Connection::new`, which initializes the associated buffers.
32 | /// `Connection` allows the handler to operate at the "frame" level and keep
33 | /// the byte level protocol parsing details encapsulated in `Connection`.
34 | pub(crate) connection: Connection,
35 | }
36 |
37 | impl Client {
38 | /// Ping to the server.
39 | ///
40 | /// Returns PONG if no argument is provided, otherwise
41 | /// return a copy of the argument as a bulk.
42 | ///
43 | /// This command is often used to test if a connection
44 | /// is still alive, or to measure latency.
45 | ///
46 | /// # Examples
47 | ///
48 | /// Demonstrates basic usage.
49 | /// ```no_run
50 | ///
51 | /// #[tokio::main]
52 | /// async fn main() {
53 | /// let mut client = mini_redis::client::connect("localhost:6379").await.unwrap();
54 | ///
55 | /// let pong = client.ping(None).await.unwrap();
56 | /// assert_eq!(b"PONG", &pong[..]);
57 | /// }
58 | /// ```
59 | pub async fn ping(&mut self, msg: Option) -> Result {
60 | let frame = Ping::new(msg).into_frame()?;
61 | debug!("request: {:?}", frame);
62 |
63 | self.connection.write_frame(&frame).await?;
64 |
65 | match self.read_response().await? {
66 | Frame::Simple(value) => Ok(value.into()),
67 | Frame::Bulk(value) => Ok(value),
68 | frame => Err(MiniRedisConnectionError::CommandExecute(frame.to_string())),
69 | }
70 | }
71 |
72 | /// Get the value of key.
73 | ///
74 | /// If the key does not exist the special value `None` is returned.
75 | ///
76 | /// # Examples
77 | ///
78 | /// Demonstrates basic usage.
79 | ///
80 | /// ```no_run
81 | /// #[tokio::main]
82 | /// async fn main() {
83 | /// let mut client = mini_redis::client::connect("localhost:6379").await.unwrap();
84 | ///
85 | /// let val = client.get("foo").await.unwrap();
86 | /// println!("Got = {:?}", val);
87 | /// }
88 | /// ```
89 | pub async fn get(&mut self, key: &str) -> Result