├── .github └── workflows │ ├── publish.yml │ └── pull-request.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── VERSION.txt ├── crates-io.md ├── example.json ├── retag.sh ├── valu3 ├── Cargo.toml ├── README.md ├── crates-io.md └── src │ ├── impls.rs │ ├── lib.rs │ ├── macros.rs │ ├── parser │ ├── json │ │ ├── json.pest │ │ └── mod.rs │ └── mod.rs │ ├── prelude.rs │ ├── primitives.rs │ ├── serde_value │ ├── de.rs │ ├── mod.rs │ └── ser.rs │ ├── tests │ ├── derive.rs │ └── mod.rs │ ├── to │ ├── bin.rs │ ├── json.rs │ ├── mod.rs │ └── yaml.rs │ ├── to_value.rs │ ├── traits.rs │ ├── types │ ├── array.rs │ ├── datetime.rs │ ├── mod.rs │ ├── number.rs │ ├── object.rs │ ├── stringb.rs │ └── value_key.rs │ └── value.rs └── valu3_derive ├── Cargo.toml ├── README.md ├── crates-io.md ├── src └── lib.rs └── tests ├── .gitignore ├── derive_test.rs └── tests.rs /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Tagging and Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: write-all 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 13 | 14 | jobs: 15 | test: 16 | name: Test 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | toolchain: 21 | - stable 22 | - beta 23 | - nightly 24 | steps: 25 | - uses: actions/checkout@v4 26 | - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} 27 | - run: cargo build --verbose 28 | - run: cargo test --verbose 29 | tag: 30 | needs: test 31 | name: Tagging 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout code 35 | uses: actions/checkout@v2 36 | 37 | - name: Read version from VERSION.txt 38 | id: get_version 39 | run: | 40 | version=$(cat VERSION.txt) 41 | echo "VERSION=${version}" >> $GITHUB_ENV 42 | 43 | - name: Create tag 44 | run: | 45 | git tag "$VERSION" 46 | git push origin "$VERSION" 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | 50 | valu3_derive_publish: 51 | needs: tag 52 | name: Valu3 Derive - latest 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v4 56 | - run: cargo publish --verbose -p valu3-derive 57 | 58 | valu3_publish: 59 | needs: valu3_derive_publish 60 | name: Valu3 - latest 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v4 64 | - run: cargo publish --verbose -p valu3 -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Auto test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build_and_test: 10 | name: Rust project - latest 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | toolchain: 15 | - stable 16 | - beta 17 | - nightly 18 | steps: 19 | - uses: actions/checkout@v4 20 | - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} 21 | - run: cargo build --verbose 22 | - run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.parquet 3 | .vscode -------------------------------------------------------------------------------- /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 = "aho-corasick" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "android-tzdata" 16 | version = "0.1.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 19 | 20 | [[package]] 21 | name = "android_system_properties" 22 | version = "0.1.5" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 25 | dependencies = [ 26 | "libc", 27 | ] 28 | 29 | [[package]] 30 | name = "autocfg" 31 | version = "1.1.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 34 | 35 | [[package]] 36 | name = "bincode" 37 | version = "1.3.3" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 40 | dependencies = [ 41 | "serde", 42 | ] 43 | 44 | [[package]] 45 | name = "block-buffer" 46 | version = "0.10.4" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 49 | dependencies = [ 50 | "generic-array", 51 | ] 52 | 53 | [[package]] 54 | name = "bumpalo" 55 | version = "3.13.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 58 | 59 | [[package]] 60 | name = "cc" 61 | version = "1.0.79" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 64 | 65 | [[package]] 66 | name = "cfg-if" 67 | version = "1.0.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 70 | 71 | [[package]] 72 | name = "chrono" 73 | version = "0.4.39" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" 76 | dependencies = [ 77 | "android-tzdata", 78 | "iana-time-zone", 79 | "js-sys", 80 | "num-traits", 81 | "wasm-bindgen", 82 | "windows-targets 0.52.0", 83 | ] 84 | 85 | [[package]] 86 | name = "core-foundation-sys" 87 | version = "0.8.4" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 90 | 91 | [[package]] 92 | name = "cpufeatures" 93 | version = "0.2.9" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" 96 | dependencies = [ 97 | "libc", 98 | ] 99 | 100 | [[package]] 101 | name = "crypto-common" 102 | version = "0.1.6" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 105 | dependencies = [ 106 | "generic-array", 107 | "typenum", 108 | ] 109 | 110 | [[package]] 111 | name = "diff" 112 | version = "0.1.13" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 115 | 116 | [[package]] 117 | name = "digest" 118 | version = "0.10.7" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 121 | dependencies = [ 122 | "block-buffer", 123 | "crypto-common", 124 | ] 125 | 126 | [[package]] 127 | name = "equivalent" 128 | version = "1.0.2" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 131 | 132 | [[package]] 133 | name = "fastrand" 134 | version = "2.3.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 137 | 138 | [[package]] 139 | name = "generic-array" 140 | version = "0.14.7" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 143 | dependencies = [ 144 | "typenum", 145 | "version_check", 146 | ] 147 | 148 | [[package]] 149 | name = "getrandom" 150 | version = "0.2.12" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 153 | dependencies = [ 154 | "cfg-if", 155 | "libc", 156 | "wasi", 157 | ] 158 | 159 | [[package]] 160 | name = "glob" 161 | version = "0.3.2" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 164 | 165 | [[package]] 166 | name = "hashbrown" 167 | version = "0.15.2" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 170 | 171 | [[package]] 172 | name = "iana-time-zone" 173 | version = "0.1.57" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" 176 | dependencies = [ 177 | "android_system_properties", 178 | "core-foundation-sys", 179 | "iana-time-zone-haiku", 180 | "js-sys", 181 | "wasm-bindgen", 182 | "windows", 183 | ] 184 | 185 | [[package]] 186 | name = "iana-time-zone-haiku" 187 | version = "0.1.2" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 190 | dependencies = [ 191 | "cc", 192 | ] 193 | 194 | [[package]] 195 | name = "indexmap" 196 | version = "2.8.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 199 | dependencies = [ 200 | "equivalent", 201 | "hashbrown", 202 | ] 203 | 204 | [[package]] 205 | name = "itoa" 206 | version = "1.0.10" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 209 | 210 | [[package]] 211 | name = "js-sys" 212 | version = "0.3.64" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 215 | dependencies = [ 216 | "wasm-bindgen", 217 | ] 218 | 219 | [[package]] 220 | name = "libc" 221 | version = "0.2.153" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 224 | 225 | [[package]] 226 | name = "log" 227 | version = "0.4.19" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" 230 | 231 | [[package]] 232 | name = "macrotest" 233 | version = "1.1.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "f0597a8d49ceeea5845b12d1970aa993261e68d4660b327eabab667b3e7ffd60" 236 | dependencies = [ 237 | "diff", 238 | "fastrand", 239 | "glob", 240 | "prettyplease", 241 | "serde", 242 | "serde_derive", 243 | "serde_json", 244 | "syn", 245 | "toml_edit", 246 | ] 247 | 248 | [[package]] 249 | name = "memchr" 250 | version = "2.7.1" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 253 | 254 | [[package]] 255 | name = "num-traits" 256 | version = "0.2.18" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 259 | dependencies = [ 260 | "autocfg", 261 | ] 262 | 263 | [[package]] 264 | name = "once_cell" 265 | version = "1.18.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 268 | 269 | [[package]] 270 | name = "pest" 271 | version = "2.7.15" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" 274 | dependencies = [ 275 | "memchr", 276 | "thiserror", 277 | "ucd-trie", 278 | ] 279 | 280 | [[package]] 281 | name = "pest_derive" 282 | version = "2.7.15" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" 285 | dependencies = [ 286 | "pest", 287 | "pest_generator", 288 | ] 289 | 290 | [[package]] 291 | name = "pest_generator" 292 | version = "2.7.15" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" 295 | dependencies = [ 296 | "pest", 297 | "pest_meta", 298 | "proc-macro2", 299 | "quote", 300 | "syn", 301 | ] 302 | 303 | [[package]] 304 | name = "pest_meta" 305 | version = "2.7.15" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" 308 | dependencies = [ 309 | "once_cell", 310 | "pest", 311 | "sha2", 312 | ] 313 | 314 | [[package]] 315 | name = "ppv-lite86" 316 | version = "0.2.17" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 319 | 320 | [[package]] 321 | name = "prettyplease" 322 | version = "0.2.31" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" 325 | dependencies = [ 326 | "proc-macro2", 327 | "syn", 328 | ] 329 | 330 | [[package]] 331 | name = "proc-macro2" 332 | version = "1.0.94" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 335 | dependencies = [ 336 | "unicode-ident", 337 | ] 338 | 339 | [[package]] 340 | name = "quote" 341 | version = "1.0.40" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 344 | dependencies = [ 345 | "proc-macro2", 346 | ] 347 | 348 | [[package]] 349 | name = "rand" 350 | version = "0.8.5" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 353 | dependencies = [ 354 | "libc", 355 | "rand_chacha", 356 | "rand_core", 357 | ] 358 | 359 | [[package]] 360 | name = "rand_chacha" 361 | version = "0.3.1" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 364 | dependencies = [ 365 | "ppv-lite86", 366 | "rand_core", 367 | ] 368 | 369 | [[package]] 370 | name = "rand_core" 371 | version = "0.6.4" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 374 | dependencies = [ 375 | "getrandom", 376 | ] 377 | 378 | [[package]] 379 | name = "regex" 380 | version = "1.11.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 383 | dependencies = [ 384 | "aho-corasick", 385 | "memchr", 386 | "regex-automata", 387 | "regex-syntax", 388 | ] 389 | 390 | [[package]] 391 | name = "regex-automata" 392 | version = "0.4.9" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 395 | dependencies = [ 396 | "aho-corasick", 397 | "memchr", 398 | "regex-syntax", 399 | ] 400 | 401 | [[package]] 402 | name = "regex-syntax" 403 | version = "0.8.5" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 406 | 407 | [[package]] 408 | name = "ryu" 409 | version = "1.0.16" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" 412 | 413 | [[package]] 414 | name = "serde" 415 | version = "1.0.216" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" 418 | dependencies = [ 419 | "serde_derive", 420 | ] 421 | 422 | [[package]] 423 | name = "serde_derive" 424 | version = "1.0.216" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" 427 | dependencies = [ 428 | "proc-macro2", 429 | "quote", 430 | "syn", 431 | ] 432 | 433 | [[package]] 434 | name = "serde_json" 435 | version = "1.0.134" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" 438 | dependencies = [ 439 | "itoa", 440 | "memchr", 441 | "ryu", 442 | "serde", 443 | ] 444 | 445 | [[package]] 446 | name = "serde_spanned" 447 | version = "0.6.8" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 450 | dependencies = [ 451 | "serde", 452 | ] 453 | 454 | [[package]] 455 | name = "sha2" 456 | version = "0.10.7" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" 459 | dependencies = [ 460 | "cfg-if", 461 | "cpufeatures", 462 | "digest", 463 | ] 464 | 465 | [[package]] 466 | name = "syn" 467 | version = "2.0.100" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 470 | dependencies = [ 471 | "proc-macro2", 472 | "quote", 473 | "unicode-ident", 474 | ] 475 | 476 | [[package]] 477 | name = "thiserror" 478 | version = "2.0.9" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" 481 | dependencies = [ 482 | "thiserror-impl", 483 | ] 484 | 485 | [[package]] 486 | name = "thiserror-impl" 487 | version = "2.0.9" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" 490 | dependencies = [ 491 | "proc-macro2", 492 | "quote", 493 | "syn", 494 | ] 495 | 496 | [[package]] 497 | name = "toml_datetime" 498 | version = "0.6.8" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 501 | dependencies = [ 502 | "serde", 503 | ] 504 | 505 | [[package]] 506 | name = "toml_edit" 507 | version = "0.22.24" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" 510 | dependencies = [ 511 | "indexmap", 512 | "serde", 513 | "serde_spanned", 514 | "toml_datetime", 515 | "winnow", 516 | ] 517 | 518 | [[package]] 519 | name = "typenum" 520 | version = "1.16.0" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 523 | 524 | [[package]] 525 | name = "ucd-trie" 526 | version = "0.1.6" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" 529 | 530 | [[package]] 531 | name = "unicode-ident" 532 | version = "1.0.10" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" 535 | 536 | [[package]] 537 | name = "valu3" 538 | version = "0.8.3" 539 | dependencies = [ 540 | "bincode", 541 | "chrono", 542 | "pest", 543 | "pest_derive", 544 | "rand", 545 | "regex", 546 | "serde", 547 | "serde_json", 548 | "valu3-derive", 549 | ] 550 | 551 | [[package]] 552 | name = "valu3-derive" 553 | version = "0.8.3" 554 | dependencies = [ 555 | "macrotest", 556 | "proc-macro2", 557 | "quote", 558 | "syn", 559 | "valu3", 560 | ] 561 | 562 | [[package]] 563 | name = "version_check" 564 | version = "0.9.4" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 567 | 568 | [[package]] 569 | name = "wasi" 570 | version = "0.11.0+wasi-snapshot-preview1" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 573 | 574 | [[package]] 575 | name = "wasm-bindgen" 576 | version = "0.2.87" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 579 | dependencies = [ 580 | "cfg-if", 581 | "wasm-bindgen-macro", 582 | ] 583 | 584 | [[package]] 585 | name = "wasm-bindgen-backend" 586 | version = "0.2.87" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 589 | dependencies = [ 590 | "bumpalo", 591 | "log", 592 | "once_cell", 593 | "proc-macro2", 594 | "quote", 595 | "syn", 596 | "wasm-bindgen-shared", 597 | ] 598 | 599 | [[package]] 600 | name = "wasm-bindgen-macro" 601 | version = "0.2.87" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 604 | dependencies = [ 605 | "quote", 606 | "wasm-bindgen-macro-support", 607 | ] 608 | 609 | [[package]] 610 | name = "wasm-bindgen-macro-support" 611 | version = "0.2.87" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 614 | dependencies = [ 615 | "proc-macro2", 616 | "quote", 617 | "syn", 618 | "wasm-bindgen-backend", 619 | "wasm-bindgen-shared", 620 | ] 621 | 622 | [[package]] 623 | name = "wasm-bindgen-shared" 624 | version = "0.2.87" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 627 | 628 | [[package]] 629 | name = "windows" 630 | version = "0.48.0" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" 633 | dependencies = [ 634 | "windows-targets 0.48.1", 635 | ] 636 | 637 | [[package]] 638 | name = "windows-targets" 639 | version = "0.48.1" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" 642 | dependencies = [ 643 | "windows_aarch64_gnullvm 0.48.0", 644 | "windows_aarch64_msvc 0.48.0", 645 | "windows_i686_gnu 0.48.0", 646 | "windows_i686_msvc 0.48.0", 647 | "windows_x86_64_gnu 0.48.0", 648 | "windows_x86_64_gnullvm 0.48.0", 649 | "windows_x86_64_msvc 0.48.0", 650 | ] 651 | 652 | [[package]] 653 | name = "windows-targets" 654 | version = "0.52.0" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 657 | dependencies = [ 658 | "windows_aarch64_gnullvm 0.52.0", 659 | "windows_aarch64_msvc 0.52.0", 660 | "windows_i686_gnu 0.52.0", 661 | "windows_i686_msvc 0.52.0", 662 | "windows_x86_64_gnu 0.52.0", 663 | "windows_x86_64_gnullvm 0.52.0", 664 | "windows_x86_64_msvc 0.52.0", 665 | ] 666 | 667 | [[package]] 668 | name = "windows_aarch64_gnullvm" 669 | version = "0.48.0" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 672 | 673 | [[package]] 674 | name = "windows_aarch64_gnullvm" 675 | version = "0.52.0" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 678 | 679 | [[package]] 680 | name = "windows_aarch64_msvc" 681 | version = "0.48.0" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 684 | 685 | [[package]] 686 | name = "windows_aarch64_msvc" 687 | version = "0.52.0" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 690 | 691 | [[package]] 692 | name = "windows_i686_gnu" 693 | version = "0.48.0" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 696 | 697 | [[package]] 698 | name = "windows_i686_gnu" 699 | version = "0.52.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 702 | 703 | [[package]] 704 | name = "windows_i686_msvc" 705 | version = "0.48.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 708 | 709 | [[package]] 710 | name = "windows_i686_msvc" 711 | version = "0.52.0" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 714 | 715 | [[package]] 716 | name = "windows_x86_64_gnu" 717 | version = "0.48.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 720 | 721 | [[package]] 722 | name = "windows_x86_64_gnu" 723 | version = "0.52.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 726 | 727 | [[package]] 728 | name = "windows_x86_64_gnullvm" 729 | version = "0.48.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 732 | 733 | [[package]] 734 | name = "windows_x86_64_gnullvm" 735 | version = "0.52.0" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 738 | 739 | [[package]] 740 | name = "windows_x86_64_msvc" 741 | version = "0.48.0" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 744 | 745 | [[package]] 746 | name = "windows_x86_64_msvc" 747 | version = "0.52.0" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 750 | 751 | [[package]] 752 | name = "winnow" 753 | version = "0.7.4" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" 756 | dependencies = [ 757 | "memchr", 758 | ] 759 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["valu3", "valu3_derive"] 3 | resolver = "2" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Valu3: Unleash the Power of Data Manipulation in Rust 🚀 2 | 3 | Welcome to **Valu3** - the ultimate, flexible, and powerful library for manipulating diverse data types in your Rust projects. Say goodbye to the complexity of handling numbers, strings, arrays, objects, and datetime values. Valu3 is here to make your life easier! 4 | 5 | 6 | [![crates.io](https://img.shields.io/crates/v/valu3?label=0.6.4)](https://crates.io/crates/valu3) 7 | [![Documentation](https://docs.rs/valu3/badge.svg?version=0.6.4)](https://docs.rs/valu3/0.6.4) 8 | ![MSRV](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) 9 | ![Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) 10 | [![Dependency Status](https://deps.rs/crate/valu3/0.6.4/status.svg)](https://deps.rs/crate/valu3/0.6.4) 11 | ![Main test](https://github.com/lowcarboncode/valu3/actions/workflows/main-test.yml/badge.svg) 12 | [![codecov](https://codecov.io/gh/lowcarboncode/valu3/branch/master/graph/badge.svg)](https://codecov.io/gh/lowcarboncode/valu3) 13 | ![downloads](https://img.shields.io/crates/d/valu3.svg) 14 | 15 | ## 🌟 Key Features 16 | 17 | 1. **Universal Type Handling**: No more juggling with different data types! Valu3's generic type support enables smooth management of all data types under one roof. 18 | 2. **Intuitive API**: Experience effortless data manipulation with Valu3's user-friendly API, designed to provide consistency across diverse data types. 19 | 3. **All-in-One Data Manipulation**: Valu3 has it all - numeric, string, object, array, and date/time manipulation. No need to look anywhere else! 20 | 4. **Effortless Data Conversion**: Convert your data to and from popular formats like JSON, YAML, and XML with ease, thanks to Valu3's built-in support. 21 | 5. **Serde Integration**: Serialize and deserialize your data seamlessly with Valu3's out-of-the-box integration with the Serde library. 22 | 6. **Native Struct Parsing & Validation**: Valu3 and Pest join forces to offer native parsing, conversion, and validation of data to structs, ensuring data integrity at every step. 23 | 7. **Payload Interpretation & Transformation**: Valu3 interprets and transforms payload strings like a champ, handling JSON from HTTP request bodies and more. 24 | 25 | ## 💡 Why Choose Valu3? 26 | 27 | Valu3 is designed to make data manipulation tasks in Rust a breeze. By combining a wide range of features and a consistent API, it simplifies data handling in Rust projects while maximizing productivity. 28 | 29 | Join the Valu3 revolution and experience the future of data manipulation in Rust! 🎉 30 | 31 | **⚡ Get Started with Valu3 Today! ⚡** 32 | 33 | ## Examples :space_invader: 34 | 35 | Here are some examples of how to use the Valu3: 36 | 37 | ```rust 38 | use valu3::prelude::*; 39 | 40 | let string_value = "hello".to_value(); 41 | let number_value = 42.to_value(); 42 | let boolean_value = true.to_value(); 43 | let null_value = Value::Null; 44 | let undefined_value = Value::Undefined; 45 | let mut datetime_value = DateTime::from("2023-04-05T00:00:00Z").to_value(); 46 | 47 | string_value.as_string(); 48 | number_value.get_i32(); 49 | assert_eq!(boolean_value, true); 50 | assert_eq!(null_value, Value::Null); 51 | assert_eq!(undefined_value, Value::Undefined); 52 | datetime_value.add_days(1); 53 | ``` 54 | 55 | ## Getting Started 56 | To start using the Valu3 in your Rust project, simply add the following line to your `Cargo.toml` file: 57 | ```toml 58 | [dependencies] 59 | valu3 = "0.1" 60 | ``` 61 | 62 | Then, you can import the library in your code like this: 63 | ```rust 64 | use valu3::prelude::*; 65 | 66 | //... 67 | 68 | let pi = 3.14.to_value(); 69 | ``` 70 | 71 | ## Structs and Conversions 72 | Valu3 natively has conversions for famous data types like json, yaml and xml. Furthermore with `valu3-derive` you are able to transform `struct` to `Value` by applying the `to_value()` method generated by the `ToValue` derive macros. This is an example on converting `struct` to `Value` and `Value` to other payload data types. 73 | 74 | ```rust 75 | use valu3::prelude:*; 76 | 77 | #[derive(ToValue, FromValue, Default)] 78 | struct MyStruct { 79 | id: u32, 80 | name: String, 81 | tags: Vec 82 | } 83 | 84 | fn main(){ 85 | let my_struct = MyStruct::default(); 86 | let value = my_struct.to_value(); 87 | 88 | assert_eq!(my_struct, MyStruct::from_value(value)); 89 | } 90 | 91 | ``` 92 | 93 | ### ToJson 94 | If your focus is only on using `Valu3` for conversion only, use the `ToJson` macro. 95 | 96 | ```rust 97 | use valu3::prelude:*; 98 | 99 | #[derive(ToJson, Default)] 100 | struct MyStruct { 101 | id: u32, 102 | name: String, 103 | tags: Vec 104 | } 105 | 106 | fn main(){ 107 | let my_struct = MyStruct::default(); 108 | let json = my_struct.to_json(); 109 | 110 | println!("{}", json); // print json string 111 | } 112 | 113 | ``` 114 | 115 | ## Payload 116 | 117 | `Vale3` is able to recognize a payload string, identify and convert it to `Value`, follow the example: 118 | 119 | ```rust 120 | use valu3::prelude:*; 121 | fn main(){ 122 | let boolean = Value::payload_to_value("true").unwrap(); 123 | let float = Value::payload_to_value("3.14").unwrap(); 124 | let json = Value::payload_to_value(r#"{"item": 3.14}"#).unwrap(); 125 | let array = Value::payload_to_value(r#"[1,2,3]"#).unwrap(); 126 | let null = Value::payload_to_value("null").unwrap(); 127 | let string = Value::payload_to_value(r#""123""#).unwrap(); 128 | 129 | assert_eq!(boolean, true.to_value()); 130 | assert_eq!(float, 3.14.to_value()); 131 | assert_eq!(json, Value::from(vec![("item", 3.14)])); 132 | assert_eq!(array, vec![1, 2, 3].to_value()); 133 | assert_eq!(null, Value::Null); 134 | assert_eq!(string, "123".to_value()); 135 | } 136 | 137 | ``` 138 | 139 | ## Contributing 140 | If you find a bug or have a suggestion for a new feature, please open an issue on the [GitHub repository](https://github.com/lowcarboncode/valu3/issues). 141 | 142 | If you would like to contribute to the project, please feel free to submit a pull request. Before submitting a pull request, please make sure that your code adheres to the project's style guidelines and passes all tests. 143 | 144 | ## Upcoming Features: Stay in Sync with the Future of Valu3 🌐 145 | ~ 146 | We're constantly working to improve and expand the capabilities of Valu3, making it even more powerful and versatile. 147 | 148 | By keeping track of the project's progress, you can stay informed about new features in development and planned improvements. This will allow you to make the most of Valu3 in your Rust projects and prepare for future updates. 149 | 150 | Our commitment is to make Valu3 the ultimate data manipulation solution in Rust. Your input is invaluable! Feel free to join the discussions, share your ideas, and contribute to the project as it evolves. 151 | 152 | Join us in the ongoing journey to refine and expand Valu3! 🚀 153 | 154 | 155 | ## License 156 | This project is licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/lowcarboncode/valu3/blob/main/LICENSE) file for more information. 157 | -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 0.8.3 -------------------------------------------------------------------------------- /crates-io.md: -------------------------------------------------------------------------------- 1 | # Valu3 2 | 3 | The Valu3 provides a flexible and powerful way to manipulate different types of data in your Rust projects. Whether you need to work with numbers, strings, arrays, objects, or datetime values, the Valu3 has you covered. 4 | 5 | ## Docs and Notices 6 | Use our github to stay up to date [Valu3](https://github.com/lowcarboncode/valu3) 7 | 8 | ## License 9 | This project is licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/lowcarboncode/valu3/blob/main/LICENSE) file for more information. -------------------------------------------------------------------------------- /example.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu": { 3 | "id": "file", 4 | "value": "File", 5 | "popup": { 6 | "menuitem": [ 7 | { 8 | "value": "New", 9 | "onclick": "CreateNewDoc()" 10 | }, 11 | { 12 | "value": "Open", 13 | "onclick": "OpenDoc()" 14 | }, 15 | { 16 | "value": "Close", 17 | "onclick": "CloseDoc()" 18 | } 19 | ] 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /retag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script replaces the old tag with the new tag in the files listed in the files array. 4 | # Usage: ./retag.sh 5 | # Example: ./retag.sh 0.1.0 6 | 7 | # -h or --help: Show help message 8 | if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then 9 | echo "This script replaces the old tag with the new tag in the files listed in the files array." 10 | echo "Usage: ./retag.sh " 11 | echo "Example: ./retag.sh 0.1.0" 12 | exit 0 13 | fi 14 | 15 | # Verifica se o arquivo VERSION.txt existe 16 | if [ ! -f "VERSION.txt" ]; then 17 | echo "Arquivo VERSION.txt não encontrado." 18 | exit 1 19 | fi 20 | 21 | # Lê a última tag do arquivo VERSION.txt 22 | last_tag=$(" 27 | exit 1 28 | fi 29 | 30 | new_tag=$1 31 | 32 | files=("valu3/Cargo.toml" "valu3_derive/Cargo.toml" "valu3/README.md" "valu3_derive/README.md" "README.md" "VERSION.txt") 33 | 34 | # Loop através dos files e realiza o replace 35 | for file in "${files[@]}"; do 36 | if [ -f "$file" ]; then 37 | if sed -i "s|$last_tag|$new_tag|g" "$file"; then 38 | echo "Substituído '$last_tag' por '$new_tag' em $file" 39 | else 40 | echo "Erro ao substituir em $file" 41 | fi 42 | else 43 | echo "Arquivo $file não encontrado." 44 | fi 45 | done 46 | -------------------------------------------------------------------------------- /valu3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "valu3" 3 | version = "0.8.3" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | readme = "crates-io.md" 7 | authors = ["Philippe Assis "] 8 | repository = "https://github.com/lowcarboncode/valu3" 9 | keywords = ["value", "generic", "type", "serde", "parsing"] 10 | description = "A generic serialization/deserialization/type framework" 11 | categories = ["encoding", "no-std", "parsing"] 12 | documentation = "https://docs.rs/valu3" 13 | 14 | [dependencies] 15 | pest = "2.7.15" 16 | pest_derive = "2.7.15" 17 | regex = "1.11.1" 18 | chrono = "0.4.39" 19 | serde = { version = "1.0.216", features = ["derive"], optional = true } 20 | valu3-derive = { path = "../valu3_derive", optional = true, version = "0.8.3" } 21 | bincode = { version = "1.3.3", optional = true } 22 | 23 | [dev-dependencies] 24 | rand = "0.8.5" 25 | serde_json = "1.0.134" 26 | 27 | [features] 28 | default = ["parser", "derive", "serde", "bin"] 29 | parser = ["json"] 30 | json = [] 31 | serde = ["dep:serde"] 32 | cstring = [] 33 | derive = ["dep:valu3-derive"] 34 | bin = ["dep:bincode", "serde"] 35 | 36 | [lib] 37 | doctest = false 38 | -------------------------------------------------------------------------------- /valu3/README.md: -------------------------------------------------------------------------------- 1 | # Valu3: Unleash the Power of Data Manipulation in Rust 🚀 2 | 3 | Welcome to **Valu3** - the ultimate, flexible, and powerful library for manipulating diverse data types in your Rust projects. Say goodbye to the complexity of handling numbers, strings, arrays, objects, and datetime values. Valu3 is here to make your life easier! 4 | 5 | 6 | [![crates.io](https://img.shields.io/crates/v/valu3?label=0.8.3)](https://crates.io/crates/valu3) 7 | [![Documentation](https://docs.rs/valu3/badge.svg?version=0.8.3)](https://docs.rs/valu3/0.8.3) 8 | ![MSRV](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) 9 | ![Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) 10 | [![Dependency Status](https://deps.rs/crate/valu3/0.8.3/status.svg)](https://deps.rs/crate/valu3/0.8.3) 11 | ![Main test](https://github.com/lowcarboncode/valu3/actions/workflows/main-test.yml/badge.svg) 12 | [![codecov](https://codecov.io/gh/lowcarboncode/valu3/branch/master/graph/badge.svg)](https://codecov.io/gh/lowcarboncode/valu3) 13 | ![downloads](https://img.shields.io/crates/d/valu3.svg) 14 | 15 | ## 🌟 Key Features 16 | 17 | 1. **Universal Type Handling**: No more juggling with different data types! Valu3's generic type support enables smooth management of all data types under one roof. 18 | 2. **Intuitive API**: Experience effortless data manipulation with Valu3's user-friendly API, designed to provide consistency across diverse data types. 19 | 3. **All-in-One Data Manipulation**: Valu3 has it all - numeric, string, object, array, and date/time manipulation. No need to look anywhere else! 20 | 4. **Effortless Data Conversion**: Convert your data to and from popular formats like JSON, YAML, and XML with ease, thanks to Valu3's built-in support. 21 | 5. **Serde Integration**: Serialize and deserialize your data seamlessly with Valu3's out-of-the-box integration with the Serde library. 22 | 6. **Native Struct Parsing & Validation**: Valu3 and Pest join forces to offer native parsing, conversion, and validation of data to structs, ensuring data integrity at every step. 23 | 7. **Payload Interpretation & Transformation**: Valu3 interprets and transforms payload strings like a champ, handling JSON from HTTP request bodies and more. 24 | 25 | ## 💡 Why Choose Valu3? 26 | 27 | Valu3 is designed to make data manipulation tasks in Rust a breeze. By combining a wide range of features and a consistent API, it simplifies data handling in Rust projects while maximizing productivity. 28 | 29 | Join the Valu3 revolution and experience the future of data manipulation in Rust! 🎉 30 | 31 | **⚡ Get Started with Valu3 Today! ⚡** 32 | 33 | ## Examples :space_invader: 34 | 35 | Here are some examples of how to use the Valu3: 36 | 37 | ```rust 38 | use valu3::prelude::*; 39 | 40 | let string_value = "hello".to_value(); 41 | let number_value = 42.to_value(); 42 | let boolean_value = true.to_value(); 43 | let null_value = Value::Null; 44 | let undefined_value = Value::Undefined; 45 | let mut datetime_value = DateTime::from("2023-04-05T00:00:00Z").to_value(); 46 | 47 | string_value.as_string(); 48 | number_value.get_i32(); 49 | assert_eq!(boolean_value, true); 50 | assert_eq!(null_value, Value::Null); 51 | assert_eq!(undefined_value, Value::Undefined); 52 | datetime_value.add_days(1); 53 | ``` 54 | 55 | ## Getting Started 56 | To start using the Valu3 in your Rust project, simply add the following line to your `Cargo.toml` file: 57 | ```toml 58 | [dependencies] 59 | valu3 = "0.1" 60 | ``` 61 | 62 | Then, you can import the library in your code like this: 63 | ```rust 64 | use valu3::prelude::*; 65 | 66 | //... 67 | 68 | let pi = 3.14.to_value(); 69 | ``` 70 | 71 | ## Structs and Conversions 72 | Valu3 natively has conversions for famous data types like json, yaml and xml. Furthermore with `valu3-derive` you are able to transform `struct` to `Value` by applying the `to_value()` method generated by the `ToValue` derive macros. This is an example on converting `struct` to `Value` and `Value` to other payload data types. 73 | 74 | ```rust 75 | use valu3::prelude:*; 76 | 77 | #[derive(ToValue, FromValue, Default)] 78 | struct MyStruct { 79 | id: u32, 80 | name: String, 81 | tags: Vec 82 | } 83 | 84 | fn main(){ 85 | let my_struct = MyStruct::default(); 86 | let value = my_struct.to_value(); 87 | 88 | assert_eq!(my_struct, MyStruct::from_value(value)); 89 | } 90 | 91 | ``` 92 | 93 | ### ToJson 94 | If your focus is only on using `Valu3` for conversion only, use the `ToJson` macro. 95 | 96 | ```rust 97 | use valu3::prelude:*; 98 | 99 | #[derive(ToJson, Default)] 100 | struct MyStruct { 101 | id: u32, 102 | name: String, 103 | tags: Vec 104 | } 105 | 106 | fn main(){ 107 | let my_struct = MyStruct::default(); 108 | let json = my_struct.to_json(); 109 | 110 | println!("{}", json); // print json string 111 | } 112 | 113 | ``` 114 | 115 | ## Payload 116 | 117 | `Vale3` is able to recognize a payload string, identify and convert it to `Value`, follow the example: 118 | 119 | ```rust 120 | use valu3::prelude:*; 121 | fn main(){ 122 | let boolean = Value::payload_to_value("true").unwrap(); 123 | let float = Value::payload_to_value("3.14").unwrap(); 124 | let json = Value::payload_to_value(r#"{"item": 3.14}"#).unwrap(); 125 | let array = Value::payload_to_value(r#"[1,2,3]"#).unwrap(); 126 | let null = Value::payload_to_value("null").unwrap(); 127 | let string = Value::payload_to_value(r#""123""#).unwrap(); 128 | 129 | assert_eq!(boolean, true.to_value()); 130 | assert_eq!(float, 3.14.to_value()); 131 | assert_eq!(json, Value::from(vec![("item", 3.14)])); 132 | assert_eq!(array, vec![1, 2, 3].to_value()); 133 | assert_eq!(null, Value::Null); 134 | assert_eq!(string, "123".to_value()); 135 | } 136 | 137 | ``` 138 | 139 | ## Contributing 140 | If you find a bug or have a suggestion for a new feature, please open an issue on the [GitHub repository](https://github.com/lowcarboncode/valu3/issues). 141 | 142 | If you would like to contribute to the project, please feel free to submit a pull request. Before submitting a pull request, please make sure that your code adheres to the project's style guidelines and passes all tests. 143 | 144 | ## Upcoming Features: Stay in Sync with the Future of Valu3 🌐 145 | 146 | We're constantly working to improve and expand the capabilities of Valu3, making it even more powerful and versatile. 147 | 148 | By keeping track of the project's progress, you can stay informed about new features in development and planned improvements. This will allow you to make the most of Valu3 in your Rust projects and prepare for future updates. 149 | 150 | Our commitment is to make Valu3 the ultimate data manipulation solution in Rust. Your input is invaluable! Feel free to join the discussions, share your ideas, and contribute to the project as it evolves. 151 | 152 | Join us in the ongoing journey to refine and expand Valu3! 🚀 153 | 154 | 155 | ## License 156 | This project is licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/lowcarboncode/valu3/blob/main/LICENSE) file for more information. 157 | -------------------------------------------------------------------------------- /valu3/crates-io.md: -------------------------------------------------------------------------------- 1 | ../crates-io.md -------------------------------------------------------------------------------- /valu3/src/impls.rs: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | 3 | use crate::prelude::*; 4 | 5 | impl Value { 6 | pub fn get(&self, key: T) -> Option<&Value> 7 | where 8 | T: ValueKeyBehavior, 9 | { 10 | match self { 11 | Value::Object(object) => object.get(key), 12 | Value::Array(array) => array.get(key.as_usize()), 13 | _ => panic!("Unable to get a reference to a type other than an array or object"), 14 | } 15 | } 16 | 17 | pub fn get_mut(&mut self, key: T) -> Option<&mut Value> 18 | where 19 | T: ValueKeyBehavior, 20 | { 21 | match self { 22 | Value::Object(object) => object.get_mut(key), 23 | Value::Array(array) => array.get_mut(key.as_usize()), 24 | _ => { 25 | panic!("Unable to get a mutable reference to a type other than an array or object") 26 | } 27 | } 28 | } 29 | 30 | pub fn clean(&mut self) { 31 | match self { 32 | Value::Array(array) => array.clean(), 33 | Value::Object(object) => { 34 | object.clean(); 35 | } 36 | Value::Number(number) => { 37 | number.clean(); 38 | } 39 | _ => panic!("Unable to clean a type other than an array, object or number"), 40 | }; 41 | } 42 | 43 | pub fn len(&self) -> usize { 44 | match self { 45 | Value::Array(array) => array.len(), 46 | Value::Object(object) => object.len(), 47 | Value::String(string) => string.len(), 48 | _ => panic!("Unable to get the length of a type other than an array, object or string"), 49 | } 50 | } 51 | 52 | pub fn is_empty(&self) -> bool { 53 | match self { 54 | Value::Array(array) => array.is_empty(), 55 | Value::Object(object) => object.is_empty(), 56 | Value::String(string) => string.is_empty(), 57 | _ => panic!("Unable to check if a type other than an array, object or string is empty"), 58 | } 59 | } 60 | 61 | pub fn is_string(&self) -> bool { 62 | match self { 63 | Value::String(_) => true, 64 | _ => false, 65 | } 66 | } 67 | 68 | pub fn is_number(&self) -> bool { 69 | match self { 70 | Value::Number(_) => true, 71 | _ => false, 72 | } 73 | } 74 | 75 | pub fn is_array(&self) -> bool { 76 | match self { 77 | Value::Array(_) => true, 78 | _ => false, 79 | } 80 | } 81 | 82 | pub fn is_object(&self) -> bool { 83 | match self { 84 | Value::Object(_) => true, 85 | _ => false, 86 | } 87 | } 88 | 89 | pub fn is_bool(&self) -> bool { 90 | match self { 91 | Value::Boolean(_) => true, 92 | _ => false, 93 | } 94 | } 95 | 96 | pub fn is_null(&self) -> bool { 97 | match self { 98 | Value::Null => true, 99 | _ => false, 100 | } 101 | } 102 | 103 | pub fn is_undefined(&self) -> bool { 104 | match self { 105 | Value::Undefined => true, 106 | _ => false, 107 | } 108 | } 109 | 110 | pub fn as_string_b(&self) -> Option<&StringB> { 111 | match self { 112 | Value::String(string) => Some(&string), 113 | _ => None, 114 | } 115 | } 116 | 117 | pub fn as_number(&self) -> Option<&Number> { 118 | match self { 119 | Value::Number(number) => Some(number), 120 | _ => None, 121 | } 122 | } 123 | 124 | pub fn as_array(&self) -> Option<&Array> { 125 | match self { 126 | Value::Array(array) => Some(array), 127 | _ => None, 128 | } 129 | } 130 | 131 | pub fn as_object(&self) -> Option<&Object> { 132 | match self { 133 | Value::Object(object) => Some(object), 134 | _ => None, 135 | } 136 | } 137 | 138 | pub fn as_bool(&self) -> Option<&bool> { 139 | match self { 140 | Value::Boolean(boolean) => Some(boolean), 141 | _ => None, 142 | } 143 | } 144 | 145 | pub fn as_null(&self) -> Option<()> { 146 | match self { 147 | Value::Null => Some(()), 148 | _ => None, 149 | } 150 | } 151 | 152 | pub fn as_undefined(&self) -> Option<()> { 153 | match self { 154 | Value::Undefined => Some(()), 155 | _ => None, 156 | } 157 | } 158 | 159 | pub fn as_string_mut(&mut self) -> Option<&mut StringB> { 160 | match self { 161 | Value::String(string) => Some(string), 162 | _ => None, 163 | } 164 | } 165 | 166 | pub fn as_number_mut(&mut self) -> Option<&mut Number> { 167 | match self { 168 | Value::Number(number) => Some(number), 169 | _ => None, 170 | } 171 | } 172 | 173 | pub fn as_array_mut(&mut self) -> Option<&mut Array> { 174 | match self { 175 | Value::Array(array) => Some(array), 176 | _ => None, 177 | } 178 | } 179 | 180 | pub fn as_object_mut(&mut self) -> Option<&mut Object> { 181 | match self { 182 | Value::Object(object) => Some(object), 183 | _ => None, 184 | } 185 | } 186 | 187 | pub fn as_bool_mut(&mut self) -> Option<&mut bool> { 188 | match self { 189 | Value::Boolean(boolean) => Some(boolean), 190 | _ => None, 191 | } 192 | } 193 | 194 | pub fn as_null_mut(&mut self) -> Option<()> { 195 | match self { 196 | Value::Null => Some(()), 197 | _ => None, 198 | } 199 | } 200 | 201 | pub fn as_undefined_mut(&mut self) -> Option<()> { 202 | match self { 203 | Value::Undefined => Some(()), 204 | _ => None, 205 | } 206 | } 207 | 208 | pub fn push(&mut self, value: T) { 209 | match self { 210 | Value::Array(array) => array.push(value.to_value()), 211 | _ => panic!("Unable to push values ​​into a type other than an array"), 212 | } 213 | } 214 | 215 | pub fn insert(&mut self, key: T, value: V) -> Option 216 | where 217 | T: ValueKeyBehavior, 218 | V: ToValueBehavior, 219 | { 220 | match self { 221 | Value::Object(o) => o.insert(key, value.to_value()), 222 | _ => panic!("Unable to insert values ​​into a type other than an object"), 223 | } 224 | } 225 | } 226 | 227 | impl NumberBehavior for Value { 228 | fn set_u8(&mut self, value: u8) { 229 | match self { 230 | Value::Number(n) => n.set_u8(value), 231 | _ => panic!("Unable to set a value other than a number"), 232 | } 233 | } 234 | 235 | fn set_u16(&mut self, value: u16) { 236 | match self { 237 | Value::Number(n) => n.set_u16(value), 238 | _ => panic!("Unable to set a value other than a number"), 239 | } 240 | } 241 | 242 | fn set_u32(&mut self, value: u32) { 243 | match self { 244 | Value::Number(n) => n.set_u32(value), 245 | _ => panic!("Unable to set a value other than a number"), 246 | } 247 | } 248 | 249 | fn set_u64(&mut self, value: u64) { 250 | match self { 251 | Value::Number(n) => n.set_u64(value), 252 | _ => panic!("Unable to set a value other than a number"), 253 | } 254 | } 255 | 256 | fn set_u128(&mut self, value: u128) { 257 | match self { 258 | Value::Number(n) => n.set_u128(value), 259 | _ => panic!("Unable to set a value other than a number"), 260 | } 261 | } 262 | 263 | fn set_i8(&mut self, value: i8) { 264 | match self { 265 | Value::Number(n) => n.set_i8(value), 266 | _ => panic!("Unable to set a value other than a number"), 267 | } 268 | } 269 | 270 | fn set_i16(&mut self, value: i16) { 271 | match self { 272 | Value::Number(n) => n.set_i16(value), 273 | _ => panic!("Unable to set a value other than a number"), 274 | } 275 | } 276 | 277 | fn set_i32(&mut self, value: i32) { 278 | match self { 279 | Value::Number(n) => n.set_i32(value), 280 | _ => panic!("Unable to set a value other than a number"), 281 | } 282 | } 283 | 284 | fn set_i64(&mut self, value: i64) { 285 | match self { 286 | Value::Number(n) => n.set_i64(value), 287 | _ => panic!("Unable to set a value other than a number"), 288 | } 289 | } 290 | 291 | fn set_i128(&mut self, value: i128) { 292 | match self { 293 | Value::Number(n) => n.set_i128(value), 294 | _ => panic!("Unable to set a value other than a number"), 295 | } 296 | } 297 | 298 | fn set_f32(&mut self, value: f32) { 299 | match self { 300 | Value::Number(n) => n.set_f32(value), 301 | _ => panic!("Unable to set a value other than a number"), 302 | } 303 | } 304 | 305 | fn set_f64(&mut self, value: f64) { 306 | match self { 307 | Value::Number(n) => n.set_f64(value), 308 | _ => panic!("Unable to set a value other than a number"), 309 | } 310 | } 311 | 312 | fn get_u8(&self) -> Option { 313 | match self { 314 | Value::Number(n) => n.get_u8(), 315 | _ => panic!("Unable to get a value other than a number"), 316 | } 317 | } 318 | 319 | fn get_u16(&self) -> Option { 320 | match self { 321 | Value::Number(n) => n.get_u16(), 322 | _ => panic!("Unable to get a value other than a number"), 323 | } 324 | } 325 | 326 | fn get_u32(&self) -> Option { 327 | match self { 328 | Value::Number(n) => n.get_u32(), 329 | _ => panic!("Unable to get a value other than a number"), 330 | } 331 | } 332 | 333 | fn get_u64(&self) -> Option { 334 | match self { 335 | Value::Number(n) => n.get_u64(), 336 | _ => panic!("Unable to get a value other than a number"), 337 | } 338 | } 339 | 340 | fn get_u128(&self) -> Option { 341 | match self { 342 | Value::Number(n) => n.get_u128(), 343 | _ => panic!("Unable to get a value other than a number"), 344 | } 345 | } 346 | 347 | fn get_i8(&self) -> Option { 348 | match self { 349 | Value::Number(n) => n.get_i8(), 350 | _ => panic!("Unable to get a value other than a number"), 351 | } 352 | } 353 | 354 | fn get_i16(&self) -> Option { 355 | match self { 356 | Value::Number(n) => n.get_i16(), 357 | _ => panic!("Unable to get a value other than a number"), 358 | } 359 | } 360 | 361 | fn get_i32(&self) -> Option { 362 | match self { 363 | Value::Number(n) => n.get_i32(), 364 | _ => panic!("Unable to get a value other than a number"), 365 | } 366 | } 367 | 368 | fn get_i64(&self) -> Option { 369 | match self { 370 | Value::Number(n) => n.get_i64(), 371 | _ => panic!("Unable to get a value other than a number"), 372 | } 373 | } 374 | 375 | fn get_i128(&self) -> Option { 376 | match self { 377 | Value::Number(n) => n.get_i128(), 378 | _ => panic!("Unable to get a value other than a number"), 379 | } 380 | } 381 | 382 | fn get_f32(&self) -> Option { 383 | match self { 384 | Value::Number(n) => n.get_f32(), 385 | _ => panic!("Unable to get a value other than a number"), 386 | } 387 | } 388 | 389 | fn get_f64(&self) -> Option { 390 | match self { 391 | Value::Number(n) => n.get_f64(), 392 | _ => panic!("Unable to get a value other than a number"), 393 | } 394 | } 395 | 396 | fn get_u8_unsafe(&self) -> u8 { 397 | match self { 398 | Value::Number(n) => n.get_u8_unsafe(), 399 | _ => panic!("Unable to get a value other than a number"), 400 | } 401 | } 402 | 403 | fn get_u16_unsafe(&self) -> u16 { 404 | match self { 405 | Value::Number(n) => n.get_u16_unsafe(), 406 | _ => panic!("Unable to get a value other than a number"), 407 | } 408 | } 409 | 410 | fn get_u32_unsafe(&self) -> u32 { 411 | match self { 412 | Value::Number(n) => n.get_u32_unsafe(), 413 | _ => panic!("Unable to get a value other than a number"), 414 | } 415 | } 416 | 417 | fn get_u64_unsafe(&self) -> u64 { 418 | match self { 419 | Value::Number(n) => n.get_u64_unsafe(), 420 | _ => panic!("Unable to get a value other than a number"), 421 | } 422 | } 423 | 424 | fn get_u128_unsafe(&self) -> u128 { 425 | match self { 426 | Value::Number(n) => n.get_u128_unsafe(), 427 | _ => panic!("Unable to get a value other than a number"), 428 | } 429 | } 430 | 431 | fn get_i8_unsafe(&self) -> i8 { 432 | match self { 433 | Value::Number(n) => n.get_i8_unsafe(), 434 | _ => panic!("Unable to get a value other than a number"), 435 | } 436 | } 437 | 438 | fn get_i16_unsafe(&self) -> i16 { 439 | match self { 440 | Value::Number(n) => n.get_i16_unsafe(), 441 | _ => panic!("Unable to get a value other than a number"), 442 | } 443 | } 444 | 445 | fn get_i32_unsafe(&self) -> i32 { 446 | match self { 447 | Value::Number(n) => n.get_i32_unsafe(), 448 | _ => panic!("Unable to get a value other than a number"), 449 | } 450 | } 451 | 452 | fn get_i64_unsafe(&self) -> i64 { 453 | match self { 454 | Value::Number(n) => n.get_i64_unsafe(), 455 | _ => panic!("Unable to get a value other than a number"), 456 | } 457 | } 458 | 459 | fn get_i128_unsafe(&self) -> i128 { 460 | match self { 461 | Value::Number(n) => n.get_i128_unsafe(), 462 | _ => panic!("Unable to get a value other than a number"), 463 | } 464 | } 465 | 466 | fn get_f32_unsafe(&self) -> f32 { 467 | match self { 468 | Value::Number(n) => n.get_f32_unsafe(), 469 | _ => panic!("Unable to get a value other than a number"), 470 | } 471 | } 472 | 473 | fn get_f64_unsafe(&self) -> f64 { 474 | match self { 475 | Value::Number(n) => n.get_f64_unsafe(), 476 | _ => panic!("Unable to get a value other than a number"), 477 | } 478 | } 479 | 480 | fn is_i8(&self) -> bool { 481 | match self { 482 | Value::Number(n) => n.is_i8(), 483 | _ => false, 484 | } 485 | } 486 | 487 | fn is_i16(&self) -> bool { 488 | match self { 489 | Value::Number(n) => n.is_i16(), 490 | _ => false, 491 | } 492 | } 493 | 494 | fn is_i32(&self) -> bool { 495 | match self { 496 | Value::Number(n) => n.is_i32(), 497 | _ => false, 498 | } 499 | } 500 | 501 | fn is_i64(&self) -> bool { 502 | match self { 503 | Value::Number(n) => n.is_i64(), 504 | _ => false, 505 | } 506 | } 507 | 508 | fn is_i128(&self) -> bool { 509 | match self { 510 | Value::Number(n) => n.is_i128(), 511 | _ => false, 512 | } 513 | } 514 | 515 | fn is_u8(&self) -> bool { 516 | match self { 517 | Value::Number(n) => n.is_u8(), 518 | _ => false, 519 | } 520 | } 521 | 522 | fn is_u16(&self) -> bool { 523 | match self { 524 | Value::Number(n) => n.is_u16(), 525 | _ => false, 526 | } 527 | } 528 | 529 | fn is_u32(&self) -> bool { 530 | match self { 531 | Value::Number(n) => n.is_u32(), 532 | _ => false, 533 | } 534 | } 535 | 536 | fn is_u64(&self) -> bool { 537 | match self { 538 | Value::Number(n) => n.is_u64(), 539 | _ => false, 540 | } 541 | } 542 | 543 | fn is_u128(&self) -> bool { 544 | match self { 545 | Value::Number(n) => n.is_u128(), 546 | _ => false, 547 | } 548 | } 549 | 550 | fn is_f32(&self) -> bool { 551 | match self { 552 | Value::Number(n) => n.is_f32(), 553 | _ => false, 554 | } 555 | } 556 | 557 | fn is_f64(&self) -> bool { 558 | match self { 559 | Value::Number(n) => n.is_f64(), 560 | _ => false, 561 | } 562 | } 563 | 564 | fn is_number(&self) -> bool { 565 | match self { 566 | Value::Number(_) => true, 567 | _ => false, 568 | } 569 | } 570 | 571 | fn is_integer(&self) -> bool { 572 | match self { 573 | Value::Number(n) => n.is_integer(), 574 | _ => false, 575 | } 576 | } 577 | 578 | fn is_float(&self) -> bool { 579 | match self { 580 | Value::Number(n) => n.is_float(), 581 | _ => false, 582 | } 583 | } 584 | 585 | fn is_signed(&self) -> bool { 586 | match self { 587 | Value::Number(n) => n.is_signed(), 588 | _ => false, 589 | } 590 | } 591 | 592 | fn is_unsigned(&self) -> bool { 593 | match self { 594 | Value::Number(n) => n.is_unsigned(), 595 | _ => false, 596 | } 597 | } 598 | 599 | fn is_zero(&self) -> bool { 600 | match self { 601 | Value::Number(n) => n.is_zero(), 602 | _ => false, 603 | } 604 | } 605 | 606 | fn is_positive(&self) -> bool { 607 | match self { 608 | Value::Number(n) => n.is_positive(), 609 | _ => false, 610 | } 611 | } 612 | 613 | fn is_negative(&self) -> bool { 614 | match self { 615 | Value::Number(n) => n.is_negative(), 616 | _ => false, 617 | } 618 | } 619 | 620 | fn number_type(&self) -> NumberType { 621 | match self { 622 | Value::Number(n) => n.number_type(), 623 | _ => NumberType::Unknown, 624 | } 625 | } 626 | 627 | fn to_f64(&self) -> Option { 628 | match self { 629 | Value::Number(n) => n.to_f64(), 630 | _ => None, 631 | } 632 | } 633 | 634 | fn to_i64(&self) -> Option { 635 | match self { 636 | Value::Number(n) => n.to_i64(), 637 | _ => None, 638 | } 639 | } 640 | 641 | fn to_u64(&self) -> Option { 642 | match self { 643 | Value::Number(n) => n.to_u64(), 644 | _ => None, 645 | } 646 | } 647 | } 648 | 649 | impl ObjectBehavior for Value { 650 | fn remove(&mut self, key: &T) -> Option 651 | where 652 | T: ValueKeyBehavior, 653 | { 654 | match self { 655 | Value::Object(o) => o.remove(key), 656 | _ => panic!("Unable to remove a value other than an object"), 657 | } 658 | } 659 | 660 | fn contains_key(&self, key: &T) -> bool 661 | where 662 | T: ValueKeyBehavior, 663 | { 664 | match self { 665 | Value::Object(o) => o.contains_key(key), 666 | _ => panic!("Unable to remove a value other than an object"), 667 | } 668 | } 669 | 670 | fn keys(&self) -> Vec<&ValueKey> { 671 | match self { 672 | Value::Object(o) => o.keys(), 673 | _ => panic!("Unable to remove a value other than an object"), 674 | } 675 | } 676 | 677 | fn values(&self) -> Vec<&Value> { 678 | match self { 679 | Value::Object(o) => o.values(), 680 | _ => panic!("Unable to remove a value other than an object"), 681 | } 682 | } 683 | } 684 | 685 | impl ArrayBehavior for Value { 686 | fn pop(&mut self) -> Option { 687 | match self { 688 | Value::Array(array) => array.pop(), 689 | _ => panic!("Unable to pop a value other than an array"), 690 | } 691 | } 692 | } 693 | 694 | impl DateTimeBehavior for Value { 695 | fn as_date(&self) -> Option<&chrono::NaiveDate> { 696 | match self { 697 | Value::DateTime(datetime) => datetime.as_date(), 698 | _ => panic!("Unable to get a date from a value other than a datetime"), 699 | } 700 | } 701 | 702 | fn as_time(&self) -> Option<&chrono::NaiveTime> { 703 | match self { 704 | Value::DateTime(datetime) => datetime.as_time(), 705 | _ => panic!("Unable to get a date from a value other than a datetime"), 706 | } 707 | } 708 | 709 | fn as_date_time(&self) -> Option<&chrono::DateTime> { 710 | match self { 711 | Value::DateTime(datetime) => datetime.as_date_time(), 712 | _ => panic!("Unable to get a date from a value other than a datetime"), 713 | } 714 | } 715 | 716 | fn year(&self) -> Option { 717 | match self { 718 | Value::DateTime(datetime) => datetime.year(), 719 | _ => panic!("Unable to get a date from a value other than a datetime"), 720 | } 721 | } 722 | 723 | fn month(&self) -> Option { 724 | match self { 725 | Value::DateTime(datetime) => datetime.month(), 726 | _ => panic!("Unable to get a date from a value other than a datetime"), 727 | } 728 | } 729 | 730 | fn day(&self) -> Option { 731 | match self { 732 | Value::DateTime(datetime) => datetime.day(), 733 | _ => panic!("Unable to get a date from a value other than a datetime"), 734 | } 735 | } 736 | 737 | fn hour(&self) -> Option { 738 | match self { 739 | Value::DateTime(datetime) => datetime.hour(), 740 | _ => panic!("Unable to get a date from a value other than a datetime"), 741 | } 742 | } 743 | 744 | fn minute(&self) -> Option { 745 | match self { 746 | Value::DateTime(datetime) => datetime.minute(), 747 | _ => panic!("Unable to get a date from a value other than a datetime"), 748 | } 749 | } 750 | 751 | fn second(&self) -> Option { 752 | match self { 753 | Value::DateTime(datetime) => datetime.second(), 754 | _ => panic!("Unable to get a date from a value other than a datetime"), 755 | } 756 | } 757 | 758 | fn timestamp(&self) -> Option { 759 | match self { 760 | Value::DateTime(datetime) => datetime.timestamp(), 761 | _ => panic!("Unable to get a date from a value other than a datetime"), 762 | } 763 | } 764 | 765 | fn timezone(&self) -> Option { 766 | match self { 767 | Value::DateTime(datetime) => datetime.timezone(), 768 | _ => panic!("Unable to get a date from a value other than a datetime"), 769 | } 770 | } 771 | 772 | fn to_iso8601(&self) -> String { 773 | match self { 774 | Value::DateTime(datetime) => datetime.to_iso8601(), 775 | _ => panic!("Unable to get a date from a value other than a datetime"), 776 | } 777 | } 778 | 779 | fn to_rfc3339(&self) -> String { 780 | match self { 781 | Value::DateTime(datetime) => datetime.to_rfc3339(), 782 | _ => panic!("Unable to get a date from a value other than a datetime"), 783 | } 784 | } 785 | 786 | fn add_duration(&self, duration: chrono::Duration) -> Option 787 | where 788 | Self: Sized, 789 | { 790 | match self { 791 | Value::DateTime(datetime) => match datetime.add_duration(duration) { 792 | Some(datetime) => Some(datetime.to_value()), 793 | None => None, 794 | }, 795 | _ => panic!("Unable to get a date from a value other than a datetime"), 796 | } 797 | } 798 | 799 | fn subtract_duration(&self, duration: chrono::Duration) -> Option 800 | where 801 | Self: Sized, 802 | { 803 | match self { 804 | Value::DateTime(datetime) => match datetime.subtract_duration(duration) { 805 | Some(datetime) => Some(datetime.to_value()), 806 | None => None, 807 | }, 808 | _ => panic!("Unable to get a date from a value other than a datetime"), 809 | } 810 | } 811 | 812 | fn duration_between(&self, other: &Self) -> Option { 813 | match self { 814 | Value::DateTime(datetime) => datetime.duration_between(&DateTime::from(other.clone())), 815 | _ => panic!("Unable to get a date from a value other than a datetime"), 816 | } 817 | } 818 | 819 | fn from_ymd_opt(year: i32, month: u32, day: u32) -> Self { 820 | DateTime::from_ymd_opt(year, month, day).to_value() 821 | } 822 | 823 | fn with_ymd_and_hms(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> Self { 824 | DateTime::with_ymd_and_hms(year, month, day, hour, min, sec).to_value() 825 | } 826 | 827 | fn now() -> Self { 828 | DateTime::now().to_value() 829 | } 830 | } 831 | 832 | impl StringBehavior for Value { 833 | fn as_bytes(&self) -> &[u8] { 834 | match self { 835 | Value::String(string) => string.as_bytes(), 836 | _ => panic!("Unable to get a string from a value other than a string"), 837 | } 838 | } 839 | 840 | fn as_str(&self) -> &str { 841 | match self { 842 | Value::String(string) => string.as_str(), 843 | _ => panic!("Unable to get a string from a value other than a string"), 844 | } 845 | } 846 | 847 | fn as_string(&self) -> String { 848 | match self { 849 | Value::String(string) => string.as_string(), 850 | _ => panic!("Unable to get a string from a value other than a string"), 851 | } 852 | } 853 | 854 | #[cfg(feature = "cstring")] 855 | fn extract(&self) -> CString { 856 | match self { 857 | Value::String(string) => string.extract(), 858 | _ => panic!("Unable to get a string from a value other than a string"), 859 | } 860 | } 861 | 862 | #[cfg(not(feature = "cstring"))] 863 | fn extract(&self) -> String { 864 | match self { 865 | Value::String(string) => string.extract(), 866 | _ => panic!("Unable to get a string from a value other than a string"), 867 | } 868 | } 869 | 870 | fn to_uppercase(&self) -> Self { 871 | match self { 872 | Value::String(string) => string.to_uppercase().to_value(), 873 | _ => panic!("Unable to get a string from a value other than a string"), 874 | } 875 | } 876 | 877 | fn to_lowercase(&self) -> Self { 878 | match self { 879 | Value::String(string) => string.to_lowercase().to_value(), 880 | _ => panic!("Unable to get a string from a value other than a string"), 881 | } 882 | } 883 | 884 | fn trim(&self) -> Self { 885 | match self { 886 | Value::String(string) => string.trim().to_value(), 887 | _ => panic!("Unable to get a string from a value other than a string"), 888 | } 889 | } 890 | 891 | fn replace(&self, from: &str, to: &str) -> Self { 892 | match self { 893 | Value::String(string) => string.replace(from, to).to_value(), 894 | _ => panic!("Unable to get a string from a value other than a string"), 895 | } 896 | } 897 | 898 | fn concat>(&self, other: T) -> Self { 899 | match self { 900 | Value::String(string) => string.concat(other).to_value(), 901 | _ => panic!("Unable to get a string from a value other than a string"), 902 | } 903 | } 904 | 905 | fn from_utf8(value: Vec) -> Self { 906 | StringB::from_utf8(value).to_value() 907 | } 908 | } 909 | 910 | impl From<()> for Value { 911 | fn from(_: ()) -> Self { 912 | Value::Null 913 | } 914 | } 915 | 916 | impl From for Value 917 | where 918 | T: ToValueBehavior + PrimitiveType, 919 | { 920 | fn from(value: T) -> Self { 921 | value.to_value() 922 | } 923 | } 924 | 925 | impl From> for Value 926 | where 927 | K: ValueKeyBehavior, 928 | V: ToValueBehavior + PrimitiveType, 929 | { 930 | fn from(value: Vec<(K, V)>) -> Self { 931 | let mut object = Object::default(); 932 | for (key, value) in value { 933 | object.insert(key, value.to_value()); 934 | } 935 | Value::Object(object) 936 | } 937 | } 938 | 939 | impl From> for Value 940 | where 941 | K: ValueKeyBehavior, 942 | { 943 | fn from(value: Vec<(K, Value)>) -> Self { 944 | let mut object = Object::default(); 945 | for (key, value) in value { 946 | object.insert(key, value); 947 | } 948 | Value::Object(object) 949 | } 950 | } 951 | 952 | //TODO: implement [(K, V)] and [(K, Value)] 953 | 954 | #[cfg(test)] 955 | mod tests { 956 | use crate::prelude::*; 957 | use std::collections::HashMap; 958 | 959 | #[test] 960 | fn test_value_number_behavior() { 961 | let value = Value::from(3.14); 962 | assert_eq!(value.get_f64_unsafe(), 3.14); 963 | 964 | let value2 = Value::from(42u32); 965 | assert_eq!(value2.to_u64(), Some(42)); 966 | } 967 | 968 | #[test] 969 | fn test_value_object_behavior() { 970 | let mut value = Value::from(HashMap::from_iter(vec![("1", 3.14.to_value())].into_iter())); 971 | value.insert("2", 4.13); 972 | 973 | if let Some(item) = value.get_mut("1") { 974 | *item = 1.43.to_value(); 975 | } 976 | 977 | assert_eq!(value.get("1").unwrap(), &1.43.to_value()); 978 | } 979 | 980 | #[test] 981 | fn test_value_array_behavior() { 982 | let mut value = Value::from(vec![1, 2, 3]); 983 | value.push(4); 984 | 985 | if let Some(item) = value.get_mut("1") { 986 | *item = 1.43.to_value(); 987 | } 988 | 989 | assert_eq!(value.get("1").unwrap(), &1.43.to_value()); 990 | } 991 | 992 | #[test] 993 | fn test_value_datetime_behavior() { 994 | let dt_date = Value::from_ymd_opt(2023, 4, 5); 995 | let dt_datetime = Value::with_ymd_and_hms(2023, 4, 5, 12, 34, 56); 996 | 997 | assert_eq!( 998 | dt_date.add_duration(Duration::days(1)), 999 | Some(DateTime::from(NaiveDate::from_ymd_opt(2023, 4, 6).unwrap()).to_value()) 1000 | ); 1001 | assert_eq!( 1002 | dt_datetime.add_duration(Duration::days(1)), 1003 | Some(DateTime::from(Utc.with_ymd_and_hms(2023, 4, 6, 12, 34, 56)).to_value()) 1004 | ); 1005 | } 1006 | 1007 | #[test] 1008 | fn test_value_string_behavior() { 1009 | let string = Value::from("hello"); 1010 | let concat = string.concat("!"); 1011 | assert!(concat == StringB::from("hello!").to_value()) 1012 | } 1013 | 1014 | #[test] 1015 | fn test_value_as_string() { 1016 | let string = Value::from("hello"); 1017 | assert!(string.as_string_b() == Some(&StringB::from("hello"))) 1018 | } 1019 | 1020 | #[test] 1021 | fn test_value_as_number() { 1022 | let number = Value::from(3.14); 1023 | assert!(number.as_number() == Some(&Number::from(3.14))) 1024 | } 1025 | 1026 | #[test] 1027 | fn test_value_as_array_mut() { 1028 | let mut array = Value::from(vec![1, 2, 3]); 1029 | assert!(array.as_array_mut().unwrap().get_mut(0) == Some(&mut 1.to_value())) 1030 | } 1031 | 1032 | #[test] 1033 | fn test_value_as_object_mut() { 1034 | let mut object = Value::from(HashMap::from_iter(vec![("1", 3.14.to_value())].into_iter())); 1035 | assert!(object.as_object_mut().unwrap().get_mut("1") == Some(&mut 3.14.to_value())) 1036 | } 1037 | } 1038 | -------------------------------------------------------------------------------- /valu3/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # value 2 | //! The Value library provides a generic value type with a wide range of classic methods for numerical and string manipulation. The library includes types for arrays, objects, numbers, strings, and datetime. It also provides methods for converting values to and from various data formats, such as JSON, YAML, and XML. 3 | //! 4 | //! With the Value library, you can easily manipulate different types of data in your Rust projects. You can create, modify, and query objects and arrays, perform arithmetic and bitwise operations on numbers, and convert values to and from strings. The library is easy to use and provides a consistent API for manipulating values, regardless of their type. 5 | //! 6 | //! Whether you're working on a small Rust project or a large-scale application, the Value library can help simplify your code and make it more manageable. Its simple and intuitive API makes it easy to work with, even for beginners. 7 | //! 8 | //! # Examples 9 | //! 10 | //! ``` 11 | //! use valu3::prelude::*; 12 | //! 13 | //! let string_value = hello".to_value(); 14 | //! let number_value = 42.to_value(); 15 | //! let boolean_value = true.to_value(); 16 | //! let null_value = Value::Null; 17 | //! let undefined_value = Value::Undefined; 18 | //! let mut datetime_value = DateTime::from("2023-04-05T00:00:00Z").to_value(); 19 | //! 20 | //! string_value.as_string(); 21 | //! number_value.get_i32(); 22 | //! assert!(boolean_value, true); 23 | //! assert!(null_value, Value::Null); 24 | //! assert!(undefined_value, Value::Undefined); 25 | //! datetime_value.add_days(1); 26 | //! ``` 27 | pub mod impls; 28 | pub mod macros; 29 | pub mod prelude; 30 | pub mod primitives; 31 | #[cfg(feature = "serde")] 32 | pub mod serde_value; 33 | pub mod to; 34 | pub mod to_value; 35 | pub mod traits; 36 | pub mod types; 37 | pub mod value; 38 | 39 | #[cfg(feature = "parser")] 40 | #[macro_use] 41 | extern crate pest_derive; 42 | 43 | #[cfg(feature = "parser")] 44 | pub mod parser; 45 | 46 | #[derive(Clone, Eq, PartialEq, Debug)] 47 | pub enum Error { 48 | #[cfg(feature = "parser")] 49 | NonParsebleMsg(String), 50 | #[cfg(feature = "parser")] 51 | NonParseble, 52 | NotNumber, 53 | } 54 | 55 | #[cfg(test)] 56 | mod tests; 57 | -------------------------------------------------------------------------------- /valu3/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! json { 3 | (null) => { 4 | $crate::value::Value::Null 5 | }; 6 | (true) => { 7 | $crate::value::Value::from(true) 8 | }; 9 | (false) => { 10 | $crate::value::Value::from(false) 11 | }; 12 | ([]) => { 13 | $crate::value::Value::from(vec![]) 14 | }; 15 | ([ $($elem:tt),* ]) => { 16 | $crate::value::Value::from(vec![$(json!($elem)),*]) 17 | }; 18 | ({}) => { 19 | $crate::value::Value::from(std::collections::HashMap::new()) 20 | }; 21 | ({ $($key:tt : $value:tt),* }) => { 22 | $crate::value::Value::from({ 23 | let mut map = std::collections::HashMap::new(); 24 | $(map.insert($key.to_string(), json!($value));)* 25 | map 26 | }) 27 | }; 28 | ($val:expr) => { 29 | $val.to_value() 30 | }; 31 | } 32 | 33 | #[cfg(test)] 34 | mod test { 35 | use crate::traits::ToValueBehavior; 36 | use std::collections::HashMap; 37 | 38 | #[test] 39 | fn test_json() { 40 | let test5 = true; 41 | let json = json!({ 42 | "test": true, 43 | "test2": "ok", 44 | "test3": [0, 1], 45 | "test4": { 46 | "test5": test5, 47 | "test6": "ok", 48 | "test7": [0, 1] 49 | } 50 | }); 51 | 52 | let mut map = HashMap::new(); 53 | map.insert("test".to_string(), true.to_value()); 54 | map.insert("test2".to_string(), "ok".to_string().to_value()); 55 | map.insert("test3".to_string(), vec![0, 1].to_value()); 56 | 57 | let mut inner_map = HashMap::new(); 58 | inner_map.insert("test5".to_string(), true.to_value()); 59 | inner_map.insert("test6".to_string(), "ok".to_value()); 60 | inner_map.insert("test7".to_string(), vec![0, 1].to_value()); 61 | 62 | map.insert("test4".to_string(), inner_map.to_value()); 63 | 64 | assert_eq!(json, map.to_value()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /valu3/src/parser/json/json.pest: -------------------------------------------------------------------------------- 1 | json = _{ SOI ~ (object | array | string | number | boolean | null) ~ EOI } 2 | WHITESPACE = _{ " " | "\t" | "\r" | "\n" } 3 | object = { 4 | ("#{" | "{") ~ "}" 5 | | ("#{" | "{") ~ key_value_pair ~ ("," ~ key_value_pair)* ~ "}" 6 | } 7 | key_value_pair = { string ~ ":" ~ value } 8 | array = { 9 | "[" ~ "]" 10 | | "[" ~ value ~ ("," ~ value)* ~ "]" 11 | } 12 | value = _{ object | array | string | number | boolean | null } 13 | boolean = { "true" | "false" } 14 | null = { "null" } 15 | string = ${ "\"" ~ inner ~ "\"" } 16 | inner = @{ char* } 17 | char = { 18 | !("\"" | "\\") ~ ANY 19 | | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") 20 | | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) 21 | } 22 | number = @{ 23 | "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? 24 | } 25 | -------------------------------------------------------------------------------- /valu3/src/parser/json/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use pest::Parser; 3 | use std::collections::HashMap; 4 | 5 | #[derive(Parser)] 6 | #[grammar = "parser/json/json.pest"] 7 | struct JSONParser; 8 | 9 | use pest::iterators::Pair; 10 | 11 | impl Value { 12 | pub fn json_to_value(str: &str) -> Result { 13 | let value = match JSONParser::parse(Rule::json, str.trim()) { 14 | Ok(mut pairs) => match pairs.next() { 15 | Some(pair) => Self::json_parse_value_inner(pair), 16 | None => return Err(Error::NonParseble), 17 | }, 18 | Err(msg) => return Err(Error::NonParsebleMsg(msg.to_string())), 19 | }; 20 | Ok(value) 21 | } 22 | 23 | fn json_parse_value_inner(pair: Pair) -> Self { 24 | match pair.as_rule() { 25 | Rule::object => { 26 | let map = pair 27 | .into_inner() 28 | .map(|pair| { 29 | let mut inner_rules = pair.into_inner(); 30 | let name = inner_rules 31 | .next() 32 | .unwrap() 33 | .into_inner() 34 | .next() 35 | .unwrap() 36 | .as_str() 37 | .to_string(); 38 | let value = Self::json_parse_value_inner(inner_rules.next().unwrap()); 39 | (name, value) 40 | }) 41 | .collect::>(); 42 | 43 | Self::from(map) 44 | } 45 | Rule::array => Self::from( 46 | pair.into_inner() 47 | .map(Self::json_parse_value_inner) 48 | .collect::>(), 49 | ), 50 | Rule::string => Self::from(StringB::from(pair.into_inner().next().unwrap().as_str())), 51 | Rule::number => Self::from(Number::try_from(pair.as_str()).unwrap()), 52 | Rule::boolean => Self::Boolean(pair.as_str().parse().unwrap()), 53 | Rule::null => Self::Null, 54 | Rule::json 55 | | Rule::EOI 56 | | Rule::key_value_pair 57 | | Rule::value 58 | | Rule::inner 59 | | Rule::char 60 | | Rule::WHITESPACE => Self::Undefined, 61 | } 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use crate::prelude::*; 68 | use std::collections::HashMap; 69 | 70 | #[test] 71 | fn json() { 72 | let raw: &str = "{ 73 | \"test\": true, 74 | \"test2\": \"ok\", 75 | \"test3\": [0, 1] 76 | }"; 77 | 78 | let compare = Value::from({ 79 | let mut map = HashMap::new(); 80 | map.insert("test".to_string(), true.to_value()); 81 | map.insert("test2".to_string(), "ok".to_value()); 82 | map.insert("test3".to_string(), Value::from(vec![0, 1])); 83 | map 84 | }); 85 | 86 | assert_eq!(Value::json_to_value(raw), Ok(compare)); 87 | } 88 | 89 | #[test] 90 | fn array() { 91 | let raw = "[0, true, null, \"ok\"]"; 92 | 93 | let compare = { 94 | let mut list = Vec::new(); 95 | list.push(Value::Number(Number::from(0))); 96 | list.push(Value::Boolean(true)); 97 | list.push(Value::Null); 98 | list.push(Value::String(StringB::from("ok"))); 99 | Value::from(list) 100 | }; 101 | 102 | assert_eq!(Value::json_to_value(raw), Ok(compare)); 103 | } 104 | 105 | #[test] 106 | fn number() { 107 | let int = "0"; 108 | let float = "1.0"; 109 | 110 | assert_eq!( 111 | Value::json_to_value(int), 112 | Ok(Value::Number(Number::from(0))) 113 | ); 114 | assert_eq!( 115 | Value::json_to_value(float), 116 | Ok(Value::Number(Number::from(1.0))) 117 | ); 118 | } 119 | 120 | #[test] 121 | fn string() { 122 | let string = r#""string""#; 123 | 124 | assert_eq!( 125 | Value::json_to_value(string), 126 | Ok(Value::String(StringB::from("string"))) 127 | ); 128 | } 129 | 130 | #[test] 131 | fn null() { 132 | let null = "null"; 133 | 134 | assert_eq!(Value::json_to_value(null), Ok(Value::Null)); 135 | } 136 | #[test] 137 | fn boolean() { 138 | let boolean = "true"; 139 | 140 | assert_eq!(Value::json_to_value(boolean), Ok(Value::Boolean(true))); 141 | } 142 | 143 | #[test] 144 | fn all() { 145 | let boolean = Value::json_to_value("true").unwrap(); 146 | let float = Value::json_to_value("3.14").unwrap(); 147 | let json = Value::json_to_value(r#"{"item": 3.14}"#).unwrap(); 148 | let array = Value::json_to_value(r#"[1,2,3]"#).unwrap(); 149 | let null = Value::json_to_value("null").unwrap(); 150 | let string = Value::json_to_value(r#""123""#).unwrap(); 151 | 152 | assert_eq!(boolean, true.to_value()); 153 | assert_eq!(float, 3.14.to_value()); 154 | assert_eq!(json, Value::from(vec![("item", 3.14)])); 155 | assert_eq!(array, vec![1, 2, 3].to_value()); 156 | assert_eq!(null, Value::Null); 157 | assert_eq!(string, "123".to_value()); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /valu3/src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod json; 2 | -------------------------------------------------------------------------------- /valu3/src/prelude.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | pub use crate::types::stringb::*; 3 | pub use crate::types::number::*; 4 | pub use crate::types::array::*; 5 | pub use crate::types::object::*; 6 | pub use crate::types::datetime::*; 7 | pub use crate::types::value_key::*; 8 | pub use crate::traits::*; 9 | pub use crate::to_value::*; 10 | pub use crate::to::json::*; 11 | pub use crate::to::yaml::*; 12 | pub use crate::value::*; 13 | pub use crate::Error; 14 | pub use crate::impls::*; 15 | #[cfg(feature = "cstring")] 16 | pub use std::ffi::CString; 17 | #[cfg(feature = "derive")] 18 | pub use valu3_derive::*; 19 | #[cfg(feature = "serde")] 20 | pub use crate::serde_value::*; 21 | -------------------------------------------------------------------------------- /valu3/src/primitives.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashMap}; 2 | use crate::prelude::*; 3 | 4 | #[cfg(feature = "cstring")] 5 | impl PrimitiveType for CString {} 6 | 7 | impl PrimitiveType for String {} 8 | 9 | impl PrimitiveType for bool {} 10 | 11 | impl PrimitiveType for str {} 12 | 13 | impl PrimitiveType for &str {} 14 | 15 | impl PrimitiveType for StringB {} 16 | 17 | impl PrimitiveType for Array {} 18 | 19 | impl PrimitiveType for DateTime {} 20 | 21 | impl PrimitiveType for Number {} 22 | 23 | impl PrimitiveType for Object {} 24 | 25 | impl PrimitiveType for HashMap {} 26 | 27 | impl PrimitiveType for BTreeMap {} 28 | 29 | impl PrimitiveType for Vec {} 30 | 31 | impl PrimitiveType for Option {} 32 | 33 | // Numerics 34 | impl PrimitiveType for u8 {} 35 | 36 | impl PrimitiveType for u16 {} 37 | 38 | impl PrimitiveType for u32 {} 39 | 40 | impl PrimitiveType for u64 {} 41 | 42 | impl PrimitiveType for u128 {} 43 | 44 | impl PrimitiveType for i8 {} 45 | 46 | impl PrimitiveType for i16 {} 47 | 48 | impl PrimitiveType for i32 {} 49 | 50 | impl PrimitiveType for i64 {} 51 | 52 | impl PrimitiveType for i128 {} 53 | 54 | impl PrimitiveType for f32 {} 55 | 56 | impl PrimitiveType for f64 {} 57 | 58 | impl PrimitiveType for usize {} 59 | 60 | impl PrimitiveType for isize {} 61 | -------------------------------------------------------------------------------- /valu3/src/serde_value/de.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use serde::de::{self, Deserialize, Visitor}; 3 | use serde::Deserializer; 4 | use std::collections::HashMap; 5 | use std::fmt; 6 | 7 | impl<'de> Deserialize<'de> for Value { 8 | fn deserialize(deserializer: D) -> Result 9 | where 10 | D: Deserializer<'de>, 11 | { 12 | struct ValueVisitor; 13 | 14 | impl<'de> Visitor<'de> for ValueVisitor { 15 | type Value = Value; 16 | 17 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 18 | formatter.write_str("any valid value") 19 | } 20 | 21 | fn visit_i8(self, v: i8) -> Result 22 | where 23 | E: de::Error, 24 | { 25 | Ok(Number::from(v).to_value()) 26 | } 27 | 28 | fn visit_i16(self, v: i16) -> Result 29 | where 30 | E: de::Error, 31 | { 32 | Ok(Number::from(v).to_value()) 33 | } 34 | 35 | fn visit_i32(self, v: i32) -> Result 36 | where 37 | E: de::Error, 38 | { 39 | Ok(Number::from(v).to_value()) 40 | } 41 | 42 | fn visit_i64(self, v: i64) -> Result 43 | where 44 | E: de::Error, 45 | { 46 | Ok(Number::from(v).to_value()) 47 | } 48 | 49 | fn visit_i128(self, v: i128) -> Result 50 | where 51 | E: de::Error, 52 | { 53 | Ok(Number::from(v).to_value()) 54 | } 55 | 56 | fn visit_u128(self, v: u128) -> Result 57 | where 58 | E: de::Error, 59 | { 60 | Ok(Number::from(v).to_value()) 61 | } 62 | 63 | fn visit_f32(self, v: f32) -> Result 64 | where 65 | E: de::Error, 66 | { 67 | Ok(Number::from(v).to_value()) 68 | } 69 | 70 | fn visit_f64(self, v: f64) -> Result 71 | where 72 | E: de::Error, 73 | { 74 | Ok(Number::from(v).to_value()) 75 | } 76 | 77 | fn visit_u8(self, v: u8) -> Result 78 | where 79 | E: de::Error, 80 | { 81 | Ok(Number::from(v).to_value()) 82 | } 83 | 84 | fn visit_u16(self, v: u16) -> Result 85 | where 86 | E: de::Error, 87 | { 88 | Ok(Number::from(v).to_value()) 89 | } 90 | 91 | fn visit_u32(self, v: u32) -> Result 92 | where 93 | E: de::Error, 94 | { 95 | Ok(Number::from(v).to_value()) 96 | } 97 | 98 | fn visit_u64(self, v: u64) -> Result 99 | where 100 | E: de::Error, 101 | { 102 | Ok(Number::from(v).to_value()) 103 | } 104 | 105 | fn visit_bool(self, v: bool) -> Result 106 | where 107 | E: de::Error, 108 | { 109 | Ok(Value::Boolean(v)) 110 | } 111 | 112 | fn visit_string(self, v: String) -> Result 113 | where 114 | E: de::Error, 115 | { 116 | Ok(StringB::from(v).to_value()) 117 | } 118 | 119 | fn visit_str(self, v: &str) -> Result 120 | where 121 | E: de::Error, 122 | { 123 | Ok(StringB::from(v).to_value()) 124 | } 125 | 126 | fn visit_map(self, mut access: A) -> Result 127 | where 128 | A: de::MapAccess<'de>, 129 | { 130 | let mut map: HashMap = HashMap::default(); 131 | 132 | while let Some((key, value)) = access.next_entry()? { 133 | map.insert(key, value); 134 | } 135 | 136 | Ok(Object::from(map).to_value()) 137 | } 138 | 139 | fn visit_seq(self, mut seq: A) -> Result 140 | where 141 | A: de::SeqAccess<'de>, 142 | { 143 | let mut vec: Vec = Vec::new(); 144 | // Update the max while there are additional values. 145 | while let Some(value) = seq.next_element()? { 146 | vec.push(value); 147 | } 148 | 149 | Ok(Array::from(vec).to_value()) 150 | } 151 | 152 | fn visit_none(self) -> Result 153 | where 154 | E: de::Error, 155 | { 156 | Ok(Value::Null) 157 | } 158 | 159 | fn visit_unit(self) -> Result 160 | where 161 | E: de::Error, 162 | { 163 | Ok(Value::Null) 164 | } 165 | 166 | fn visit_newtype_struct(self, deserializer: D) -> Result 167 | where 168 | D: Deserializer<'de>, 169 | { 170 | Deserialize::deserialize(deserializer) 171 | } 172 | 173 | fn visit_char(self, v: char) -> Result 174 | where 175 | E: de::Error, 176 | { 177 | self.visit_str(v.encode_utf8(&mut [0u8; 4])) 178 | } 179 | 180 | fn visit_borrowed_str(self, v: &'de str) -> Result 181 | where 182 | E: de::Error, 183 | { 184 | self.visit_str(v) 185 | } 186 | 187 | fn visit_bytes(self, v: &[u8]) -> Result 188 | where 189 | E: de::Error, 190 | { 191 | Err(de::Error::invalid_type(de::Unexpected::Bytes(v), &self)) 192 | } 193 | 194 | fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result 195 | where 196 | E: de::Error, 197 | { 198 | self.visit_bytes(v) 199 | } 200 | 201 | fn visit_byte_buf(self, v: Vec) -> Result 202 | where 203 | E: de::Error, 204 | { 205 | self.visit_bytes(&v) 206 | } 207 | 208 | fn visit_some(self, deserializer: D) -> Result 209 | where 210 | D: Deserializer<'de>, 211 | { 212 | let _ = deserializer; 213 | Err(de::Error::invalid_type(de::Unexpected::Option, &self)) 214 | } 215 | 216 | fn visit_enum(self, data: A) -> Result 217 | where 218 | A: de::EnumAccess<'de>, 219 | { 220 | let _ = data; 221 | Err(de::Error::invalid_type(de::Unexpected::Enum, &self)) 222 | } 223 | 224 | } 225 | 226 | deserializer.deserialize_any(ValueVisitor) 227 | } 228 | 229 | fn deserialize_in_place(deserializer: D, place: &mut Self) -> Result<(), D::Error> 230 | where 231 | D: Deserializer<'de>, 232 | { 233 | *place = Deserialize::deserialize(deserializer)?; 234 | Ok(()) 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /valu3/src/serde_value/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod de; 2 | pub mod ser; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use crate::prelude::*; 7 | use std::collections::HashMap; 8 | 9 | #[test] 10 | fn test_serde_number() { 11 | let value = Value::from(42u64); 12 | let serialized = serde_json::to_string(&value).unwrap(); 13 | assert_eq!(serialized, "42"); 14 | 15 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 16 | assert_eq!(deserialized, value); 17 | 18 | let value = Value::from(3.14); 19 | let serialized = serde_json::to_string(&value).unwrap(); 20 | 21 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 22 | assert_eq!(deserialized, value); 23 | 24 | let value = Value::from(-3.14); 25 | let serialized = serde_json::to_string(&value).unwrap(); 26 | 27 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 28 | assert_eq!(deserialized, value); 29 | 30 | let value = Value::from(3.14e10); 31 | let serialized = serde_json::to_string(&value).unwrap(); 32 | 33 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 34 | assert_eq!(deserialized, value); 35 | } 36 | 37 | #[test] 38 | fn test_serde_string() { 39 | let value = Value::from("hello"); 40 | let serialized = serde_json::to_string(&value).unwrap(); 41 | assert_eq!(serialized, "\"hello\""); 42 | 43 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 44 | assert_eq!(deserialized, value); 45 | } 46 | 47 | #[test] 48 | fn test_serde_array() { 49 | let value = Value::from(vec![ 50 | Value::from(1u64), 51 | Value::from(2u64), 52 | Value::from(3u64), 53 | ]); 54 | let serialized = serde_json::to_string(&value).unwrap(); 55 | assert_eq!(serialized, "[1,2,3]"); 56 | 57 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 58 | assert_eq!(deserialized, value); 59 | } 60 | 61 | #[test] 62 | fn test_serde_object() { 63 | let mut object = HashMap::new(); 64 | object.insert("a", Value::from(1u64)); 65 | object.insert("b", Value::from(2u64)); 66 | object.insert("c", Value::from(3u64)); 67 | let value = Value::from(object); 68 | let serialized = serde_json::to_string(&value).unwrap(); 69 | 70 | let cases = [ 71 | r#"{"a":1,"b":2,"c":3}"#, 72 | r#"{"a":1,"c":3,"b":2}"#, 73 | r#"{"b":2,"a":1,"c":3}"#, 74 | r#"{"b":2,"c":3,"a":1}"#, 75 | r#"{"c":3,"b":2,"a":1}"#, 76 | r#"{"c":3,"a":1,"b":2}"#, 77 | ]; 78 | assert_eq!(cases.contains(&serialized.as_str()), true); 79 | 80 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 81 | assert_eq!(deserialized, value); 82 | } 83 | 84 | #[test] 85 | fn test_serde_bool() { 86 | let value = Value::from(true); 87 | let serialized = serde_json::to_string(&value).unwrap(); 88 | assert_eq!(serialized, "true"); 89 | 90 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 91 | assert_eq!(deserialized, value); 92 | } 93 | 94 | #[test] 95 | fn test_serde_null() { 96 | let value = Value::Null; 97 | let serialized = serde_json::to_string(&value).unwrap(); 98 | assert_eq!(serialized, "null"); 99 | 100 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 101 | assert_eq!(deserialized, value); 102 | } 103 | 104 | #[test] 105 | fn test_serde_value() { 106 | let value = Value::from(42u64); 107 | let serialized = serde_json::to_string(&value).unwrap(); 108 | assert_eq!(serialized, "42"); 109 | 110 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 111 | assert_eq!(deserialized, value); 112 | 113 | let value = Value::from("hello"); 114 | let serialized = serde_json::to_string(&value).unwrap(); 115 | assert_eq!(serialized, "\"hello\""); 116 | 117 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 118 | assert_eq!(deserialized, value); 119 | 120 | let value = Value::from(vec![ 121 | Value::from(1u64), 122 | Value::from(2u64), 123 | Value::from(3u64), 124 | ]); 125 | let serialized = serde_json::to_string(&value).unwrap(); 126 | assert_eq!(serialized, "[1,2,3]"); 127 | 128 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 129 | assert_eq!(deserialized, value); 130 | 131 | let mut object = HashMap::new(); 132 | object.insert("a", Value::from(1u64)); 133 | object.insert("b", Value::from(2u64)); 134 | object.insert("c", Value::from(3u64)); 135 | let value = Value::from(object); 136 | let serialized = serde_json::to_string(&value).unwrap(); 137 | let cases = [ 138 | r#"{"a":1,"b":2,"c":3}"#, 139 | r#"{"a":1,"c":3,"b":2}"#, 140 | r#"{"b":2,"a":1,"c":3}"#, 141 | r#"{"b":2,"c":3,"a":1}"#, 142 | r#"{"c":3,"b":2,"a":1}"#, 143 | r#"{"c":3,"a":1,"b":2}"#, 144 | ]; 145 | assert_eq!(cases.contains(&serialized.as_str()), true); 146 | 147 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 148 | assert_eq!(deserialized, value); 149 | 150 | let value = Value::from(true); 151 | let serialized = serde_json::to_string(&value).unwrap(); 152 | assert_eq!(serialized, "true"); 153 | 154 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 155 | assert_eq!(deserialized, value); 156 | 157 | let value = Value::Null; 158 | let serialized = serde_json::to_string(&value).unwrap(); 159 | assert_eq!(serialized, "null"); 160 | 161 | let deserialized: Value = serde_json::from_str(&serialized).unwrap(); 162 | assert_eq!(deserialized, value); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /valu3/src/serde_value/ser.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use crate::types::number::NumberType; 3 | use serde::ser::SerializeSeq; 4 | use serde::ser::{Error, Serialize, Serializer}; 5 | 6 | 7 | impl Serialize for Value { 8 | fn serialize(&self, serializer: S) -> Result 9 | where 10 | S: Serializer, 11 | { 12 | match self { 13 | Value::Object(value) => { 14 | use serde::ser::SerializeMap; 15 | let mut map = serializer.serialize_map(Some(value.len()))?; 16 | for (k, v) in value.iter() { 17 | map.serialize_entry(&k.to_string(), &v)?; 18 | } 19 | map.end() 20 | } 21 | Value::Array(value) => { 22 | let mut seq = serializer.serialize_seq(Some(value.len()))?; 23 | for elem in value { 24 | seq.serialize_element(elem)?; 25 | } 26 | seq.end() 27 | } 28 | Value::String(value) => serializer.serialize_str(value.as_str()), 29 | Value::Number(value) => match &value.number_type() { 30 | NumberType::U8 => serializer.serialize_u8(value.get_u8_unsafe()), 31 | NumberType::U16 => serializer.serialize_u16(value.get_u16_unsafe()), 32 | NumberType::U32 => serializer.serialize_u32(value.get_u32_unsafe()), 33 | NumberType::U64 => serializer.serialize_u64(value.get_u64_unsafe()), 34 | NumberType::U128 => serializer.serialize_u128(value.get_u128_unsafe()), 35 | NumberType::I8 => serializer.serialize_i8(value.get_i8_unsafe()), 36 | NumberType::I16 => serializer.serialize_i16(value.get_i16_unsafe()), 37 | NumberType::I32 => serializer.serialize_i32(value.get_i32_unsafe()), 38 | NumberType::I64 => serializer.serialize_i64(value.get_i64_unsafe()), 39 | NumberType::I128 => serializer.serialize_i128(value.get_i128_unsafe()), 40 | NumberType::F32 => serializer.serialize_f32(value.get_f32_unsafe()), 41 | NumberType::F64 => serializer.serialize_f64(value.get_f64_unsafe()), 42 | NumberType::Unknown => Err(Error::custom("Unknown number type")), 43 | }, 44 | Value::Boolean(value) => serializer.serialize_bool(*value), 45 | Value::Null => serializer.serialize_none(), 46 | Value::Undefined => serializer.serialize_none(), 47 | Value::DateTime(value) => serializer.serialize_str(&value.to_iso8601()), 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /valu3/src/tests/derive.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod test { 3 | use crate::prelude::*; 4 | use std::collections::{BTreeMap, HashMap}; 5 | 6 | #[derive(ToValue, FromValue, PartialEq, Debug, Clone)] 7 | enum ExampleType { 8 | Example1, 9 | Example2, 10 | } 11 | 12 | #[derive(ToValue, FromValue, PartialEq, Debug, Default, Clone, ToJson)] 13 | struct Inner { 14 | item_a: bool, 15 | tree: BTreeMap, 16 | } 17 | 18 | #[derive(ToValue, FromValue, PartialEq, Debug, Clone, ToJson)] 19 | struct Example 20 | where 21 | T: PrimitiveType + FromValueBehavior + Clone + ToValueBehavior, 22 | { 23 | item_a: i32, 24 | item_b: String, 25 | item_c: Option>, 26 | item_d: HashMap, 27 | item_e: ExampleType, 28 | item_f: T, 29 | } 30 | 31 | #[test] 32 | fn test_example() { 33 | let example = Example { 34 | item_a: 1, 35 | item_b: "Hello".to_string(), 36 | item_c: Some(vec!["World".to_string()]), 37 | item_d: HashMap::default(), 38 | item_e: ExampleType::Example1, 39 | item_f: "Generic".to_string(), 40 | }; 41 | 42 | let value = example.to_value(); 43 | 44 | assert_eq!(example, Example::from_value(value).unwrap()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /valu3/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "derive")] 2 | mod derive; -------------------------------------------------------------------------------- /valu3/src/to/bin.rs: -------------------------------------------------------------------------------- 1 | // use crate::prelude::*; 2 | // use bincode::{deserialize, serialize}; 3 | 4 | // #[cfg(test)] 5 | // mod tests { 6 | // use crate::prelude::*; 7 | // use bincode::{deserialize, serialize}; 8 | // use crate::serde_value::de::*; 9 | // use crate::serde_value::ser::*; 10 | 11 | // #[test] 12 | // fn test_value_object() { 13 | // let mut object = Object::default(); 14 | // object.insert( 15 | // "key".to_string(), 16 | // Value::String(StringB::from("value".to_string())), 17 | // ); 18 | // object.insert( 19 | // "key2".to_string(), 20 | // Value::Number(Number::from(42)), 21 | // ); 22 | // object.insert( 23 | // "key3".to_string(), 24 | // Value::Boolean(true), 25 | // ); 26 | // object.insert( 27 | // "key4".to_string(), 28 | // Value::Null, 29 | // ); 30 | // let value = object.to_value(); 31 | 32 | // let bytes = serialize(&value).unwrap(); 33 | // let deserialized: Value = deserialize(&bytes).unwrap(); 34 | 35 | // assert_eq!(value, deserialized); 36 | // } 37 | // } -------------------------------------------------------------------------------- /valu3/src/to/json.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use regex::Regex; 3 | 4 | /// An enum representing the JSON output format mode. 5 | pub enum JsonMode { 6 | /// Outputs the JSON in an indented format. 7 | Indented, 8 | /// Outputs the JSON in an inline format. 9 | Inline, 10 | } 11 | 12 | impl Value { 13 | /// Converts a `Value` into a JSON string. 14 | /// 15 | /// # Arguments 16 | /// 17 | /// * `mode` - A `JsonMode` value representing the JSON output format mode. 18 | /// 19 | /// # Examples 20 | /// 21 | /// ```no_run 22 | /// use json_utils::{Value, JsonMode}; 23 | /// 24 | /// let value = Value::payload_to_value("{\"name\":\"John Doe\",\"age\":30,\"is_active\":true}").unwrap(); 25 | /// let json_string = value.to_json(JsonMode::Indented); 26 | /// println!("{}", json_string); 27 | /// ``` 28 | pub fn to_json(&self, mode: JsonMode) -> String { 29 | let value = Value::to_json_inner(self, 0); 30 | 31 | match mode { 32 | JsonMode::Inline => Self::inline(value), 33 | JsonMode::Indented => value, 34 | } 35 | } 36 | 37 | /// Converts the inline JSON string into an indented JSON string. 38 | fn inline(value: String) -> String { 39 | let re = Regex::new(r"(\n)|(\t)").unwrap(); 40 | let result = re.replace_all(&value, ""); 41 | result.to_string() 42 | } 43 | 44 | /// Generates tab indentation. 45 | fn tabs(total: i32) -> String { 46 | vec!["\t"; total as usize].join("") 47 | } 48 | 49 | /// Converts a `Value` into a JSON string. 50 | fn to_json_inner(val: &Value, children: i32) -> String { 51 | match val { 52 | Value::Object(o) => { 53 | let contents: Vec<_> = o 54 | .iter() 55 | .map(|(name, value)| { 56 | format!( 57 | "\n\t{}\"{}\": {}", 58 | &Self::tabs(children), 59 | name, 60 | Value::to_json_inner(value, children + 1) 61 | ) 62 | }) 63 | .collect(); 64 | format!("{{{}\n{}}}", contents.join(","), &Self::tabs(children)) 65 | } 66 | Value::Array(a) => { 67 | let contents: Vec<_> = a 68 | .into_iter() 69 | .map(|value| Value::to_json_inner(value, children + 1)) 70 | .collect(); 71 | format!( 72 | "[\n\t{}{}\n{}]", 73 | &Self::tabs(children), 74 | contents.join(&format!(",\n\t{}", &Self::tabs(children))), 75 | &Self::tabs(children) 76 | ) 77 | } 78 | Value::String(s) => { 79 | let string = s.as_str(); 80 | let re = Regex::new(r#"""#).unwrap(); 81 | let list = string 82 | .chars() 83 | .into_iter() 84 | .map(|c| c.to_string()) 85 | .collect::>(); 86 | let mut result = list.clone(); 87 | let mut add_posi = 0; 88 | 89 | for item in re.captures_iter(string) { 90 | let range = item.get(0).unwrap().range(); 91 | 92 | if range.start.eq(&0) { 93 | result.insert(range.start + add_posi, r#"\"#.to_string()); 94 | add_posi += 1; 95 | } else { 96 | let before = range.start - 1; 97 | 98 | if list.get(before).unwrap().ne(r#"\"#) { 99 | result.insert(range.start + add_posi, r#"\"#.to_string()); 100 | add_posi += 1; 101 | } 102 | } 103 | } 104 | 105 | format!("\"{}\"", result.join("")) 106 | } 107 | Value::Number(n) => format!("{}", n), 108 | Value::Boolean(b) => format!("{}", b), 109 | Value::Null => "null".to_string(), 110 | Value::Undefined => "undefined".to_string(), 111 | Value::DateTime(date_time) => format!("\"{}\"", date_time), 112 | } 113 | } 114 | } 115 | 116 | #[cfg(test)] 117 | mod tests { 118 | use super::*; 119 | 120 | #[test] 121 | fn it_should_remove_tabs_and_empty_lines() { 122 | let str = 123 | String::from("{\n\t\"name\":\"John Doe\",\n\t\"age\":30,\n\t\"is_active\":true\n}"); 124 | let expected = String::from("{\"name\":\"John Doe\",\"age\":30,\"is_active\":true}"); 125 | assert_eq!(expected, Value::inline(str)); 126 | } 127 | 128 | #[test] 129 | fn it_should_add_tabs_by_number() { 130 | assert_eq!("\t\t\t", Value::tabs(3)); 131 | } 132 | 133 | #[test] 134 | fn it_should_convert_a_value_to_json_string() { 135 | let value_str = Value::json_to_value("{\"name\":\"John Doe\"}").unwrap(); 136 | let value_number = Value::json_to_value("{\"age\":30}").unwrap(); 137 | let value_boolean = Value::json_to_value("{\"is_active\":true}").unwrap(); 138 | assert_eq!( 139 | "{\n\t\"name\": \"John Doe\"\n}", 140 | value_str.to_json(JsonMode::Indented) 141 | ); 142 | assert_eq!( 143 | "{\n\t\"age\": 30\n}", 144 | value_number.to_json(JsonMode::Indented) 145 | ); 146 | assert_eq!( 147 | "{\n\t\"is_active\": true\n}", 148 | value_boolean.to_json(JsonMode::Indented) 149 | ) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /valu3/src/to/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod json; 2 | pub mod yaml; 3 | // pub mod bin; 4 | -------------------------------------------------------------------------------- /valu3/src/to/yaml.rs: -------------------------------------------------------------------------------- 1 | use crate::value::Value; 2 | 3 | impl Value { 4 | /// Returns the YAML representation of the given `Value` with the specified indentation. 5 | /// 6 | /// # Arguments 7 | /// 8 | /// * `indent` - The number of spaces to use for indentation. 9 | /// 10 | /// # Example 11 | /// 12 | /// ```no_run 13 | /// # use json_utils::{Value, StringB}; 14 | /// let value = Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]); 15 | /// assert_eq!(value.to_yaml_with_indent(2), " - 1\n - 2\n - 3\n".to_string()); 16 | /// ``` 17 | pub fn to_yaml_with_indent(&self, indent: usize) -> String { 18 | let prefix = " ".repeat(indent); 19 | match self { 20 | Value::Null => " null\n".to_string(), 21 | Value::Boolean(b) => format!(" {}\n", b), 22 | Value::Number(n) => format!(" {}\n", n), 23 | Value::String(s) => format!(r#" "{}"\n"#, s.to_string()), 24 | Value::Array(a) => { 25 | let elements: Vec = a 26 | .into_iter() 27 | .map(|v| format!(" - {}", v.to_yaml_with_indent(indent + 2))) 28 | .collect(); 29 | format!("\n{}", elements.join("")) 30 | } 31 | Value::Object(o) => { 32 | let elements: Vec = o 33 | .iter() 34 | .map(|(k, v)| format!("{}{}:{}", prefix, k, v.to_yaml_with_indent(indent + 2))) 35 | .collect(); 36 | if indent > 0 { 37 | format!("\n{}", elements.join("")) 38 | } else { 39 | elements.join("") 40 | } 41 | } 42 | Value::Undefined => " ~\n".to_string(), 43 | Value::DateTime(dt) => format!(" {}\n", dt.to_string()), 44 | } 45 | } 46 | 47 | /// Returns the YAML representation of the given `Value`. 48 | /// 49 | /// # Example 50 | /// 51 | /// ```no_run 52 | /// # use json_utils::{Value, StringB}; 53 | /// let value = Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]); 54 | /// assert_eq!(value.to_yaml(), "- 1\n - 2\n - 3\n".to_string()); 55 | /// ``` 56 | pub fn to_yaml(&self) -> String { 57 | self.to_yaml_with_indent(0).to_string() 58 | } 59 | } 60 | 61 | #[test] 62 | fn test_to_yaml() { 63 | use std::collections::BTreeMap; 64 | use crate::prelude::*; 65 | 66 | let object = Object::from( 67 | vec![ 68 | ("null_value".to_string(), Value::Null), 69 | ("boolean_value".to_string(), Value::Boolean(true)), 70 | ("number_value".to_string(), Number::from(42).to_value()), 71 | ( 72 | "string_value".to_string(), 73 | StringB::from("Hello, world!".to_string()).to_value(), 74 | ), 75 | ( 76 | "array_value".to_string(), 77 | Array::from(vec![ 78 | Number::from(1).to_value(), 79 | Number::from(2).to_value(), 80 | Number::from(3).to_value(), 81 | ]) 82 | .to_value(), 83 | ), 84 | ( 85 | "object_value".to_string(), 86 | Object::from( 87 | vec![ 88 | ( 89 | "key1".to_string(), 90 | StringB::from("value1".to_string()).to_value(), 91 | ), 92 | ( 93 | "key2".to_string(), 94 | StringB::from("value2".to_string()).to_value(), 95 | ), 96 | ] 97 | .into_iter() 98 | .collect::>(), 99 | ) 100 | .to_value(), 101 | ), 102 | ("undefined_value".to_string(), Value::Undefined), 103 | ] 104 | .into_iter() 105 | .collect::>(), 106 | ); 107 | 108 | let value = Value::Object(object); 109 | let yaml_output = value.to_yaml(); 110 | let mut yaml_lines: Vec<_> = yaml_output.lines().collect(); 111 | yaml_lines.sort(); 112 | 113 | assert!(true); 114 | } 115 | -------------------------------------------------------------------------------- /valu3/src/to_value.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use std::collections::{BTreeMap, HashMap}; 3 | 4 | impl ToValueBehavior for Value { 5 | fn to_value(&self) -> Value { 6 | self.clone() 7 | } 8 | } 9 | 10 | #[cfg(feature = "cstring")] 11 | impl ToValueBehavior for CString { 12 | fn to_value(&self) -> Value { 13 | Value::String(StringB::from(self.clone())) 14 | } 15 | } 16 | 17 | #[cfg(feature = "cstring")] 18 | impl ToValueBehavior for String { 19 | fn to_value(&self) -> Value { 20 | Value::String(StringB::from(CString::new(self.clone()).unwrap())) 21 | } 22 | } 23 | 24 | #[cfg(not(feature = "cstring"))] 25 | impl ToValueBehavior for String { 26 | fn to_value(&self) -> Value { 27 | Value::String(StringB::from(self.clone())) 28 | } 29 | } 30 | 31 | impl ToValueBehavior for bool { 32 | fn to_value(&self) -> Value { 33 | Value::Boolean(*self) 34 | } 35 | } 36 | 37 | impl ToValueBehavior for str { 38 | fn to_value(&self) -> Value { 39 | Value::String(StringB::from(self.to_string())) 40 | } 41 | } 42 | 43 | impl ToValueBehavior for &str { 44 | fn to_value(&self) -> Value { 45 | Value::String(StringB::from(self.to_string())) 46 | } 47 | } 48 | 49 | impl ToValueBehavior for StringB { 50 | fn to_value(&self) -> Value { 51 | Value::String(StringB::from(self.clone())) 52 | } 53 | } 54 | 55 | impl ToValueBehavior for Array { 56 | fn to_value(&self) -> Value { 57 | Value::Array(self.clone()) 58 | } 59 | } 60 | 61 | impl ToValueBehavior for DateTime { 62 | fn to_value(&self) -> Value { 63 | Value::DateTime(self.clone()) 64 | } 65 | } 66 | 67 | impl ToValueBehavior for Number { 68 | fn to_value(&self) -> Value { 69 | Value::Number(self.clone()) 70 | } 71 | } 72 | 73 | impl ToValueBehavior for Object { 74 | fn to_value(&self) -> Value { 75 | Value::Object(self.clone()) 76 | } 77 | } 78 | 79 | impl ToValueBehavior for Option 80 | where 81 | T: ToValueBehavior, 82 | { 83 | fn to_value(&self) -> Value { 84 | match self { 85 | Some(value) => value.to_value(), 86 | None => Value::Null, 87 | } 88 | } 89 | } 90 | 91 | impl ToValueBehavior for Vec 92 | where 93 | V: ToValueBehavior, 94 | { 95 | fn to_value(&self) -> Value { 96 | Array::from(self.iter().map(|v| v.to_value()).collect::>()).to_value() 97 | } 98 | } 99 | 100 | impl ToValueBehavior for HashMap 101 | where 102 | K: ValueKeyBehavior, 103 | V: ToValueBehavior, 104 | { 105 | fn to_value(&self) -> Value { 106 | Object::from( 107 | self.iter() 108 | .map(|(k, v)| (k.to_value_key(), v.to_value())) 109 | .collect::>(), 110 | ) 111 | .to_value() 112 | } 113 | } 114 | 115 | impl ToValueBehavior for BTreeMap 116 | where 117 | T: ValueKeyBehavior, 118 | V: ToValueBehavior, 119 | { 120 | fn to_value(&self) -> Value { 121 | Object::from( 122 | self.iter() 123 | .map(|(k, v)| (k.to_value_key(), v.to_value())) 124 | .collect::>(), 125 | ) 126 | .to_value() 127 | } 128 | } 129 | 130 | // Numerics 131 | impl ToValueBehavior for u8 { 132 | fn to_value(&self) -> Value { 133 | Value::Number(Number::from(*self)) 134 | } 135 | } 136 | 137 | impl ToValueBehavior for u16 { 138 | fn to_value(&self) -> Value { 139 | Value::Number(Number::from(*self)) 140 | } 141 | } 142 | 143 | impl ToValueBehavior for u32 { 144 | fn to_value(&self) -> Value { 145 | Value::Number(Number::from(*self)) 146 | } 147 | } 148 | 149 | impl ToValueBehavior for u64 { 150 | fn to_value(&self) -> Value { 151 | Value::Number(Number::from(*self)) 152 | } 153 | } 154 | 155 | impl ToValueBehavior for u128 { 156 | fn to_value(&self) -> Value { 157 | Value::Number(Number::from(*self)) 158 | } 159 | } 160 | 161 | impl ToValueBehavior for i8 { 162 | fn to_value(&self) -> Value { 163 | Value::Number(Number::from(*self)) 164 | } 165 | } 166 | 167 | impl ToValueBehavior for i16 { 168 | fn to_value(&self) -> Value { 169 | Value::Number(Number::from(*self)) 170 | } 171 | } 172 | 173 | impl ToValueBehavior for i32 { 174 | fn to_value(&self) -> Value { 175 | Value::Number(Number::from(*self)) 176 | } 177 | } 178 | 179 | impl ToValueBehavior for i64 { 180 | fn to_value(&self) -> Value { 181 | Value::Number(Number::from(*self)) 182 | } 183 | } 184 | 185 | impl ToValueBehavior for i128 { 186 | fn to_value(&self) -> Value { 187 | Value::Number(Number::from(*self)) 188 | } 189 | } 190 | 191 | impl ToValueBehavior for f32 { 192 | fn to_value(&self) -> Value { 193 | Value::Number(Number::from(*self)) 194 | } 195 | } 196 | 197 | impl ToValueBehavior for f64 { 198 | fn to_value(&self) -> Value { 199 | Value::Number(Number::from(*self)) 200 | } 201 | } 202 | impl ToValueBehavior for usize { 203 | fn to_value(&self) -> Value { 204 | Value::Number(Number::from(*self)) 205 | } 206 | } 207 | 208 | impl ToValueBehavior for isize { 209 | fn to_value(&self) -> Value { 210 | Value::Number(Number::from(*self)) 211 | } 212 | } 213 | 214 | /// Set to_value all items in a vector 215 | /// # Example 216 | /// ``` 217 | /// use valu3_parquet::vec_value; 218 | /// use valu3::Value; 219 | /// let vec = vec_value![1, 2, 3]; 220 | /// 221 | /// assert_eq!(vec, vec![Value::Number(1), Value::Number(2), Value::Number(3)]); 222 | /// ``` 223 | #[macro_export] 224 | macro_rules! vec_value { 225 | ($($x:expr),*) => { 226 | vec![$($x.to_value()),*] 227 | }; 228 | } 229 | 230 | #[cfg(test)] 231 | mod test { 232 | use std::collections::HashMap; 233 | 234 | use crate::prelude::*; 235 | 236 | #[test] 237 | fn test_boolean() { 238 | assert_eq!(true.to_value(), Value::Boolean(true)); 239 | assert_eq!(false.to_value(), Value::Boolean(false)); 240 | } 241 | 242 | #[test] 243 | fn test_string() { 244 | assert_eq!( 245 | "test".to_value(), 246 | Value::String(StringB::from("test".to_string())) 247 | ); 248 | } 249 | 250 | #[test] 251 | fn test_array() { 252 | assert_eq!( 253 | vec![1, 2, 3].to_value(), 254 | Value::Array(Array::from(vec![1, 2, 3])) 255 | ); 256 | } 257 | 258 | #[test] 259 | fn test_object() { 260 | let mut map = HashMap::new(); 261 | map.insert("test", 1); 262 | assert_eq!(map.to_value(), Object::from(map).to_value()); 263 | } 264 | 265 | #[test] 266 | fn test_vec_value_macro() { 267 | assert_eq!( 268 | vec_value![1, 2, vec![1, 2, 3]], 269 | vec![Value::from(1), Value::from(2), Value::from(vec![1, 2, 3])] 270 | ); 271 | } 272 | 273 | #[test] 274 | fn test_from_usize() { 275 | let number = 1 as usize; 276 | assert_eq!(number.to_value(), Value::Number(Number::from(number))); 277 | } 278 | 279 | #[test] 280 | fn test_from_isize() { 281 | let number = 1 as isize; 282 | assert_eq!(number.to_value(), Value::Number(Number::from(number))); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /valu3/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use std::collections::{BTreeMap, HashMap}; 3 | 4 | pub trait PrimitiveType {} 5 | 6 | /// A trait for converting types to `Value`. 7 | pub trait ToValueBehavior { 8 | /// Converts a type into a `Value`. 9 | fn to_value(&self) -> Value; 10 | } 11 | /// A trait for converting `Value` to types. 12 | pub trait FromValueBehavior { 13 | type Item; 14 | /// Converts a `Value` into a type. 15 | fn from_value(value: Value) -> Option; 16 | } 17 | 18 | impl FromValueBehavior for i8 { 19 | type Item = i8; 20 | 21 | fn from_value(value: Value) -> Option { 22 | if let Value::Number(number) = value { 23 | number.get_i8() 24 | } else { 25 | None 26 | } 27 | } 28 | } 29 | 30 | impl FromValueBehavior for i16 { 31 | type Item = i16; 32 | 33 | fn from_value(value: Value) -> Option { 34 | if let Value::Number(number) = value { 35 | number.get_i16() 36 | } else { 37 | None 38 | } 39 | } 40 | } 41 | 42 | impl FromValueBehavior for i32 { 43 | type Item = i32; 44 | 45 | fn from_value(value: Value) -> Option { 46 | if let Value::Number(number) = value { 47 | number.get_i32() 48 | } else { 49 | None 50 | } 51 | } 52 | } 53 | 54 | impl FromValueBehavior for i64 { 55 | type Item = i64; 56 | 57 | fn from_value(value: Value) -> Option { 58 | if let Value::Number(number) = value { 59 | number.get_i64() 60 | } else { 61 | None 62 | } 63 | } 64 | } 65 | 66 | impl FromValueBehavior for i128 { 67 | type Item = i128; 68 | 69 | fn from_value(value: Value) -> Option { 70 | if let Value::Number(number) = value { 71 | number.get_i128() 72 | } else { 73 | None 74 | } 75 | } 76 | } 77 | 78 | impl FromValueBehavior for u8 { 79 | type Item = u8; 80 | 81 | fn from_value(value: Value) -> Option { 82 | if let Value::Number(number) = value { 83 | number.get_u8() 84 | } else { 85 | None 86 | } 87 | } 88 | } 89 | 90 | impl FromValueBehavior for u16 { 91 | type Item = u16; 92 | 93 | fn from_value(value: Value) -> Option { 94 | if let Value::Number(number) = value { 95 | number.get_u16() 96 | } else { 97 | None 98 | } 99 | } 100 | } 101 | 102 | impl FromValueBehavior for u32 { 103 | type Item = u32; 104 | 105 | fn from_value(value: Value) -> Option { 106 | if let Value::Number(number) = value { 107 | number.get_u32() 108 | } else { 109 | None 110 | } 111 | } 112 | } 113 | 114 | impl FromValueBehavior for u64 { 115 | type Item = u64; 116 | 117 | fn from_value(value: Value) -> Option { 118 | if let Value::Number(number) = value { 119 | number.get_u64() 120 | } else { 121 | None 122 | } 123 | } 124 | } 125 | 126 | impl FromValueBehavior for u128 { 127 | type Item = u128; 128 | 129 | fn from_value(value: Value) -> Option { 130 | if let Value::Number(number) = value { 131 | number.get_u128() 132 | } else { 133 | None 134 | } 135 | } 136 | } 137 | 138 | impl FromValueBehavior for f32 { 139 | type Item = f32; 140 | 141 | fn from_value(value: Value) -> Option { 142 | if let Value::Number(number) = value { 143 | number.get_f32() 144 | } else { 145 | None 146 | } 147 | } 148 | } 149 | 150 | impl FromValueBehavior for f64 { 151 | type Item = f64; 152 | 153 | fn from_value(value: Value) -> Option { 154 | if let Value::Number(number) = value { 155 | number.get_f64() 156 | } else { 157 | None 158 | } 159 | } 160 | } 161 | 162 | impl FromValueBehavior for &str { 163 | type Item = String; 164 | 165 | fn from_value(value: Value) -> Option { 166 | if let Value::String(string_b) = value { 167 | Some(string_b.as_string()) 168 | } else { 169 | None 170 | } 171 | } 172 | } 173 | 174 | impl FromValueBehavior for str { 175 | type Item = String; 176 | 177 | fn from_value(value: Value) -> Option { 178 | if let Value::String(string_b) = value { 179 | Some(string_b.as_string()) 180 | } else { 181 | None 182 | } 183 | } 184 | } 185 | 186 | #[cfg(feature = "cstring")] 187 | impl FromValueBehavior for String { 188 | type Item = String; 189 | 190 | fn from_value(value: Value) -> Option { 191 | if let Value::String(string_b) = value { 192 | Some(string_b.as_string()) 193 | } else { 194 | None 195 | } 196 | } 197 | } 198 | 199 | #[cfg(not(feature = "cstring"))] 200 | impl FromValueBehavior for String { 201 | type Item = String; 202 | 203 | fn from_value(value: Value) -> Option { 204 | if let Value::String(string_b) = value { 205 | Some(string_b.as_string()) 206 | } else { 207 | None 208 | } 209 | } 210 | } 211 | 212 | impl FromValueBehavior for bool { 213 | type Item = bool; 214 | 215 | fn from_value(value: Value) -> Option { 216 | if let Value::Boolean(bool) = value { 217 | Some(bool) 218 | } else { 219 | None 220 | } 221 | } 222 | } 223 | 224 | impl FromValueBehavior for Vec 225 | where 226 | T: FromValueBehavior, 227 | { 228 | type Item = Vec<::Item>; 229 | 230 | fn from_value(value: Value) -> Option { 231 | if let Value::Array(array) = value { 232 | Some( 233 | array 234 | .into_iter() 235 | .map(|value| T::from_value(value).unwrap()) 236 | .collect::>(), 237 | ) 238 | } else { 239 | None 240 | } 241 | } 242 | } 243 | 244 | impl FromValueBehavior for Value { 245 | type Item = Value; 246 | 247 | fn from_value(value: Value) -> Option { 248 | Some(value) 249 | } 250 | } 251 | 252 | #[cfg(feature = "cstring")] 253 | impl FromValueBehavior for HashMap 254 | where 255 | T: FromValueBehavior, 256 | { 257 | type Item = HashMap::Item>; 258 | 259 | fn from_value(value: Value) -> Option { 260 | if let Value::Object(array) = value { 261 | Some(array.iter().fold(HashMap::new(), |mut map, (key, value)| { 262 | map.insert( 263 | key.as_string_b().extract(), 264 | T::from_value(value.clone()).unwrap(), 265 | ); 266 | map 267 | })) 268 | } else { 269 | None 270 | } 271 | } 272 | } 273 | 274 | #[cfg(feature = "cstring")] 275 | impl FromValueBehavior for BTreeMap 276 | where 277 | T: FromValueBehavior, 278 | { 279 | type Item = BTreeMap::Item>; 280 | 281 | fn from_value(value: Value) -> Option { 282 | if let Value::Object(array) = value { 283 | Some(array.iter().fold(BTreeMap::new(), |mut map, (key, value)| { 284 | map.insert( 285 | key.as_string_b().extract(), 286 | T::from_value(value.clone()).unwrap(), 287 | ); 288 | map 289 | })) 290 | } else { 291 | None 292 | } 293 | } 294 | } 295 | 296 | #[cfg(feature = "cstring")] 297 | impl FromValueBehavior for HashMap 298 | where 299 | T: FromValueBehavior, 300 | { 301 | type Item = HashMap::Item>; 302 | 303 | fn from_value(value: Value) -> Option { 304 | if let Value::Object(array) = value { 305 | Some(array.iter().fold(HashMap::new(), |mut map, (key, value)| { 306 | map.insert( 307 | key.as_string_b().as_string(), 308 | T::from_value(value.clone()).unwrap(), 309 | ); 310 | map 311 | })) 312 | } else { 313 | None 314 | } 315 | } 316 | } 317 | 318 | #[cfg(feature = "cstring")] 319 | impl FromValueBehavior for BTreeMap 320 | where 321 | T: FromValueBehavior, 322 | { 323 | type Item = BTreeMap::Item>; 324 | 325 | fn from_value(value: Value) -> Option { 326 | if let Value::Object(array) = value { 327 | Some(array.iter().fold(BTreeMap::new(), |mut map, (key, value)| { 328 | map.insert( 329 | key.as_string_b().as_string(), 330 | T::from_value(value.clone()).unwrap(), 331 | ); 332 | map 333 | })) 334 | } else { 335 | None 336 | } 337 | } 338 | } 339 | 340 | #[cfg(not(feature = "cstring"))] 341 | impl FromValueBehavior for HashMap 342 | where 343 | T: FromValueBehavior, 344 | { 345 | type Item = HashMap::Item>; 346 | 347 | fn from_value(value: Value) -> Option { 348 | if let Value::Object(array) = value { 349 | Some(array.iter().fold(HashMap::new(), |mut map, (key, value)| { 350 | map.insert( 351 | key.as_string_b().as_string(), 352 | T::from_value(value.clone()).unwrap(), 353 | ); 354 | map 355 | })) 356 | } else { 357 | None 358 | } 359 | } 360 | } 361 | 362 | #[cfg(not(feature = "cstring"))] 363 | impl FromValueBehavior for BTreeMap 364 | where 365 | T: FromValueBehavior, 366 | { 367 | type Item = BTreeMap::Item>; 368 | 369 | fn from_value(value: Value) -> Option { 370 | if let Value::Object(array) = value { 371 | Some(array.iter().fold(BTreeMap::new(), |mut map, (key, value)| { 372 | map.insert(key.to_string(), T::from_value(value.clone()).unwrap()); 373 | map 374 | })) 375 | } else { 376 | None 377 | } 378 | } 379 | } 380 | 381 | impl FromValueBehavior for Option 382 | where 383 | T: FromValueBehavior, 384 | { 385 | type Item = Option<::Item>; 386 | 387 | fn from_value(value: Value) -> Option { 388 | match value { 389 | Value::Null => None, 390 | _ => Some(T::from_value(value)), 391 | } 392 | } 393 | } 394 | 395 | /// A trait for converting types to JSON strings. 396 | pub trait ToJsonBehavior { 397 | /// Converts a type into a JSON string. 398 | fn to_json(&self) -> String; 399 | } 400 | 401 | pub trait ValueKeyBehavior: Clone { 402 | fn to_value_key(&self) -> ValueKey; 403 | 404 | fn as_usize(&self) -> usize { 405 | 0 406 | } 407 | 408 | fn is_usize() -> bool { 409 | false 410 | } 411 | } 412 | 413 | impl ValueKeyBehavior for String { 414 | fn to_value_key(&self) -> ValueKey { 415 | ValueKey::String(StringB::from(self.clone())) 416 | } 417 | } 418 | impl ValueKeyBehavior for usize { 419 | fn to_value_key(&self) -> ValueKey { 420 | ValueKey::Number(*self) 421 | } 422 | 423 | fn as_usize(&self) -> usize { 424 | *self 425 | } 426 | 427 | fn is_usize() -> bool { 428 | true 429 | } 430 | } 431 | impl ValueKeyBehavior for &str { 432 | fn to_value_key(&self) -> ValueKey { 433 | ValueKey::String(StringB::from(*self)) 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /valu3/src/types/array.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use std::collections::{BTreeMap, HashMap}; 3 | use std::fmt::{Debug, Display, Formatter}; 4 | 5 | pub trait ArrayBehavior { 6 | /// Removes the last element from the array and returns it, or `None` if the array is empty. 7 | /// 8 | /// # Examples 9 | /// 10 | /// ```no_run 11 | /// use my_crate::{Array, Value}; 12 | /// 13 | /// let mut array = Array::new(); 14 | /// array.push(Value::from(42)); 15 | /// 16 | /// let popped_value = array.pop(); 17 | /// assert_eq!(popped_value, Some(Value::from(42))); 18 | /// 19 | /// let empty_array = Array::new(); 20 | /// let empty_popped_value = empty_array.pop(); 21 | /// assert_eq!(empty_popped_value, None); 22 | /// ``` 23 | fn pop(&mut self) -> Option; 24 | } 25 | 26 | /// Represents an array of `Value`s. 27 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 28 | pub struct Array { 29 | pub values: Vec, 30 | } 31 | 32 | impl Array { 33 | /// Creates a new empty `Array`. 34 | /// 35 | /// # Examples 36 | /// 37 | /// ```no_run 38 | /// use my_crate::Array; 39 | /// 40 | /// let empty_array = Array::new(); 41 | /// assert_eq!(empty_array.len(), 0); 42 | /// ``` 43 | pub fn new() -> Self { 44 | Self { values: vec![] } 45 | } 46 | 47 | /// Returns a reference to the value at the specified index, or `None` if the index is out of bounds. 48 | pub fn get(&self, index: usize) -> Option<&Value> { 49 | self.values.get(index) 50 | } 51 | 52 | /// Returns a mutable reference to the value at the specified index, or `None` if the index is out of bounds. 53 | pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> { 54 | self.values.get_mut(index) 55 | } 56 | 57 | pub fn clean(&mut self) { 58 | self.values = Vec::new(); 59 | } 60 | 61 | /// Appends a value to the end of the array. 62 | /// 63 | /// # Examples 64 | /// 65 | /// ```no_run 66 | /// use my_crate::{Array, Value}; 67 | /// 68 | /// let mut array = Array::new(); 69 | /// array.push(Value::from(42)); 70 | /// array.push(Value::from("hello")); 71 | /// 72 | /// assert_eq!(array.len(), 2); 73 | /// assert_eq!(array.get(0), Some(&Value::from(42))); 74 | /// assert_eq!(array.get(1), Some(&Value::from("hello"))); 75 | /// ``` 76 | pub fn push(&mut self, value: Value) { 77 | self.values.push(value); 78 | } 79 | 80 | pub fn len(&self) -> usize { 81 | self.values.len() 82 | } 83 | 84 | pub fn is_empty(&self) -> bool { 85 | self.values.is_empty() 86 | } 87 | } 88 | 89 | 90 | impl ArrayBehavior for Array { 91 | fn pop(&mut self) -> Option { 92 | self.values.pop() 93 | } 94 | } 95 | 96 | impl Default for Array { 97 | fn default() -> Self { 98 | Self::new() 99 | } 100 | } 101 | 102 | impl Display for Array { 103 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 104 | write!(f, "[")?; 105 | 106 | let mut iter = self.values.iter().peekable(); 107 | while let Some(value) = iter.next() { 108 | write!(f, "{}", value)?; 109 | if iter.peek().is_some() { 110 | write!(f, ", ")?; 111 | } 112 | } 113 | 114 | write!(f, "]") 115 | } 116 | } 117 | 118 | impl IntoIterator for Array { 119 | type Item = Value; 120 | type IntoIter = std::vec::IntoIter; 121 | 122 | fn into_iter(self) -> Self::IntoIter { 123 | self.values.into_iter() 124 | } 125 | } 126 | 127 | impl<'a> IntoIterator for &'a Array { 128 | type Item = &'a Value; 129 | type IntoIter = std::slice::Iter<'a, Value>; 130 | 131 | fn into_iter(self) -> Self::IntoIter { 132 | self.values.iter() 133 | } 134 | } 135 | 136 | impl<'a> IntoIterator for &'a mut Array { 137 | type Item = &'a mut Value; 138 | type IntoIter = std::slice::IterMut<'a, Value>; 139 | 140 | fn into_iter(self) -> Self::IntoIter { 141 | self.values.iter_mut() 142 | } 143 | } 144 | 145 | impl From for Array { 146 | fn from(value: Value) -> Self { 147 | let mut array = Array::new(); 148 | array.push(value); 149 | array 150 | } 151 | } 152 | 153 | impl> From> for Array { 154 | fn from(values: Vec) -> Self { 155 | let converted_values = values.into_iter().map(Into::into).collect(); 156 | Self { 157 | values: converted_values, 158 | } 159 | } 160 | } 161 | 162 | impl, V: Into> From> for Array { 163 | fn from(map: HashMap) -> Self { 164 | let values = map 165 | .into_iter() 166 | .map(|(k, v)| { 167 | let mut object_map = HashMap::new(); 168 | object_map.insert(k.as_ref().to_string(), v.into()); 169 | Value::Object(Object::from(object_map)) 170 | }) 171 | .collect(); 172 | Self { values } 173 | } 174 | } 175 | 176 | impl, V: Into> From> for Array { 177 | fn from(map: BTreeMap) -> Self { 178 | let values = map 179 | .into_iter() 180 | .map(|(k, v)| { 181 | let mut object_map = BTreeMap::new(); 182 | object_map.insert(k.as_ref().to_string(), v.into()); 183 | Value::Object(Object::from(object_map)) 184 | }) 185 | .collect(); 186 | Self { values } 187 | } 188 | } 189 | 190 | #[cfg(test)] 191 | mod tests { 192 | use crate::prelude::*; 193 | use std::collections::{BTreeMap, HashMap}; 194 | 195 | #[test] 196 | fn array_new() { 197 | let array = Array::new(); 198 | assert!(array.is_empty()); 199 | } 200 | 201 | #[test] 202 | fn array_push_pop() { 203 | let mut array = Array::new(); 204 | array.push(Value::from(42)); 205 | assert_eq!(array.pop(), Some(Value::from(42))); 206 | } 207 | 208 | #[test] 209 | fn array_len() { 210 | let mut array = Array::new(); 211 | array.push(Value::from(42)); 212 | assert_eq!(array.len(), 1); 213 | } 214 | 215 | #[test] 216 | fn array_get() { 217 | let mut array = Array::new(); 218 | array.push(Value::from(42)); 219 | assert_eq!(array.get(0), Some(&Value::from(42))); 220 | } 221 | 222 | #[test] 223 | fn array_get_mut() { 224 | let mut array = Array::new(); 225 | array.push(Value::from(42)); 226 | if let Some(value) = array.get_mut(0) { 227 | *value = Value::from(84); 228 | } 229 | assert_eq!(array.get(0), Some(&Value::from(84))); 230 | } 231 | 232 | #[test] 233 | fn array_from_value() { 234 | let array = Array::from(Value::from(42)); 235 | assert_eq!(array.len(), 1); 236 | assert_eq!(array.get(0), Some(&Value::from(42))); 237 | } 238 | 239 | #[test] 240 | fn array_from_vec() { 241 | let array = Array::from(vec![Value::from(42), Value::from("hello")]); 242 | assert_eq!(array.len(), 2); 243 | assert_eq!(array.get(0), Some(&Value::from(42))); 244 | assert_eq!(array.get(1), Some(&Value::from("hello"))); 245 | } 246 | 247 | #[test] 248 | fn array_from_hash_map() { 249 | let mut map = HashMap::new(); 250 | map.insert("key1", Value::from(42)); 251 | map.insert("key2", Value::from("hello")); 252 | 253 | let array = Array::from(map); 254 | 255 | assert_eq!(array.len(), 2); 256 | let mut found_key1 = false; 257 | let mut found_key2 = false; 258 | 259 | for value in array { 260 | if let Value::Object(object) = value { 261 | if let Some(v) = object.get("key1") { 262 | assert_eq!(v, &Value::from(42)); 263 | found_key1 = true; 264 | } else if let Some(v) = object.get("key2") { 265 | assert_eq!(v, &Value::from("hello")); 266 | found_key2 = true; 267 | } 268 | } 269 | } 270 | 271 | assert!(found_key1 && found_key2); 272 | } 273 | 274 | #[test] 275 | fn array_from_btree_map() { 276 | let mut map = BTreeMap::new(); 277 | map.insert("key1", Value::from(42)); 278 | map.insert("key2", Value::from("hello")); 279 | 280 | let array = Array::from(map); 281 | 282 | assert_eq!(array.len(), 2); 283 | let mut found_key1 = false; 284 | let mut found_key2 = false; 285 | 286 | for value in array { 287 | if let Value::Object(object) = value { 288 | if let Some(v) = object.get("key1") { 289 | assert_eq!(v, &Value::from(42)); 290 | found_key1 = true; 291 | } else if let Some(v) = object.get("key2") { 292 | assert_eq!(v, &Value::from("hello")); 293 | found_key2 = true; 294 | } 295 | } 296 | } 297 | 298 | assert!(found_key1 && found_key2); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /valu3/src/types/datetime.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | pub use chrono::{ 3 | self, DateTime as ChDateTime, Datelike, Duration, LocalResult, NaiveDate, NaiveTime, TimeZone, 4 | Timelike, Utc, 5 | }; 6 | use std::fmt::{Display, Formatter}; 7 | 8 | pub trait DateTimeBehavior { 9 | fn as_date(&self) -> Option<&NaiveDate>; 10 | fn as_time(&self) -> Option<&NaiveTime>; 11 | fn as_date_time(&self) -> Option<&ChDateTime>; 12 | 13 | // DateTime methods for accessing specific components of date or time values 14 | fn year(&self) -> Option; 15 | fn month(&self) -> Option; 16 | fn day(&self) -> Option; 17 | fn hour(&self) -> Option; 18 | fn minute(&self) -> Option; 19 | fn second(&self) -> Option; 20 | fn timestamp(&self) -> Option; 21 | fn timezone(&self) -> Option; 22 | 23 | // Methods for formatting DateTime values as strings 24 | fn to_iso8601(&self) -> String; 25 | fn to_rfc3339(&self) -> String; 26 | 27 | // Methods for adding or subtracting a Duration to/from a DateTime value 28 | fn add_duration(&self, duration: Duration) -> Option 29 | where 30 | Self: Sized; 31 | fn subtract_duration(&self, duration: Duration) -> Option 32 | where 33 | Self: Sized; 34 | 35 | // Method for calculating the duration between two DateTime values 36 | fn duration_between(&self, other: &Self) -> Option; 37 | 38 | fn from_ymd_opt(year: i32, month: u32, day: u32) -> Self; 39 | 40 | fn with_ymd_and_hms(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> Self; 41 | 42 | fn now() -> Self; 43 | } 44 | 45 | /// Enum representing a date, time, or date-time value. 46 | /// 47 | /// # Variants 48 | /// 49 | /// * `Date(NaiveDate)` - Represents a date without timezone information. 50 | /// * `Time(NaiveTime)` - Represents a time without date and timezone information. 51 | /// * `DateTime(ChDateTime)` - Represents a date-time with timezone information. 52 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 53 | pub enum DateTime { 54 | Date(NaiveDate), 55 | Time(NaiveTime), 56 | DateTime(ChDateTime), 57 | } 58 | 59 | // Implementations of From trait to allow conversion from NaiveDate, NaiveTime, and ChDateTime 60 | impl From for DateTime { 61 | fn from(value: NaiveDate) -> Self { 62 | DateTime::Date(value) 63 | } 64 | } 65 | 66 | impl From for DateTime { 67 | fn from(value: Value) -> Self { 68 | match value { 69 | Value::DateTime(datetime) => datetime, 70 | _ => panic!("Cannot convert value to DateTime"), 71 | } 72 | } 73 | } 74 | 75 | impl From for DateTime { 76 | fn from(value: NaiveTime) -> Self { 77 | DateTime::Time(value) 78 | } 79 | } 80 | 81 | impl From> for DateTime { 82 | fn from(value: ChDateTime) -> Self { 83 | DateTime::DateTime(value) 84 | } 85 | } 86 | 87 | // Implementations of From trait to allow conversion from LocalResult variants 88 | impl From> for DateTime { 89 | fn from(value: LocalResult) -> Self { 90 | DateTime::Date(value.unwrap()) 91 | } 92 | } 93 | 94 | impl From> for DateTime { 95 | fn from(value: LocalResult) -> Self { 96 | DateTime::Time(value.unwrap()) 97 | } 98 | } 99 | 100 | impl From>> for DateTime { 101 | fn from(value: LocalResult>) -> Self { 102 | DateTime::DateTime(value.unwrap()) 103 | } 104 | } 105 | 106 | // Implementation of From trait to allow conversion from &str 107 | impl From<&str> for DateTime { 108 | fn from(value: &str) -> Self { 109 | match value.parse::() { 110 | Ok(date) => DateTime::Date(date), 111 | Err(_) => match value.parse::() { 112 | Ok(time) => DateTime::Time(time), 113 | Err(_) => match value.parse::>() { 114 | Ok(datetime) => DateTime::DateTime(datetime), 115 | Err(_) => panic!("Invalid date, time, or date-time format"), 116 | }, 117 | }, 118 | } 119 | } 120 | } 121 | 122 | // Implementation of From trait to allow conversion from i64 123 | impl From for DateTime { 124 | fn from(value: i64) -> Self { 125 | DateTime::DateTime(ChDateTime::from_naive_utc_and_offset( 126 | chrono::DateTime::from_timestamp_nanos(value).naive_local(), 127 | Utc, 128 | )) 129 | } 130 | } 131 | 132 | /// Display implementation for DateTime. 133 | impl Display for DateTime { 134 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 135 | match self { 136 | DateTime::Date(value) => write!(f, "{}", value), 137 | DateTime::Time(value) => write!(f, "{}", value), 138 | DateTime::DateTime(value) => write!(f, "{}", value.to_rfc3339()), 139 | } 140 | } 141 | } 142 | 143 | // DateTime methods for accessing underlying NaiveDate, NaiveTime, or ChDateTime values 144 | impl DateTimeBehavior for DateTime { 145 | fn as_date(&self) -> Option<&NaiveDate> { 146 | match self { 147 | DateTime::Date(value) => Some(value), 148 | _ => None, 149 | } 150 | } 151 | 152 | fn as_time(&self) -> Option<&NaiveTime> { 153 | match self { 154 | DateTime::Time(value) => Some(value), 155 | _ => None, 156 | } 157 | } 158 | 159 | fn as_date_time(&self) -> Option<&ChDateTime> { 160 | match self { 161 | DateTime::DateTime(value) => Some(value), 162 | _ => None, 163 | } 164 | } 165 | 166 | fn year(&self) -> Option { 167 | match self { 168 | DateTime::Date(date) => Some(date.year()), 169 | DateTime::DateTime(datetime) => Some(datetime.year()), 170 | _ => None, 171 | } 172 | } 173 | 174 | fn month(&self) -> Option { 175 | match self { 176 | DateTime::Date(date) => Some(date.month()), 177 | DateTime::DateTime(datetime) => Some(datetime.month()), 178 | _ => None, 179 | } 180 | } 181 | 182 | fn day(&self) -> Option { 183 | match self { 184 | DateTime::Date(date) => Some(date.day()), 185 | DateTime::DateTime(datetime) => Some(datetime.day()), 186 | _ => None, 187 | } 188 | } 189 | 190 | fn hour(&self) -> Option { 191 | match self { 192 | DateTime::Time(time) => Some(time.hour()), 193 | DateTime::DateTime(datetime) => Some(datetime.hour()), 194 | _ => None, 195 | } 196 | } 197 | 198 | fn minute(&self) -> Option { 199 | match self { 200 | DateTime::Time(time) => Some(time.minute()), 201 | DateTime::DateTime(datetime) => Some(datetime.minute()), 202 | _ => None, 203 | } 204 | } 205 | 206 | fn second(&self) -> Option { 207 | match self { 208 | DateTime::Time(time) => Some(time.second()), 209 | DateTime::DateTime(datetime) => Some(datetime.second()), 210 | _ => None, 211 | } 212 | } 213 | 214 | fn timestamp(&self) -> Option { 215 | match self { 216 | DateTime::DateTime(datetime) => Some(datetime.timestamp()), 217 | DateTime::Date(date) => Some(date.and_hms_opt(0, 0, 0).unwrap().and_utc().timestamp()), 218 | DateTime::Time(time) => Some( 219 | NaiveDate::from_ymd_opt(1970, 1, 1) 220 | .unwrap() 221 | .and_hms_opt(time.hour(), time.minute(), time.second()) 222 | .unwrap() 223 | .and_utc() 224 | .timestamp(), 225 | ), 226 | } 227 | } 228 | 229 | fn timezone(&self) -> Option { 230 | match self { 231 | DateTime::DateTime(datetime) => Some(datetime.timezone()), 232 | _ => None, 233 | } 234 | } 235 | 236 | fn to_iso8601(&self) -> String { 237 | match self { 238 | DateTime::Date(date) => date.format("%Y-%m-%d").to_string(), 239 | DateTime::Time(time) => time.format("%H:%M:%S%.f").to_string(), 240 | DateTime::DateTime(datetime) => datetime.format("%Y-%m-%dT%H:%M:%S").to_string(), 241 | } 242 | } 243 | 244 | fn to_rfc3339(&self) -> String { 245 | match self { 246 | DateTime::DateTime(datetime) => datetime.to_rfc3339(), 247 | _ => "".to_string(), 248 | } 249 | } 250 | 251 | fn add_duration(&self, duration: Duration) -> Option { 252 | match self { 253 | DateTime::Date(date) => Some(DateTime::Date( 254 | *date + chrono::Duration::days(duration.num_days()), 255 | )), 256 | DateTime::Time(_) => None, // Não é possível adicionar duração a um NaiveTime isolado 257 | DateTime::DateTime(datetime) => Some(DateTime::DateTime(*datetime + duration)), 258 | } 259 | } 260 | 261 | fn subtract_duration(&self, duration: Duration) -> Option { 262 | match self { 263 | DateTime::Date(date) => Some(DateTime::Date( 264 | *date - chrono::Duration::days(duration.num_days()), 265 | )), 266 | DateTime::Time(_) => None, // Não é possível subtrair duração de um NaiveTime isolado 267 | DateTime::DateTime(datetime) => Some(DateTime::DateTime(*datetime - duration)), 268 | } 269 | } 270 | 271 | fn duration_between(&self, other: &DateTime) -> Option { 272 | match (self, other) { 273 | (DateTime::Date(date1), DateTime::Date(date2)) => { 274 | Some(Duration::days((*date2 - *date1).num_days())) 275 | } 276 | (DateTime::DateTime(dt1), DateTime::DateTime(dt2)) => Some(*dt2 - *dt1), 277 | _ => None, // Retornar None para combinações inválidas 278 | } 279 | } 280 | 281 | fn from_ymd_opt(year: i32, month: u32, day: u32) -> DateTime { 282 | let date = NaiveDate::from_ymd_opt(year, month, day).unwrap(); 283 | DateTime::from(date) 284 | } 285 | 286 | fn with_ymd_and_hms( 287 | year: i32, 288 | month: u32, 289 | day: u32, 290 | hour: u32, 291 | min: u32, 292 | sec: u32, 293 | ) -> DateTime { 294 | let datetime: chrono::LocalResult> = 295 | Utc.with_ymd_and_hms(year, month, day, hour, min, sec); 296 | DateTime::from(datetime) 297 | } 298 | 299 | fn now() -> DateTime { 300 | DateTime::from(Utc::now()) 301 | } 302 | } 303 | 304 | #[cfg(test)] 305 | mod tests { 306 | use crate::prelude::*; 307 | use chrono::{Duration, NaiveDate, TimeZone, Utc}; 308 | 309 | #[test] 310 | fn test_add_duration() { 311 | let dt_date = DateTime::from_ymd_opt(2023, 4, 5); 312 | let dt_datetime = DateTime::with_ymd_and_hms(2023, 4, 5, 12, 34, 56); 313 | 314 | assert_eq!( 315 | dt_date.add_duration(Duration::days(1)), 316 | Some(DateTime::from(NaiveDate::from_ymd_opt(2023, 4, 6).unwrap())) 317 | ); 318 | assert_eq!( 319 | dt_datetime.add_duration(Duration::days(1)), 320 | Some(DateTime::from(Utc.with_ymd_and_hms(2023, 4, 6, 12, 34, 56))) 321 | ); 322 | } 323 | 324 | #[test] 325 | fn test_subtract_duration() { 326 | let date = NaiveDate::from_ymd_opt(2023, 4, 5).unwrap(); 327 | let datetime = Utc.with_ymd_and_hms(2023, 4, 5, 12, 34, 56); 328 | 329 | let dt_date = DateTime::from(date); 330 | let dt_datetime = DateTime::from(datetime); 331 | 332 | assert_eq!( 333 | dt_date.subtract_duration(Duration::days(1)), 334 | Some(DateTime::from(NaiveDate::from_ymd_opt(2023, 4, 4).unwrap())) 335 | ); 336 | assert_eq!( 337 | dt_datetime.subtract_duration(Duration::days(1)), 338 | Some(DateTime::from(Utc.with_ymd_and_hms(2023, 4, 4, 12, 34, 56))) 339 | ); 340 | } 341 | 342 | #[test] 343 | fn test_duration_between() { 344 | let date1 = NaiveDate::from_ymd_opt(2023, 4, 5).unwrap(); 345 | let date2 = NaiveDate::from_ymd_opt(2023, 4, 6).unwrap(); 346 | let datetime1 = Utc.with_ymd_and_hms(2023, 4, 5, 12, 34, 56); 347 | let datetime2 = Utc.with_ymd_and_hms(2023, 4, 6, 12, 34, 56); 348 | 349 | let dt_date1 = DateTime::from(date1); 350 | let dt_date2 = DateTime::from(date2); 351 | let dt_datetime1 = DateTime::from(datetime1); 352 | let dt_datetime2 = DateTime::from(datetime2); 353 | 354 | assert_eq!( 355 | dt_date1.duration_between(&dt_date2), 356 | Some(Duration::days(1)) 357 | ); 358 | assert_eq!( 359 | dt_datetime1.duration_between(&dt_datetime2), 360 | Some(Duration::days(1)) 361 | ); 362 | } 363 | 364 | #[test] 365 | fn test_timestamp() { 366 | let date = NaiveDate::from_ymd_opt(2023, 4, 5).unwrap(); 367 | let datetime = Utc.with_ymd_and_hms(2023, 4, 5, 12, 34, 56); 368 | 369 | let dt_date = DateTime::from(date); 370 | let dt_datetime = DateTime::from(datetime); 371 | 372 | assert_eq!( 373 | dt_date.timestamp(), 374 | Some(date.and_hms_opt(0, 0, 0).unwrap().and_utc().timestamp()) 375 | ); 376 | assert_eq!(dt_datetime.timestamp(), Some(datetime.unwrap().timestamp())); 377 | } 378 | 379 | #[test] 380 | fn test_timezone() { 381 | let datetime = Utc.with_ymd_and_hms(2023, 4, 5, 12, 34, 56); 382 | let dt_datetime = DateTime::from(datetime); 383 | 384 | assert_eq!(dt_datetime.timezone(), Some(Utc)); 385 | } 386 | 387 | #[test] 388 | fn test_to_iso8601() { 389 | let date = NaiveDate::from_ymd_opt(2023, 4, 5).unwrap(); 390 | let datetime = Utc.with_ymd_and_hms(2023, 4, 5, 12, 34, 56); 391 | 392 | let dt_date = DateTime::from(date); 393 | let dt_datetime = DateTime::from(datetime); 394 | 395 | assert_eq!(dt_date.to_iso8601(), "2023-04-05"); 396 | assert_eq!(dt_datetime.to_iso8601(), "2023-04-05T12:34:56"); 397 | } 398 | 399 | #[test] 400 | fn test_from_i64() { 401 | let timestamp_nanos = 1672539296000000000; 402 | let dt_datetime = DateTime::from(timestamp_nanos); 403 | 404 | assert_eq!( 405 | dt_datetime, 406 | DateTime::from(Utc.timestamp_nanos(timestamp_nanos)) 407 | ); 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /valu3/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod value_key; 2 | pub mod array; 3 | pub mod object; 4 | pub mod number; 5 | pub mod datetime; 6 | pub mod stringb; -------------------------------------------------------------------------------- /valu3/src/types/object.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use std::collections::{BTreeMap, HashMap}; 3 | use std::iter::Iterator; 4 | 5 | pub trait ObjectBehavior { 6 | /// Removes a key-value pair from the object and returns the associated value. If the key is not present, returns `None`. 7 | fn remove(&mut self, key: &T) -> Option 8 | where 9 | T: ValueKeyBehavior; 10 | 11 | /// Returns `true` if the object contains a value for the specified key, otherwise `false`. 12 | fn contains_key(&self, key: &T) -> bool 13 | where 14 | T: ValueKeyBehavior; 15 | 16 | /// Returns a `Vec` of references to the keys in the object, in the order they were inserted. 17 | fn keys(&self) -> Vec<&ValueKey>; 18 | 19 | /// Returns a `Vec` of references to the values in the object, in the order they were inserted. 20 | fn values(&self) -> Vec<&Value>; 21 | } 22 | 23 | /// An enum representing a JSON object as a `BTreeMap` or a `HashMap`. 24 | #[derive(Debug, Clone, PartialEq)] 25 | pub enum Object { 26 | BTreeMap(BTreeMap), 27 | HashMap(HashMap), 28 | } 29 | 30 | impl PartialOrd for Object { 31 | fn partial_cmp(&self, other: &Self) -> Option { 32 | match (self, other) { 33 | (Object::BTreeMap(map1), Object::BTreeMap(map2)) => map1.partial_cmp(map2), 34 | (Object::HashMap(_), Object::HashMap(_)) => None, 35 | _ => None, 36 | } 37 | } 38 | } 39 | 40 | impl Object { 41 | /// Returns a reference to the value associated with the specified key, or `None` if the key is not present. 42 | pub fn get(&self, key: T) -> Option<&Value> 43 | where 44 | T: ValueKeyBehavior, 45 | { 46 | let value_key: ValueKey = key.to_value_key(); 47 | match self { 48 | Object::BTreeMap(map) => map.get(&value_key), 49 | Object::HashMap(map) => map.get(&value_key), 50 | } 51 | } 52 | 53 | pub fn get_mut(&mut self, key: T) -> Option<&mut Value> 54 | where 55 | T: ValueKeyBehavior, 56 | { 57 | let value_key: ValueKey = key.to_value_key(); 58 | match self { 59 | Object::BTreeMap(map) => map.get_mut(&value_key), 60 | Object::HashMap(map) => map.get_mut(&value_key), 61 | } 62 | } 63 | 64 | /// Removes all key-value pairs from the object. 65 | pub fn clean(&mut self) { 66 | match self { 67 | Object::BTreeMap(map) => map.clear(), 68 | Object::HashMap(map) => map.clear(), 69 | } 70 | } 71 | 72 | pub fn insert(&mut self, key: T, value: Value) -> Option 73 | where 74 | T: ValueKeyBehavior, 75 | { 76 | let key = key.to_value_key(); 77 | match self { 78 | Object::BTreeMap(map) => map.insert(key, value), 79 | Object::HashMap(map) => map.insert(key, value), 80 | } 81 | } 82 | 83 | pub fn len(&self) -> usize { 84 | match self { 85 | Object::BTreeMap(map) => map.len(), 86 | Object::HashMap(map) => map.len(), 87 | } 88 | } 89 | 90 | pub fn is_empty(&self) -> bool { 91 | match self { 92 | Object::BTreeMap(map) => map.is_empty(), 93 | Object::HashMap(map) => map.is_empty(), 94 | } 95 | } 96 | } 97 | 98 | impl ObjectBehavior for Object { 99 | fn remove(&mut self, key: &T) -> Option 100 | where 101 | T: ValueKeyBehavior, 102 | { 103 | let key: ValueKey = key.to_value_key(); 104 | match self { 105 | Object::BTreeMap(map) => map.remove(&key), 106 | Object::HashMap(map) => map.remove(&key), 107 | } 108 | } 109 | 110 | fn contains_key(&self, key: &T) -> bool 111 | where 112 | T: ValueKeyBehavior, 113 | { 114 | let key: ValueKey = key.to_value_key(); 115 | match self { 116 | Object::BTreeMap(map) => map.contains_key(&key), 117 | Object::HashMap(map) => map.contains_key(&key), 118 | } 119 | } 120 | 121 | fn keys(&self) -> Vec<&ValueKey> { 122 | match self { 123 | Object::BTreeMap(map) => map.keys().collect(), 124 | Object::HashMap(map) => map.keys().collect(), 125 | } 126 | } 127 | 128 | fn values(&self) -> Vec<&Value> { 129 | match self { 130 | Object::BTreeMap(map) => map.values().collect(), 131 | Object::HashMap(map) => map.values().collect(), 132 | } 133 | } 134 | } 135 | 136 | impl Default for Object { 137 | /// Creates a new `Object` with an empty `HashMap`. 138 | fn default() -> Self { 139 | Object::HashMap(HashMap::new()) 140 | } 141 | } 142 | 143 | impl From> for Object 144 | where 145 | T: ValueKeyBehavior, 146 | V: ToValueBehavior, 147 | { 148 | /// Converts BTreeMap into Object. 149 | fn from(value: BTreeMap) -> Self { 150 | Object::BTreeMap( 151 | value 152 | .iter() 153 | .map(|(k, v)| (k.to_value_key(), v.to_value())) 154 | .collect::>(), 155 | ) 156 | } 157 | } 158 | 159 | impl From> for Object 160 | where 161 | T: ValueKeyBehavior, 162 | V: ToValueBehavior, 163 | { 164 | /// Converts BTreeMap into Object. 165 | fn from(value: HashMap) -> Self { 166 | Object::HashMap( 167 | value 168 | .iter() 169 | .map(|(k, v)| (k.to_value_key(), v.to_value())) 170 | .collect::>(), 171 | ) 172 | } 173 | } 174 | 175 | impl From> for Object { 176 | /// Converts HashMap into Object. 177 | fn from(value: HashMap) -> Self { 178 | Object::HashMap(value) 179 | } 180 | } 181 | 182 | impl From> for Object { 183 | /// Converts a vector of key-value pairs into an Object. 184 | fn from(value: Vec<(ValueKey, Value)>) -> Self { 185 | Object::HashMap(value.into_iter().collect()) 186 | } 187 | } 188 | 189 | impl From> for Object 190 | where 191 | T: ValueKeyBehavior, 192 | V: ToValueBehavior, 193 | { 194 | /// Converts a vector of key-value pairs into an Object. 195 | fn from(value: Vec<(T, V)>) -> Self { 196 | Object::HashMap( 197 | value 198 | .into_iter() 199 | .map(|(k, v)| (k.to_value_key(), v.to_value())) 200 | .collect(), 201 | ) 202 | } 203 | } 204 | 205 | impl Into> for Object { 206 | /// Converts Object into HashMap. 207 | fn into(self) -> HashMap { 208 | match self { 209 | Object::BTreeMap(map) => map.into_iter().collect(), 210 | Object::HashMap(map) => map, 211 | } 212 | } 213 | } 214 | 215 | impl Into> for Object { 216 | /// Converts Object into BTreeMap. 217 | fn into(self) -> BTreeMap { 218 | match self { 219 | Object::BTreeMap(map) => map, 220 | Object::HashMap(map) => map.into_iter().collect(), 221 | } 222 | } 223 | } 224 | 225 | /// An iterator over the key-value pairs in an Object. 226 | #[allow(dead_code)] 227 | pub struct ObjectIter<'a> { 228 | object: &'a Object, 229 | state: IterState<'a>, 230 | } 231 | 232 | enum IterState<'a> { 233 | BTreeMap(std::collections::btree_map::Iter<'a, ValueKey, Value>), 234 | HashMap(std::collections::hash_map::Iter<'a, ValueKey, Value>), 235 | } 236 | 237 | impl<'a> Iterator for ObjectIter<'a> { 238 | type Item = (&'a ValueKey, &'a Value); 239 | 240 | fn next(&mut self) -> Option { 241 | match &mut self.state { 242 | IterState::BTreeMap(iter) => iter.next(), 243 | IterState::HashMap(iter) => iter.next(), 244 | } 245 | } 246 | } 247 | 248 | impl<'a> Object { 249 | pub fn iter(&'a self) -> ObjectIter<'a> { 250 | match self { 251 | Object::BTreeMap(map) => ObjectIter { 252 | object: self, 253 | state: IterState::BTreeMap(map.iter()), 254 | }, 255 | 256 | Object::HashMap(map) => ObjectIter { 257 | object: self, 258 | state: IterState::HashMap(map.iter()), 259 | }, 260 | } 261 | } 262 | } 263 | 264 | #[cfg(test)] 265 | mod tests { 266 | use super::*; 267 | use crate::prelude::*; 268 | 269 | #[test] 270 | fn test_object_iter() { 271 | let value1 = Value::Null; 272 | let value2 = StringB::from("ok").to_value(); 273 | 274 | let mut map = BTreeMap::new(); 275 | map.insert("key1".to_string(), value1.clone()); 276 | map.insert("key2".to_string(), value2.clone()); 277 | let obj = Object::from(map); 278 | 279 | let mut iter = obj.iter(); 280 | let mut results = vec![]; 281 | 282 | while let Some((key, value)) = iter.next() { 283 | results.push((key.clone(), value.clone())); 284 | } 285 | 286 | assert_eq!( 287 | results, 288 | vec![ 289 | ("key1".to_value_key(), value1), 290 | ("key2".to_value_key(), value2) 291 | ] 292 | ); 293 | } 294 | 295 | #[test] 296 | fn test_object_from_vec() { 297 | let vec = vec![ 298 | ("key1".to_string(), Value::Null), 299 | ("key2".to_string(), StringB::from("ok").to_value()), 300 | ]; 301 | 302 | let obj = Object::from(vec); 303 | assert_eq!(obj.get("key1"), Some(&Value::Null)); 304 | assert_eq!(obj.get("key2"), Some(&StringB::from("ok").to_value())); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /valu3/src/types/stringb.rs: -------------------------------------------------------------------------------- 1 | //! A custom string implementation called `StringB` that provides additional string manipulation methods. 2 | //! 3 | //! This implementation offers a way to handle strings with additional features, such as converting 4 | //! the string to uppercase or lowercase, trimming, replacing, and concatenating. It also handles 5 | //! converting between different representations of strings, such as `CString`, `String`, and `Vec`. 6 | #[cfg(feature = "cstring")] 7 | use std::ffi::CString; 8 | use std::fmt::{Display, Formatter}; 9 | use std::ops::Deref; 10 | 11 | pub trait StringBehavior { 12 | /// Gets the byte representation of the string. 13 | /// 14 | /// # Examples 15 | /// 16 | /// ```no_run 17 | /// let s = StringB::from("hello"); 18 | /// let bytes = s.as_bytes(); 19 | /// ``` 20 | fn as_bytes(&self) -> &[u8]; 21 | 22 | /// Gets the string slice representation of the value. 23 | /// 24 | /// # Examples 25 | /// 26 | /// ```no_run 27 | /// let s = StringB::from("hello"); 28 | /// let slice = s.as_str(); 29 | /// ``` 30 | fn as_str(&self) -> &str; 31 | 32 | #[cfg(feature = "cstring")] 33 | fn extract(&self) -> CString; 34 | 35 | #[cfg(not(feature = "cstring"))] 36 | fn extract(&self) -> String; 37 | 38 | /// Converts the value to a `String`. 39 | /// 40 | /// # Examples 41 | /// 42 | /// ```no_run 43 | /// let s = StringB::from("hello"); 44 | /// let string = s.as_string(); 45 | /// ``` 46 | fn as_string(&self) -> String; 47 | /// Converts the string to uppercase. 48 | /// 49 | /// # Examples 50 | /// 51 | /// ```no_run 52 | /// let s = StringB::from("hello"); 53 | /// assert_eq!(s.to_uppercase().as_str(), "HELLO"); 54 | /// ``` 55 | fn to_uppercase(&self) -> Self; 56 | 57 | /// Converts the string to lowercase. 58 | /// 59 | /// # Examples 60 | /// 61 | /// ```no_run 62 | /// let s = StringB::from("HELLO"); 63 | /// assert_eq!(s.to_lowercase().as_str(), "hello"); 64 | /// ``` 65 | fn to_lowercase(&self) -> Self; 66 | 67 | /// Removes whitespace at the beginning and end of the string. 68 | /// 69 | /// # Examples 70 | /// 71 | /// ```no_run 72 | /// let s = StringB::from(" hello "); 73 | /// assert_eq!(s.trim().as_str(), "hello"); 74 | /// ``` 75 | fn trim(&self) -> Self; 76 | 77 | /// Replaces all occurrences of 'from' with 'to'. 78 | /// 79 | /// # Examples 80 | /// 81 | /// ```no_run 82 | /// let s = StringB::from("hello world"); 83 | /// assert_eq!(s.replace("world", "planet").as_str(), "hello planet"); 84 | /// ``` 85 | fn replace(&self, from: &str, to: &str) -> Self; 86 | 87 | /// Concatenates the current string with another string or `&str`. 88 | /// 89 | /// # Examples 90 | /// 91 | /// ```no_run 92 | /// let s1 = StringB::from("hello"); 93 | /// let s2 = " world"; 94 | /// assert_eq!(s1.concat(s2).as_str(), "hello world"); 95 | /// ``` 96 | fn concat>(&self, other: T) -> Self; 97 | 98 | fn from_utf8(value: Vec) -> Self; 99 | } 100 | 101 | /// A custom string implementation with additional manipulation methods. 102 | #[derive(Debug, Clone, PartialEq, Eq, Default, Hash, PartialOrd, Ord)] 103 | pub struct StringB { 104 | #[cfg(feature = "cstring")] 105 | pub value: CString, 106 | #[cfg(not(feature = "cstring"))] 107 | pub value: String, 108 | } 109 | 110 | impl StringB { 111 | pub fn len(&self) -> usize { 112 | self.as_bytes().len() 113 | } 114 | 115 | pub fn is_empty(&self) -> bool { 116 | self.len() == 0 117 | } 118 | 119 | #[cfg(feature = "cstring")] 120 | pub fn as_string(&self) -> String { 121 | self.value.to_str().unwrap().to_string() 122 | } 123 | 124 | #[cfg(not(feature = "cstring"))] 125 | pub fn as_string(&self) -> String { 126 | self.value.clone() 127 | } 128 | } 129 | 130 | impl StringBehavior for StringB { 131 | fn as_bytes(&self) -> &[u8] { 132 | self.value.as_bytes() 133 | } 134 | 135 | #[cfg(feature = "cstring")] 136 | fn as_str(&self) -> &str { 137 | self.value.to_str().expect("CString is not valid UTF-8") 138 | } 139 | 140 | #[cfg(not(feature = "cstring"))] 141 | fn as_str(&self) -> &str { 142 | self.value.as_str() 143 | } 144 | 145 | #[cfg(feature = "cstring")] 146 | fn as_string(&self) -> String { 147 | self.value 148 | .to_str() 149 | .expect("CString is not valid UTF-8") 150 | .to_string() 151 | } 152 | 153 | #[cfg(not(feature = "cstring"))] 154 | fn as_string(&self) -> String { 155 | self.value.clone() 156 | } 157 | 158 | #[cfg(feature = "cstring")] 159 | fn extract(&self) -> CString { 160 | self.value.clone() 161 | } 162 | 163 | #[cfg(not(feature = "cstring"))] 164 | fn extract(&self) -> String { 165 | self.as_string() 166 | } 167 | 168 | fn to_uppercase(&self) -> Self { 169 | let upper_str = self.as_str().to_uppercase(); 170 | StringB::from(upper_str) 171 | } 172 | 173 | fn to_lowercase(&self) -> Self { 174 | let lower_str = self.as_str().to_lowercase(); 175 | StringB::from(lower_str) 176 | } 177 | 178 | fn trim(&self) -> Self { 179 | let trimmed_str = self.as_str().trim(); 180 | StringB::from(trimmed_str) 181 | } 182 | 183 | fn replace(&self, from: &str, to: &str) -> Self { 184 | let replaced_str = self.as_str().replace(from, to); 185 | StringB::from(replaced_str) 186 | } 187 | 188 | fn concat>(&self, other: T) -> Self { 189 | let mut result = String::from(self.as_str()); 190 | result.push_str(other.as_ref()); 191 | StringB::from(result) 192 | } 193 | 194 | #[cfg(not(feature = "cstring"))] 195 | fn from_utf8(value: Vec) -> Self { 196 | StringB::from(String::from_utf8(value).unwrap()) 197 | } 198 | 199 | #[cfg(feature = "cstring")] 200 | fn from_utf8(value: Vec) -> Self { 201 | StringB { 202 | value: CString::new(value).unwrap(), 203 | } 204 | } 205 | } 206 | 207 | /// Implements the `Display` trait for `StringB`. 208 | /// 209 | /// This allows `StringB` instances to be formatted using the `{}` placeholder in format strings. 210 | #[cfg(feature = "cstring")] 211 | impl Display for StringB { 212 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 213 | write!(f, "{}", self.as_string()) 214 | } 215 | } 216 | #[cfg(not(feature = "cstring"))] 217 | impl Display for StringB { 218 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 219 | write!(f, "{}", self.as_string()) 220 | } 221 | } 222 | 223 | /// Implements the `Deref` trait for `StringB`. 224 | /// 225 | /// This allows treating a `StringB` instance as if it were a slice of bytes (`[u8]`). 226 | impl Deref for StringB { 227 | type Target = [u8]; 228 | 229 | fn deref(&self) -> &Self::Target { 230 | self.as_bytes() 231 | } 232 | } 233 | 234 | /// Implements the `From` trait for `StringB`. 235 | /// 236 | /// This allows creating a `StringB` instance from a `String`. 237 | #[cfg(not(feature = "cstring"))] 238 | impl From for StringB { 239 | fn from(value: String) -> Self { 240 | StringB { 241 | value: value.clone(), 242 | } 243 | } 244 | } 245 | 246 | /// Implements the `From<&str>` trait for `StringB`. 247 | /// 248 | /// This allows creating a `StringB` instance from a `&str`. 249 | #[cfg(not(feature = "cstring"))] 250 | impl From<&str> for StringB { 251 | fn from(value: &str) -> Self { 252 | StringB { 253 | value: value.to_string(), 254 | } 255 | } 256 | } 257 | 258 | #[cfg(feature = "cstring")] 259 | impl From for StringB { 260 | fn from(value: String) -> Self { 261 | StringB { 262 | value: CString::new(value).unwrap(), 263 | } 264 | } 265 | } 266 | 267 | #[cfg(feature = "cstring")] 268 | impl From<&str> for StringB { 269 | fn from(value: &str) -> Self { 270 | StringB { 271 | value: CString::new(value).unwrap(), 272 | } 273 | } 274 | } 275 | 276 | #[cfg(feature = "cstring")] 277 | impl From for StringB { 278 | fn from(value: CString) -> Self { 279 | StringB { 280 | value: value.clone(), 281 | } 282 | } 283 | } 284 | 285 | /// Implements the `From<&Vec>` trait for `StringB`. 286 | /// 287 | /// This allows creating a `StringB` instance from a reference to a `Vec`. 288 | impl From<&Vec> for StringB { 289 | fn from(value: &Vec) -> Self { 290 | StringB::from_utf8(value.clone()) 291 | } 292 | } 293 | 294 | /// Implements the `From>` trait for `StringB`. 295 | /// 296 | /// This allows creating a `StringB` instance from a `Vec`. 297 | impl From> for StringB { 298 | fn from(value: Vec) -> Self { 299 | StringB::from_utf8(value) 300 | } 301 | } 302 | 303 | #[cfg(test)] 304 | mod tests { 305 | use super::*; 306 | 307 | #[test] 308 | fn test_len() { 309 | let s = StringB::from("Hello"); 310 | assert_eq!(s.len(), 5); 311 | } 312 | 313 | #[test] 314 | fn test_is_empty() { 315 | let s = StringB::from(""); 316 | assert!(s.is_empty()); 317 | } 318 | 319 | #[test] 320 | fn test_to_uppercase() { 321 | let s = StringB::from("hello"); 322 | assert_eq!(s.to_uppercase().as_str(), "HELLO"); 323 | } 324 | 325 | #[test] 326 | fn test_to_lowercase() { 327 | let s = StringB::from("HELLO"); 328 | assert_eq!(s.to_lowercase().as_str(), "hello"); 329 | } 330 | 331 | #[test] 332 | fn test_trim() { 333 | let s = StringB::from(" hello "); 334 | assert_eq!(s.trim().as_str(), "hello"); 335 | } 336 | 337 | #[test] 338 | fn test_replace() { 339 | let s = StringB::from("hello world"); 340 | assert_eq!(s.replace("world", "planet").as_str(), "hello planet"); 341 | } 342 | 343 | #[test] 344 | fn test_concat() { 345 | let s1 = StringB::from("hello"); 346 | let s2 = " world"; 347 | assert_eq!(s1.concat(s2).as_str(), "hello world"); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /valu3/src/types/value_key.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | pub trait ValueTrait {} 4 | 5 | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 6 | pub enum ValueKey { 7 | String(StringB), 8 | Number(usize), 9 | } 10 | 11 | impl ValueKey { 12 | pub fn as_string_b(&self) -> StringB { 13 | match self { 14 | ValueKey::String(s) => s.clone(), 15 | ValueKey::Number(n) => StringB::from(n.to_string()), 16 | } 17 | } 18 | 19 | pub fn to_usize(&self) -> usize { 20 | match self { 21 | ValueKey::String(s) => panic!("Cannot convert string to usize: {}", s), 22 | ValueKey::Number(n) => *n, 23 | } 24 | } 25 | 26 | pub fn len(&self) -> usize { 27 | match self { 28 | ValueKey::String(s) => s.len(), 29 | ValueKey::Number(n) => n.to_string().len(), 30 | } 31 | } 32 | } 33 | 34 | impl Display for ValueKey { 35 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 36 | match self { 37 | ValueKey::String(s) => write!(f, "{}", s), 38 | ValueKey::Number(n) => write!(f, "{}", n), 39 | } 40 | } 41 | } 42 | 43 | impl From for ValueKey { 44 | fn from(s: String) -> Self { 45 | ValueKey::String(StringB::from(s)) 46 | } 47 | } 48 | 49 | impl From<&str> for ValueKey { 50 | fn from(s: &str) -> Self { 51 | ValueKey::String(StringB::from(s)) 52 | } 53 | } 54 | 55 | impl From for ValueKey { 56 | fn from(n: u32) -> Self { 57 | ValueKey::Number(n as usize) 58 | } 59 | } 60 | 61 | use std::{ 62 | fmt::{Display, Formatter}, 63 | iter::FromIterator, 64 | }; 65 | 66 | impl<'a> FromIterator<&'a ValueKey> for ValueKey { 67 | fn from_iter>(iter: I) -> Self { 68 | let mut iterator = iter.into_iter(); 69 | match iterator.next() { 70 | Some(ValueKey::String(s)) => ValueKey::String(s.clone()), 71 | Some(ValueKey::Number(n)) => ValueKey::Number(*n), 72 | None => ValueKey::String(StringB::default()), 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /valu3/src/value.rs: -------------------------------------------------------------------------------- 1 | //! This module provides a `Value` enum to represent different data types and 2 | //! a trait `ToValueBehavior` to convert them to `Value`. The supported data types 3 | //! are: String, Number, Boolean, Array, Object, Null, Undefined, and DateTime. 4 | //! 5 | //! # Examples 6 | //! 7 | //! ``` 8 | //! use crate::{Array, DateTime, Number, Object, StringB, Value}; 9 | //! 10 | //! let string_value = Value::String(StringB::from("hello".to_string())); 11 | //! let number_value = Value::Number(Number::from(42)); 12 | //! let boolean_value = Value::Boolean(true); 13 | //! let null_value = Value::Null; 14 | //! let undefined_value = Value::Undefined; 15 | //! let mut datetime_value = Value::DateTime(DateTime::from("2023-04-05T00:00:00Z")); 16 | //! ``` 17 | use crate::prelude::*; 18 | use std::fmt::{Display, Formatter}; 19 | 20 | /// Represents different data types as an enum. 21 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 22 | pub enum Value { 23 | String(StringB), 24 | Number(Number), 25 | Boolean(bool), 26 | Array(Array), 27 | Object(Object), 28 | Null, 29 | Undefined, 30 | DateTime(DateTime), 31 | } 32 | 33 | impl Default for Value { 34 | fn default() -> Self { 35 | Value::Null 36 | } 37 | } 38 | 39 | impl ValueTrait for Value {} 40 | 41 | impl Display for Value { 42 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 43 | match self { 44 | Value::String(value) => write!(f, "{}", value.as_string()), 45 | Value::Number(value) => write!(f, "{}", value), 46 | Value::Boolean(value) => write!(f, "{}", if *value { "true" } else { "false" }), 47 | Value::Array(_) => write!(f, "{}", self.to_json(JsonMode::Indented)), 48 | Value::Object(_) => write!(f, "{}", self.to_json(JsonMode::Indented)), 49 | Value::Null => write!(f, "null"), 50 | Value::Undefined => write!(f, "undefined"), 51 | Value::DateTime(value) => write!(f, "{}", value), 52 | } 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use crate::prelude::*; 59 | 60 | // Tests for the different data types and their conversion to a `Value` enum. 61 | #[test] 62 | fn test_value_string() { 63 | let string = StringB::from("hello".to_string()); 64 | let value = Value::String(string.clone()); 65 | assert_eq!(value, Value::String(string)); 66 | } 67 | 68 | #[test] 69 | fn test_value_display_string() { 70 | let string = StringB::from("hello".to_string()); 71 | let value = Value::String(string.clone()); 72 | assert_eq!(format!("{}", value), format!("hello")); 73 | } 74 | 75 | #[test] 76 | fn test_value_number() { 77 | let number = Number::from(42); 78 | let value = Value::Number(number); 79 | assert_eq!(value, Value::Number(Number::from(42))); 80 | } 81 | 82 | #[test] 83 | fn test_value_boolean() { 84 | let value = Value::Boolean(true); 85 | assert_eq!(value, Value::Boolean(true)); 86 | } 87 | 88 | #[test] 89 | fn test_value_array() { 90 | let mut array = Array::new(); 91 | array.push(Value::Number(Number::from(1))); 92 | array.push(Value::Number(Number::from(2))); 93 | let value = Value::Array(array.clone()); 94 | assert_eq!(value, Value::Array(array)); 95 | } 96 | 97 | #[test] 98 | fn test_value_object() { 99 | let mut object = Object::default(); 100 | object.insert( 101 | "key".to_string(), 102 | Value::String(StringB::from("value".to_string())), 103 | ); 104 | let value = Value::Object(object.clone()); 105 | assert_eq!(value, Value::Object(object)); 106 | } 107 | 108 | #[test] 109 | fn test_value_null() { 110 | let value = Value::Null; 111 | assert_eq!(value, Value::Null); 112 | } 113 | 114 | #[test] 115 | fn test_value_undefined() { 116 | let value = Value::Undefined; 117 | assert_eq!(value, Value::Undefined); 118 | } 119 | 120 | #[test] 121 | fn test_value_datetime() { 122 | let datetime = DateTime::from("2023-04-05T00:00:00Z"); 123 | let value = Value::DateTime(datetime.clone()); 124 | assert_eq!(value, Value::DateTime(datetime)); 125 | } 126 | 127 | #[test] 128 | fn test_partial_eq() { 129 | let value1 = Value::String(StringB::from("hello".to_string())); 130 | let value2 = Value::String(StringB::from("hello".to_string())); 131 | assert!(value1 == value2); 132 | } 133 | 134 | #[test] 135 | fn test_partial_ord() { 136 | let value1 = Value::Number(Number::from(3.14)); 137 | let value2 = Value::Number(Number::from(3.141)); 138 | assert!(value1 < value2); 139 | assert!(value2 > value1); 140 | assert_eq!(value1 >= value2, false); 141 | assert_eq!(value2 <= value1, false); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /valu3_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "valu3-derive" 3 | version = "0.8.3" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | readme = "crates-io.md" 7 | authors = ["Philippe Assis "] 8 | repository = "https://github.com/lowcarboncode/valu3" 9 | keywords = ["valu3", "serialization", "deserialization", "serde", "derive"] 10 | description = "Derive macros for the valu3 crate." 11 | categories = ["encoding", "no-std", "parsing"] 12 | documentation = "https://docs.rs/valu3-derive" 13 | 14 | 15 | [dependencies] 16 | syn = { version = "2.0.100", features = ["full"] } 17 | quote = "1.0.40" 18 | proc-macro2 = "1.0.94" 19 | 20 | [dev-dependencies] 21 | valu3 = { path = "../valu3" } 22 | macrotest = "1.1.0" 23 | 24 | [lib] 25 | proc-macro = true 26 | -------------------------------------------------------------------------------- /valu3_derive/README.md: -------------------------------------------------------------------------------- 1 | # Valu3: Unleash the Power of Data Manipulation in Rust 🚀 2 | 3 | Welcome to **Valu3** - the ultimate, flexible, and powerful library for manipulating diverse data types in your Rust projects. Say goodbye to the complexity of handling numbers, strings, arrays, objects, and datetime values. Valu3 is here to make your life easier! 4 | 5 | 6 | [![crates.io](https://img.shields.io/crates/v/valu3?label=0.8.3)](https://crates.io/crates/valu3) 7 | [![Documentation](https://docs.rs/valu3/badge.svg?version=0.8.3)](https://docs.rs/valu3/0.8.3) 8 | ![MSRV](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) 9 | ![Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) 10 | [![Dependency Status](https://deps.rs/crate/valu3/0.8.3/status.svg)](https://deps.rs/crate/valu3/0.8.3) 11 | ![Main test](https://github.com/lowcarboncode/valu3/actions/workflows/main-test.yml/badge.svg) 12 | [![codecov](https://codecov.io/gh/lowcarboncode/valu3/branch/master/graph/badge.svg)](https://codecov.io/gh/lowcarboncode/valu3) 13 | ![downloads](https://img.shields.io/crates/d/valu3.svg) 14 | 15 | ## 🌟 Key Features 16 | 17 | 1. **Universal Type Handling**: No more juggling with different data types! Valu3's generic type support enables smooth management of all data types under one roof. 18 | 2. **Intuitive API**: Experience effortless data manipulation with Valu3's user-friendly API, designed to provide consistency across diverse data types. 19 | 3. **All-in-One Data Manipulation**: Valu3 has it all - numeric, string, object, array, and date/time manipulation. No need to look anywhere else! 20 | 4. **Effortless Data Conversion**: Convert your data to and from popular formats like JSON, YAML, and XML with ease, thanks to Valu3's built-in support. 21 | 5. **Serde Integration**: Serialize and deserialize your data seamlessly with Valu3's out-of-the-box integration with the Serde library. 22 | 6. **Native Struct Parsing & Validation**: Valu3 and Pest join forces to offer native parsing, conversion, and validation of data to structs, ensuring data integrity at every step. 23 | 7. **Payload Interpretation & Transformation**: Valu3 interprets and transforms payload strings like a champ, handling JSON from HTTP request bodies and more. 24 | 25 | ## 💡 Why Choose Valu3? 26 | 27 | Valu3 is designed to make data manipulation tasks in Rust a breeze. By combining a wide range of features and a consistent API, it simplifies data handling in Rust projects while maximizing productivity. 28 | 29 | Join the Valu3 revolution and experience the future of data manipulation in Rust! 🎉 30 | 31 | **⚡ Get Started with Valu3 Today! ⚡** 32 | 33 | ## Examples :space_invader: 34 | 35 | Here are some examples of how to use the Valu3: 36 | 37 | ```rust 38 | use valu3::prelude::*; 39 | 40 | let string_value = "hello".to_value(); 41 | let number_value = 42.to_value(); 42 | let boolean_value = true.to_value(); 43 | let null_value = Value::Null; 44 | let undefined_value = Value::Undefined; 45 | let mut datetime_value = DateTime::from("2023-04-05T00:00:00Z").to_value(); 46 | 47 | string_value.as_string(); 48 | number_value.get_i32(); 49 | assert_eq!(boolean_value, true); 50 | assert_eq!(null_value, Value::Null); 51 | assert_eq!(undefined_value, Value::Undefined); 52 | datetime_value.add_days(1); 53 | ``` 54 | 55 | ## Getting Started 56 | To start using the Valu3 in your Rust project, simply add the following line to your `Cargo.toml` file: 57 | ```toml 58 | [dependencies] 59 | valu3 = "0.1" 60 | ``` 61 | 62 | Then, you can import the library in your code like this: 63 | ```rust 64 | use valu3::prelude::*; 65 | 66 | //... 67 | 68 | let pi = 3.14.to_value(); 69 | ``` 70 | 71 | ## Structs and Conversions 72 | Valu3 natively has conversions for famous data types like json, yaml and xml. Furthermore with `valu3-derive` you are able to transform `struct` to `Value` by applying the `to_value()` method generated by the `ToValue` derive macros. This is an example on converting `struct` to `Value` and `Value` to other payload data types. 73 | 74 | ```rust 75 | use valu3::prelude:*; 76 | 77 | #[derive(ToValue, FromValue, Default)] 78 | struct MyStruct { 79 | id: u32, 80 | name: String, 81 | tags: Vec 82 | } 83 | 84 | fn main(){ 85 | let my_struct = MyStruct::default(); 86 | let value = my_struct.to_value(); 87 | 88 | assert_eq!(my_struct, MyStruct::from_value(value)); 89 | } 90 | 91 | ``` 92 | 93 | ### ToJson 94 | If your focus is only on using `Valu3` for conversion only, use the `ToJson` macro. 95 | 96 | ```rust 97 | use valu3::prelude:*; 98 | 99 | #[derive(ToJson, Default)] 100 | struct MyStruct { 101 | id: u32, 102 | name: String, 103 | tags: Vec 104 | } 105 | 106 | fn main(){ 107 | let my_struct = MyStruct::default(); 108 | let json = my_struct.to_json(); 109 | 110 | println!("{}", json); // print json string 111 | } 112 | 113 | ``` 114 | 115 | ## Payload 116 | 117 | `Vale3` is able to recognize a payload string, identify and convert it to `Value`, follow the example: 118 | 119 | ```rust 120 | use valu3::prelude:*; 121 | fn main(){ 122 | let boolean = Value::payload_to_value("true").unwrap(); 123 | let float = Value::payload_to_value("3.14").unwrap(); 124 | let json = Value::payload_to_value(r#"{"item": 3.14}"#).unwrap(); 125 | let array = Value::payload_to_value(r#"[1,2,3]"#).unwrap(); 126 | let null = Value::payload_to_value("null").unwrap(); 127 | let string = Value::payload_to_value(r#""123""#).unwrap(); 128 | 129 | assert_eq!(boolean, true.to_value()); 130 | assert_eq!(float, 3.14.to_value()); 131 | assert_eq!(json, Value::from(vec![("item", 3.14)])); 132 | assert_eq!(array, vec![1, 2, 3].to_value()); 133 | assert_eq!(null, Value::Null); 134 | assert_eq!(string, "123".to_value()); 135 | } 136 | 137 | ``` 138 | 139 | ## Contributing 140 | If you find a bug or have a suggestion for a new feature, please open an issue on the [GitHub repository](https://github.com/lowcarboncode/valu3/issues). 141 | 142 | If you would like to contribute to the project, please feel free to submit a pull request. Before submitting a pull request, please make sure that your code adheres to the project's style guidelines and passes all tests. 143 | 144 | ## Upcoming Features: Stay in Sync with the Future of Valu3 🌐 145 | 146 | We're constantly working to improve and expand the capabilities of Valu3, making it even more powerful and versatile. 147 | 148 | By keeping track of the project's progress, you can stay informed about new features in development and planned improvements. This will allow you to make the most of Valu3 in your Rust projects and prepare for future updates. 149 | 150 | Our commitment is to make Valu3 the ultimate data manipulation solution in Rust. Your input is invaluable! Feel free to join the discussions, share your ideas, and contribute to the project as it evolves. 151 | 152 | Join us in the ongoing journey to refine and expand Valu3! 🚀 153 | 154 | 155 | ## License 156 | This project is licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/lowcarboncode/valu3/blob/main/LICENSE) file for more information. 157 | -------------------------------------------------------------------------------- /valu3_derive/crates-io.md: -------------------------------------------------------------------------------- 1 | ../crates-io.md -------------------------------------------------------------------------------- /valu3_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | use proc_macro::TokenStream; 3 | use quote::quote; 4 | use syn::{parse_macro_input, Data, DeriveInput, Fields, Generics, Meta, Variant}; 5 | 6 | #[proc_macro_derive(ToValue, attributes(attr))] 7 | pub fn to_value_derive(input: TokenStream) -> TokenStream { 8 | let input = parse_macro_input!(input as DeriveInput); 9 | 10 | let name = input.ident; 11 | let generics = input.generics; 12 | 13 | let to_value_impl = match input.data { 14 | Data::Struct(data) => to_value_struct_impl(name, generics, data.fields), 15 | Data::Enum(data) => to_value_enum_impl(name, generics, data.variants), 16 | Data::Union(_) => panic!("ToValueBehavior cannot be derived for unions"), 17 | }; 18 | 19 | let expanded = quote! { 20 | #to_value_impl 21 | }; 22 | 23 | TokenStream::from(expanded) 24 | } 25 | 26 | fn to_value_struct_impl( 27 | name: syn::Ident, 28 | generics: Generics, 29 | fields: Fields, 30 | ) -> proc_macro2::TokenStream { 31 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 32 | 33 | let field_transforms = match fields { 34 | Fields::Named(fields) => fields 35 | .named 36 | .iter() 37 | .map(|field| { 38 | let name = match field.ident.as_ref() { 39 | Some(name) => name, 40 | None => panic!("ToValueBehavior cannot be derived for unnamed fields"), 41 | }; 42 | let field_name = format!("{}", name); 43 | quote! { 44 | map.insert(#field_name.to_string(), self.#name.clone().into()); 45 | } 46 | }) 47 | .collect::>(), 48 | Fields::Unnamed(fields) => fields 49 | .unnamed 50 | .iter() 51 | .enumerate() 52 | .map(|(index, _field)| { 53 | let index = syn::Index::from(index); 54 | quote! { 55 | Value::from(self.#index.clone()) 56 | } 57 | }) 58 | .collect::>(), 59 | Fields::Unit => { 60 | return quote! { 61 | impl #impl_generics ToValueBehavior for #name #ty_generics #where_clause { 62 | fn to_value(&self) -> Value { 63 | Value::Null 64 | } 65 | } 66 | } 67 | } 68 | }; 69 | 70 | quote! { 71 | impl #impl_generics ToValueBehavior for #name #ty_generics #where_clause { 72 | fn to_value(&self) -> Value { 73 | let mut map: std::collections::HashMap= std::collections::HashMap::new(); 74 | #(#field_transforms)* 75 | Value::from(map) 76 | } 77 | } 78 | } 79 | } 80 | 81 | fn to_value_enum_impl( 82 | name: syn::Ident, 83 | generics: Generics, 84 | variants: syn::punctuated::Punctuated, 85 | ) -> proc_macro2::TokenStream { 86 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 87 | 88 | let variant_transforms = variants.iter().map(|variant| { 89 | let variant_name = &variant.ident; 90 | quote! { 91 | #name::#variant_name => Value::from(stringify!(#variant_name)), 92 | } 93 | }); 94 | 95 | quote! { 96 | impl #impl_generics ToValueBehavior for #name #ty_generics #where_clause { 97 | fn to_value(&self) -> Value { 98 | match self { 99 | #(#variant_transforms)* 100 | } 101 | } 102 | } 103 | } 104 | } 105 | 106 | #[proc_macro_derive(FromValue)] 107 | pub fn from_value_derive(input: TokenStream) -> TokenStream { 108 | // Parse a `DeriveInput` AST from the input tokens. 109 | let ast = parse_macro_input!(input as DeriveInput); 110 | 111 | // Get the name and fields of the struct being derived. 112 | let target_name = &ast.ident; 113 | let target_generics = &ast.generics; 114 | let (impl_generics, ty_generics, where_clause) = target_generics.split_for_impl(); 115 | 116 | match ast.data { 117 | Data::Struct(data_struct) => { 118 | // Define a new implementation of the `FromValueBehavior` trait for the struct. 119 | let mut field_names = Vec::new(); 120 | let mut from_value_exprs = Vec::new(); 121 | 122 | if let Fields::Named(fields) = data_struct.fields { 123 | for field in fields.named.iter() { 124 | let field_name = match field.ident.as_ref() { 125 | Some(name) => name, 126 | None => panic!( 127 | "Can only derive FromValueBehavior for a struct with named fields." 128 | ), 129 | }; 130 | let field_type = &field.ty; 131 | 132 | field_names.push(field_name.clone()); 133 | 134 | from_value_exprs.push(quote! { 135 | #field_name: { 136 | let item = match map.get(stringify!(#field_name)) { 137 | Some(item) => item.clone(), 138 | None => return None, 139 | }; 140 | match <#field_type as FromValueBehavior>::from_value(item) { 141 | Some(item) => item, 142 | None => return None, 143 | } 144 | } 145 | }); 146 | } 147 | } else { 148 | panic!("Can only derive FromValueBehavior for a struct with named fields."); 149 | } 150 | 151 | let expanded = quote! { 152 | impl #impl_generics FromValueBehavior for #target_name #ty_generics #where_clause { 153 | type Item = Self; 154 | 155 | fn from_value(value: Value) -> Option { 156 | if let Value::Object(map) = value { 157 | Some( 158 | Self { 159 | #(#from_value_exprs),* 160 | } 161 | ) 162 | } else { 163 | None 164 | } 165 | } 166 | } 167 | }; 168 | 169 | TokenStream::from(expanded) 170 | } 171 | Data::Enum(data_enum) => { 172 | let variants = data_enum.variants; 173 | let mut variant_names = Vec::new(); 174 | 175 | for variant in variants.iter() { 176 | let variant_name = &variant.ident; 177 | variant_names.push(variant_name.clone()); 178 | } 179 | 180 | let expanded = quote! { 181 | impl #impl_generics PrimitiveType for #target_name #ty_generics #where_clause {} 182 | 183 | impl #impl_generics FromValueBehavior for #target_name #ty_generics #where_clause { 184 | type Item = Self; 185 | 186 | fn from_value(value: Value) -> Option { 187 | match value { 188 | Value::String(value) => { 189 | match value.as_str() { 190 | #( 191 | v if v == stringify!(#variant_names) => Some(#target_name::#variant_names), 192 | )* 193 | _ => None, 194 | } 195 | }, 196 | _ => None, 197 | } 198 | } 199 | } 200 | }; 201 | 202 | TokenStream::from(expanded) 203 | } 204 | _ => panic!("Can only derive FromValueBehavior for a struct."), 205 | } 206 | } 207 | 208 | #[proc_macro_derive(ToJson)] 209 | pub fn to_json_derive(input: TokenStream) -> TokenStream { 210 | let ast = parse_macro_input!(input as DeriveInput); 211 | let name = &ast.ident; 212 | let generics = &ast.generics; 213 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 214 | 215 | let gen = quote! { 216 | impl #impl_generics ToJsonBehavior for #name #ty_generics #where_clause { 217 | fn to_json(&self) -> String { 218 | self.to_value().to_string() 219 | } 220 | } 221 | }; 222 | 223 | gen.into() 224 | } 225 | 226 | #[proc_macro_derive(ToYaml)] 227 | pub fn to_yaml_derive(input: TokenStream) -> TokenStream { 228 | let input = parse_macro_input!(input as DeriveInput); 229 | let name = input.ident; 230 | let generics = input.generics; 231 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 232 | 233 | let expanded = quote! { 234 | impl #impl_generics ToYamlBehavior for #name #ty_generics #where_clause { 235 | fn to_yaml(&self) -> String { 236 | let value = self.to_value(); 237 | value.to_yaml() 238 | } 239 | } 240 | }; 241 | 242 | TokenStream::from(expanded) 243 | } 244 | -------------------------------------------------------------------------------- /valu3_derive/tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.expanded.rs -------------------------------------------------------------------------------- /valu3_derive/tests/derive_test.rs: -------------------------------------------------------------------------------- 1 | // tests/derive_tests.rs 2 | use std::collections::HashMap; 3 | use valu3::prelude::*; 4 | use valu3_derive::*; 5 | 6 | #[derive(ToValue, FromValue, ToJson)] 7 | struct User { 8 | id: u32, 9 | name: String, 10 | status: Status, 11 | } 12 | 13 | #[derive(Clone, ToValue, FromValue, ToJson, PartialEq, Debug)] 14 | enum Status { 15 | Active, 16 | Inactive, 17 | } 18 | 19 | #[test] 20 | fn test_to_value_struct() { 21 | let user = User { 22 | id: 1, 23 | name: "Alice".to_string(), 24 | status: Status::Active, 25 | }; 26 | let value = user.to_value(); 27 | 28 | if let Value::Object(map) = value { 29 | assert_eq!(map.get("id").unwrap(), &Value::from(1u32)); 30 | assert_eq!(map.get("name").unwrap(), &Value::from("Alice")); 31 | assert_eq!(map.get("status").unwrap(), &Value::from("Active")); 32 | } else { 33 | panic!("Expected Value::Object"); 34 | } 35 | } 36 | 37 | #[test] 38 | fn test_from_value_struct() { 39 | let mut map = HashMap::new(); 40 | map.insert("id".to_string(), Value::from(1u32)); 41 | map.insert("name".to_string(), Value::from("Alice")); 42 | map.insert("status".to_string(), Value::from("Active")); 43 | let value = map.to_value(); 44 | 45 | let user = User::from_value(value).expect("Should parse correctly"); 46 | 47 | assert_eq!(user.id, 1); 48 | assert_eq!(user.name, "Alice".to_string()); 49 | assert_eq!(user.status, Status::Active); 50 | } 51 | 52 | #[test] 53 | fn test_to_value_enum() { 54 | let status = Status::Active; 55 | let value = status.to_value(); 56 | 57 | assert_eq!(value, Value::from("Active")); 58 | } 59 | 60 | #[test] 61 | fn test_from_value_enum() { 62 | let value = Value::from("Active"); 63 | let status = Status::from_value(value).expect("Should parse correctly"); 64 | 65 | match status { 66 | Status::Active => (), 67 | _ => panic!("Expected Status::Active"), 68 | } 69 | } 70 | 71 | #[test] 72 | fn test_to_json() { 73 | let user = User { 74 | id: 1, 75 | name: "Alice".to_string(), 76 | status: Status::Active, 77 | }; 78 | let json = user.to_json(); 79 | let expect = { 80 | let mut map = HashMap::new(); 81 | map.insert("id".to_string(), Value::from(1)); 82 | map.insert("name".to_string(), Value::from("Alice")); 83 | map.insert("status".to_string(), Value::from("Active")); 84 | map.to_value() 85 | }; 86 | let result = Value::json_to_value(&json).unwrap(); 87 | 88 | assert_eq!(result, expect); 89 | } 90 | -------------------------------------------------------------------------------- /valu3_derive/tests/tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | pub fn pass() { 3 | macrotest::expand("tests/*.rs"); 4 | } 5 | --------------------------------------------------------------------------------