├── .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 | } --------------------------------------------------------------------------------