├── .github
├── dependabot.yml
└── workflows
│ ├── integration.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── Changelog.md
├── LICENSE
├── README.md
├── examples
├── derive_async.rs
├── derive_basic.rs
├── derive_deadpool.rs
├── derive_generic.rs
├── derive_redisjson.rs
├── derive_yaml.rs
├── json_wrapper_basic.rs
└── json_wrapper_modify.rs
├── redis-macros-derive
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
└── src
│ └── lib.rs
├── src
├── json.rs
└── lib.rs
└── tests
├── derive_from_redis_value.rs
├── derive_from_redis_value_redis_yaml.rs
├── derive_to_redis_args.rs
├── derive_to_redis_args_redis_yaml.rs
└── json_wrapper.rs
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "cargo"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | ignore:
8 | - dependency-name: "*"
9 | update-types: ["version-update:semver-patch"]
10 | - package-ecosystem: "cargo"
11 | directory: "/redis-macros-derive"
12 | schedule:
13 | interval: "weekly"
14 | ignore:
15 | - dependency-name: "*"
16 | update-types: ["version-update:semver-patch"]
17 |
--------------------------------------------------------------------------------
/.github/workflows/integration.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 |
3 | name: Integration testing
4 |
5 | jobs:
6 | test:
7 | strategy:
8 | fail-fast: false
9 | matrix:
10 | version: [stable, nightly]
11 | services:
12 | redis:
13 | image: docker.io/redis/redis-stack
14 | ports:
15 | - 6379:6379
16 | name: Test examples with ${{ matrix.version }}
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@v4
20 | - uses: Swatinem/rust-cache@v2
21 | - uses: dtolnay/rust-toolchain@stable
22 | with:
23 | toolchain: ${{ matrix.version }}
24 | - run: cargo test --examples
25 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - v*
5 |
6 | name: Publish to crates.io
7 |
8 | permissions:
9 | contents: write
10 |
11 | jobs:
12 | check:
13 | name: Publish to crates.io
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | - uses: dtolnay/rust-toolchain@stable
18 | with:
19 | toolchain: stable
20 | - name: Hack publish-crates to not fail on paths
21 | run: |
22 | sed 's/, path = ".*"//' -i Cargo.toml
23 | sed '/redis-macros = { path = ".." }/d' -i redis-macros-derive/Cargo.toml
24 | - uses: katyo/publish-crates@v2
25 | with:
26 | path: ./redis-macros-derive
27 | registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
28 | ignore-unpublished-changes: true
29 | args: --allow-dirty
30 | - uses: katyo/publish-crates@v2
31 | with:
32 | path: ./
33 | registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
34 | args: --allow-dirty
35 | - uses: taiki-e/create-gh-release-action@v1
36 | with:
37 | changelog: Changelog.md
38 | token: ${{ secrets.GITHUB_TOKEN }}
39 |
40 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 |
3 | name: Continuous integration
4 |
5 | jobs:
6 | test:
7 | strategy:
8 | fail-fast: false
9 | matrix:
10 | os: [ubuntu-latest, macos-latest, windows-latest]
11 | version: [stable, nightly]
12 | name: Check and test on ${{ matrix.os }} with ${{ matrix.version }}
13 | runs-on: ${{ matrix.os }}
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: Swatinem/rust-cache@v2
17 | - uses: dtolnay/rust-toolchain@stable
18 | with:
19 | toolchain: ${{ matrix.version }}
20 | components: clippy
21 | - run: cargo check
22 | - run: cargo clippy -- -D warnings
23 | - run: cargo test
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.coverage
2 | **/target
3 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "addr2line"
7 | version = "0.24.2"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
10 | dependencies = [
11 | "gimli",
12 | ]
13 |
14 | [[package]]
15 | name = "adler2"
16 | version = "2.0.0"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
19 |
20 | [[package]]
21 | name = "autocfg"
22 | version = "1.4.0"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
25 |
26 | [[package]]
27 | name = "backtrace"
28 | version = "0.3.75"
29 | source = "registry+https://github.com/rust-lang/crates.io-index"
30 | checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
31 | dependencies = [
32 | "addr2line",
33 | "cfg-if",
34 | "libc",
35 | "miniz_oxide",
36 | "object",
37 | "rustc-demangle",
38 | "windows-targets",
39 | ]
40 |
41 | [[package]]
42 | name = "bitflags"
43 | version = "2.9.0"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
46 |
47 | [[package]]
48 | name = "bytes"
49 | version = "1.10.1"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
52 |
53 | [[package]]
54 | name = "cfg-if"
55 | version = "1.0.0"
56 | source = "registry+https://github.com/rust-lang/crates.io-index"
57 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
58 |
59 | [[package]]
60 | name = "combine"
61 | version = "4.6.7"
62 | source = "registry+https://github.com/rust-lang/crates.io-index"
63 | checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
64 | dependencies = [
65 | "bytes",
66 | "futures-core",
67 | "memchr",
68 | "pin-project-lite",
69 | "tokio",
70 | "tokio-util",
71 | ]
72 |
73 | [[package]]
74 | name = "deadpool"
75 | version = "0.12.2"
76 | source = "registry+https://github.com/rust-lang/crates.io-index"
77 | checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f"
78 | dependencies = [
79 | "deadpool-runtime",
80 | "num_cpus",
81 | "tokio",
82 | ]
83 |
84 | [[package]]
85 | name = "deadpool-redis"
86 | version = "0.21.1"
87 | source = "registry+https://github.com/rust-lang/crates.io-index"
88 | checksum = "667d186d69c8b4dd7f8cf1ac71bec88b312e1752f2d1a63028aa9ea124c3f191"
89 | dependencies = [
90 | "deadpool",
91 | "redis",
92 | ]
93 |
94 | [[package]]
95 | name = "deadpool-runtime"
96 | version = "0.1.4"
97 | source = "registry+https://github.com/rust-lang/crates.io-index"
98 | checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
99 | dependencies = [
100 | "tokio",
101 | ]
102 |
103 | [[package]]
104 | name = "displaydoc"
105 | version = "0.2.5"
106 | source = "registry+https://github.com/rust-lang/crates.io-index"
107 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
108 | dependencies = [
109 | "proc-macro2",
110 | "quote",
111 | "syn",
112 | ]
113 |
114 | [[package]]
115 | name = "equivalent"
116 | version = "1.0.2"
117 | source = "registry+https://github.com/rust-lang/crates.io-index"
118 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
119 |
120 | [[package]]
121 | name = "form_urlencoded"
122 | version = "1.2.1"
123 | source = "registry+https://github.com/rust-lang/crates.io-index"
124 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
125 | dependencies = [
126 | "percent-encoding",
127 | ]
128 |
129 | [[package]]
130 | name = "futures-core"
131 | version = "0.3.31"
132 | source = "registry+https://github.com/rust-lang/crates.io-index"
133 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
134 |
135 | [[package]]
136 | name = "futures-sink"
137 | version = "0.3.31"
138 | source = "registry+https://github.com/rust-lang/crates.io-index"
139 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
140 |
141 | [[package]]
142 | name = "futures-task"
143 | version = "0.3.31"
144 | source = "registry+https://github.com/rust-lang/crates.io-index"
145 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
146 |
147 | [[package]]
148 | name = "futures-util"
149 | version = "0.3.31"
150 | source = "registry+https://github.com/rust-lang/crates.io-index"
151 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
152 | dependencies = [
153 | "futures-core",
154 | "futures-sink",
155 | "futures-task",
156 | "pin-project-lite",
157 | "pin-utils",
158 | "slab",
159 | ]
160 |
161 | [[package]]
162 | name = "gimli"
163 | version = "0.31.1"
164 | source = "registry+https://github.com/rust-lang/crates.io-index"
165 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
166 |
167 | [[package]]
168 | name = "hashbrown"
169 | version = "0.15.3"
170 | source = "registry+https://github.com/rust-lang/crates.io-index"
171 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
172 |
173 | [[package]]
174 | name = "hermit-abi"
175 | version = "0.3.9"
176 | source = "registry+https://github.com/rust-lang/crates.io-index"
177 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
178 |
179 | [[package]]
180 | name = "icu_collections"
181 | version = "1.5.0"
182 | source = "registry+https://github.com/rust-lang/crates.io-index"
183 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
184 | dependencies = [
185 | "displaydoc",
186 | "yoke",
187 | "zerofrom",
188 | "zerovec",
189 | ]
190 |
191 | [[package]]
192 | name = "icu_locid"
193 | version = "1.5.0"
194 | source = "registry+https://github.com/rust-lang/crates.io-index"
195 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
196 | dependencies = [
197 | "displaydoc",
198 | "litemap",
199 | "tinystr",
200 | "writeable",
201 | "zerovec",
202 | ]
203 |
204 | [[package]]
205 | name = "icu_locid_transform"
206 | version = "1.5.0"
207 | source = "registry+https://github.com/rust-lang/crates.io-index"
208 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
209 | dependencies = [
210 | "displaydoc",
211 | "icu_locid",
212 | "icu_locid_transform_data",
213 | "icu_provider",
214 | "tinystr",
215 | "zerovec",
216 | ]
217 |
218 | [[package]]
219 | name = "icu_locid_transform_data"
220 | version = "1.5.1"
221 | source = "registry+https://github.com/rust-lang/crates.io-index"
222 | checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
223 |
224 | [[package]]
225 | name = "icu_normalizer"
226 | version = "1.5.0"
227 | source = "registry+https://github.com/rust-lang/crates.io-index"
228 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
229 | dependencies = [
230 | "displaydoc",
231 | "icu_collections",
232 | "icu_normalizer_data",
233 | "icu_properties",
234 | "icu_provider",
235 | "smallvec",
236 | "utf16_iter",
237 | "utf8_iter",
238 | "write16",
239 | "zerovec",
240 | ]
241 |
242 | [[package]]
243 | name = "icu_normalizer_data"
244 | version = "1.5.1"
245 | source = "registry+https://github.com/rust-lang/crates.io-index"
246 | checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
247 |
248 | [[package]]
249 | name = "icu_properties"
250 | version = "1.5.1"
251 | source = "registry+https://github.com/rust-lang/crates.io-index"
252 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
253 | dependencies = [
254 | "displaydoc",
255 | "icu_collections",
256 | "icu_locid_transform",
257 | "icu_properties_data",
258 | "icu_provider",
259 | "tinystr",
260 | "zerovec",
261 | ]
262 |
263 | [[package]]
264 | name = "icu_properties_data"
265 | version = "1.5.1"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
268 |
269 | [[package]]
270 | name = "icu_provider"
271 | version = "1.5.0"
272 | source = "registry+https://github.com/rust-lang/crates.io-index"
273 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
274 | dependencies = [
275 | "displaydoc",
276 | "icu_locid",
277 | "icu_provider_macros",
278 | "stable_deref_trait",
279 | "tinystr",
280 | "writeable",
281 | "yoke",
282 | "zerofrom",
283 | "zerovec",
284 | ]
285 |
286 | [[package]]
287 | name = "icu_provider_macros"
288 | version = "1.5.0"
289 | source = "registry+https://github.com/rust-lang/crates.io-index"
290 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
291 | dependencies = [
292 | "proc-macro2",
293 | "quote",
294 | "syn",
295 | ]
296 |
297 | [[package]]
298 | name = "idna"
299 | version = "1.0.3"
300 | source = "registry+https://github.com/rust-lang/crates.io-index"
301 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
302 | dependencies = [
303 | "idna_adapter",
304 | "smallvec",
305 | "utf8_iter",
306 | ]
307 |
308 | [[package]]
309 | name = "idna_adapter"
310 | version = "1.2.0"
311 | source = "registry+https://github.com/rust-lang/crates.io-index"
312 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
313 | dependencies = [
314 | "icu_normalizer",
315 | "icu_properties",
316 | ]
317 |
318 | [[package]]
319 | name = "indexmap"
320 | version = "2.9.0"
321 | source = "registry+https://github.com/rust-lang/crates.io-index"
322 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
323 | dependencies = [
324 | "equivalent",
325 | "hashbrown",
326 | ]
327 |
328 | [[package]]
329 | name = "itoa"
330 | version = "1.0.15"
331 | source = "registry+https://github.com/rust-lang/crates.io-index"
332 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
333 |
334 | [[package]]
335 | name = "libc"
336 | version = "0.2.172"
337 | source = "registry+https://github.com/rust-lang/crates.io-index"
338 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
339 |
340 | [[package]]
341 | name = "litemap"
342 | version = "0.7.5"
343 | source = "registry+https://github.com/rust-lang/crates.io-index"
344 | checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
345 |
346 | [[package]]
347 | name = "lock_api"
348 | version = "0.4.12"
349 | source = "registry+https://github.com/rust-lang/crates.io-index"
350 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
351 | dependencies = [
352 | "autocfg",
353 | "scopeguard",
354 | ]
355 |
356 | [[package]]
357 | name = "memchr"
358 | version = "2.7.4"
359 | source = "registry+https://github.com/rust-lang/crates.io-index"
360 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
361 |
362 | [[package]]
363 | name = "miniz_oxide"
364 | version = "0.8.8"
365 | source = "registry+https://github.com/rust-lang/crates.io-index"
366 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
367 | dependencies = [
368 | "adler2",
369 | ]
370 |
371 | [[package]]
372 | name = "mio"
373 | version = "1.0.3"
374 | source = "registry+https://github.com/rust-lang/crates.io-index"
375 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
376 | dependencies = [
377 | "libc",
378 | "wasi",
379 | "windows-sys",
380 | ]
381 |
382 | [[package]]
383 | name = "num-bigint"
384 | version = "0.4.6"
385 | source = "registry+https://github.com/rust-lang/crates.io-index"
386 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
387 | dependencies = [
388 | "num-integer",
389 | "num-traits",
390 | ]
391 |
392 | [[package]]
393 | name = "num-integer"
394 | version = "0.1.46"
395 | source = "registry+https://github.com/rust-lang/crates.io-index"
396 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
397 | dependencies = [
398 | "num-traits",
399 | ]
400 |
401 | [[package]]
402 | name = "num-traits"
403 | version = "0.2.19"
404 | source = "registry+https://github.com/rust-lang/crates.io-index"
405 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
406 | dependencies = [
407 | "autocfg",
408 | ]
409 |
410 | [[package]]
411 | name = "num_cpus"
412 | version = "1.16.0"
413 | source = "registry+https://github.com/rust-lang/crates.io-index"
414 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
415 | dependencies = [
416 | "hermit-abi",
417 | "libc",
418 | ]
419 |
420 | [[package]]
421 | name = "object"
422 | version = "0.36.7"
423 | source = "registry+https://github.com/rust-lang/crates.io-index"
424 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
425 | dependencies = [
426 | "memchr",
427 | ]
428 |
429 | [[package]]
430 | name = "parking_lot"
431 | version = "0.12.3"
432 | source = "registry+https://github.com/rust-lang/crates.io-index"
433 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
434 | dependencies = [
435 | "lock_api",
436 | "parking_lot_core",
437 | ]
438 |
439 | [[package]]
440 | name = "parking_lot_core"
441 | version = "0.9.10"
442 | source = "registry+https://github.com/rust-lang/crates.io-index"
443 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
444 | dependencies = [
445 | "cfg-if",
446 | "libc",
447 | "redox_syscall",
448 | "smallvec",
449 | "windows-targets",
450 | ]
451 |
452 | [[package]]
453 | name = "percent-encoding"
454 | version = "2.3.1"
455 | source = "registry+https://github.com/rust-lang/crates.io-index"
456 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
457 |
458 | [[package]]
459 | name = "pin-project-lite"
460 | version = "0.2.16"
461 | source = "registry+https://github.com/rust-lang/crates.io-index"
462 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
463 |
464 | [[package]]
465 | name = "pin-utils"
466 | version = "0.1.0"
467 | source = "registry+https://github.com/rust-lang/crates.io-index"
468 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
469 |
470 | [[package]]
471 | name = "proc-macro2"
472 | version = "1.0.95"
473 | source = "registry+https://github.com/rust-lang/crates.io-index"
474 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
475 | dependencies = [
476 | "unicode-ident",
477 | ]
478 |
479 | [[package]]
480 | name = "quote"
481 | version = "1.0.40"
482 | source = "registry+https://github.com/rust-lang/crates.io-index"
483 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
484 | dependencies = [
485 | "proc-macro2",
486 | ]
487 |
488 | [[package]]
489 | name = "redis"
490 | version = "0.31.0"
491 | source = "registry+https://github.com/rust-lang/crates.io-index"
492 | checksum = "0bc1ea653e0b2e097db3ebb5b7f678be339620b8041f66b30a308c1d45d36a7f"
493 | dependencies = [
494 | "bytes",
495 | "cfg-if",
496 | "combine",
497 | "futures-util",
498 | "itoa",
499 | "num-bigint",
500 | "percent-encoding",
501 | "pin-project-lite",
502 | "ryu",
503 | "serde",
504 | "serde_json",
505 | "sha1_smol",
506 | "socket2",
507 | "tokio",
508 | "tokio-util",
509 | "url",
510 | ]
511 |
512 | [[package]]
513 | name = "redis-macros"
514 | version = "0.5.4"
515 | dependencies = [
516 | "deadpool-redis",
517 | "redis",
518 | "redis-macros-derive",
519 | "serde",
520 | "serde_json",
521 | "serde_yaml",
522 | "tokio",
523 | ]
524 |
525 | [[package]]
526 | name = "redis-macros-derive"
527 | version = "0.5.4"
528 | dependencies = [
529 | "proc-macro2",
530 | "quote",
531 | "redis",
532 | "syn",
533 | ]
534 |
535 | [[package]]
536 | name = "redox_syscall"
537 | version = "0.5.12"
538 | source = "registry+https://github.com/rust-lang/crates.io-index"
539 | checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
540 | dependencies = [
541 | "bitflags",
542 | ]
543 |
544 | [[package]]
545 | name = "rustc-demangle"
546 | version = "0.1.24"
547 | source = "registry+https://github.com/rust-lang/crates.io-index"
548 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
549 |
550 | [[package]]
551 | name = "ryu"
552 | version = "1.0.20"
553 | source = "registry+https://github.com/rust-lang/crates.io-index"
554 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
555 |
556 | [[package]]
557 | name = "scopeguard"
558 | version = "1.2.0"
559 | source = "registry+https://github.com/rust-lang/crates.io-index"
560 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
561 |
562 | [[package]]
563 | name = "serde"
564 | version = "1.0.219"
565 | source = "registry+https://github.com/rust-lang/crates.io-index"
566 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
567 | dependencies = [
568 | "serde_derive",
569 | ]
570 |
571 | [[package]]
572 | name = "serde_derive"
573 | version = "1.0.219"
574 | source = "registry+https://github.com/rust-lang/crates.io-index"
575 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
576 | dependencies = [
577 | "proc-macro2",
578 | "quote",
579 | "syn",
580 | ]
581 |
582 | [[package]]
583 | name = "serde_json"
584 | version = "1.0.140"
585 | source = "registry+https://github.com/rust-lang/crates.io-index"
586 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
587 | dependencies = [
588 | "itoa",
589 | "memchr",
590 | "ryu",
591 | "serde",
592 | ]
593 |
594 | [[package]]
595 | name = "serde_yaml"
596 | version = "0.9.34+deprecated"
597 | source = "registry+https://github.com/rust-lang/crates.io-index"
598 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
599 | dependencies = [
600 | "indexmap",
601 | "itoa",
602 | "ryu",
603 | "serde",
604 | "unsafe-libyaml",
605 | ]
606 |
607 | [[package]]
608 | name = "sha1_smol"
609 | version = "1.0.1"
610 | source = "registry+https://github.com/rust-lang/crates.io-index"
611 | checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
612 |
613 | [[package]]
614 | name = "signal-hook-registry"
615 | version = "1.4.5"
616 | source = "registry+https://github.com/rust-lang/crates.io-index"
617 | checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
618 | dependencies = [
619 | "libc",
620 | ]
621 |
622 | [[package]]
623 | name = "slab"
624 | version = "0.4.9"
625 | source = "registry+https://github.com/rust-lang/crates.io-index"
626 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
627 | dependencies = [
628 | "autocfg",
629 | ]
630 |
631 | [[package]]
632 | name = "smallvec"
633 | version = "1.15.0"
634 | source = "registry+https://github.com/rust-lang/crates.io-index"
635 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
636 |
637 | [[package]]
638 | name = "socket2"
639 | version = "0.5.9"
640 | source = "registry+https://github.com/rust-lang/crates.io-index"
641 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
642 | dependencies = [
643 | "libc",
644 | "windows-sys",
645 | ]
646 |
647 | [[package]]
648 | name = "stable_deref_trait"
649 | version = "1.2.0"
650 | source = "registry+https://github.com/rust-lang/crates.io-index"
651 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
652 |
653 | [[package]]
654 | name = "syn"
655 | version = "2.0.101"
656 | source = "registry+https://github.com/rust-lang/crates.io-index"
657 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
658 | dependencies = [
659 | "proc-macro2",
660 | "quote",
661 | "unicode-ident",
662 | ]
663 |
664 | [[package]]
665 | name = "synstructure"
666 | version = "0.13.2"
667 | source = "registry+https://github.com/rust-lang/crates.io-index"
668 | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
669 | dependencies = [
670 | "proc-macro2",
671 | "quote",
672 | "syn",
673 | ]
674 |
675 | [[package]]
676 | name = "tinystr"
677 | version = "0.7.6"
678 | source = "registry+https://github.com/rust-lang/crates.io-index"
679 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
680 | dependencies = [
681 | "displaydoc",
682 | "zerovec",
683 | ]
684 |
685 | [[package]]
686 | name = "tokio"
687 | version = "1.45.0"
688 | source = "registry+https://github.com/rust-lang/crates.io-index"
689 | checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
690 | dependencies = [
691 | "backtrace",
692 | "bytes",
693 | "libc",
694 | "mio",
695 | "parking_lot",
696 | "pin-project-lite",
697 | "signal-hook-registry",
698 | "socket2",
699 | "tokio-macros",
700 | "windows-sys",
701 | ]
702 |
703 | [[package]]
704 | name = "tokio-macros"
705 | version = "2.5.0"
706 | source = "registry+https://github.com/rust-lang/crates.io-index"
707 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
708 | dependencies = [
709 | "proc-macro2",
710 | "quote",
711 | "syn",
712 | ]
713 |
714 | [[package]]
715 | name = "tokio-util"
716 | version = "0.7.15"
717 | source = "registry+https://github.com/rust-lang/crates.io-index"
718 | checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
719 | dependencies = [
720 | "bytes",
721 | "futures-core",
722 | "futures-sink",
723 | "pin-project-lite",
724 | "tokio",
725 | ]
726 |
727 | [[package]]
728 | name = "unicode-ident"
729 | version = "1.0.18"
730 | source = "registry+https://github.com/rust-lang/crates.io-index"
731 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
732 |
733 | [[package]]
734 | name = "unsafe-libyaml"
735 | version = "0.2.11"
736 | source = "registry+https://github.com/rust-lang/crates.io-index"
737 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
738 |
739 | [[package]]
740 | name = "url"
741 | version = "2.5.4"
742 | source = "registry+https://github.com/rust-lang/crates.io-index"
743 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
744 | dependencies = [
745 | "form_urlencoded",
746 | "idna",
747 | "percent-encoding",
748 | ]
749 |
750 | [[package]]
751 | name = "utf16_iter"
752 | version = "1.0.5"
753 | source = "registry+https://github.com/rust-lang/crates.io-index"
754 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
755 |
756 | [[package]]
757 | name = "utf8_iter"
758 | version = "1.0.4"
759 | source = "registry+https://github.com/rust-lang/crates.io-index"
760 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
761 |
762 | [[package]]
763 | name = "wasi"
764 | version = "0.11.0+wasi-snapshot-preview1"
765 | source = "registry+https://github.com/rust-lang/crates.io-index"
766 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
767 |
768 | [[package]]
769 | name = "windows-sys"
770 | version = "0.52.0"
771 | source = "registry+https://github.com/rust-lang/crates.io-index"
772 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
773 | dependencies = [
774 | "windows-targets",
775 | ]
776 |
777 | [[package]]
778 | name = "windows-targets"
779 | version = "0.52.6"
780 | source = "registry+https://github.com/rust-lang/crates.io-index"
781 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
782 | dependencies = [
783 | "windows_aarch64_gnullvm",
784 | "windows_aarch64_msvc",
785 | "windows_i686_gnu",
786 | "windows_i686_gnullvm",
787 | "windows_i686_msvc",
788 | "windows_x86_64_gnu",
789 | "windows_x86_64_gnullvm",
790 | "windows_x86_64_msvc",
791 | ]
792 |
793 | [[package]]
794 | name = "windows_aarch64_gnullvm"
795 | version = "0.52.6"
796 | source = "registry+https://github.com/rust-lang/crates.io-index"
797 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
798 |
799 | [[package]]
800 | name = "windows_aarch64_msvc"
801 | version = "0.52.6"
802 | source = "registry+https://github.com/rust-lang/crates.io-index"
803 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
804 |
805 | [[package]]
806 | name = "windows_i686_gnu"
807 | version = "0.52.6"
808 | source = "registry+https://github.com/rust-lang/crates.io-index"
809 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
810 |
811 | [[package]]
812 | name = "windows_i686_gnullvm"
813 | version = "0.52.6"
814 | source = "registry+https://github.com/rust-lang/crates.io-index"
815 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
816 |
817 | [[package]]
818 | name = "windows_i686_msvc"
819 | version = "0.52.6"
820 | source = "registry+https://github.com/rust-lang/crates.io-index"
821 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
822 |
823 | [[package]]
824 | name = "windows_x86_64_gnu"
825 | version = "0.52.6"
826 | source = "registry+https://github.com/rust-lang/crates.io-index"
827 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
828 |
829 | [[package]]
830 | name = "windows_x86_64_gnullvm"
831 | version = "0.52.6"
832 | source = "registry+https://github.com/rust-lang/crates.io-index"
833 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
834 |
835 | [[package]]
836 | name = "windows_x86_64_msvc"
837 | version = "0.52.6"
838 | source = "registry+https://github.com/rust-lang/crates.io-index"
839 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
840 |
841 | [[package]]
842 | name = "write16"
843 | version = "1.0.0"
844 | source = "registry+https://github.com/rust-lang/crates.io-index"
845 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
846 |
847 | [[package]]
848 | name = "writeable"
849 | version = "0.5.5"
850 | source = "registry+https://github.com/rust-lang/crates.io-index"
851 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
852 |
853 | [[package]]
854 | name = "yoke"
855 | version = "0.7.5"
856 | source = "registry+https://github.com/rust-lang/crates.io-index"
857 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
858 | dependencies = [
859 | "serde",
860 | "stable_deref_trait",
861 | "yoke-derive",
862 | "zerofrom",
863 | ]
864 |
865 | [[package]]
866 | name = "yoke-derive"
867 | version = "0.7.5"
868 | source = "registry+https://github.com/rust-lang/crates.io-index"
869 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
870 | dependencies = [
871 | "proc-macro2",
872 | "quote",
873 | "syn",
874 | "synstructure",
875 | ]
876 |
877 | [[package]]
878 | name = "zerofrom"
879 | version = "0.1.6"
880 | source = "registry+https://github.com/rust-lang/crates.io-index"
881 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
882 | dependencies = [
883 | "zerofrom-derive",
884 | ]
885 |
886 | [[package]]
887 | name = "zerofrom-derive"
888 | version = "0.1.6"
889 | source = "registry+https://github.com/rust-lang/crates.io-index"
890 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
891 | dependencies = [
892 | "proc-macro2",
893 | "quote",
894 | "syn",
895 | "synstructure",
896 | ]
897 |
898 | [[package]]
899 | name = "zerovec"
900 | version = "0.10.4"
901 | source = "registry+https://github.com/rust-lang/crates.io-index"
902 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
903 | dependencies = [
904 | "yoke",
905 | "zerofrom",
906 | "zerovec-derive",
907 | ]
908 |
909 | [[package]]
910 | name = "zerovec-derive"
911 | version = "0.10.3"
912 | source = "registry+https://github.com/rust-lang/crates.io-index"
913 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
914 | dependencies = [
915 | "proc-macro2",
916 | "quote",
917 | "syn",
918 | ]
919 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redis-macros"
3 | description = "Simple macros and wrappers to redis-rs to automatically serialize and deserialize structs with serde."
4 | version = "0.5.4"
5 | edition = "2021"
6 | authors = ["Daniel Grant"]
7 | readme = "README.md"
8 | homepage = "https://github.com/daniel7grant/redis-macros"
9 | repository = "https://github.com/daniel7grant/redis-macros"
10 | license = "MIT"
11 | keywords = ["redis", "macro", "derive", "json"]
12 |
13 | [dependencies]
14 | redis = { version = "0.31.0", optional = true }
15 | redis-macros-derive = { version = "0.5", optional = true, path = "./redis-macros-derive" }
16 | serde = { version = "1.0", features = ["derive"], optional = true }
17 | serde_json = { version = "1.0", optional = true }
18 |
19 | [features]
20 | default = ["json", "macros"]
21 | json = ["dep:redis", "dep:serde", "dep:serde_json"]
22 | macros = ["dep:redis-macros-derive"]
23 |
24 | [dev-dependencies]
25 | deadpool-redis = "0.21"
26 | redis = { version = "0.31.0", features = ["tokio-comp", "json"] }
27 | serde_yaml = "0.9"
28 | tokio = { version = "1.41", features = ["full"] }
29 |
--------------------------------------------------------------------------------
/Changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [Unreleased]
4 |
5 | - Update deadpool-redis to 0.21
6 |
7 | ## [0.5.4]
8 |
9 | ### Updated
10 |
11 | - Update dependencies to support Redis 0.31
12 |
13 | ## [0.5.3]
14 |
15 | ### Updated
16 |
17 | - Update dependencies to support Redis 0.30
18 |
19 | ## [0.5.2]
20 |
21 | ### Updated
22 |
23 | - Update deadpool-redis and move to dev dependencies (by @negezor)
24 |
25 | ## [0.5.1]
26 |
27 | ### Updated
28 |
29 | - Update dependencies to support Redis 0.29
30 |
31 | ## [0.5.0] - 2025-01-21
32 |
33 | ### Updated
34 |
35 | - Update dependencies to support Redis 0.28
36 | - Fix examples
37 |
38 | ## [0.4.3] - 2024-12-27
39 |
40 | ### Updated
41 |
42 | - Update dependencies to fix issue with punycode parsing in idna
43 |
44 | ## [0.4.2] - 2024-09-15
45 |
46 | ### Updated
47 |
48 | - Change dependency version to be fixed for minor versions
49 |
50 | ## [0.4.1] - 2024-09-13
51 |
52 | ### Updated
53 |
54 | - Update redis to 0.27
55 | - Update deadpool-redis to 0.17
56 |
57 | ## [0.4.0] - 2024-07-29
58 |
59 | ### Updated
60 |
61 | - Update for new redis-rs (by @kristoferb)
62 | - Update redis to 0.26
63 | - Update deadpool-redis to 0.16
64 | - Update versions on other crates
65 |
66 | ## [0.3.0] - 2024-04-01
67 |
68 | ### Updated
69 |
70 | - Update dependencies (by @negezor)
71 |
72 | ## [0.2.1] - 2023-07-05
73 |
74 | ### Added
75 |
76 | - Support for generic structures
77 |
78 | ## [0.2.0] - 2023-07-02
79 |
80 | ### Changed
81 |
82 | - Remove absolute references from macros, so it works with reexporting crates
83 |
84 | ## [0.1.1] - 2023-02-05
85 |
86 | ### Added
87 |
88 | - Unit testing
89 | - Feature testing and documentation with examples
90 |
91 | ### Changed
92 |
93 | - Improve documentation
94 |
95 | ## [0.1.0] - 2023-01-30
96 |
97 | ### Added
98 |
99 | - Derive macros for `redis` `FromRedisValue` and `ToRedisArgs` traits
100 | - Wrapper type for unwrapping `Json` types
101 | - Add automatic publishing to crates.io
102 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License Copyright (c) 2021 Daniel Grant
2 |
3 | Permission is hereby granted, free of
4 | charge, to any person obtaining a copy of this software and associated
5 | documentation files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use, copy, modify, merge,
7 | publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to the
9 | following conditions:
10 |
11 | The above copyright notice and this permission notice
12 | (including the next paragraph) shall be included in all copies or substantial
13 | portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redis-macros
2 |
3 | Simple macros and wrappers to [redis-rs](https://github.com/redis-rs/redis-rs/) to automatically serialize and deserialize structs with serde.
4 |
5 | ## Installation
6 |
7 | To install it, simply add the package `redis-macros`. This package is a helper for `redis` and uses `serde` and `serde_json` (or [any other serializer](#using-other-serializer-eg-serde-yaml)), so add these too to the dependencies.
8 |
9 | ```toml
10 | [dependencies]
11 | redis-macros = "0.5.1"
12 | redis = { version = "0.29" }
13 | serde = { version = "1.0", features = ["derive"] }
14 | serde_json = { version = "1.0" }
15 | ```
16 |
17 | ## Basic usage
18 |
19 | ### Simple usage
20 |
21 | The simplest way to start is to derive `Serialize`, `Deserialize`, `FromRedisValue`, `ToRedisArgs` for any kind of struct... and that's it! You can now get and set these values with regular redis commands:
22 |
23 | ```rust
24 | use redis::{Client, Commands, RedisResult};
25 | use redis_macros::{FromRedisValue, ToRedisArgs};
26 | use serde::{Deserialize, Serialize};
27 |
28 | #[derive(Serialize, Deserialize)]
29 | enum Address {
30 | Street(String),
31 | Road(String),
32 | }
33 |
34 | // Derive the necessary traits
35 | #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
36 | struct User {
37 | id: u32,
38 | name: String,
39 | addresses: Vec
,
40 | }
41 |
42 | fn main () -> redis::RedisResult<()> {
43 | let client = redis::Client::open("redis://localhost:6379/")?;
44 | let mut con = client.get_connection()?;
45 |
46 | let user = User {
47 | id: 1,
48 | name: "Ziggy".to_string(),
49 | addresses: vec![
50 | Address::Street("Downing".to_string()),
51 | Address::Road("Abbey".to_string()),
52 | ],
53 | };
54 |
55 | // Just use it as you would a primitive
56 | con.set("user", user)?;
57 | // user and stored_user will be the same
58 | let stored_user: User = con.get("user")?;
59 | }
60 | ```
61 |
62 | For more information, see the [Basic](./examples/derive_basic.rs) or [Async](./examples/derive_async.rs) examples.
63 |
64 | ### Usage with RedisJSON
65 |
66 | You can even use it with RedisJSON, to extract separate parts of the object.
67 |
68 | ```rust
69 | // Use `JsonCommands`
70 | use redis::{Client, JsonCommands, RedisResult};
71 |
72 | // Derive FromRedisValue, ToRedisArgs to the inner struct
73 | #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
74 | enum Address { /* ... */ }
75 |
76 | // Simple usage is equivalent to set-get
77 | con.json_set("user", "$", &user)?;
78 | let stored_user: User = con.json_get("user", "$")?;
79 |
80 | // But you can get deep values - don't forget to derive traits for these too!
81 | let stored_address: Address = con.json_get("user", "$.addresses[0]")?;
82 | ```
83 |
84 | For more information, see the [RedisJSON](./examples/derive_redisjson.rs) example.
85 |
86 | One issue you might be facing is that `redis` already has overrides for some types, for example Vec, String and most primitives. For this you have to use the [Json wrapper](#json-wrapper-with-redisjson).
87 |
88 | ```rust
89 | // This WON'T work
90 | let stored_addresses: Vec = con.json_get("user", "$.addresses")?;
91 | ```
92 |
93 | ### Json wrapper with RedisJSON
94 |
95 | To deserialize Vecs and primitive types when using RedisJSON, you cannot use the regular types, because these are non-compatible with RedisJSON. However `redis-macros` exports a useful wrapper struct: `Json`. When using RedisJSON, you can wrap your non-structs return values into this:
96 |
97 | ```rust
98 | use redis_macros::Json;
99 |
100 | // Return type can be wrapped into Json
101 | let Json(stored_name): Json = con.json_get("user", "$.name")?;
102 |
103 | // It works with Vecs as well
104 | let Json(stored_addresses): Json> = con.json_get("user", "$.addresses")?;
105 | // ...now stored_addresses will be equal to user.addresses
106 | ```
107 |
108 | If you only use RedisJSON, you can even do away with deriving `FromRedisValue` and `ToRedisArgs`, and use `Json` everywhere.
109 |
110 | ```rust
111 | #[derive(Serialize, Deserialize)]
112 | struct User { /* ... */ }
113 |
114 | // This works with simple redis-rs
115 | con.json_set("user", "$", &user)?;
116 | // ...and you can get back with Json wrapper
117 | let Json(stored_user): Json = con.json_get("user", "$")?;
118 | ```
119 |
120 | For more information, see the [Json Wrapper](./examples/json_wrapper_basic.rs) and [Json Wrapper Advanced](./examples/json_wrapper_modify.rs) examples.
121 |
122 | ### Using other serializer (e.g. serde-yaml)
123 |
124 | In case you want to use another serializer, for example `serde_yaml`, you can install it and use the derives, the same way you would. The only difference should be adding an attribute `redis_serializer` under the derive, with the library you want to serialize with. You can use any Serde serializer as long as they support `from_str` and `to_string` methods. For the full list, see: [Serde data formats](https://serde.rs/#data-formats).
125 |
126 | ```rust
127 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
128 | #[redis_serializer(serde_yaml)]
129 | struct User { /* ... */ }
130 | ```
131 |
132 | For more information, see the [YAML](./examples/derive_yaml.rs) example.
133 |
134 | ### Using deadpool-redis or other crates
135 |
136 | You can still use the macros if you are using a crate that reexports the `redis` traits, for example [deadpool-redis](https://github.com/bikeshedder/deadpool). The only change you have to make is to `use` the reexported `redis` package explicitly:
137 |
138 | ```rust
139 | // In the case of deadpool-redis, bring the reexported crate into scope
140 | use deadpool_redis::redis;
141 |
142 | // Or if you are importing multiple things from redis, use redis::self
143 | use deadpool_redis::{redis::{self, AsyncCommands}, Config, Runtime};
144 | ```
145 |
146 | For more information, see the [deadpool-redis](./examples/derive_deadpool.rs) example.
147 |
148 | ## Testing
149 |
150 | You can run the unit tests on the code with `cargo test`:
151 |
152 | ```sh
153 | cargo test
154 | ```
155 |
156 | For integration testing, you can run the examples. You will need a RedisJSON compatible redis-server on port 6379, [redis-stack docker image](https://hub.docker.com/r/redis/redis-stack) is recommended:
157 |
158 | ```sh
159 | docker run -d --rm -p 6379:6379 --name redis docker.io/redis/redis-stack
160 | cargo test --examples
161 | # cleanup the container
162 | docker stop redis
163 | ```
164 |
165 | ## Coverage
166 |
167 | For coverage, you can use `grcov`. Simply install `llvm-tools-preview` and `grcov` if you don't have it already:
168 |
169 | ```sh
170 | rustup component add llvm-tools-preview
171 | cargo install grcov
172 | ```
173 |
174 | You have to export a few flags to make it work properly:
175 |
176 | ```sh
177 | export RUSTFLAGS='-Cinstrument-coverage'
178 | export LLVM_PROFILE_FILE='.coverage/cargo-test-%p-%m.profraw'
179 | ```
180 |
181 | And finally, run the tests and generate the output:
182 |
183 | ```sh
184 | cargo test
185 | cargo test --examples
186 | grcov .coverage/ -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing -o ./target/debug/coverage/
187 | ```
188 |
189 | Now you can open `./target/debug/coverage/index.html`, and view it in the browser to see the coverage.
190 |
--------------------------------------------------------------------------------
/examples/derive_async.rs:
--------------------------------------------------------------------------------
1 | use redis::{Client, AsyncCommands, ErrorKind, RedisError, RedisResult};
2 | use redis_macros::{FromRedisValue, ToRedisArgs};
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Define structs to hold the data
6 | /// Children structs don't have to implement FromRedisValue, ToRedisArgs, unless you want to use them as top level
7 | /// They have to implement serde traits though!
8 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
9 | enum Address {
10 | Street(String),
11 | Road(String),
12 | }
13 |
14 | /// Don't forget to implement serde traits and redis traits!
15 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
16 | struct User {
17 | id: u32,
18 | name: String,
19 | addresses: Vec,
20 | }
21 |
22 | /// Show a simple async usage of redis_macros traits
23 | /// Just derive the traits and forget them!
24 | #[tokio::main]
25 | async fn main() -> RedisResult<()> {
26 | // Open new async connection to localhost
27 | let client = Client::open("redis://localhost:6379")?;
28 | let mut con = client.get_multiplexed_async_connection().await.map_err(|_| {
29 | RedisError::from((
30 | ErrorKind::InvalidClientConfig,
31 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
32 | ))
33 | })?;
34 |
35 | // Define the data you want to store in Redis.
36 | let user = User {
37 | id: 1,
38 | name: "Ziggy".to_string(),
39 | addresses: vec![
40 | Address::Street("Downing".to_string()),
41 | Address::Road("Abbey".to_string()),
42 | ],
43 | };
44 |
45 | // Set and get back the user in Redis asynchronously, no problem
46 | let _: () = con.set("user_async", &user).await?;
47 | let stored_user: User = con.get("user_async").await?;
48 |
49 | // You will get back the same data
50 | assert_eq!(user, stored_user);
51 |
52 | Ok(())
53 | }
54 |
55 | #[test]
56 | fn test_derive_async() {
57 | assert_eq!(main(), Ok(()));
58 | }
59 |
--------------------------------------------------------------------------------
/examples/derive_basic.rs:
--------------------------------------------------------------------------------
1 | use redis::{Client, Commands, ErrorKind, RedisError, RedisResult};
2 | use redis_macros::{FromRedisValue, ToRedisArgs};
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Define structs to hold the data
6 | /// Children structs don't have to implement FromRedisValue, ToRedisArgs, unless you want to use them as top level
7 | /// They have to implement serde traits though!
8 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
9 | enum Address {
10 | Street(String),
11 | Road(String),
12 | }
13 |
14 | /// Don't forget to implement serde traits and redis traits!
15 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
16 | struct User {
17 | id: u32,
18 | name: String,
19 | addresses: Vec,
20 | }
21 |
22 | /// Show a simple usage of redis_macros traits
23 | /// Just derive the traits and forget them!
24 | fn main() -> RedisResult<()> {
25 | // Open new connection to localhost
26 | let client = Client::open("redis://localhost:6379")?;
27 | let mut con = client.get_connection().map_err(|_| {
28 | RedisError::from((
29 | ErrorKind::InvalidClientConfig,
30 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
31 | ))
32 | })?;
33 |
34 | // Define the data you want to store in Redis.
35 | let user = User {
36 | id: 1,
37 | name: "Ziggy".to_string(),
38 | addresses: vec![
39 | Address::Street("Downing".to_string()),
40 | Address::Road("Abbey".to_string()),
41 | ],
42 | };
43 |
44 | // Set and get back the user in Redis, no problem
45 | let _: () = con.set("user", &user)?;
46 | let stored_user: User = con.get("user")?;
47 |
48 | // You will get back the same data
49 | assert_eq!(user, stored_user);
50 |
51 | Ok(())
52 | }
53 |
54 | #[test]
55 | fn test_derive_basic() {
56 | assert_eq!(main(), Ok(()));
57 | }
58 |
--------------------------------------------------------------------------------
/examples/derive_deadpool.rs:
--------------------------------------------------------------------------------
1 | use deadpool_redis::{
2 | // Very important to import inner redis - otherwise macro expansion fails!
3 | redis,
4 | redis::{AsyncCommands, ErrorKind, RedisError, RedisResult},
5 | Config,
6 | Runtime,
7 | };
8 | use redis_macros::{FromRedisValue, ToRedisArgs};
9 | use serde::{Deserialize, Serialize};
10 |
11 | /// Define structs to hold the data
12 | /// Children structs don't have to implement FromRedisValue, ToRedisArgs, unless you want to use them as top level
13 | /// They have to implement serde traits though!
14 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
15 | enum Address {
16 | Street(String),
17 | Road(String),
18 | }
19 |
20 | /// Don't forget to implement serde traits and redis traits!
21 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
22 | struct User {
23 | id: u32,
24 | name: String,
25 | addresses: Vec,
26 | }
27 |
28 | /// Show a simple async usage of redis_macros traits
29 | /// Just derive the traits and forget them!
30 | #[tokio::main]
31 | async fn main() -> RedisResult<()> {
32 | // Open new async connection to localhost
33 | let cfg = Config::from_url("redis://localhost:6379");
34 |
35 | let pool = cfg.create_pool(Some(Runtime::Tokio1)).unwrap();
36 | let mut con = pool.get().await.map_err(|_| {
37 | RedisError::from((
38 | ErrorKind::InvalidClientConfig,
39 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
40 | ))
41 | })?;
42 |
43 | // Define the data you want to store in Redis.
44 | let user = User {
45 | id: 1,
46 | name: "Ziggy".to_string(),
47 | addresses: vec![
48 | Address::Street("Downing".to_string()),
49 | Address::Road("Abbey".to_string()),
50 | ],
51 | };
52 |
53 | // Set and get back the user in Redis asynchronously, no problem
54 | let _: () = con.set("user_deadpool", &user).await?;
55 | let stored_user: User = con.get("user_deadpool").await?;
56 |
57 | // You will get back the same data
58 | assert_eq!(user, stored_user);
59 |
60 | Ok(())
61 | }
62 |
63 | #[test]
64 | fn test_derive_async() {
65 | assert_eq!(main(), Ok(()));
66 | }
67 |
--------------------------------------------------------------------------------
/examples/derive_generic.rs:
--------------------------------------------------------------------------------
1 | use redis::{Client, Commands, ErrorKind, RedisError, RedisResult};
2 | use redis_macros::{FromRedisValue, ToRedisArgs};
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Define structs to hold the data
6 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
7 | struct Container {
8 | inner: T,
9 | }
10 |
11 | /// You can use generics with it, just derive the trait
12 | /// However generics currently only work with owned types, because FromRedisValue doesn't support lifetimes
13 | fn main() -> RedisResult<()> {
14 | // Open new connection to localhost
15 | let client = Client::open("redis://localhost:6379")?;
16 | let mut con = client.get_connection().map_err(|_| {
17 | RedisError::from((
18 | ErrorKind::InvalidClientConfig,
19 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
20 | ))
21 | })?;
22 |
23 | // Define the data you want to store in Redis.
24 | // Currently only owned types work (String, but not &str)!
25 | let container = Container {
26 | inner: "contained".to_string(),
27 | };
28 |
29 | // Set and get back the container in Redis, no problem
30 | con.set::<_, _, ()>("container", &container)?;
31 | let stored_container: Container = con.get("container")?;
32 |
33 | // You will get back the same data
34 | assert_eq!(container, stored_container);
35 |
36 | Ok(())
37 | }
38 |
39 | #[test]
40 | fn test_derive_basic() {
41 | assert_eq!(main(), Ok(()));
42 | }
43 |
--------------------------------------------------------------------------------
/examples/derive_redisjson.rs:
--------------------------------------------------------------------------------
1 | use redis::{Client, JsonAsyncCommands, ErrorKind, RedisError, RedisResult};
2 | use redis_macros::{FromRedisValue, ToRedisArgs, Json};
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Define structs to hold the data
6 | /// If we want to JSON.GET the inner struct we have to dderive FromRedisValue
7 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue)]
8 | enum Address {
9 | Street(String),
10 | Road(String),
11 | }
12 |
13 | /// Don't forget to implement serde traits and redis traits!
14 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
15 | struct User {
16 | id: u32,
17 | name: String,
18 | addresses: Vec,
19 | }
20 |
21 | /// Show a usage of redis macros with RedisJSON commands
22 | /// You can use RedisJSON paths to extract the inner paths
23 | #[tokio::main]
24 | async fn main() -> RedisResult<()> {
25 | // Open new connection to localhost
26 | let client = Client::open("redis://localhost:6379")?;
27 | let mut con = client.get_multiplexed_async_connection().await.map_err(|_| {
28 | RedisError::from((
29 | ErrorKind::InvalidClientConfig,
30 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
31 | ))
32 | })?;
33 |
34 | // Define the data you want to store in Redis.
35 | let user = User {
36 | id: 1,
37 | name: "Ziggy".to_string(),
38 | addresses: vec![
39 | Address::Street("Downing".to_string()),
40 | Address::Road("Abbey".to_string()),
41 | ],
42 | };
43 |
44 | // Set and get the data in Redis with RedisJSON
45 | let _: () = con.json_set("user_json", "$", &user).await?;
46 | let stored_user: User = con.json_get("user_json", "$").await?;
47 | assert_eq!(user, stored_user);
48 |
49 | // Even with inner structs (don't forget to derive FromRedisValue for them)
50 | let stored_address: Address = con.json_get("user_json", "$.addresses[0]").await?;
51 | assert_eq!(user.addresses[0], stored_address);
52 |
53 | // However it doesn't work with types that redis overrides (e.g. String, Vec)
54 | // You have to wrap them in Json instead
55 | let Json(stored_name): Json = con.json_get("user_json", "$.name").await?;
56 | assert_eq!(user.name, stored_name);
57 | let Json(stored_addresses): Json> = con.json_get("user_json", "$.addresses").await?;
58 | assert_eq!(user.addresses, stored_addresses);
59 |
60 | Ok(())
61 | }
62 |
63 | #[test]
64 | fn test_derive_redisjson() {
65 | assert_eq!(main(), Ok(()));
66 | }
67 |
--------------------------------------------------------------------------------
/examples/derive_yaml.rs:
--------------------------------------------------------------------------------
1 | use redis::{AsyncCommands, Client, ErrorKind, RedisError, RedisResult};
2 | use redis_macros::{FromRedisValue, ToRedisArgs};
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Define structs to hold the data
6 | /// Children structs don't have to implement FromRedisValue, ToRedisArgs, unless you want to use them as top level
7 | /// They have to implement serde traits though!
8 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
9 | enum Address {
10 | Street(String),
11 | Road(String),
12 | }
13 |
14 | /// Derive the traits and set the `redis_serializer` attrribute
15 | #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
16 | #[redis_serializer(serde_yaml)]
17 | struct User {
18 | id: u32,
19 | name: String,
20 | addresses: Vec,
21 | }
22 |
23 | /// This example shows how to use different serializer, in this example serde_yaml
24 | #[tokio::main]
25 | async fn main() -> RedisResult<()> {
26 | // Open new async connection to localhost
27 | let client = Client::open("redis://localhost:6379")?;
28 | let mut con = client.get_multiplexed_async_connection().await.map_err(|_| {
29 | RedisError::from((
30 | ErrorKind::InvalidClientConfig,
31 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
32 | ))
33 | })?;
34 |
35 | // Define the data you want to store in Redis.
36 | let user = User {
37 | id: 1,
38 | name: "Ziggy".to_string(),
39 | addresses: vec![
40 | Address::Street("Downing".to_string()),
41 | Address::Road("Abbey".to_string()),
42 | ],
43 | };
44 |
45 | // Set and get back the user in YAML format, no problem
46 | let _: () = con.set("user_yaml", &user).await?;
47 | let stored_user: User = con.get("user_yaml").await?;
48 | assert_eq!(user, stored_user);
49 |
50 | // If we get this out in string, it will be YAML
51 | let stored_yaml: String = con.get("user_yaml").await?;
52 | assert_eq!(
53 | "id: 1
54 | name: Ziggy
55 | addresses:
56 | - !Street Downing
57 | - !Road Abbey
58 | ",
59 | stored_yaml
60 | );
61 |
62 | Ok(())
63 | }
64 |
65 | #[test]
66 | fn test_derive_yaml() {
67 | assert_eq!(main(), Ok(()));
68 | }
69 |
--------------------------------------------------------------------------------
/examples/json_wrapper_basic.rs:
--------------------------------------------------------------------------------
1 | use redis::{Client, ErrorKind, JsonAsyncCommands, RedisError, RedisResult};
2 | use redis_macros::Json;
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Define structs to hold the data
6 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
7 | enum Address {
8 | Street(String),
9 | Road(String),
10 | }
11 |
12 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
13 | struct User {
14 | id: u32,
15 | name: String,
16 | addresses: Vec,
17 | }
18 |
19 | /// Instead of deriving the data, use Json wrappers
20 | /// This will make it compatible with any kind of data (for example Vec)
21 | #[tokio::main]
22 | async fn main() -> RedisResult<()> {
23 | // Open new connection to localhost
24 | let client = Client::open("redis://localhost:6379")?;
25 | let mut con = client.get_multiplexed_async_connection().await.map_err(|_| {
26 | RedisError::from((
27 | ErrorKind::InvalidClientConfig,
28 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
29 | ))
30 | })?;
31 |
32 | // Define the data you want to store in Redis.
33 | let user = User {
34 | id: 1,
35 | name: "Ziggy".to_string(),
36 | addresses: vec![
37 | Address::Street("Downing".to_string()),
38 | Address::Road("Abbey".to_string()),
39 | ],
40 | };
41 |
42 | // Wrap the data in `Json(..)` when reading from from Redis
43 | let _: () = con.json_set("user_wrapped", "$", &user).await?;
44 | let Json(stored_user): Json = con.json_get("user_wrapped", "$").await?;
45 | assert_eq!(user, stored_user);
46 |
47 | // You can unwrap inner structs as well
48 | let Json(stored_address): Json =
49 | con.json_get("user_wrapped", "$.addresses[0]").await?;
50 | assert_eq!(user.addresses[0], stored_address);
51 |
52 | // Even with types that redis normally overrides (e.g. String, Vec)
53 | let Json(stored_name): Json = con.json_get("user_wrapped", "$.name").await?;
54 | assert_eq!(user.name, stored_name);
55 | let Json(stored_addresses): Json> =
56 | con.json_get("user_wrapped", "$.addresses").await?;
57 | assert_eq!(user.addresses, stored_addresses);
58 |
59 | // You can even use these types as inputs
60 | let users = vec![user];
61 | let _: () = con.json_set("users_wrapped", "$", &users).await?;
62 | let Json(stored_users): Json> = con.json_get("users_wrapped", "$").await?;
63 | assert_eq!(users, stored_users);
64 |
65 |
66 | Ok(())
67 | }
68 |
69 | #[test]
70 | fn test_json_wrapper_basic() {
71 | assert_eq!(main(), Ok(()));
72 | }
73 |
--------------------------------------------------------------------------------
/examples/json_wrapper_modify.rs:
--------------------------------------------------------------------------------
1 | use redis::{Client, ErrorKind, JsonAsyncCommands, RedisError, RedisResult};
2 | use redis_macros::Json;
3 | use serde::{Deserialize, Serialize};
4 |
5 | /// Define structs to hold the data
6 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
7 | enum Address {
8 | Street(String),
9 | Road(String),
10 | }
11 |
12 | #[derive(Debug, PartialEq, Serialize, Deserialize)]
13 | struct User {
14 | id: u32,
15 | name: String,
16 | addresses: Vec,
17 | }
18 |
19 | /// This example shows how to use more exotic RedisJSON commands
20 | #[tokio::main]
21 | async fn main() -> RedisResult<()> {
22 | // Open new connection to localhost
23 | let client = Client::open("redis://localhost:6379")?;
24 | let mut con = client.get_multiplexed_async_connection().await.map_err(|_| {
25 | RedisError::from((
26 | ErrorKind::InvalidClientConfig,
27 | "Cannot connect to localhost:6379. Try starting a redis-server process or container.",
28 | ))
29 | })?;
30 |
31 | // Define the data you want to store in Redis.
32 | let user = User {
33 | id: 1,
34 | name: "Ziggy".to_string(),
35 | addresses: vec![
36 | Address::Street("Downing".to_string()),
37 | Address::Road("Abbey".to_string()),
38 | ],
39 | };
40 |
41 | // Wrap the data in `Json(..)` when passing to and from Redis
42 | let _: () = con.json_set("user_wrapped_modify", "$", &user).await?;
43 |
44 | // Modify inner values with JSON.SET
45 | let _: () = con.json_set("user_wrapped_modify", "$.name", &"Bowie")
46 | .await?;
47 | let Json(stored_name): Json = con.json_get("user_wrapped_modify", "$.name").await?;
48 | assert_eq!("Bowie", stored_name);
49 |
50 | // Increment numbers with JSON.NUMINCRBY
51 | let _: () = con.json_num_incr_by("user_wrapped_modify", "$.id", 1)
52 | .await?;
53 | let Json(stored_id): Json = con.json_get("user_wrapped_modify", "$.id").await?;
54 | assert_eq!(2, stored_id);
55 |
56 | // Append item to array with JSON.ARR_APPEND
57 | let _: () = con.json_arr_append(
58 | "user_wrapped_modify",
59 | "$.addresses",
60 | &Address::Street("Oxford".to_string()),
61 | )
62 | .await?;
63 | let Json(stored_addresses): Json> =
64 | con.json_get("user_wrapped_modify", "$.addresses").await?;
65 | assert_eq!(
66 | vec![
67 | Address::Street("Downing".to_string()),
68 | Address::Road("Abbey".to_string()),
69 | Address::Street("Oxford".to_string())
70 | ],
71 | stored_addresses
72 | );
73 |
74 | Ok(())
75 | }
76 |
77 | #[test]
78 | fn test_json_wrapper_modify() {
79 | assert_eq!(main(), Ok(()));
80 | }
81 |
--------------------------------------------------------------------------------
/redis-macros-derive/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "addr2line"
7 | version = "0.24.2"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
10 | dependencies = [
11 | "gimli",
12 | ]
13 |
14 | [[package]]
15 | name = "adler2"
16 | version = "2.0.0"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
19 |
20 | [[package]]
21 | name = "autocfg"
22 | version = "1.4.0"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
25 |
26 | [[package]]
27 | name = "backtrace"
28 | version = "0.3.74"
29 | source = "registry+https://github.com/rust-lang/crates.io-index"
30 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
31 | dependencies = [
32 | "addr2line",
33 | "cfg-if",
34 | "libc",
35 | "miniz_oxide",
36 | "object",
37 | "rustc-demangle",
38 | "windows-targets",
39 | ]
40 |
41 | [[package]]
42 | name = "bytes"
43 | version = "1.10.1"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
46 |
47 | [[package]]
48 | name = "cfg-if"
49 | version = "1.0.0"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
52 |
53 | [[package]]
54 | name = "combine"
55 | version = "4.6.7"
56 | source = "registry+https://github.com/rust-lang/crates.io-index"
57 | checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
58 | dependencies = [
59 | "bytes",
60 | "futures-core",
61 | "memchr",
62 | "pin-project-lite",
63 | "tokio",
64 | "tokio-util",
65 | ]
66 |
67 | [[package]]
68 | name = "displaydoc"
69 | version = "0.2.5"
70 | source = "registry+https://github.com/rust-lang/crates.io-index"
71 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
72 | dependencies = [
73 | "proc-macro2",
74 | "quote",
75 | "syn",
76 | ]
77 |
78 | [[package]]
79 | name = "equivalent"
80 | version = "1.0.2"
81 | source = "registry+https://github.com/rust-lang/crates.io-index"
82 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
83 |
84 | [[package]]
85 | name = "form_urlencoded"
86 | version = "1.2.1"
87 | source = "registry+https://github.com/rust-lang/crates.io-index"
88 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
89 | dependencies = [
90 | "percent-encoding",
91 | ]
92 |
93 | [[package]]
94 | name = "futures-core"
95 | version = "0.3.31"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
98 |
99 | [[package]]
100 | name = "futures-sink"
101 | version = "0.3.31"
102 | source = "registry+https://github.com/rust-lang/crates.io-index"
103 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
104 |
105 | [[package]]
106 | name = "futures-task"
107 | version = "0.3.31"
108 | source = "registry+https://github.com/rust-lang/crates.io-index"
109 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
110 |
111 | [[package]]
112 | name = "futures-util"
113 | version = "0.3.31"
114 | source = "registry+https://github.com/rust-lang/crates.io-index"
115 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
116 | dependencies = [
117 | "futures-core",
118 | "futures-sink",
119 | "futures-task",
120 | "pin-project-lite",
121 | "pin-utils",
122 | "slab",
123 | ]
124 |
125 | [[package]]
126 | name = "gimli"
127 | version = "0.31.1"
128 | source = "registry+https://github.com/rust-lang/crates.io-index"
129 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
130 |
131 | [[package]]
132 | name = "hashbrown"
133 | version = "0.15.2"
134 | source = "registry+https://github.com/rust-lang/crates.io-index"
135 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
136 |
137 | [[package]]
138 | name = "icu_collections"
139 | version = "1.5.0"
140 | source = "registry+https://github.com/rust-lang/crates.io-index"
141 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
142 | dependencies = [
143 | "displaydoc",
144 | "yoke",
145 | "zerofrom",
146 | "zerovec",
147 | ]
148 |
149 | [[package]]
150 | name = "icu_locid"
151 | version = "1.5.0"
152 | source = "registry+https://github.com/rust-lang/crates.io-index"
153 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
154 | dependencies = [
155 | "displaydoc",
156 | "litemap",
157 | "tinystr",
158 | "writeable",
159 | "zerovec",
160 | ]
161 |
162 | [[package]]
163 | name = "icu_locid_transform"
164 | version = "1.5.0"
165 | source = "registry+https://github.com/rust-lang/crates.io-index"
166 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
167 | dependencies = [
168 | "displaydoc",
169 | "icu_locid",
170 | "icu_locid_transform_data",
171 | "icu_provider",
172 | "tinystr",
173 | "zerovec",
174 | ]
175 |
176 | [[package]]
177 | name = "icu_locid_transform_data"
178 | version = "1.5.1"
179 | source = "registry+https://github.com/rust-lang/crates.io-index"
180 | checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
181 |
182 | [[package]]
183 | name = "icu_normalizer"
184 | version = "1.5.0"
185 | source = "registry+https://github.com/rust-lang/crates.io-index"
186 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
187 | dependencies = [
188 | "displaydoc",
189 | "icu_collections",
190 | "icu_normalizer_data",
191 | "icu_properties",
192 | "icu_provider",
193 | "smallvec",
194 | "utf16_iter",
195 | "utf8_iter",
196 | "write16",
197 | "zerovec",
198 | ]
199 |
200 | [[package]]
201 | name = "icu_normalizer_data"
202 | version = "1.5.1"
203 | source = "registry+https://github.com/rust-lang/crates.io-index"
204 | checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
205 |
206 | [[package]]
207 | name = "icu_properties"
208 | version = "1.5.1"
209 | source = "registry+https://github.com/rust-lang/crates.io-index"
210 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
211 | dependencies = [
212 | "displaydoc",
213 | "icu_collections",
214 | "icu_locid_transform",
215 | "icu_properties_data",
216 | "icu_provider",
217 | "tinystr",
218 | "zerovec",
219 | ]
220 |
221 | [[package]]
222 | name = "icu_properties_data"
223 | version = "1.5.1"
224 | source = "registry+https://github.com/rust-lang/crates.io-index"
225 | checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
226 |
227 | [[package]]
228 | name = "icu_provider"
229 | version = "1.5.0"
230 | source = "registry+https://github.com/rust-lang/crates.io-index"
231 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
232 | dependencies = [
233 | "displaydoc",
234 | "icu_locid",
235 | "icu_provider_macros",
236 | "stable_deref_trait",
237 | "tinystr",
238 | "writeable",
239 | "yoke",
240 | "zerofrom",
241 | "zerovec",
242 | ]
243 |
244 | [[package]]
245 | name = "icu_provider_macros"
246 | version = "1.5.0"
247 | source = "registry+https://github.com/rust-lang/crates.io-index"
248 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
249 | dependencies = [
250 | "proc-macro2",
251 | "quote",
252 | "syn",
253 | ]
254 |
255 | [[package]]
256 | name = "idna"
257 | version = "1.0.3"
258 | source = "registry+https://github.com/rust-lang/crates.io-index"
259 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
260 | dependencies = [
261 | "idna_adapter",
262 | "smallvec",
263 | "utf8_iter",
264 | ]
265 |
266 | [[package]]
267 | name = "idna_adapter"
268 | version = "1.2.0"
269 | source = "registry+https://github.com/rust-lang/crates.io-index"
270 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
271 | dependencies = [
272 | "icu_normalizer",
273 | "icu_properties",
274 | ]
275 |
276 | [[package]]
277 | name = "indexmap"
278 | version = "2.9.0"
279 | source = "registry+https://github.com/rust-lang/crates.io-index"
280 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
281 | dependencies = [
282 | "equivalent",
283 | "hashbrown",
284 | ]
285 |
286 | [[package]]
287 | name = "itoa"
288 | version = "1.0.15"
289 | source = "registry+https://github.com/rust-lang/crates.io-index"
290 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
291 |
292 | [[package]]
293 | name = "libc"
294 | version = "0.2.171"
295 | source = "registry+https://github.com/rust-lang/crates.io-index"
296 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
297 |
298 | [[package]]
299 | name = "litemap"
300 | version = "0.7.5"
301 | source = "registry+https://github.com/rust-lang/crates.io-index"
302 | checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
303 |
304 | [[package]]
305 | name = "memchr"
306 | version = "2.7.4"
307 | source = "registry+https://github.com/rust-lang/crates.io-index"
308 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
309 |
310 | [[package]]
311 | name = "miniz_oxide"
312 | version = "0.8.8"
313 | source = "registry+https://github.com/rust-lang/crates.io-index"
314 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
315 | dependencies = [
316 | "adler2",
317 | ]
318 |
319 | [[package]]
320 | name = "mio"
321 | version = "1.0.3"
322 | source = "registry+https://github.com/rust-lang/crates.io-index"
323 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
324 | dependencies = [
325 | "libc",
326 | "wasi",
327 | "windows-sys",
328 | ]
329 |
330 | [[package]]
331 | name = "num-bigint"
332 | version = "0.4.6"
333 | source = "registry+https://github.com/rust-lang/crates.io-index"
334 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
335 | dependencies = [
336 | "num-integer",
337 | "num-traits",
338 | ]
339 |
340 | [[package]]
341 | name = "num-integer"
342 | version = "0.1.46"
343 | source = "registry+https://github.com/rust-lang/crates.io-index"
344 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
345 | dependencies = [
346 | "num-traits",
347 | ]
348 |
349 | [[package]]
350 | name = "num-traits"
351 | version = "0.2.19"
352 | source = "registry+https://github.com/rust-lang/crates.io-index"
353 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
354 | dependencies = [
355 | "autocfg",
356 | ]
357 |
358 | [[package]]
359 | name = "object"
360 | version = "0.36.7"
361 | source = "registry+https://github.com/rust-lang/crates.io-index"
362 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
363 | dependencies = [
364 | "memchr",
365 | ]
366 |
367 | [[package]]
368 | name = "percent-encoding"
369 | version = "2.3.1"
370 | source = "registry+https://github.com/rust-lang/crates.io-index"
371 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
372 |
373 | [[package]]
374 | name = "pin-project-lite"
375 | version = "0.2.16"
376 | source = "registry+https://github.com/rust-lang/crates.io-index"
377 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
378 |
379 | [[package]]
380 | name = "pin-utils"
381 | version = "0.1.0"
382 | source = "registry+https://github.com/rust-lang/crates.io-index"
383 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
384 |
385 | [[package]]
386 | name = "proc-macro2"
387 | version = "1.0.94"
388 | source = "registry+https://github.com/rust-lang/crates.io-index"
389 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
390 | dependencies = [
391 | "unicode-ident",
392 | ]
393 |
394 | [[package]]
395 | name = "quote"
396 | version = "1.0.40"
397 | source = "registry+https://github.com/rust-lang/crates.io-index"
398 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
399 | dependencies = [
400 | "proc-macro2",
401 | ]
402 |
403 | [[package]]
404 | name = "redis"
405 | version = "0.31.0"
406 | source = "registry+https://github.com/rust-lang/crates.io-index"
407 | checksum = "0bc1ea653e0b2e097db3ebb5b7f678be339620b8041f66b30a308c1d45d36a7f"
408 | dependencies = [
409 | "bytes",
410 | "cfg-if",
411 | "combine",
412 | "futures-util",
413 | "itoa",
414 | "num-bigint",
415 | "percent-encoding",
416 | "pin-project-lite",
417 | "ryu",
418 | "serde",
419 | "serde_json",
420 | "sha1_smol",
421 | "socket2",
422 | "tokio",
423 | "tokio-util",
424 | "url",
425 | ]
426 |
427 | [[package]]
428 | name = "redis-macros"
429 | version = "0.5.4"
430 | dependencies = [
431 | "redis",
432 | "redis-macros-derive",
433 | "serde",
434 | "serde_json",
435 | ]
436 |
437 | [[package]]
438 | name = "redis-macros-derive"
439 | version = "0.5.4"
440 | dependencies = [
441 | "proc-macro2",
442 | "quote",
443 | "redis",
444 | "redis-macros",
445 | "serde",
446 | "serde_json",
447 | "serde_yaml",
448 | "syn",
449 | ]
450 |
451 | [[package]]
452 | name = "rustc-demangle"
453 | version = "0.1.24"
454 | source = "registry+https://github.com/rust-lang/crates.io-index"
455 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
456 |
457 | [[package]]
458 | name = "ryu"
459 | version = "1.0.20"
460 | source = "registry+https://github.com/rust-lang/crates.io-index"
461 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
462 |
463 | [[package]]
464 | name = "serde"
465 | version = "1.0.219"
466 | source = "registry+https://github.com/rust-lang/crates.io-index"
467 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
468 | dependencies = [
469 | "serde_derive",
470 | ]
471 |
472 | [[package]]
473 | name = "serde_derive"
474 | version = "1.0.219"
475 | source = "registry+https://github.com/rust-lang/crates.io-index"
476 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
477 | dependencies = [
478 | "proc-macro2",
479 | "quote",
480 | "syn",
481 | ]
482 |
483 | [[package]]
484 | name = "serde_json"
485 | version = "1.0.140"
486 | source = "registry+https://github.com/rust-lang/crates.io-index"
487 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
488 | dependencies = [
489 | "itoa",
490 | "memchr",
491 | "ryu",
492 | "serde",
493 | ]
494 |
495 | [[package]]
496 | name = "serde_yaml"
497 | version = "0.9.34+deprecated"
498 | source = "registry+https://github.com/rust-lang/crates.io-index"
499 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
500 | dependencies = [
501 | "indexmap",
502 | "itoa",
503 | "ryu",
504 | "serde",
505 | "unsafe-libyaml",
506 | ]
507 |
508 | [[package]]
509 | name = "sha1_smol"
510 | version = "1.0.1"
511 | source = "registry+https://github.com/rust-lang/crates.io-index"
512 | checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
513 |
514 | [[package]]
515 | name = "slab"
516 | version = "0.4.9"
517 | source = "registry+https://github.com/rust-lang/crates.io-index"
518 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
519 | dependencies = [
520 | "autocfg",
521 | ]
522 |
523 | [[package]]
524 | name = "smallvec"
525 | version = "1.15.0"
526 | source = "registry+https://github.com/rust-lang/crates.io-index"
527 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
528 |
529 | [[package]]
530 | name = "socket2"
531 | version = "0.5.9"
532 | source = "registry+https://github.com/rust-lang/crates.io-index"
533 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
534 | dependencies = [
535 | "libc",
536 | "windows-sys",
537 | ]
538 |
539 | [[package]]
540 | name = "stable_deref_trait"
541 | version = "1.2.0"
542 | source = "registry+https://github.com/rust-lang/crates.io-index"
543 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
544 |
545 | [[package]]
546 | name = "syn"
547 | version = "2.0.100"
548 | source = "registry+https://github.com/rust-lang/crates.io-index"
549 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
550 | dependencies = [
551 | "proc-macro2",
552 | "quote",
553 | "unicode-ident",
554 | ]
555 |
556 | [[package]]
557 | name = "synstructure"
558 | version = "0.13.1"
559 | source = "registry+https://github.com/rust-lang/crates.io-index"
560 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
561 | dependencies = [
562 | "proc-macro2",
563 | "quote",
564 | "syn",
565 | ]
566 |
567 | [[package]]
568 | name = "tinystr"
569 | version = "0.7.6"
570 | source = "registry+https://github.com/rust-lang/crates.io-index"
571 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
572 | dependencies = [
573 | "displaydoc",
574 | "zerovec",
575 | ]
576 |
577 | [[package]]
578 | name = "tokio"
579 | version = "1.44.2"
580 | source = "registry+https://github.com/rust-lang/crates.io-index"
581 | checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
582 | dependencies = [
583 | "backtrace",
584 | "bytes",
585 | "libc",
586 | "mio",
587 | "pin-project-lite",
588 | "socket2",
589 | "windows-sys",
590 | ]
591 |
592 | [[package]]
593 | name = "tokio-util"
594 | version = "0.7.14"
595 | source = "registry+https://github.com/rust-lang/crates.io-index"
596 | checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
597 | dependencies = [
598 | "bytes",
599 | "futures-core",
600 | "futures-sink",
601 | "pin-project-lite",
602 | "tokio",
603 | ]
604 |
605 | [[package]]
606 | name = "unicode-ident"
607 | version = "1.0.18"
608 | source = "registry+https://github.com/rust-lang/crates.io-index"
609 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
610 |
611 | [[package]]
612 | name = "unsafe-libyaml"
613 | version = "0.2.11"
614 | source = "registry+https://github.com/rust-lang/crates.io-index"
615 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
616 |
617 | [[package]]
618 | name = "url"
619 | version = "2.5.4"
620 | source = "registry+https://github.com/rust-lang/crates.io-index"
621 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
622 | dependencies = [
623 | "form_urlencoded",
624 | "idna",
625 | "percent-encoding",
626 | ]
627 |
628 | [[package]]
629 | name = "utf16_iter"
630 | version = "1.0.5"
631 | source = "registry+https://github.com/rust-lang/crates.io-index"
632 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
633 |
634 | [[package]]
635 | name = "utf8_iter"
636 | version = "1.0.4"
637 | source = "registry+https://github.com/rust-lang/crates.io-index"
638 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
639 |
640 | [[package]]
641 | name = "wasi"
642 | version = "0.11.0+wasi-snapshot-preview1"
643 | source = "registry+https://github.com/rust-lang/crates.io-index"
644 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
645 |
646 | [[package]]
647 | name = "windows-sys"
648 | version = "0.52.0"
649 | source = "registry+https://github.com/rust-lang/crates.io-index"
650 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
651 | dependencies = [
652 | "windows-targets",
653 | ]
654 |
655 | [[package]]
656 | name = "windows-targets"
657 | version = "0.52.6"
658 | source = "registry+https://github.com/rust-lang/crates.io-index"
659 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
660 | dependencies = [
661 | "windows_aarch64_gnullvm",
662 | "windows_aarch64_msvc",
663 | "windows_i686_gnu",
664 | "windows_i686_gnullvm",
665 | "windows_i686_msvc",
666 | "windows_x86_64_gnu",
667 | "windows_x86_64_gnullvm",
668 | "windows_x86_64_msvc",
669 | ]
670 |
671 | [[package]]
672 | name = "windows_aarch64_gnullvm"
673 | version = "0.52.6"
674 | source = "registry+https://github.com/rust-lang/crates.io-index"
675 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
676 |
677 | [[package]]
678 | name = "windows_aarch64_msvc"
679 | version = "0.52.6"
680 | source = "registry+https://github.com/rust-lang/crates.io-index"
681 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
682 |
683 | [[package]]
684 | name = "windows_i686_gnu"
685 | version = "0.52.6"
686 | source = "registry+https://github.com/rust-lang/crates.io-index"
687 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
688 |
689 | [[package]]
690 | name = "windows_i686_gnullvm"
691 | version = "0.52.6"
692 | source = "registry+https://github.com/rust-lang/crates.io-index"
693 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
694 |
695 | [[package]]
696 | name = "windows_i686_msvc"
697 | version = "0.52.6"
698 | source = "registry+https://github.com/rust-lang/crates.io-index"
699 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
700 |
701 | [[package]]
702 | name = "windows_x86_64_gnu"
703 | version = "0.52.6"
704 | source = "registry+https://github.com/rust-lang/crates.io-index"
705 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
706 |
707 | [[package]]
708 | name = "windows_x86_64_gnullvm"
709 | version = "0.52.6"
710 | source = "registry+https://github.com/rust-lang/crates.io-index"
711 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
712 |
713 | [[package]]
714 | name = "windows_x86_64_msvc"
715 | version = "0.52.6"
716 | source = "registry+https://github.com/rust-lang/crates.io-index"
717 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
718 |
719 | [[package]]
720 | name = "write16"
721 | version = "1.0.0"
722 | source = "registry+https://github.com/rust-lang/crates.io-index"
723 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
724 |
725 | [[package]]
726 | name = "writeable"
727 | version = "0.5.5"
728 | source = "registry+https://github.com/rust-lang/crates.io-index"
729 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
730 |
731 | [[package]]
732 | name = "yoke"
733 | version = "0.7.5"
734 | source = "registry+https://github.com/rust-lang/crates.io-index"
735 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
736 | dependencies = [
737 | "serde",
738 | "stable_deref_trait",
739 | "yoke-derive",
740 | "zerofrom",
741 | ]
742 |
743 | [[package]]
744 | name = "yoke-derive"
745 | version = "0.7.5"
746 | source = "registry+https://github.com/rust-lang/crates.io-index"
747 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
748 | dependencies = [
749 | "proc-macro2",
750 | "quote",
751 | "syn",
752 | "synstructure",
753 | ]
754 |
755 | [[package]]
756 | name = "zerofrom"
757 | version = "0.1.6"
758 | source = "registry+https://github.com/rust-lang/crates.io-index"
759 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
760 | dependencies = [
761 | "zerofrom-derive",
762 | ]
763 |
764 | [[package]]
765 | name = "zerofrom-derive"
766 | version = "0.1.6"
767 | source = "registry+https://github.com/rust-lang/crates.io-index"
768 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
769 | dependencies = [
770 | "proc-macro2",
771 | "quote",
772 | "syn",
773 | "synstructure",
774 | ]
775 |
776 | [[package]]
777 | name = "zerovec"
778 | version = "0.10.4"
779 | source = "registry+https://github.com/rust-lang/crates.io-index"
780 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
781 | dependencies = [
782 | "yoke",
783 | "zerofrom",
784 | "zerovec-derive",
785 | ]
786 |
787 | [[package]]
788 | name = "zerovec-derive"
789 | version = "0.10.3"
790 | source = "registry+https://github.com/rust-lang/crates.io-index"
791 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
792 | dependencies = [
793 | "proc-macro2",
794 | "quote",
795 | "syn",
796 | ]
797 |
--------------------------------------------------------------------------------
/redis-macros-derive/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redis-macros-derive"
3 | description = "Derive macros for the redis-macros package"
4 | version = "0.5.4"
5 | edition = "2021"
6 | authors = ["Daniel Grant"]
7 | readme = "README.md"
8 | homepage = "https://github.com/daniel7grant/redis-macros"
9 | repository = "https://github.com/daniel7grant/redis-macros"
10 | license = "MIT"
11 | keywords = ["redis", "macro", "derive", "json"]
12 |
13 | [lib]
14 | proc-macro = true
15 |
16 | [dependencies]
17 | proc-macro2 = "1.0"
18 | quote = "1.0"
19 | redis = "0.31.0"
20 | syn = { version = "2.0" }
21 |
22 | [dev-dependencies]
23 | redis = { version = "0.31.0", features = ["tokio-comp", "json"] }
24 | redis-macros = { path = ".." }
25 | serde = { version = "1.0", features = ["derive"] }
26 | serde_json = { version = "1.0" }
27 | serde_yaml = "0.9"
28 |
--------------------------------------------------------------------------------
/redis-macros-derive/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License Copyright (c) 2021 Daniel Grant
2 |
3 | Permission is hereby granted, free of
4 | charge, to any person obtaining a copy of this software and associated
5 | documentation files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use, copy, modify, merge,
7 | publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to the
9 | following conditions:
10 |
11 | The above copyright notice and this permission notice
12 | (including the next paragraph) shall be included in all copies or substantial
13 | portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/redis-macros-derive/README.md:
--------------------------------------------------------------------------------
1 | # redis-macros-derive
2 |
3 | Derive macros for the [redis-macros](https://crates.io/crates/redis-macros) package. For more detailed information read this [repository](https://github.com/daniel7grant/redis-macros).
4 |
--------------------------------------------------------------------------------
/redis-macros-derive/src/lib.rs:
--------------------------------------------------------------------------------
1 | use proc_macro::TokenStream;
2 | use proc_macro2::TokenStream as TokenStream2;
3 | use quote::{quote, ToTokens};
4 | use syn::{parse_macro_input, Attribute, DeriveInput, Expr, GenericParam};
5 |
6 | fn get_serializer(attrs: Vec, default: &str) -> TokenStream2 {
7 | let default_token = default.parse::().unwrap();
8 |
9 | attrs
10 | .into_iter()
11 | .find(|attr| attr.path().is_ident("redis_serializer"))
12 | .and_then(|attr| {
13 | let Ok(Expr::Path(path)) = attr.parse_args::() else {
14 | return None;
15 | };
16 |
17 | Some(path.to_token_stream())
18 | })
19 | .unwrap_or(default_token)
20 | }
21 |
22 | /// Derive macro for the redis crate's [`FromRedisValue`](../redis/trait.FromRedisValue.html) trait to allow parsing Redis responses to this type.
23 | ///
24 | /// *NOTE: This trait requires serde's [`Deserialize`](../serde/trait.Deserialize.html) to also be derived (or implemented).*
25 | ///
26 | /// Simply use the `#[derive(FromRedisValue, Deserialize)]` before any structs (or serializable elements).
27 | /// This allows, when using Redis commands, to set this as the return type and deserialize from JSON automatically, while reading from Redis.
28 | ///
29 | /// ```rust,no_run
30 | /// # use redis::{Client, Commands, RedisResult};
31 | /// use redis_macros::{FromRedisValue};
32 | /// use serde::{Deserialize};
33 | ///
34 | /// #[derive(FromRedisValue, Deserialize)]
35 | /// struct User { id: u32 }
36 | ///
37 | /// # fn main () -> redis::RedisResult<()> {
38 | /// # let client = redis::Client::open("redis://localhost:6379/")?;
39 | /// # let mut con = client.get_connection()?;
40 | /// con.set("user", &r#"{ "id": 1 }"#)?;
41 | /// let user: User = con.get("user")?; // => User { id: 1 }
42 | /// # Ok(())
43 | /// # }
44 | /// ```
45 | ///
46 | /// If you want to use a different serde format, for example `serde_yaml`, you can set this with the `redis_serializer` attribute.
47 | /// The only restriction is to have the deserializer implement the `from_str` function.
48 | ///
49 | /// ```rust,no_run
50 | /// use redis_macros::{FromRedisValue};
51 | /// use serde::{Deserialize};
52 | ///
53 | /// #[derive(FromRedisValue, Deserialize)]
54 | /// #[redis_serializer(serde_yaml)]
55 | /// struct User { id: u32 }
56 | /// ```
57 | ///
58 | /// For more information see the isomorphic pair of this trait: [ToRedisArgs].
59 | #[proc_macro_derive(FromRedisValue, attributes(redis_serializer))]
60 | pub fn from_redis_value_macro(input: TokenStream) -> TokenStream {
61 | let DeriveInput {
62 | ident,
63 | attrs,
64 | generics,
65 | ..
66 | } = parse_macro_input!(input as DeriveInput);
67 | let serializer = get_serializer(attrs, "serde_json");
68 | let ident_str = format!("{}", ident);
69 | let serializer_str = format!("{}", serializer);
70 |
71 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
72 |
73 | let has_types = generics
74 | .params
75 | .iter()
76 | .any(|g| matches!(g, GenericParam::Type(_)));
77 |
78 | let where_with_serialize = if let Some(w) = where_clause {
79 | quote! { #w, #ident #ty_generics : serde::de::DeserializeOwned }
80 | } else if has_types {
81 | quote! { where #ident #ty_generics : serde::de::DeserializeOwned }
82 | } else {
83 | quote! {}
84 | };
85 |
86 | let failed_parse_error = quote! {
87 | Err(redis::RedisError::from((
88 | redis::ErrorKind::TypeError,
89 | "Response was of incompatible type",
90 | format!("Response type not deserializable to {} with {}. (response was {:?})", #ident_str, #serializer_str, v)
91 | )))
92 | };
93 |
94 | // If the parsing failed, the issue might simply be that the user is using a RedisJSON command
95 | // RedisJSON commands wrap the response into square brackets for some godforesaken reason
96 | // We can try removing the brackets and try the parse again
97 | let redis_json_hack = quote! {
98 | let mut ch = s.chars();
99 | if ch.next() == Some('[') && ch.next_back() == Some(']') {
100 | if let Ok(s) = #serializer::from_str(ch.as_str()) {
101 | Ok(s)
102 | } else {
103 | Err(redis::RedisError::from((
104 | redis::ErrorKind::TypeError,
105 | "Response was of incompatible type",
106 | format!("Response type not RedisJSON deserializable to {}. (response was {:?})", #ident_str, v)
107 | )))
108 | }
109 | } else {
110 | #failed_parse_error
111 | }
112 | };
113 |
114 | // The Redis JSON hack only relevant if we are using serde_json
115 | let failed_parse = if serializer_str == "serde_json" {
116 | redis_json_hack
117 | } else {
118 | failed_parse_error
119 | };
120 |
121 | quote! {
122 | impl #impl_generics redis::FromRedisValue for #ident #ty_generics #where_with_serialize {
123 | fn from_redis_value(v: &redis::Value) -> redis::RedisResult {
124 | match *v {
125 | redis::Value::BulkString(ref bytes) => {
126 | if let Ok(s) = std::str::from_utf8(bytes) {
127 | if let Ok(s) = #serializer::from_str(s) {
128 | Ok(s)
129 | } else {
130 | #failed_parse
131 | }
132 | } else {
133 | Err(redis::RedisError::from((
134 | redis::ErrorKind::TypeError,
135 | "Response was of incompatible type",
136 | format!("Response was not valid UTF-8 string. (response was {:?})", v)
137 | )))
138 | }
139 | },
140 | _ => Err(redis::RedisError::from((
141 | redis::ErrorKind::TypeError,
142 | "Response was of incompatible type",
143 | format!("Response type was not deserializable to {}. (response was {:?})", #ident_str, v)
144 | ))),
145 | }
146 | }
147 | }
148 | }
149 | .into()
150 | }
151 |
152 | /// Derive macro for the redis crate's [`ToRedisArgs`](../redis/trait.ToRedisArgs.html) trait to allow passing the type to Redis commands.
153 | ///
154 | /// *NOTE: This trait requires serde's [`Serialize`](../serde/trait.Serialize.html) to also be derived (or implemented).*
155 | ///
156 | /// ***WARNING: This trait panics if the underlying serialization fails.***
157 | ///
158 | /// Simply use the `#[derive(ToRedisArgs, Serialize)]` before any structs (or serializable elements).
159 | /// This allows to pass this type to Redis commands like SET. The type will be serialized into JSON automatically while saving to Redis.
160 | ///
161 | /// ```rust,no_run
162 | /// # use redis::{Client, Commands, RedisResult};
163 | /// use redis_macros::{ToRedisArgs};
164 | /// use serde::{Serialize};
165 | ///
166 | /// #[derive(ToRedisArgs, Serialize)]
167 | /// struct User { id: u32 }
168 | ///
169 | /// # fn main () -> redis::RedisResult<()> {
170 | /// # let client = redis::Client::open("redis://localhost:6379/")?;
171 | /// # let mut con = client.get_connection()?;
172 | /// con.set("user", User { id: 1 })?;
173 | /// let user: String = con.get("user")?; // => "{ \"id\": 1 }"
174 | /// # Ok(())
175 | /// # }
176 | /// ```
177 | ///
178 | /// If you want to use a different serde format, for example `serde_yaml`, you can set this with the `redis_serializer` attribute.
179 | /// The only restriciton is to have the serializer implement the `to_string` function.
180 | ///
181 | /// ```rust,no_run
182 | /// # use redis::{Client, Commands, RedisResult};
183 | /// use redis_macros::{ToRedisArgs};
184 | /// use serde::{Serialize};
185 | ///
186 | /// #[derive(ToRedisArgs, Serialize)]
187 | /// #[redis_serializer(serde_yaml)]
188 | /// struct User { id: u32 }
189 | /// ```
190 | ///
191 | /// For more information see the isomorphic pair of this trait: [FromRedisValue].
192 | #[proc_macro_derive(ToRedisArgs, attributes(redis_serializer))]
193 | pub fn to_redis_args_macro(input: TokenStream) -> TokenStream {
194 | let DeriveInput {
195 | ident,
196 | attrs,
197 | generics,
198 | ..
199 | } = parse_macro_input!(input as DeriveInput);
200 | let serializer = get_serializer(attrs, "serde_json");
201 |
202 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
203 |
204 | let has_types = generics
205 | .params
206 | .iter()
207 | .any(|g| matches!(g, GenericParam::Type(_)));
208 |
209 | let where_with_serialize = if let Some(w) = where_clause {
210 | quote! { #w, #ident #ty_generics : serde::Serialize }
211 | } else if has_types {
212 | quote! { where #ident #ty_generics : serde::Serialize }
213 | } else {
214 | quote! {}
215 | };
216 |
217 | quote! {
218 | impl #impl_generics redis::ToRedisArgs for #ident #ty_generics #where_with_serialize {
219 | fn write_redis_args(&self, out: &mut W)
220 | where
221 | W: ?Sized + redis::RedisWrite,
222 | {
223 | let buf = #serializer::to_string(&self).unwrap();
224 | out.write_arg(&buf.as_bytes())
225 | }
226 | }
227 | }
228 | .into()
229 | }
230 |
--------------------------------------------------------------------------------
/src/json.rs:
--------------------------------------------------------------------------------
1 | use redis::{RedisResult, Value};
2 | use serde::de::DeserializeOwned;
3 |
4 | /// Json struct is a wrapper to handle the return types from the RedisJSON commands.
5 | ///
6 | /// RedisJSON usually returns values in square brackets, which you usually had to handle manually:
7 | ///
8 | /// ```rust,no_run
9 | /// # use redis::{Client, JsonCommands, RedisResult};
10 | /// # use redis_macros::{FromRedisValue, ToRedisArgs, Json};
11 | /// # use serde::{Deserialize, Serialize};
12 | /// # #[derive(Serialize, Deserialize)]
13 | /// # struct User { id: u32 }
14 | /// #
15 | /// # fn main () -> redis::RedisResult<()> {
16 | /// # let client = redis::Client::open("redis://localhost:6379/")?;
17 | /// # let mut con = client.get_connection()?;
18 | /// # con.json_set("user", "$", &r#"{ "id": 1 }"#)?;
19 | /// // You have to manually deserialize this and pull from the Vec
20 | /// let user_id: String = con.json_get("user", "$.id")?; // => "[1]"
21 | /// # Ok(())
22 | /// # }
23 | /// ```
24 | ///
25 | /// Instead, `Json` implements the `FromRedisValue` trait, removes the square brackets and deserializes from JSON.
26 | /// For this your type don't even have to implement `FromRedisValue`, it only requires to be serde `Deserialize`-able.
27 | ///
28 | /// ```rust,no_run
29 | /// # use redis::{Client, JsonCommands, RedisResult};
30 | /// # use redis_macros::Json;
31 | /// # use serde::{Deserialize, Serialize};
32 | /// #[derive(Serialize, Deserialize)]
33 | /// struct User { id: u32 }
34 | ///
35 | /// # fn main () -> redis::RedisResult<()> {
36 | /// # let client = redis::Client::open("redis://localhost:6379/")?;
37 | /// # let mut con = client.get_connection()?;
38 | /// # con.json_set("user", "$", &r#"{ "id": 1 }"#)?;
39 | /// let Json(user_id): Json = con.json_get("user", "$.id")?; // => 1
40 | /// let Json(user): Json = con.json_get("user", "$")?; // => User { id: 1 }
41 | /// # Ok(())
42 | /// # }
43 | /// ```
44 | ///
45 | /// This command is designed to use RedisJSON commands. You could probably use this type
46 | /// to parse normal command outputs, but it removes the first and last character
47 | /// so it is not recommended.
48 | ///
49 | #[derive(Debug)]
50 | pub struct Json(
51 | /// The inner type to deserialize
52 | pub T
53 | );
54 |
55 | impl ::redis::FromRedisValue for Json
56 | where
57 | T: DeserializeOwned,
58 | {
59 | fn from_redis_value(v: &Value) -> RedisResult {
60 | match *v {
61 | Value::BulkString(ref bytes) => {
62 | if let Ok(s) = ::std::str::from_utf8(bytes) {
63 | let mut ch = s.chars();
64 | if ch.next() == Some('[') && ch.next_back() == Some(']') {
65 | if let Ok(t) = serde_json::from_str(ch.as_str()) {
66 | Ok(Json(t))
67 | } else {
68 | Err(::redis::RedisError::from((
69 | ::redis::ErrorKind::TypeError,
70 | "Response was of incompatible type",
71 | format!("Response type in JSON was not deserializable. (response was {v:?})"),
72 | )))
73 | }
74 | } else {
75 | Err(::redis::RedisError::from((
76 | ::redis::ErrorKind::TypeError,
77 | "Response was of incompatible type",
78 | format!("Response type was not JSON type. (response was {v:?})"),
79 | )))
80 | }
81 | } else {
82 | Err(::redis::RedisError::from((
83 | ::redis::ErrorKind::TypeError,
84 | "Response was of incompatible type",
85 | format!("Response was not valid UTF-8 string. (response was {v:?})"),
86 | )))
87 | }
88 | }
89 | _ => Err(::redis::RedisError::from((
90 | ::redis::ErrorKind::TypeError,
91 | "Response was of incompatible type",
92 | format!("Response type not RedisJSON deserializable. (response was {v:?})"),
93 | ))),
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! Simple macros and wrappers to [redis-rs](https://github.com/redis-rs/redis-rs/)
2 | //! to automatically serialize and deserialize structs with serde.
3 | //!
4 | //! ## Simple usage
5 | //!
6 | //! The simplest way to start is to derive `Serialize`, `Deserialize`,
7 | //! [`FromRedisValue`], [`ToRedisArgs`] for any kind of struct... and that's it!
8 | //! You can now get and set these values with regular redis commands:
9 | //!
10 | //! ```rust,no_run
11 | //! use redis::{Client, Commands, RedisResult};
12 | //! use redis_macros_derive::{FromRedisValue, ToRedisArgs};
13 | //! use serde::{Deserialize, Serialize};
14 | //!
15 | //! #[derive(Serialize, Deserialize)]
16 | //! enum Address {
17 | //! Street(String),
18 | //! Road(String),
19 | //! }
20 | //!
21 | //! // Derive the necessary traits
22 | //! #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
23 | //! struct User {
24 | //! id: u32,
25 | //! name: String,
26 | //! addresses: Vec,
27 | //! }
28 | //!
29 | //! fn main () -> redis::RedisResult<()> {
30 | //! let client = redis::Client::open("redis://localhost:6379/")?;
31 | //! let mut con = client.get_connection()?;
32 | //!
33 | //! let user = User {
34 | //! id: 1,
35 | //! name: "Ziggy".to_string(),
36 | //! addresses: vec![
37 | //! Address::Street("Downing".to_string()),
38 | //! Address::Road("Abbey".to_string()),
39 | //! ],
40 | //! };
41 | //!
42 | //! // Just use it as you would a primitive
43 | //! con.set("user", user)?;
44 | //! // user and stored_user will be the same
45 | //! let stored_user: User = con.get("user")?;
46 | //! # Ok(())
47 | //! }
48 | //! ```
49 | //!
50 | //! ## Usage with RedisJSON
51 | //!
52 | //! You can even use it with RedisJSON, to extract separate parts of the object.
53 | //!
54 | //! ```rust,no_run
55 | //! # use redis::{Client, RedisResult};
56 | //! use redis::JsonCommands;
57 | //! # use redis_macros_derive::{FromRedisValue, ToRedisArgs};
58 | //! # use serde::{Deserialize, Serialize};
59 | //!
60 | //! // Derive FromRedisValue, ToRedisArgs to the inner struct
61 | //! #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
62 | //! enum Address { Street(String), Road(String) }
63 | //! # #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
64 | //! # struct User { id: u32, name: String, addresses: Vec }
65 | //!
66 | //! # fn main () -> redis::RedisResult<()> {
67 | //! # let client = redis::Client::open("redis://localhost:6379/")?;
68 | //! # let mut con = client.get_connection()?;
69 | //! # let user = User { id: 1, name: "Ziggy".to_string(), addresses: vec![ Address::Street("Downing".to_string()), Address::Road("Abbey".to_string()) ] };
70 | //! // Simple usage is equivalent to set-get
71 | //! con.json_set("user", "$", &user)?;
72 | //! let stored_user: User = con.json_get("user", "$")?;
73 | //!
74 | //! // But you can get deep values - don't forget to derive traits for these too!
75 | //! let stored_address: Address = con.json_get("user", "$.addresses[0]")?;
76 | //! # Ok(())
77 | //! # }
78 | //! ```
79 | //!
80 | //! One issue you might be facing is that `redis` already has overrides for some types,
81 | //! for example Vec, String and most primitives. For this you have to use the [Json wrapper](#json-wrapper-with-redisjson).
82 | //!
83 | //! ```rust,no_run
84 | //! # use redis::{Client, JsonCommands, RedisResult};
85 | //! # use redis_macros_derive::{FromRedisValue, ToRedisArgs};
86 | //! # use serde::{Deserialize, Serialize};
87 | //! # #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
88 | //! # enum Address { Street(String), Road(String) }
89 | //! # #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
90 | //! # struct User { id: u32, name: String, addresses: Vec }
91 | //! # fn main () -> redis::RedisResult<()> {
92 | //! # let client = redis::Client::open("redis://localhost:6379/")?;
93 | //! # let mut con = client.get_connection()?;
94 | //! # let user = User { id: 1, name: "Ziggy".to_string(), addresses: vec![ Address::Street("Downing".to_string()), Address::Road("Abbey".to_string()) ] };
95 | //! # con.json_set("user", "$", &user)?;
96 | //! // This WON'T work
97 | //! let stored_addresses: Vec = con.json_get("user", "$.addresses")?;
98 | //! # Ok(())
99 | //! # }
100 | //! ```
101 | //!
102 | //! ## Json wrapper with RedisJSON
103 | //!
104 | //! To deserialize Vecs and primitive types when using RedisJSON, you cannot use the regular types,
105 | //! because these are non-compatible with RedisJSON. However `redis-macros` exports a useful wrapper
106 | //! struct: [`Json`]. When using RedisJSON, you can wrap your non-structs return values into this:
107 | //!
108 | //! ```rust,no_run
109 | //! use redis_macros::Json;
110 | //! # use redis::{Client, JsonCommands, RedisResult};
111 | //! # use redis_macros_derive::{FromRedisValue, ToRedisArgs};
112 | //! # use serde::{Deserialize, Serialize};
113 | //! # #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
114 | //! # enum Address { Street(String), Road(String) }
115 | //! # #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
116 | //! # struct User { id: u32, name: String, addresses: Vec }
117 | //! # fn main () -> redis::RedisResult<()> {
118 | //! # let client = redis::Client::open("redis://localhost:6379/")?;
119 | //! # let mut con = client.get_connection()?;
120 | //! # let user = User { id: 1, name: "Ziggy".to_string(), addresses: vec![ Address::Street("Downing".to_string()), Address::Road("Abbey".to_string()) ] };
121 | //! # con.json_set("user", "$", &user)?;
122 |
123 | //! // Return type can be wrapped into Json
124 | //! let Json(stored_name): Json = con.json_get("user", "$.name")?;
125 | //!
126 | //! // It works with Vecs as well
127 | //! let Json(stored_addresses): Json> = con.json_get("user", "$.addresses")?;
128 | //! // ...now stored_addresses will be equal to user.addresses
129 | //! # Ok(())
130 | //! # }
131 | //! ```
132 | //!
133 | //! If you only use RedisJSON, you can even do away with deriving `FromRedisValue` and `ToRedisArgs`, and use `Json` everywhere.
134 | //!
135 | //! ```rust,no_run
136 | //! # use redis::{Client, JsonCommands, RedisResult};
137 | //! # use redis_macros::Json;
138 | //! # use redis_macros_derive::{FromRedisValue, ToRedisArgs};
139 | //! # use serde::{Deserialize, Serialize};
140 | //! # #[derive(Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
141 | //! # enum Address { Street(String), Road(String) }
142 | //! #[derive(Serialize, Deserialize)]
143 | //! struct User { /* .. */ }
144 | //! # fn main () -> redis::RedisResult<()> {
145 | //! # let client = redis::Client::open("redis://localhost:6379/")?;
146 | //! # let mut con = client.get_connection()?;
147 | //! # let user = User {};
148 | //! # con.json_set("user", "$", &user)?;
149 | //!
150 | //! // This works with simple redis-rs
151 | //! con.json_set("user", "$", &user)?;
152 | //! // ...and you can get back with Json wrapper
153 | //! let Json(stored_user): Json = con.json_get("user", "$")?;
154 | //! # Ok(())
155 | //! # }
156 | //! ```
157 | //!
158 | //! ## Using other serializer (e.g. serde-yaml)
159 | //!
160 | //! In case you want to use another serializer, for example `serde_yaml`, you can install it and use the derives,
161 | //! the same way you would. The only difference should be adding an attribute `redis_serializer` under the derive,
162 | //! with the library you want to serialize with. You can use any Serde serializer as long as they support
163 | //! `from_str` and `to_string` methods. For the full list, see: [Serde data formats](https://serde.rs/#data-formats).
164 | //!
165 | //! ```rust,no_run
166 | //! # use redis::{Client, JsonCommands, RedisResult};
167 | //! # use redis_macros_derive::{FromRedisValue, ToRedisArgs};
168 | //! # use serde::{Deserialize, Serialize};
169 | //!
170 | //! #[derive(Debug, PartialEq, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
171 | //! #[redis_serializer(serde_yaml)]
172 | //! struct User { /* ... */ }
173 | //! ```
174 |
175 | #[cfg(feature = "macros")]
176 | extern crate redis_macros_derive;
177 |
178 | #[cfg(feature = "json")]
179 | mod json;
180 |
181 | #[cfg(feature = "json")]
182 | pub use json::Json;
183 |
184 | /// Derive macro for the redis crate's [`FromRedisValue`](../redis/trait.FromRedisValue.html) trait to allow parsing Redis responses to this type.
185 | ///
186 | /// For more information see the `redis_macros_derive` crate: [`FromRedisValue`](../redis_macros_derive/derive.FromRedisValue.html)
187 | #[cfg(feature = "macros")]
188 | pub use redis_macros_derive::FromRedisValue;
189 |
190 | /// Derive macro for the redis crate's [`ToRedisArgs`](../redis/trait.ToRedisArgs.html) trait to allow passing the type to Redis commands.
191 | ///
192 | /// For more information see the `redis_macros_derive` crate: [`ToRedisArgs`](../redis_macros_derive/derive.FromRedisValue.html)
193 | #[cfg(feature = "macros")]
194 | pub use redis_macros_derive::ToRedisArgs;
195 |
--------------------------------------------------------------------------------
/tests/derive_from_redis_value.rs:
--------------------------------------------------------------------------------
1 | use redis::{FromRedisValue, Value};
2 | use redis_macros::FromRedisValue;
3 | use serde::Deserialize;
4 |
5 | #[derive(Debug, PartialEq, Deserialize)]
6 | enum Address {
7 | Street(String),
8 | Road(String),
9 | }
10 |
11 | #[derive(Debug, PartialEq, Deserialize, FromRedisValue)]
12 | struct User {
13 | id: u32,
14 | name: String,
15 | addresses: Vec,
16 | }
17 |
18 | #[test]
19 | pub fn it_should_implement_the_from_redis_value_trait() {
20 | let user = User {
21 | id: 1,
22 | name: "Ziggy".to_string(),
23 | addresses: vec![
24 | Address::Street("Downing".to_string()),
25 | Address::Road("Abbey".to_string()),
26 | ],
27 | };
28 |
29 | let val = Value::BulkString("{\"id\":1,\"name\":\"Ziggy\",\"addresses\":[{\"Street\":\"Downing\"},{\"Road\":\"Abbey\"}]}".as_bytes().into());
30 | let result = User::from_redis_value(&val);
31 | assert_eq!(result, Ok(user));
32 | }
33 |
34 | #[test]
35 | pub fn it_should_also_deserialize_if_the_input_is_in_brackets() {
36 | let user = User {
37 | id: 1,
38 | name: "Ziggy".to_string(),
39 | addresses: vec![
40 | Address::Street("Downing".to_string()),
41 | Address::Road("Abbey".to_string()),
42 | ],
43 | };
44 |
45 | let val = Value::BulkString("[{\"id\":1,\"name\":\"Ziggy\",\"addresses\":[{\"Street\":\"Downing\"},{\"Road\":\"Abbey\"}]}]".as_bytes().into());
46 | let result = User::from_redis_value(&val);
47 | assert_eq!(result, Ok(user));
48 | }
49 |
50 | #[test]
51 | pub fn it_should_fail_if_input_is_not_compatible_with_type() {
52 | let val = Value::BulkString("{}".as_bytes().into());
53 | let result = User::from_redis_value(&val);
54 | if let Err(err) = result {
55 | assert_eq!(err.to_string(), "Response was of incompatible type - TypeError: Response type not deserializable to User with serde_json. (response was bulk-string('\"{}\"'))".to_string());
56 | } else {
57 | panic!("Deserialization should fail.");
58 | }
59 | }
60 |
61 | #[test]
62 | pub fn it_should_fail_if_input_is_not_valid_utf8() {
63 | let val = Value::BulkString(vec![0, 159, 146, 150]); // Some invalid utf8
64 | let result = User::from_redis_value(&val);
65 | if let Err(err) = result {
66 | assert_eq!(err.to_string(), "Response was of incompatible type - TypeError: Response was not valid UTF-8 string. (response was binary-data([0, 159, 146, 150]))".to_string());
67 | } else {
68 | panic!("UTF-8 parsing should fail.");
69 | }
70 | }
71 |
72 | #[test]
73 | pub fn it_should_fail_if_input_is_missing() {
74 | let val = Value::Nil;
75 | let result = User::from_redis_value(&val);
76 | if let Err(err) = result {
77 | assert_eq!(err.to_string(), "Response was of incompatible type - TypeError: Response type was not deserializable to User. (response was nil)".to_string());
78 | } else {
79 | panic!("UTF-8 parsing should fail.");
80 | }
81 | }
--------------------------------------------------------------------------------
/tests/derive_from_redis_value_redis_yaml.rs:
--------------------------------------------------------------------------------
1 | use redis::{FromRedisValue, Value};
2 | use redis_macros::FromRedisValue;
3 | use serde::Deserialize;
4 |
5 | #[derive(Debug, PartialEq, Deserialize)]
6 | enum Address {
7 | Street(String),
8 | Road(String),
9 | }
10 |
11 | #[derive(Debug, PartialEq, Deserialize, FromRedisValue)]
12 | #[redis_serializer(serde_yaml)]
13 | struct User {
14 | id: u32,
15 | name: String,
16 | addresses: Vec,
17 | }
18 |
19 | #[test]
20 | pub fn it_should_implement_the_from_redis_value_trait_with_redis_yaml() {
21 | let user = User {
22 | id: 1,
23 | name: "Ziggy".to_string(),
24 | addresses: vec![
25 | Address::Street("Downing".to_string()),
26 | Address::Road("Abbey".to_string()),
27 | ],
28 | };
29 |
30 | let val = Value::BulkString(
31 | "id: 1
32 | name: Ziggy
33 | addresses:
34 | - !Street Downing
35 | - !Road Abbey
36 | "
37 | .as_bytes()
38 | .into(),
39 | );
40 | let result = User::from_redis_value(&val);
41 | assert_eq!(result, Ok(user));
42 | }
43 |
--------------------------------------------------------------------------------
/tests/derive_to_redis_args.rs:
--------------------------------------------------------------------------------
1 | use redis::ToRedisArgs;
2 | use redis_macros::ToRedisArgs;
3 | use serde::Serialize;
4 |
5 | #[derive(Debug, Serialize)]
6 | enum Address {
7 | Street(String),
8 | Road(String),
9 | }
10 |
11 | #[derive(Debug, Serialize, ToRedisArgs)]
12 | struct User {
13 | id: u32,
14 | name: String,
15 | addresses: Vec,
16 | }
17 |
18 | #[test]
19 | pub fn it_should_implement_the_to_redis_args_trait() {
20 | let user = User {
21 | id: 1,
22 | name: "Ziggy".to_string(),
23 | addresses: vec![
24 | Address::Street("Downing".to_string()),
25 | Address::Road("Abbey".to_string()),
26 | ],
27 | };
28 |
29 | let bytes = user.to_redis_args();
30 | assert_eq!(bytes[0], "{\"id\":1,\"name\":\"Ziggy\",\"addresses\":[{\"Street\":\"Downing\"},{\"Road\":\"Abbey\"}]}".as_bytes());
31 | }
32 |
--------------------------------------------------------------------------------
/tests/derive_to_redis_args_redis_yaml.rs:
--------------------------------------------------------------------------------
1 | use redis::ToRedisArgs;
2 | use redis_macros::ToRedisArgs;
3 | use serde::Serialize;
4 |
5 | #[derive(Debug, Serialize)]
6 | enum Address {
7 | Street(String),
8 | Road(String),
9 | }
10 |
11 | #[derive(Debug, Serialize, ToRedisArgs)]
12 | #[redis_serializer(serde_yaml)]
13 | struct User {
14 | id: u32,
15 | name: String,
16 | addresses: Vec,
17 | }
18 |
19 | #[test]
20 | pub fn it_should_implement_the_to_redis_args_trait() {
21 | let user = User {
22 | id: 1,
23 | name: "Ziggy".to_string(),
24 | addresses: vec![
25 | Address::Street("Downing".to_string()),
26 | Address::Road("Abbey".to_string()),
27 | ],
28 | };
29 |
30 | let bytes = user.to_redis_args();
31 | println!("{}", std::str::from_utf8(&bytes[0]).unwrap());
32 | assert_eq!(
33 | bytes[0],
34 | "id: 1
35 | name: Ziggy
36 | addresses:
37 | - !Street Downing
38 | - !Road Abbey
39 | "
40 | .as_bytes()
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/tests/json_wrapper.rs:
--------------------------------------------------------------------------------
1 | use redis::{FromRedisValue, Value};
2 | use redis_macros::Json;
3 | use serde::Deserialize;
4 |
5 | #[derive(Debug, PartialEq, Deserialize)]
6 | enum Address {
7 | Street(String),
8 | Road(String),
9 | }
10 |
11 | #[derive(Debug, PartialEq, Deserialize)]
12 | struct User {
13 | id: u32,
14 | name: String,
15 | addresses: Vec,
16 | }
17 |
18 | #[test]
19 | pub fn it_should_deserialize_json_results() {
20 | let user = User {
21 | id: 1,
22 | name: "Ziggy".to_string(),
23 | addresses: vec![
24 | Address::Street("Downing".to_string()),
25 | Address::Road("Abbey".to_string()),
26 | ],
27 | };
28 |
29 | let val = Value::BulkString("[{\"id\":1,\"name\":\"Ziggy\",\"addresses\":[{\"Street\":\"Downing\"},{\"Road\":\"Abbey\"}]}]".as_bytes().into());
30 | let result = Json::::from_redis_value(&val);
31 | if let Ok(Json(parsed_user)) = result {
32 | assert_eq!(parsed_user, user);
33 | } else {
34 | panic!("JSON parsing should succeed.");
35 | }
36 | }
37 |
38 | #[test]
39 | pub fn it_should_also_deserialize_json_wrappable_arguments() {
40 | let addresses = vec![
41 | Address::Street("Downing".to_string()),
42 | Address::Road("Abbey".to_string()),
43 | ];
44 |
45 | let val = Value::BulkString(
46 | "[[{\"Street\":\"Downing\"},{\"Road\":\"Abbey\"}]]"
47 | .as_bytes()
48 | .into(),
49 | );
50 | // This would fail without the JSON wrapper
51 | let result = Json::>::from_redis_value(&val);
52 | if let Ok(Json(parsed_addresses)) = result {
53 | assert_eq!(parsed_addresses, addresses);
54 | } else {
55 | panic!("JSON parsing should succeed.");
56 | }
57 | }
58 |
59 | #[test]
60 | pub fn it_should_fail_if_the_result_is_not_redis_json() {
61 | // RedisJSON responses should have wrapping brackets (i.e. [{...}])
62 | let val = Value::BulkString("{\"id\":1,\"name\":\"Ziggy\",\"addresses\":[{\"Street\":\"Downing\"},{\"Road\":\"Abbey\"}]}".as_bytes().into());
63 | let result = Json::::from_redis_value(&val);
64 | if let Err(err) = result {
65 | assert_eq!(err.to_string(), "Response was of incompatible type - TypeError: Response type was not JSON type. (response was bulk-string('\"{\\\"id\\\":1,\\\"name\\\":\\\"Ziggy\\\",\\\"addresses\\\":[{\\\"Street\\\":\\\"Downing\\\"},{\\\"Road\\\":\\\"Abbey\\\"}]}\"'))".to_string());
66 | } else {
67 | panic!("RedisJSON unwrapping should fail.");
68 | }
69 | }
70 |
71 | #[test]
72 | pub fn it_should_fail_if_input_is_not_compatible_with_type() {
73 | let val = Value::BulkString("[{}]".as_bytes().into());
74 | let result = Json::::from_redis_value(&val);
75 | if let Err(err) = result {
76 | assert_eq!(err.to_string(), "Response was of incompatible type - TypeError: Response type in JSON was not deserializable. (response was bulk-string('\"[{}]\"'))".to_string());
77 | } else {
78 | panic!("Deserialization should fail.");
79 | }
80 | }
81 |
82 | #[test]
83 | pub fn it_should_fail_if_input_is_not_valid_utf8() {
84 | let val = Value::BulkString(vec![0, 159, 146, 150]); // Some invalid utf8
85 | let result = Json::::from_redis_value(&val);
86 | if let Err(err) = result {
87 | assert_eq!(err.to_string(), "Response was of incompatible type - TypeError: Response was not valid UTF-8 string. (response was binary-data([0, 159, 146, 150]))".to_string());
88 | } else {
89 | panic!("UTF-8 parsing should fail.");
90 | }
91 | }
92 |
93 | #[test]
94 | pub fn it_should_fail_if_input_is_missing() {
95 | let val = Value::Nil;
96 | let result = Json::::from_redis_value(&val);
97 | if let Err(err) = result {
98 | assert_eq!(err.to_string(), "Response was of incompatible type - TypeError: Response type not RedisJSON deserializable. (response was nil)".to_string());
99 | } else {
100 | panic!("Value Nil should fail.");
101 | }
102 | }
--------------------------------------------------------------------------------