valuable-0.1.1/.cargo_vcs_info.json0000644000000001460000000000100126270ustar { "git": { "sha1": "9efc29b6e58cef28f6566a47aa7e142a55fead77" }, "path_in_vcs": "valuable" }valuable-0.1.1/Cargo.lock0000644000000402460000000000100106070ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags", "textwrap", "unicode-width", ] [[package]] name = "criterion" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_cbor", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "csv" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "half" version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "plotters" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_cbor" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", "serde", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "valuable" version = "0.1.1" dependencies = [ "criterion", "valuable-derive", ] [[package]] name = "valuable-derive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e3a32a9bcc0f6c6ccfd5b27bcf298c58e753bcc9eeff268157a303393183a6d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" valuable-0.1.1/Cargo.toml0000644000000031250000000000100106250ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56" name = "valuable" version = "0.1.1" build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Object-safe value inspection, used to pass un-typed structured data across trait-object boundaries. """ readme = "README.md" keywords = [ "valuable", "serialization", "debugging", "no_std", ] categories = [ "development-tools::debugging", "encoding", ] license = "MIT" repository = "https://github.com/tokio-rs/valuable" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [lib] name = "valuable" path = "src/lib.rs" [[example]] name = "derive" path = "examples/derive.rs" [[example]] name = "hello_world" path = "examples/hello_world.rs" [[example]] name = "print" path = "examples/print.rs" [[bench]] name = "structable" path = "benches/structable.rs" harness = false [dependencies.valuable-derive] version = "=0.1.1" optional = true [dev-dependencies.criterion] version = "0.3" [features] alloc = [] default = ["std"] derive = ["valuable-derive"] std = ["alloc"] valuable-0.1.1/Cargo.toml.orig000064400000000000000000000017211046102023000143060ustar 00000000000000[package] name = "valuable" version = "0.1.1" edition = "2021" license = "MIT" rust-version = "1.56" readme = "../README.md" repository = "https://github.com/tokio-rs/valuable" description = """ Object-safe value inspection, used to pass un-typed structured data across trait-object boundaries. """ categories = [ "development-tools::debugging", "encoding", ] keywords = [ "valuable", "serialization", "debugging", "no_std", ] [features] default = ["std"] # Provide derive(Valuable) macro derive = ["valuable-derive"] # Provide impls for common standard library types like Vec and HashMap. std = ["alloc"] # Provide imps for types in Rust's `alloc` library. alloc = [] [dependencies] valuable-derive = { version = "=0.1.1", optional = true, path = "../valuable-derive" } [dev-dependencies] criterion = "0.3" [[bench]] name = "structable" harness = false [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] valuable-0.1.1/README.md000064400000000000000000000006501046102023000126760ustar 00000000000000# Valuable Valuable provides object-safe value inspection. Use cases include passing structured data to trait objects and object-safe serialization. ## License This project is licensed under the [MIT license](LICENSE). ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Valuable by you, shall be licensed as MIT, without any additional terms or conditions.valuable-0.1.1/benches/structable.rs000064400000000000000000000065111046102023000155460ustar 00000000000000use valuable::*; use criterion::{black_box, criterion_group, criterion_main, Criterion}; #[derive(Default)] struct HelloWorld { one: usize, two: usize, three: usize, four: usize, five: usize, six: usize, } static FIELDS: &[NamedField<'static>] = &[ NamedField::new("one"), NamedField::new("two"), NamedField::new("three"), NamedField::new("four"), NamedField::new("five"), NamedField::new("six"), ]; impl Structable for HelloWorld { fn definition(&self) -> StructDef<'_> { StructDef::new_static("HelloWorld", Fields::Named(FIELDS)) } } impl Valuable for HelloWorld { fn as_value(&self) -> Value<'_> { Value::Structable(self) } fn visit(&self, v: &mut dyn Visit) { v.visit_named_fields(&NamedValues::new( FIELDS, &[ Value::Usize(self.one), Value::Usize(self.two), Value::Usize(self.three), Value::Usize(self.four), Value::Usize(self.five), Value::Usize(self.six), ], )); } } fn criterion_benchmark(c: &mut Criterion) { const NUM: usize = 50; let hello_world = black_box(HelloWorld::default()); let structable = &hello_world as &dyn Structable; let f = match structable.definition() { StructDef::Static { fields: Fields::Named(fields), .. } => &fields[5], _ => unreachable!(), }; struct Sum(usize, &'static NamedField<'static>); impl Visit for Sum { fn visit_named_fields(&mut self, record: &NamedValues<'_>) { self.0 += match record.get(self.1) { Some(Value::Usize(v)) => v, _ => return, } } fn visit_value(&mut self, _: Value<'_>) { unimplemented!() } } c.bench_function("struct", |b| { b.iter(|| { let mut num = 0; for _ in 0..NUM { let hello_world = black_box(HelloWorld::default()); num += hello_world.six; } black_box(num); }) }); c.bench_function("valuable", |b| { b.iter(|| { let mut v = Sum(black_box(0), f); for _ in 0..NUM { v.visit_named_fields(&NamedValues::new( FIELDS, &[ Value::Usize(0), Value::Usize(0), Value::Usize(0), Value::Usize(0), Value::Usize(0), Value::Usize(0), ], )); /* v.visit_struct(&Record::new( &definition, &[ Value::Usize(hello_world.one), Value::Usize(hello_world.two), Value::Usize(hello_world.three), Value::Usize(hello_world.four), Value::Usize(hello_world.five), Value::Usize(hello_world.six), ] )); */ // hello_world.visit(&mut v); } black_box(v.0); }) }); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); valuable-0.1.1/build.rs000064400000000000000000000026671046102023000130760ustar 00000000000000#![warn(rust_2018_idioms, single_use_lifetimes)] use std::env; include!("no_atomic.rs"); // The rustc-cfg strings below are *not* public API. Please let us know by // opening a GitHub issue if your build environment requires some way to enable // these cfgs other than by executing our build script. fn main() { println!("cargo:rerun-if-changed=no_atomic.rs"); println!("cargo:rustc-check-cfg=cfg(valuable_no_atomic_cas,valuable_no_atomic,valuable_no_atomic_64)"); let target = match env::var("TARGET") { Ok(target) => target, Err(e) => { println!( "cargo:warning=valuable: unable to get TARGET environment variable: {}", e ); return; } }; // Note that this is `no_*`, not `has_*`. This allows treating // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't // run. This is needed for compatibility with non-cargo build systems that // don't run the build script. if NO_ATOMIC_CAS.contains(&&*target) { println!("cargo:rustc-cfg=valuable_no_atomic_cas"); } if NO_ATOMIC.contains(&&*target) { println!("cargo:rustc-cfg=valuable_no_atomic"); println!("cargo:rustc-cfg=valuable_no_atomic_64"); } else if NO_ATOMIC_64.contains(&&*target) { println!("cargo:rustc-cfg=valuable_no_atomic_64"); } else { // Otherwise, assuming `"max-atomic-width" == 64`. } } valuable-0.1.1/examples/derive.rs000064400000000000000000000010701046102023000150560ustar 00000000000000use valuable::Valuable; use std::collections::HashMap; // `Debug` not implemented for struct, the debug implementation is going via // valuable. #[derive(Valuable)] struct Person { name: String, age: u8, phones: Vec, favorites: HashMap, } fn main() { let mut p = Person { name: "John Doe".to_string(), age: 42, phones: vec!["876-5309".to_string()], favorites: HashMap::new(), }; p.favorites.insert("color".to_string(), "blue".to_string()); println!("{:#?}", p.as_value()); } valuable-0.1.1/examples/hello_world.rs000064400000000000000000000027701046102023000161220ustar 00000000000000use valuable::*; struct HelloWorld { hello: &'static str, world: World, } struct World { answer: usize, } static HELLO_WORLD_FIELDS: &[NamedField<'static>] = &[NamedField::new("hello"), NamedField::new("world")]; impl Structable for HelloWorld { fn definition(&self) -> StructDef<'_> { StructDef::new_static("HelloWorld", Fields::Named(HELLO_WORLD_FIELDS)) } } impl Valuable for HelloWorld { fn as_value(&self) -> Value<'_> { Value::Structable(self) } fn visit(&self, v: &mut dyn Visit) { v.visit_named_fields(&NamedValues::new( HELLO_WORLD_FIELDS, &[Value::String(self.hello), Value::Structable(&self.world)], )); } } static WORLD_FIELDS: &[NamedField<'static>] = &[NamedField::new("answer")]; impl Valuable for World { fn as_value(&self) -> Value<'_> { Value::Structable(self) } fn visit(&self, v: &mut dyn Visit) { v.visit_named_fields(&NamedValues::new( WORLD_FIELDS, &[Value::Usize(self.answer)], )); } } impl Structable for World { fn definition(&self) -> StructDef<'_> { StructDef::new_static("World", Fields::Named(WORLD_FIELDS)) } } fn main() { let hello_world = HelloWorld { hello: "wut", world: World { answer: 42 }, }; let value = Value::Structable(&hello_world); println!("{:#?}", value); let slice = &[1, 2, 3][..]; let value = &slice as &dyn Valuable; println!("{:?}", value); } valuable-0.1.1/examples/print.rs000064400000000000000000000053521046102023000147430ustar 00000000000000use valuable::{NamedValues, Valuable, Value, Visit}; struct Print(String); impl Print { fn indent(&self) -> Print { Print(format!("{} ", self.0)) } } impl Visit for Print { fn visit_value(&mut self, value: Value<'_>) { match value { Value::Structable(v) => { let def = v.definition(); // Print the struct name println!("{}{}:", self.0, def.name()); // Visit fields let mut visit = self.indent(); v.visit(&mut visit); } Value::Enumerable(v) => { let def = v.definition(); let variant = v.variant(); // Print the enum name println!("{}{}::{}:", self.0, def.name(), variant.name()); // Visit fields let mut visit = self.indent(); v.visit(&mut visit); } Value::Listable(v) => { println!("{}", self.0); // Visit fields let mut visit = self.indent(); v.visit(&mut visit); } Value::Mappable(v) => { println!("{}", self.0); // Visit fields let mut visit = self.indent(); v.visit(&mut visit); } // Primitive or unknown type, just render Debug v => println!("{:?}", v), } } fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { for (field, value) in named_values { print!("{}- {}: ", self.0, field.name()); value.visit(self); } } fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { for value in values { print!("{}- ", self.0); value.visit(self); } } fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { print!("{}- {:?}: ", self.0, key); value.visit(self); } } #[derive(Valuable)] struct Person { name: String, age: u32, addresses: Vec
, } #[derive(Valuable)] struct Address { street: String, city: String, zip: String, } fn main() { let person = Person { name: "Angela Ashton".to_string(), age: 31, addresses: vec![ Address { street: "123 1st Ave".to_string(), city: "Townsville".to_string(), zip: "12345".to_string(), }, Address { street: "555 Main St.".to_string(), city: "New Old Town".to_string(), zip: "55555".to_string(), }, ], }; let mut print = Print("".to_string()); valuable::visit(&person, &mut print); } valuable-0.1.1/no_atomic.rs000064400000000000000000000062451046102023000137430ustar 00000000000000// This file is @generated by no_atomic.sh. // It is not intended for manual editing. const NO_ATOMIC_CAS: &[&str] = &[ "armv4t-none-eabi", "armv5te-none-eabi", "avr-unknown-gnu-atmega328", "bpfeb-unknown-none", "bpfel-unknown-none", "msp430-none-elf", "riscv32e-unknown-none-elf", "riscv32em-unknown-none-elf", "riscv32emc-unknown-none-elf", "riscv32i-unknown-none-elf", "riscv32im-unknown-none-elf", "riscv32imc-unknown-none-elf", "thumbv4t-none-eabi", "thumbv5te-none-eabi", "thumbv6m-none-eabi", "thumbv6m-nuttx-eabi", "xtensa-esp32s2-none-elf", ]; const NO_ATOMIC_64: &[&str] = &[ "arm-linux-androideabi", "armv4t-none-eabi", "armv4t-unknown-linux-gnueabi", "armv5te-none-eabi", "armv5te-unknown-linux-gnueabi", "armv5te-unknown-linux-musleabi", "armv5te-unknown-linux-uclibceabi", "armv6k-nintendo-3ds", "csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2hf", "hexagon-unknown-linux-musl", "hexagon-unknown-none-elf", "m68k-unknown-linux-gnu", "mips-unknown-linux-gnu", "mips-unknown-linux-musl", "mips-unknown-linux-uclibc", "mipsel-sony-psp", "mipsel-unknown-linux-gnu", "mipsel-unknown-linux-musl", "mipsel-unknown-linux-uclibc", "mipsel-unknown-netbsd", "mipsel-unknown-none", "mipsisa32r6-unknown-linux-gnu", "mipsisa32r6el-unknown-linux-gnu", "powerpc-unknown-freebsd", "powerpc-unknown-linux-gnu", "powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-musl", "powerpc-unknown-linux-muslspe", "powerpc-unknown-netbsd", "powerpc-unknown-openbsd", "powerpc-wrs-vxworks", "powerpc-wrs-vxworks-spe", "riscv32-wrs-vxworks", "riscv32e-unknown-none-elf", "riscv32em-unknown-none-elf", "riscv32emc-unknown-none-elf", "riscv32gc-unknown-linux-gnu", "riscv32gc-unknown-linux-musl", "riscv32i-unknown-none-elf", "riscv32im-unknown-none-elf", "riscv32ima-unknown-none-elf", "riscv32imac-esp-espidf", "riscv32imac-unknown-none-elf", "riscv32imac-unknown-nuttx-elf", "riscv32imac-unknown-xous-elf", "riscv32imafc-esp-espidf", "riscv32imafc-unknown-none-elf", "riscv32imafc-unknown-nuttx-elf", "riscv32imc-esp-espidf", "riscv32imc-unknown-none-elf", "riscv32imc-unknown-nuttx-elf", "sparc-unknown-linux-gnu", "sparc-unknown-none-elf", "thumbv4t-none-eabi", "thumbv5te-none-eabi", "thumbv6m-none-eabi", "thumbv6m-nuttx-eabi", "thumbv7em-none-eabi", "thumbv7em-none-eabihf", "thumbv7em-nuttx-eabi", "thumbv7em-nuttx-eabihf", "thumbv7m-none-eabi", "thumbv7m-nuttx-eabi", "thumbv8m.base-none-eabi", "thumbv8m.base-nuttx-eabi", "thumbv8m.main-none-eabi", "thumbv8m.main-none-eabihf", "thumbv8m.main-nuttx-eabi", "thumbv8m.main-nuttx-eabihf", "xtensa-esp32-espidf", "xtensa-esp32-none-elf", "xtensa-esp32s2-espidf", "xtensa-esp32s2-none-elf", "xtensa-esp32s3-espidf", "xtensa-esp32s3-none-elf", ]; const NO_ATOMIC: &[&str] = &[ "avr-unknown-gnu-atmega328", "bpfeb-unknown-none", "bpfel-unknown-none", "mipsel-sony-psx", "msp430-none-elf", ]; valuable-0.1.1/src/enumerable.rs000064400000000000000000000423221046102023000146750ustar 00000000000000use crate::field::*; use crate::*; #[cfg(feature = "alloc")] use alloc::format; use core::fmt; /// An enum-like [`Valuable`] sub-type. /// /// Implemented by [`Valuable`] types that have an enum-like shape. Fields may /// be named or unnamed (tuple). Values that implement `Enumerable` must return /// [`Value::Enumerable`] from their [`Valuable::as_value`] implementation. /// /// # Inspecting /// /// The [`variant()`] method returns the `Enumerable` instance's variant. The /// `Enumerable` may also have unnamed fields (tuple) or named fields. /// Inspecting the field values is done by visiting the enum. When visiting an /// `Enumerable`, either the [`visit_named_fields()`] or the /// [`visit_unnamed_fields()`] methods of [`Visit`] are called. Each method may /// be called multiple times per `Enumerable`, but the two methods are never /// mixed. /// /// [`variant()`]: Enumerable::variant /// [`visit_named_fields()`]: Visit::visit_named_fields /// [`visit_unnamed_fields()`]: Visit::visit_unnamed_fields /// /// ``` /// use valuable::{Valuable, Value, Visit}; /// /// #[derive(Valuable)] /// enum MyEnum { /// Foo, /// Bar(u32), /// } /// /// struct PrintVariant; /// /// impl Visit for PrintVariant { /// fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { /// for value in values { /// println!(" - {:?}", value); /// } /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Enumerable(v) => { /// println!("{}", v.variant().name()); /// v.visit(self) /// } /// _ => {} /// } /// } /// } /// /// let my_enum = MyEnum::Bar(123); /// /// valuable::visit(&my_enum, &mut PrintVariant); /// ``` /// /// If the enum is **statically** defined, then all variants, and variant fields /// are known ahead of time and may be accessed via the [`EnumDef`] instance /// returned by [`definition()`]. /// /// [`definition()`]: Enumerable::definition /// /// # Implementing /// /// Implementing `Enumerable` is usually done by adding `#[derive(Valuable)]` to /// a Rust `enum` definition. /// /// ``` /// use valuable::{Valuable, Enumerable, EnumDef}; /// /// #[derive(Valuable)] /// enum MyEnum { /// Foo, /// Bar(u32), /// } /// /// let my_enum = MyEnum::Bar(123); /// /// let variants = match my_enum.definition() { /// EnumDef::Static { name, variants, .. } => { /// assert_eq!("MyEnum", name); /// variants /// } /// _ => unreachable!(), /// }; /// /// assert_eq!(2, variants.len()); /// assert_eq!("Foo", variants[0].name()); /// assert!(variants[0].fields().is_unnamed()); /// ``` pub trait Enumerable: Valuable { /// Returns the enum's definition. /// /// See [`EnumDef`] documentation for more details. /// /// # Examples /// /// ``` /// use valuable::{Enumerable, Valuable}; /// /// #[derive(Valuable)] /// enum MyEnum { /// Foo, /// Bar(u32), /// } /// /// let my_enum = MyEnum::Bar(123); /// /// assert_eq!("MyEnum", my_enum.definition().name()); /// ``` fn definition(&self) -> EnumDef<'_>; /// Returns the `enum`'s current variant. /// /// # Examples /// /// ``` /// use valuable::{Enumerable, Valuable}; /// /// #[derive(Valuable)] /// enum MyEnum { /// Foo, /// Bar(u32), /// } /// /// let my_enum = MyEnum::Foo; /// assert_eq!("Foo", my_enum.variant().name()); /// ``` fn variant(&self) -> Variant<'_>; } /// An enum's variants, variant fields, and other enum-level information. /// /// Returned by [`Enumerable::definition()`], `EnumDef` provides the caller with /// information about the enum's definition. #[non_exhaustive] #[derive(Debug)] pub enum EnumDef<'a> { /// The enum is statically-defined, all variants and variant-level fields /// are known ahead of time. /// /// Most `Enumerable` definitions for Rust enum types will be /// `EnumDef::Static`. /// /// # Examples /// /// A statically defined enum /// /// ``` /// use valuable::{Valuable, Enumerable, EnumDef}; /// /// #[derive(Valuable)] /// enum MyEnum { /// Foo, /// Bar(u32), /// } /// /// let my_enum = MyEnum::Bar(123); /// /// let variants = match my_enum.definition() { /// EnumDef::Static { name, variants, .. } => { /// assert_eq!("MyEnum", name); /// variants /// } /// _ => unreachable!(), /// }; /// /// assert_eq!(2, variants.len()); /// assert_eq!("Foo", variants[0].name()); /// assert_eq!("Bar", variants[1].name()); /// ``` #[non_exhaustive] Static { /// The enum's name name: &'static str, /// The enum's variants variants: &'static [VariantDef<'static>], }, /// The enum is dynamically-defined, not all variants and fields are known /// ahead of time. /// /// # Examples /// /// The enum variant is tracked as a string /// /// ``` /// use valuable::{Enumerable, EnumDef, Fields, VariantDef, Valuable, Value, Variant, Visit}; /// /// /// A dynamic enum /// struct DynEnum { /// // The enum name /// name: String, /// /// // The current variant /// variant: String, /// } /// /// impl Valuable for DynEnum { /// fn as_value(&self) -> Value<'_> { /// Value::Enumerable(self) /// } /// /// fn visit(&self, _visit: &mut dyn Visit) { /// // No variant fields, so there is nothing to call here. /// } /// } /// /// impl Enumerable for DynEnum { /// fn definition(&self) -> EnumDef<'_> { /// EnumDef::new_dynamic(&self.name, &[]) /// } /// /// fn variant(&self) -> Variant<'_> { /// Variant::Dynamic(VariantDef::new(&self.variant, Fields::Unnamed(0))) /// } /// } /// ``` #[non_exhaustive] Dynamic { /// The enum's name name: &'a str, /// The enum's variants variants: &'a [VariantDef<'a>], }, } /// An enum variant definition. /// /// Included with [`EnumDef`] returned by [`Enumerable::definition()`], /// `VariantDef` provides the caller with information about a specific variant. #[derive(Debug)] pub struct VariantDef<'a> { /// Variant name name: &'a str, /// Variant fields fields: Fields<'a>, } /// An enum variant /// /// Returned by [`Enumerable::variant()`], `Variant` represents a single enum /// variant. #[derive(Debug)] pub enum Variant<'a> { /// The variant is statically defined by the associated enum. Static(&'static VariantDef<'static>), /// The variant is dynamically defined and not included as part of /// [`Enumerable::definition()`]. Dynamic(VariantDef<'a>), } impl<'a> EnumDef<'a> { /// Create a new [`EnumDef::Static`] instance. /// /// This should be used when an enum's variants are fixed and known ahead of /// time. /// /// # Examples /// /// ``` /// use valuable::{EnumDef, Fields, VariantDef}; /// /// static VARIANTS: &[VariantDef<'static>] = &[ /// VariantDef::new("Bar", Fields::Unnamed(1)), /// ]; /// /// let def = EnumDef::new_static( "Foo", VARIANTS); /// ``` pub const fn new_static( name: &'static str, variants: &'static [VariantDef<'static>], ) -> EnumDef<'a> { EnumDef::Static { name, variants } } /// Create a new [`EnumDef::Dynamic`] instance. /// /// This is used when the enum's variants may vary at runtime. /// /// # Examples /// /// ``` /// use valuable::{EnumDef, Fields, VariantDef}; /// /// let def = EnumDef::new_dynamic( /// "Foo", /// &[VariantDef::new("Bar", Fields::Unnamed(1))] /// ); /// ``` pub const fn new_dynamic(name: &'a str, variants: &'a [VariantDef<'a>]) -> EnumDef<'a> { EnumDef::Dynamic { name, variants } } /// Returns the enum's name /// /// # Examples /// /// ``` /// use valuable::{Enumerable, Valuable}; /// /// #[derive(Valuable)] /// enum Foo { /// Bar, /// Baz, /// } /// /// let def = Foo::Bar.definition(); /// assert_eq!("Foo", def.name()); /// ``` pub fn name(&self) -> &str { match self { EnumDef::Static { name, .. } => name, EnumDef::Dynamic { name, .. } => name, } } /// Returns the enum's variants /// /// # Examples /// /// ``` /// use valuable::{Enumerable, Valuable}; /// /// #[derive(Valuable)] /// enum Foo { /// Bar, /// Baz, /// } /// /// let def = Foo::Bar.definition(); /// let variants = def.variants(); /// /// assert_eq!(2, variants.len()); /// assert_eq!("Bar", variants[0].name()); /// ``` pub fn variants(&self) -> &[VariantDef<'_>] { match self { EnumDef::Static { variants, .. } => variants, EnumDef::Dynamic { variants, .. } => variants, } } /// Returns `true` if the enum is [statically defined](EnumDef::Static). /// /// # Examples /// /// With a static enum /// /// ``` /// use valuable::{Enumerable, Valuable}; /// /// #[derive(Valuable)] /// enum Foo { /// Bar, /// Baz, /// } /// /// let def = Foo::Bar.definition(); /// assert!(def.is_static()); /// ``` /// /// With a dynamic enum /// /// ``` /// use valuable::{EnumDef, Fields, VariantDef}; /// /// let def = EnumDef::new_dynamic("Foo", &[]); /// assert!(!def.is_static()); /// ``` pub fn is_static(&self) -> bool { matches!(self, EnumDef::Static { .. }) } /// Returns `true` if the enum is [dynamically defined](EnumDef::Dynamic). /// /// # Examples /// /// With a static enum /// /// ``` /// use valuable::{Enumerable, Valuable}; /// /// #[derive(Valuable)] /// enum Foo { /// Bar, /// Baz, /// } /// /// let def = Foo::Bar.definition(); /// assert!(!def.is_dynamic()); /// ``` /// /// With a dynamic enum /// /// ``` /// use valuable::{EnumDef, Fields, VariantDef}; /// /// let def = EnumDef::new_dynamic("Foo", &[]); /// assert!(def.is_dynamic()); /// ``` pub fn is_dynamic(&self) -> bool { matches!(self, EnumDef::Dynamic { .. }) } } impl<'a> VariantDef<'a> { /// Creates a new `VariantDef` instance. /// /// # Examples /// /// ``` /// use valuable::{Fields, VariantDef}; /// /// let def = VariantDef::new("Foo", Fields::Unnamed(2)); /// ``` pub const fn new(name: &'a str, fields: Fields<'a>) -> VariantDef<'a> { VariantDef { name, fields } } /// Returns the variant's name /// /// # Examples /// /// ``` /// use valuable::{Fields, VariantDef}; /// /// let def = VariantDef::new("Foo", Fields::Unnamed(2)); /// assert_eq!("Foo", def.name()); /// ``` pub fn name(&self) -> &str { self.name } /// Returns the variant's fields /// /// # Examples /// /// ``` /// use valuable::{Fields, VariantDef}; /// /// let def = VariantDef::new("Foo", Fields::Unnamed(3)); /// assert!(matches!(def.fields(), Fields::Unnamed(_))); /// ``` pub fn fields(&self) -> &Fields<'_> { &self.fields } } impl Variant<'_> { /// Returns the variant's name /// /// # Examples /// /// ``` /// use valuable::{Fields, Variant, VariantDef}; /// /// static VARIANT: &VariantDef<'static> = &VariantDef::new( /// "Foo", Fields::Unnamed(2)); /// /// let variant = Variant::Static(VARIANT); /// assert_eq!("Foo", variant.name()); /// ``` pub fn name(&self) -> &str { match self { Variant::Static(v) => v.name(), Variant::Dynamic(v) => v.name(), } } /// Returns the variant's fields pub fn fields(&self) -> &Fields<'_> { match self { Variant::Static(v) => v.fields(), Variant::Dynamic(v) => v.fields(), } } /// Returns `true` if the variant has associated named fields. /// /// # Examples /// /// With named fields /// /// ``` /// use valuable::{Fields, NamedField, Variant, VariantDef}; /// /// static VARIANT: &VariantDef<'static> = &VariantDef::new( /// "Foo", Fields::Named(&[NamedField::new("hello")])); /// /// let variant = Variant::Static(VARIANT); /// assert!(variant.is_named_fields()); /// ``` /// /// With unnamed fields /// /// ``` /// use valuable::{Fields, Variant, VariantDef}; /// /// static VARIANT: &VariantDef<'static> = &VariantDef::new( /// "Foo", Fields::Unnamed(1)); /// /// let variant = Variant::Static(VARIANT); /// assert!(!variant.is_named_fields()); /// ``` pub fn is_named_fields(&self) -> bool { self.fields().is_named() } /// Returns `true` if the variant has associated unnamed fields. /// /// # Examples /// /// With named fields /// /// ``` /// use valuable::{Fields, NamedField, Variant, VariantDef}; /// /// static VARIANT: &VariantDef<'static> = &VariantDef::new( /// "Foo", Fields::Named(&[NamedField::new("hello")])); /// /// let variant = Variant::Static(VARIANT); /// assert!(!variant.is_unnamed_fields()); /// ``` /// /// With unnamed fields /// /// ``` /// use valuable::{Fields, Variant, VariantDef}; /// /// static VARIANT: &VariantDef<'static> = &VariantDef::new( /// "Foo", Fields::Unnamed(1)); /// /// let variant = Variant::Static(VARIANT); /// assert!(variant.is_unnamed_fields()); /// ``` pub fn is_unnamed_fields(&self) -> bool { !self.is_named_fields() } } impl fmt::Debug for dyn Enumerable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let variant = self.variant(); #[cfg(feature = "alloc")] let name = format!("{}::{}", self.definition().name(), variant.name()); #[cfg(not(feature = "alloc"))] let name = variant.name(); if variant.is_named_fields() { struct DebugEnum<'a, 'b> { fmt: fmt::DebugStruct<'a, 'b>, } let mut debug = DebugEnum { fmt: fmt.debug_struct(&name), }; impl Visit for DebugEnum<'_, '_> { fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { for (field, value) in named_values { self.fmt.field(field.name(), value); } } fn visit_value(&mut self, _: Value<'_>) { unreachable!(); } } self.visit(&mut debug); debug.fmt.finish() } else { struct DebugEnum<'a, 'b> { fmt: fmt::DebugTuple<'a, 'b>, } let mut debug = DebugEnum { fmt: fmt.debug_tuple(&name), }; impl Visit for DebugEnum<'_, '_> { fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { for value in values { self.fmt.field(value); } } fn visit_value(&mut self, _: Value<'_>) { unreachable!(); } } self.visit(&mut debug); debug.fmt.finish() } } } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Enumerable for $ty { fn definition(&self) -> EnumDef<'_> { T::definition(&**self) } fn variant(&self) -> Variant<'_> { T::variant(&**self) } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } static RESULT_VARIANTS: &[VariantDef<'static>] = &[ VariantDef::new("Ok", Fields::Unnamed(1)), VariantDef::new("Err", Fields::Unnamed(1)), ]; impl Enumerable for Result where T: Valuable, E: Valuable, { fn definition(&self) -> EnumDef<'_> { EnumDef::new_static("Result", RESULT_VARIANTS) } fn variant(&self) -> Variant<'_> { match self { Ok(_) => Variant::Static(&RESULT_VARIANTS[0]), Err(_) => Variant::Static(&RESULT_VARIANTS[1]), } } } impl Valuable for Result where T: Valuable, E: Valuable, { fn as_value(&self) -> Value<'_> { Value::Enumerable(self) } fn visit(&self, visitor: &mut dyn Visit) { match self { Ok(val) => visitor.visit_unnamed_fields(&[val.as_value()]), Err(val) => visitor.visit_unnamed_fields(&[val.as_value()]), } } } valuable-0.1.1/src/field.rs000064400000000000000000000071001046102023000136340ustar 00000000000000/// Data stored within a `Structable` or an `Enumerable`. #[derive(Debug)] pub enum Fields<'a> { /// Named fields Named(&'a [NamedField<'a>]), /// Unnamed (positional) fields or unit /// /// The `usize` value represents the number of fields. Unnamed(usize), } /// A named field #[derive(Debug, Clone, Copy)] pub struct NamedField<'a>(&'a str); impl Fields<'_> { /// Returns `true` if the fields are named. /// /// # Examples /// /// Named fields /// /// ``` /// use valuable::Fields; /// /// let fields = Fields::Named(&[]); /// assert!(fields.is_named()); /// ``` /// /// Unnamed fields /// /// ``` /// use valuable::Fields; /// /// let fields = Fields::Unnamed(2); /// assert!(!fields.is_named()); /// ``` pub const fn is_named(&self) -> bool { matches!(self, Fields::Named(..)) } /// Returns `true` if the fields are unnamed. /// /// # Examples /// /// Named fields /// /// ``` /// use valuable::Fields; /// /// let fields = Fields::Named(&[]); /// assert!(!fields.is_unnamed()); /// ``` /// /// Unnamed fields /// /// ``` /// use valuable::Fields; /// /// let fields = Fields::Unnamed(3); /// assert!(fields.is_unnamed()); /// ``` pub const fn is_unnamed(&self) -> bool { matches!(self, Fields::Unnamed(_)) } /// Returns the number of fields. /// /// # Examples /// /// Named fields /// /// ``` /// use valuable::{Fields, NamedField}; /// /// let fields = &[ /// NamedField::new("alice"), /// NamedField::new("bob"), /// ]; /// let fields = Fields::Named(fields); /// /// assert_eq!(fields.len(), 2); /// ``` /// /// Unnamed fields /// /// ``` /// use valuable::Fields; /// /// let fields = Fields::Unnamed(2); /// assert_eq!(fields.len(), 2); /// ``` pub const fn len(&self) -> usize { match self { Self::Named(names) => names.len(), Self::Unnamed(len) => *len, } } /// Returns `true` if this set of fields defines no fields. /// /// # Examples /// /// Named fields /// /// ``` /// use valuable::{Fields, NamedField}; /// /// let fields = &[ /// NamedField::new("alice"), /// NamedField::new("bob"), /// ]; /// let non_empty = Fields::Named(fields); /// /// let empty = Fields::Named(&[]); /// /// assert!(!non_empty.is_empty()); /// assert!(empty.is_empty()); /// ``` /// /// Unnamed fields /// /// ``` /// use valuable::Fields; /// /// let non_empty = Fields::Unnamed(2); /// let empty = Fields::Unnamed(0); /// /// assert!(!non_empty.is_empty()); /// assert!(empty.is_empty()); /// ``` pub const fn is_empty(&self) -> bool { self.len() == 0 } } impl<'a> NamedField<'a> { /// Create a new `NamedField` instance with the given name. /// /// # Examples /// /// ``` /// use valuable::NamedField; /// /// let field = NamedField::new("hello"); /// assert_eq!("hello", field.name()); /// ``` pub const fn new(name: &'a str) -> NamedField<'a> { NamedField(name) } /// Returns the field name /// /// # Examples /// /// ``` /// use valuable::NamedField; /// /// let field = NamedField::new("hello"); /// assert_eq!("hello", field.name()); /// ``` pub const fn name(&self) -> &str { self.0 } } valuable-0.1.1/src/lib.rs000064400000000000000000000100731046102023000133220ustar 00000000000000#![warn( missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub )] //! Valuable provides object-safe value inspection. Use cases include passing //! structured data to trait objects and object-safe serialization. //! //! # Getting started //! //! First, derive [`Valuable`][macro@crate::Valuable] on your types. //! //! ``` //! use valuable::Valuable; //! //! #[derive(Valuable)] //! struct HelloWorld { //! message: Message, //! } //! //! #[derive(Valuable)] //! enum Message { //! HelloWorld, //! Custom(String), //! } //! ``` //! //! Then, implement a [visitor][Visit] to inspect the data. //! //! ``` //! use valuable::{NamedValues, Value, Valuable, Visit}; //! //! struct Print; //! //! impl Visit for Print { //! fn visit_value(&mut self, value: Value<'_>) { //! match value { //! Value::Structable(v) => { //! println!("struct {}", v.definition().name()); //! v.visit(self); //! } //! Value::Enumerable(v) => { //! println!("enum {}::{}", v.definition().name(), v.variant().name()); //! v.visit(self); //! } //! Value::Listable(v) => { //! println!("list"); //! v.visit(self); //! } //! Value::Mappable(v) => { //! println!("map"); //! v.visit(self); //! } //! _ => { //! println!("value {:?}", value); //! } //! } //! } //! //! fn visit_named_fields(&mut self, named_fields: &NamedValues<'_>) { //! for (field, value) in named_fields.iter() { //! println!("named field {}", field.name()); //! value.visit(self); //! } //! } //! //! fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { //! for value in values { //! value.visit(self); //! } //! } //! //! fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { //! println!("key / value"); //! key.visit(self); //! value.visit(self); //! } //! } //! ``` //! //! Then, use the visitor to visit the value. //! //! ``` //! # use valuable::*; //! # #[derive(Valuable)] //! # struct HelloWorld { message: Message } //! # #[derive(Valuable)] //! # enum Message { HelloWorld } //! # struct Print; //! # impl Visit for Print { //! # fn visit_value(&mut self, _: Value<'_>) {} //! # } //! let hello_world = HelloWorld { message: Message::HelloWorld }; //! hello_world.visit(&mut Print); //! ``` //! //! # Related Crates //! //! - [`valuable-serde`] provides a bridge between `valuable` and the [`serde`] //! serialization ecosystem. Using [`valuable_serde::Serializable`] allows any //! type that implements [`Valuable`] to be serialized by any //! [`serde::ser::Serializer`]. //! //! [`valuable-serde`]: https://crates.io/crates/valuable-serde //! [`serde`]: https://crates.io/crates/serde //! [`valuable_serde::Serializable`]: https://docs.rs/valuable-serde/latest/valuable_serde/struct.Serializable.html //! [`serde::ser::Serializer`]: https://docs.rs/serde/latest/serde/ser/trait.Serializer.html #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg, doc_cfg_hide))] #![cfg_attr( docsrs, doc(cfg_hide( not(valuable_no_atomic_cas), not(valuable_no_atomic), not(valuable_no_atomic_64) )) )] #[cfg(feature = "alloc")] extern crate alloc; mod enumerable; pub use enumerable::{EnumDef, Enumerable, Variant, VariantDef}; mod field; pub use field::{Fields, NamedField}; mod listable; pub use listable::Listable; mod mappable; pub use mappable::Mappable; mod named_values; pub use named_values::NamedValues; mod slice; pub use slice::Slice; mod structable; pub use structable::{StructDef, Structable}; mod tuplable; pub use tuplable::{Tuplable, TupleDef}; mod valuable; pub use crate::valuable::Valuable; mod value; pub use value::Value; mod visit; pub use visit::{visit, Visit}; #[cfg(feature = "derive")] pub use valuable_derive::Valuable; valuable-0.1.1/src/listable.rs000064400000000000000000000167071046102023000143650ustar 00000000000000use crate::*; use core::fmt; /// A list-like [`Valuable`] sub-type. /// /// Implemented by [`Valuable`] types that have a list-like shape. This includes /// [`Vec`] and other Rust [collection] types. `Listable` types may or may not /// store items in contiguous memory. Any type that implements [`IntoIterator`] /// may implement `Listable`. Values that implement `Listable` must return /// [`Value::Listable`] from their [`Valuable::as_value`] implementation. /// /// [collection]: https://doc.rust-lang.org/stable/std/collections/index.html /// /// # Inspecting /// /// Inspecting `Listable` items is done by visiting the collection. When /// visiting a `Listable`, contained values are either passed one-by-one by /// repeatedly calling [`visit_value()`] or all at once by calling /// [`visit_primitive_slice()`]. The [`visit_primitive_slice()`] method has /// lower overhead but can only be used when the `Listable` type contains /// primitive values. /// /// See [`Visit`] documentation for more details. /// /// # Implementing /// /// If the type stores values in slices internally, then those slices are passed /// to [`Valuable::visit_slice`], which handles calling /// [`visit_primitive_slice()`] if possible. /// /// [`visit_value()`]: Visit::visit_value /// [`visit_primitive_slice()`]: Visit::visit_primitive_slice /// /// ``` /// use valuable::{Listable, Valuable, Value, Visit}; /// /// struct MyCollection { /// chunks: Vec>, /// } /// /// impl Valuable for MyCollection { /// fn as_value(&self) -> Value<'_> { /// Value::Listable(self) /// } /// /// fn visit(&self, visit: &mut dyn Visit) { /// for chunk in &self.chunks { /// // Handles visiting the slice /// Valuable::visit_slice(chunk, visit); /// } /// } /// } /// /// impl Listable for MyCollection { /// fn size_hint(&self) -> (usize, Option) { /// let len = self.chunks.iter().map(|chunk| chunk.len()).sum(); /// (len, Some(len)) /// } /// } /// ``` pub trait Listable: Valuable { /// Returns the bounds on the remaining length of the `Listable`. /// /// Specifically, `size_hint()` returns a tuple where the first element /// is the lower bound, and the second element is the upper bound. /// /// The second half of the tuple that is returned is an [`Option`]`<`[`usize`]`>`. /// A [`None`] here means that either there is no known upper bound, or the /// upper bound is larger than [`usize`]. /// /// # Implementation notes /// /// It is not enforced that a `Listable` implementation yields the declared /// number of elements. A buggy iterator may yield less than the lower bound /// or more than the upper bound of elements. /// /// `size_hint()` is primarily intended to be used for optimizations such as /// reserving space for the elements of the `Listable`, but must not be /// trusted to e.g., omit bounds checks in unsafe code. An incorrect /// implementation of `size_hint()` should not lead to memory safety /// violations. /// /// That said, the implementation should provide a correct estimation, /// because otherwise it would be a violation of the trait's protocol. /// /// [`usize`]: type@usize /// /// # Examples /// /// Basic usage: /// /// ``` /// use valuable::Listable; /// /// let a = vec![1, 2, 3]; /// /// assert_eq!((3, Some(3)), a.size_hint()); /// ``` fn size_hint(&self) -> (usize, Option); } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Listable for $ty { fn size_hint(&self) -> (usize, Option) { T::size_hint(&**self) } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } macro_rules! slice { ( $( $(#[$attrs:meta])* ($($generics:tt)*) $ty:ty, )* ) => { $( $(#[$attrs])* impl<$($generics)*> Valuable for $ty { fn as_value(&self) -> Value<'_> { Value::Listable(self as &dyn Listable) } fn visit(&self, visit: &mut dyn Visit) { T::visit_slice(self, visit); } } $(#[$attrs])* impl<$($generics)*> Listable for $ty { fn size_hint(&self) -> (usize, Option) { (self.len(), Some(self.len())) } } )* }; } slice! { (T: Valuable) &'_ [T], #[cfg(feature = "alloc")] (T: Valuable) alloc::boxed::Box<[T]>, #[cfg(feature = "alloc")] (T: Valuable) alloc::rc::Rc<[T]>, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] (T: Valuable) alloc::sync::Arc<[T]>, (T: Valuable, const N: usize) [T; N], #[cfg(feature = "alloc")] (T: Valuable) alloc::vec::Vec, } macro_rules! collection { ( $( $(#[$attrs:meta])* ($($generics:tt)*) $ty:ty, )* ) => { $( $(#[$attrs])* impl<$($generics)*> Valuable for $ty { fn as_value(&self) -> Value<'_> { Value::Listable(self as &dyn Listable) } fn visit(&self, visit: &mut dyn Visit) { for value in self.iter() { visit.visit_value(value.as_value()); } } } $(#[$attrs])* impl<$($generics)*> Listable for $ty { fn size_hint(&self) -> (usize, Option) { (self.len(), Some(self.len())) } } )* }; } collection! { #[cfg(feature = "alloc")] (T: Valuable) alloc::collections::LinkedList, #[cfg(feature = "alloc")] (T: Valuable + Ord) alloc::collections::BinaryHeap, #[cfg(feature = "alloc")] (T: Valuable + Ord) alloc::collections::BTreeSet, #[cfg(feature = "std")] (T: Valuable + Eq + std::hash::Hash, H: std::hash::BuildHasher) std::collections::HashSet, } #[cfg(feature = "alloc")] impl Valuable for alloc::collections::VecDeque { fn as_value(&self) -> Value<'_> { Value::Listable(self as &dyn Listable) } fn visit(&self, visit: &mut dyn Visit) { let (first, second) = self.as_slices(); T::visit_slice(first, visit); T::visit_slice(second, visit); } } #[cfg(feature = "alloc")] impl Listable for alloc::collections::VecDeque { fn size_hint(&self) -> (usize, Option) { (self.len(), Some(self.len())) } } impl fmt::Debug for dyn Listable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { struct DebugListable<'a, 'b> { fmt: fmt::DebugList<'a, 'b>, } impl Visit for DebugListable<'_, '_> { fn visit_value(&mut self, value: Value<'_>) { self.fmt.entry(&value); } } let mut debug = DebugListable { fmt: fmt.debug_list(), }; self.visit(&mut debug); debug.fmt.finish() } } valuable-0.1.1/src/mappable.rs000064400000000000000000000127131046102023000143400ustar 00000000000000use crate::*; use core::fmt; /// A map-like [`Valuable`] sub-type. /// /// Implemented by [`Valuable`] types that have a map-like shape. This includes /// [`HashMap`] and other Rust [collection] types. Values that implement /// `Mappable` must return [`Value::Mappable`] from their [`Value::as_value`] /// implementation. /// /// [collection]: https://doc.rust-lang.org/stable/std/collections/index.html /// /// # Inspecting /// /// Inspecting `Mappable` entries is done by visiting the value. When visiting a /// `Mappable`, contained entries are passed one-by-one to the visitor by /// repeatedly calling [`visit_entry()`]. /// /// See [`Visit`] documentation for more details. /// /// [`visit_entry()`]: Visit::visit_entry /// [`HashMap`]: std::collections::HashMap /// /// # Implementing /// /// Implementing `Mappable` for a custom map type. The map is represented using /// a `Vec` of key/value pairs. /// /// ``` /// use valuable::{Mappable, Valuable, Value, Visit}; /// /// struct MyMap { /// entries: Vec<(K, V)>, /// } /// /// impl Valuable for MyMap { /// fn as_value(&self) -> Value<'_> { /// Value::Mappable(self) /// } /// /// fn visit(&self, visit: &mut dyn Visit) { /// for (k, v) in &self.entries { /// visit.visit_entry(k.as_value(), v.as_value()); /// } /// } /// } /// /// impl Mappable for MyMap { /// fn size_hint(&self) -> (usize, Option) { /// let len = self.entries.len(); /// (len, Some(len)) /// } /// } /// ``` pub trait Mappable: Valuable { /// Returns the bounds on the remaining length of the `Mappable`. /// /// Specifically, `size_hint()` returns a tuple where the first element is /// the lower bound, and the second element is the upper bound. /// /// The second half of the tuple that is returned is an /// [`Option`]`<`[`usize`]`>`. A [`None`] here means that either there is no /// known upper bound, or the upper bound is larger than [`usize`]. /// /// # Implementation notes /// /// It is not enforced that a `Mappable` implementation yields the declared /// number of elements. A buggy implementation may yield less than the lower /// bound or more than the upper bound of elements. /// /// `size_hint()` is primarily intended to be used for optimizations such as /// reserving space for the elements of the `Mappable`, but must not be /// trusted to e.g., omit bounds checks in unsafe code. An incorrect /// implementation of `size_hint()` should not lead to memory safety /// violations. /// /// That said, the implementation should provide a correct estimation, /// because otherwise it would be a violation of the trait's protocol. /// /// [`usize`]: type@usize /// /// # Examples /// /// Basic usage: /// /// ``` /// use valuable::Mappable; /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); /// map.insert("one", 1); /// map.insert("two", 2); /// map.insert("three", 3); /// /// assert_eq!((3, Some(3)), map.size_hint()); /// ``` fn size_hint(&self) -> (usize, Option); } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Mappable for $ty { fn size_hint(&self) -> (usize, Option) { T::size_hint(&**self) } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } #[cfg(feature = "std")] impl Valuable for std::collections::HashMap { fn as_value(&self) -> Value<'_> { Value::Mappable(self) } fn visit(&self, visit: &mut dyn Visit) { for (key, value) in self.iter() { visit.visit_entry(key.as_value(), value.as_value()); } } } #[cfg(feature = "std")] impl Mappable for std::collections::HashMap { fn size_hint(&self) -> (usize, Option) { self.iter().size_hint() } } #[cfg(feature = "alloc")] impl Valuable for alloc::collections::BTreeMap { fn as_value(&self) -> Value<'_> { Value::Mappable(self) } fn visit(&self, visit: &mut dyn Visit) { for (key, value) in self.iter() { visit.visit_entry(key.as_value(), value.as_value()); } } } #[cfg(feature = "alloc")] impl Mappable for alloc::collections::BTreeMap { fn size_hint(&self) -> (usize, Option) { self.iter().size_hint() } } impl fmt::Debug for dyn Mappable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { struct DebugMappable<'a, 'b> { fmt: fmt::DebugMap<'a, 'b>, } impl Visit for DebugMappable<'_, '_> { fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { self.fmt.entry(&key, &value); } fn visit_value(&mut self, _: Value<'_>) {} } let mut debug = DebugMappable { fmt: fmt.debug_map(), }; self.visit(&mut debug); debug.fmt.finish() } } valuable-0.1.1/src/named_values.rs000064400000000000000000000126661046102023000152310ustar 00000000000000use core::iter::{self, FusedIterator}; use crate::field::*; use crate::*; /// Set of values from a `Structable` or `Enumerable` with named fields. #[derive(Debug)] pub struct NamedValues<'a> { fields: &'a [NamedField<'a>], values: &'a [Value<'a>], } impl<'a> NamedValues<'a> { /// Create a new `NamedValues` instance. /// /// Both `fields` and `values` must be the same length. /// /// # Panics /// /// The method panics if `fields` and `values` are different lengths. /// /// # Examples /// /// ``` /// use valuable::{NamedField, NamedValues, Value}; /// /// let fields = [ /// NamedField::new("foo"), /// NamedField::new("bar") /// ]; /// let values = [ /// Value::U32(123), /// Value::U32(456), /// ]; /// /// let named_values = NamedValues::new(&fields, &values); /// /// assert_eq!( /// named_values.get(&fields[0]).unwrap().as_u32(), /// Some(123)); /// ``` pub fn new(fields: &'a [NamedField<'a>], values: &'a [Value<'a>]) -> NamedValues<'a> { assert!( fields.len() == values.len(), "`fields` and `values` must be the same length" ); NamedValues { fields, values } } /// Get a value using a `NamedField` reference. /// /// # Examples /// /// ``` /// use valuable::{NamedField, NamedValues, Value}; /// /// let fields = [ /// NamedField::new("foo"), /// NamedField::new("bar") /// ]; /// let values = [ /// Value::U32(123), /// Value::U32(456), /// ]; /// /// let named_values = NamedValues::new(&fields, &values); /// /// assert_eq!( /// named_values.get(&fields[0]).unwrap().as_u32(), /// Some(123)); /// ``` pub fn get(&self, field: &NamedField<'_>) -> Option<&Value<'_>> { use core::mem; let idx = (field as *const _ as usize - &self.fields[0] as *const _ as usize) / mem::size_of::>(); self.values.get(idx) } /// Get a value using string. /// /// # Examples /// /// ``` /// use valuable::{NamedField, NamedValues, Value}; /// /// let fields = [ /// NamedField::new("foo"), /// NamedField::new("bar") /// ]; /// let values = [ /// Value::U32(123), /// Value::U32(456), /// ]; /// /// let named_values = NamedValues::new(&fields, &values); /// /// assert_eq!( /// named_values.get_by_name("foo").unwrap().as_u32(), /// Some(123)); /// ``` pub fn get_by_name(&self, name: impl AsRef) -> Option<&Value<'_>> { let name = name.as_ref(); for (index, field) in self.fields.iter().enumerate() { if field.name() == name { return Some(&self.values[index]); } } None } /// Iterate all name-value pairs. /// /// # Examples /// /// ``` /// use valuable::{NamedField, NamedValues, Value}; /// /// let fields = [ /// NamedField::new("foo"), /// NamedField::new("bar") /// ]; /// let values = [ /// Value::U32(123), /// Value::U32(456), /// ]; /// /// let named_values = NamedValues::new(&fields, &values); /// /// for (field, value) in named_values.iter() { /// println!("{:?}: {:?}", field, value); /// } /// ``` pub fn iter<'b>(&'b self) -> Iter<'a, 'b> { Iter { iter: self.fields.iter().enumerate(), values: self.values, } } /// Returns the length of fields. pub fn len(&self) -> usize { self.fields.len() } /// Returns `true` if fields have a length of 0. pub fn is_empty(&self) -> bool { self.fields.is_empty() } } impl<'a, 'b> IntoIterator for &'b NamedValues<'a> { type Item = (&'b NamedField<'a>, &'b Value<'a>); type IntoIter = Iter<'a, 'b>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// An iterator of name-value pairs contained by [`NamedValues`]. /// /// Instances are created by the [`iter()`][NamedValues::iter] method on /// [`NamedValues`]. See its documentation for more. /// /// # Examples /// /// ``` /// use valuable::{NamedField, NamedValues, Value}; /// /// let fields = [ /// NamedField::new("foo"), /// NamedField::new("bar") /// ]; /// let values = [ /// Value::U32(123), /// Value::U32(456), /// ]; /// /// let named_values = NamedValues::new(&fields, &values); /// /// for (field, value) in named_values.iter() { /// println!("{:?}: {:?}", field, value); /// } /// ``` #[derive(Debug)] pub struct Iter<'a, 'b> { iter: iter::Enumerate>>, values: &'a [Value<'a>], } impl<'a, 'b> Iterator for Iter<'a, 'b> { type Item = (&'b NamedField<'a>, &'b Value<'a>); fn next(&mut self) -> Option { self.iter .next() .map(move |(i, field)| (field, &self.values[i])) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl DoubleEndedIterator for Iter<'_, '_> { fn next_back(&mut self) -> Option { self.iter .next_back() .map(move |(i, field)| (field, &self.values[i])) } } impl ExactSizeIterator for Iter<'_, '_> { fn len(&self) -> usize { self.iter.len() } } impl FusedIterator for Iter<'_, '_> {} valuable-0.1.1/src/slice.rs000064400000000000000000000233331046102023000136560ustar 00000000000000use crate::*; use core::fmt; use core::iter::FusedIterator; macro_rules! slice { ( $( $(#[$attrs:meta])* $variant:ident($ty:ty), )* ) => { /// A slice containing primitive values. /// /// The `Slice` enum is used to pass multiple primitive-values to the /// [visitor][`Visit`]. This is used as an optimization when visiting /// [`Listable`] types to avoid a dynamic dispatch call to [`Visit`] for /// each element in the collection. /// /// `Slice` instances are usually not created explicitly. Instead, they /// are created when calling [`Valuable::visit_slice()`]. #[non_exhaustive] pub enum Slice<'a> { $( $(#[$attrs])* $variant(&'a [$ty]), )* } /// [`Slice`] iterator /// /// Instances are created by the [`iter()`][Slice::iter] method on /// [`Slice`]. See its documentation for more. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); /// /// for value in slice.iter() { /// println!("{:?}", value); /// } /// ``` #[derive(Debug)] pub struct Iter<'a>(IterKind<'a>); #[derive(Debug)] enum IterKind<'a> { $( $(#[$attrs])* $variant(core::slice::Iter<'a, $ty>), )* } impl<'a> Slice<'a> { /// Returns the number of elements in the slice /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); /// assert_eq!(5, slice.len()); /// ``` pub fn len(&self) -> usize { #[allow(unused_doc_comments)] match self { $( $(#[$attrs])* Slice::$variant(s) => s.len(), )* } } /// Returns `true` if the slice is not empty. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); /// assert!(!slice.is_empty()); /// ``` /// ``` /// # use valuable::Slice; /// let slice = Slice::U32(&[]); /// assert!(slice.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns an iterator over the slice. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); /// /// for value in slice.iter() { /// println!("{:?}", value); /// } /// ``` pub fn iter(&self) -> Iter<'a> { self.into_iter() } } impl<'a> IntoIterator for Slice<'a> { type Item = Value<'a>; type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { (&self).into_iter() } } impl<'a> IntoIterator for &'_ Slice<'a> { type Item = Value<'a>; type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { #[allow(unused_doc_comments)] Iter(match self { $( $(#[$attrs])* Slice::$variant(s) => IterKind::$variant(s.iter()), )* }) } } impl fmt::Debug for Slice<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { use Slice::*; let mut d = fmt.debug_list(); #[allow(unused_doc_comments)] match *self { $( $(#[$attrs])* $variant(v) => d.entries(v), )* }; d.finish() } } impl<'a> Iterator for Iter<'a> { type Item = Value<'a>; fn size_hint(&self) -> (usize, Option) { use IterKind::*; #[allow(unused_doc_comments)] match &self.0 { $( $(#[$attrs])* $variant(v) => v.size_hint(), )* } } fn next(&mut self) -> Option> { use IterKind::*; #[allow(unused_doc_comments)] match &mut self.0 { $( $(#[$attrs])* $variant(v) => v.next().map(Valuable::as_value), )* } } } impl DoubleEndedIterator for Iter<'_> { fn next_back(&mut self) -> Option { use IterKind::*; #[allow(unused_doc_comments)] match &mut self.0 { $( $(#[$attrs])* $variant(v) => v.next_back().map(Valuable::as_value), )* } } } impl ExactSizeIterator for Iter<'_> { fn len(&self) -> usize { use IterKind::*; #[allow(unused_doc_comments)] match &self.0 { $( $(#[$attrs])* $variant(v) => v.len(), )* } } } impl FusedIterator for Iter<'_> {} } } slice! { /// A slice containing `bool` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::Bool(&[true, true, false]); /// ``` Bool(bool), /// A slice containing `char` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::Char(&['a', 'b', 'c']); /// ``` Char(char), /// A slice containing `f32` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::F32(&[3.1415, 2.71828]); /// ``` F32(f32), /// A slice containing `f64` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::F64(&[3.1415, 2.71828]); /// ``` F64(f64), /// A slice containing `i8` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::I8(&[1, 1, 2, 3, 5]); /// ``` I8(i8), /// A slice containing `i16` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::I16(&[1, 1, 2, 3, 5]); /// ``` I16(i16), /// A slice containing `I32` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::I32(&[1, 1, 2, 3, 5]); /// ``` I32(i32), /// A slice containing `I64` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::I64(&[1, 1, 2, 3, 5]); /// ``` I64(i64), /// A slice containing `I128` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::I128(&[1, 1, 2, 3, 5]); /// ``` I128(i128), /// A slice containing `isize` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::Isize(&[1, 1, 2, 3, 5]); /// ``` Isize(isize), /// A slice containing `str` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::Str(&["foo", "bar", "baz"]); /// ``` Str(&'a str), /// A slice containing `String` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::String(&["foo".to_string(), "bar".to_string()]); /// ``` #[cfg(feature = "alloc")] String(alloc::string::String), /// A slice containing `u8` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::U8(&[1, 1, 2, 3, 5]); /// ``` U8(u8), /// A slice containing `u16` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::U16(&[1, 1, 2, 3, 5]); /// ``` U16(u16), /// A slice containing `u32` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::U32(&[1, 1, 2, 3, 5]); /// ``` U32(u32), /// A slice containing `u64` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::U64(&[1, 1, 2, 3, 5]); /// ``` U64(u64), /// A slice containing `u128` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::U128(&[1, 1, 2, 3, 5]); /// ``` U128(u128), /// A slice containing `usize` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::Usize(&[1, 1, 2, 3, 5]); /// ``` Usize(usize), /// A slice containing `()` values. /// /// # Examples /// /// ``` /// use valuable::Slice; /// /// let v = Slice::Unit(&[(), (), ()]); /// ``` Unit(()), } valuable-0.1.1/src/structable.rs000064400000000000000000000332661046102023000147350ustar 00000000000000use crate::field::*; use crate::*; use core::fmt; /// A struct-like [`Valuable`] sub-type. /// /// Implemented by [`Valuable`] types that have a struct-like shape. Fields may /// be named or unnamed (tuple). Values that implement `Structable` must return /// [`Value::Structable`] from their [`Valuable::as_value`] implementation. /// /// # Inspecting /// /// Inspecting fields contained by a `Structable` instance is done by visiting /// the struct. When visiting a `Structable`, either the `visit_named_fields()` /// or the `visit_unnamed_fields()` methods of `Visit` are called. Each method /// may be called multiple times per `Structable`, but the two methods are never /// mixed. /// /// ``` /// use valuable::{NamedValues, Valuable, Value, Visit}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: u32, /// bar: u32, /// } /// /// struct PrintFields; /// /// impl Visit for PrintFields { /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { /// for (field, value) in named_values.iter() { /// println!("{}: {:?}", field.name(), value); /// } /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Structable(v) => v.visit(self), /// _ => {} // do nothing for other types /// } /// } /// } /// /// let my_struct = MyStruct { /// foo: 123, /// bar: 456, /// }; /// /// valuable::visit(&my_struct, &mut PrintFields); /// ``` /// /// If the struct is **statically** defined, then all fields are known ahead of /// time and may be accessed via the [`StructDef`] instance returned by /// [`definition()`]. [`NamedField`] instances returned by [`definition()`] /// maybe used to efficiently extract specific field values. /// /// # Implementing /// /// Implementing `Structable` is usually done by adding `#[derive(Valuable)]` to /// a Rust `struct` definition. /// /// ``` /// use valuable::{Fields, Valuable, Structable, StructDef}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: &'static str, /// } /// /// let my_struct = MyStruct { foo: "Hello" }; /// let fields = match my_struct.definition() { /// StructDef::Static { name, fields, .. } => { /// assert_eq!("MyStruct", name); /// fields /// } /// _ => unreachable!(), /// }; /// /// match fields { /// Fields::Named(named_fields) => { /// assert_eq!(1, named_fields.len()); /// assert_eq!("foo", named_fields[0].name()); /// } /// _ => unreachable!(), /// } /// ``` /// /// [`definition()`]: Structable::definition() pub trait Structable: Valuable { /// Returns the struct's definition. /// /// See [`StructDef`] documentation for more details. /// /// # Examples /// /// ``` /// use valuable::{Structable, Valuable}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: u32, /// } /// /// let my_struct = MyStruct { /// foo: 123, /// }; /// /// assert_eq!("MyStruct", my_struct.definition().name()); fn definition(&self) -> StructDef<'_>; } /// A struct's name, fields, and other struct-level information. /// /// Returned by [`Structable::definition()`], `StructDef` provides the caller /// with information about the struct's definition. /// /// [`Structable::definition()`]: Structable::definition #[derive(Debug)] #[non_exhaustive] pub enum StructDef<'a> { /// The struct is statically-defined, all fields are known ahead of time. /// /// Most `Structable` definitions for Rust struct types will be /// `StructDef::Static`. /// /// # Examples /// /// A statically defined struct /// /// ``` /// use valuable::{Fields, Valuable, Structable, StructDef}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: &'static str, /// } /// /// let my_struct = MyStruct { foo: "Hello" }; /// let fields = match my_struct.definition() { /// StructDef::Static { name, fields, ..} => { /// assert_eq!("MyStruct", name); /// fields /// } /// _ => unreachable!(), /// }; /// /// match fields { /// Fields::Named(named_fields) => { /// assert_eq!(1, named_fields.len()); /// assert_eq!("foo", named_fields[0].name()); /// } /// _ => unreachable!(), /// } /// ``` #[non_exhaustive] Static { /// The struct's name. name: &'static str, /// The struct's fields. fields: Fields<'static>, }, /// The struct is dynamically-defined, not all fields are known ahead of /// time. /// /// A dynamically-defined struct **could** be represented using /// [`Mappable`], though, using `Structable` offers benefits in a couple of /// cases. For example, when serializing a `Value`, some formats will /// serialize maps and structs differently. In this case, differentiating /// the two is required. There also are times when **some** struct fields /// are known statically, but not all of them (see second example). /// /// # Examples /// /// The struct stores field values in a `HashMap`. /// /// ``` /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit}; /// use std::collections::HashMap; /// /// /// A dynamic struct /// struct Dyn { /// // The struct name /// name: String, /// /// // Named values. /// values: HashMap>, /// } /// /// impl Valuable for Dyn { /// fn as_value(&self) -> Value<'_> { /// Value::Structable(self) /// } /// /// fn visit(&self, visit: &mut dyn Visit) { /// // This could be optimized to batch some. /// for (field, value) in self.values.iter() { /// visit.visit_named_fields(&NamedValues::new( /// &[NamedField::new(field)], /// &[value.as_value()], /// )); /// } /// } /// } /// /// impl Structable for Dyn { /// fn definition(&self) -> StructDef<'_> { /// StructDef::new_dynamic(&self.name, Fields::Named(&[])) /// } /// } /// ``` /// /// Some fields are known statically. /// /// ``` /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit}; /// use std::collections::HashMap; /// /// struct HalfStatic { /// foo: u32, /// bar: u32, /// extra_values: HashMap>, /// } /// /// impl Valuable for HalfStatic { /// fn as_value(&self) -> Value<'_> { /// Value::Structable(self) /// } /// /// fn visit(&self, visit: &mut dyn Visit) { /// // First, visit static fields /// visit.visit_named_fields(&NamedValues::new( /// FIELDS, /// &[self.foo.as_value(), self.bar.as_value()], /// )); /// /// // This could be optimized to batch some. /// for (field, value) in self.extra_values.iter() { /// visit.visit_named_fields(&NamedValues::new( /// &[NamedField::new(field)], /// &[value.as_value()], /// )); /// } /// } /// } /// /// static FIELDS: &[NamedField<'static>] = &[ /// NamedField::new("foo"), /// NamedField::new("bar"), /// ]; /// /// impl Structable for HalfStatic { /// fn definition(&self) -> StructDef<'_> { /// // Include known fields. /// StructDef::new_dynamic( /// "HalfStatic", /// Fields::Named(FIELDS)) /// } /// } /// ``` #[non_exhaustive] Dynamic { /// The struct's name name: &'a str, /// The struct's fields. fields: Fields<'a>, }, } impl fmt::Debug for dyn Structable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let def = self.definition(); if def.fields().is_named() { struct DebugStruct<'a, 'b> { fmt: fmt::DebugStruct<'a, 'b>, } let mut debug = DebugStruct { fmt: fmt.debug_struct(def.name()), }; impl Visit for DebugStruct<'_, '_> { fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { for (field, value) in named_values { self.fmt.field(field.name(), value); } } fn visit_value(&mut self, _: Value<'_>) { unreachable!() } } self.visit(&mut debug); debug.fmt.finish() } else { struct DebugStruct<'a, 'b> { fmt: fmt::DebugTuple<'a, 'b>, } let mut debug = DebugStruct { fmt: fmt.debug_tuple(def.name()), }; impl Visit for DebugStruct<'_, '_> { fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { for value in values { self.fmt.field(value); } } fn visit_value(&mut self, _: Value<'_>) { unreachable!(); } } self.visit(&mut debug); debug.fmt.finish() } } } impl<'a> StructDef<'a> { /// Create a new [`StructDef::Static`] instance. /// /// This should be used when a struct's fields are fixed and known ahead of time. /// /// # Examples /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(2)); /// ``` pub const fn new_static(name: &'static str, fields: Fields<'static>) -> StructDef<'a> { StructDef::Static { name, fields } } /// Create a new [`StructDef::Dynamic`] instance. /// /// This is used when the struct's fields may vary at runtime. /// /// # Examples /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(3)); /// ``` pub const fn new_dynamic(name: &'a str, fields: Fields<'a>) -> StructDef<'a> { StructDef::Dynamic { name, fields } } /// Returns the struct's name /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(1)); /// assert_eq!("Foo", def.name()); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(2)); /// assert_eq!("Foo", def.name()); /// ``` pub const fn name(&self) -> &'a str { match self { StructDef::Static { name, .. } => name, StructDef::Dynamic { name, .. } => name, } } /// Returns the struct's fields /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(3)); /// assert!(matches!(def.fields(), Fields::Unnamed(_))); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1)); /// assert!(matches!(def.fields(), Fields::Unnamed(_))); /// ``` pub const fn fields(&self) -> &Fields<'a> { match self { StructDef::Static { fields, .. } => fields, StructDef::Dynamic { fields, .. } => fields, } } /// Returns `true` if the struct is [statically defined](StructDef::Static). /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(2)); /// assert!(def.is_static()); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(4)); /// assert!(!def.is_static()); /// ``` pub const fn is_static(&self) -> bool { matches!(self, StructDef::Static { .. }) } /// Returns `true` if the struct is [dynamically defined](StructDef::Dynamic). /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(1)); /// assert!(!def.is_dynamic()); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1)); /// assert!(def.is_dynamic()); /// ``` pub const fn is_dynamic(&self) -> bool { matches!(self, StructDef::Dynamic { .. }) } } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Structable for $ty { fn definition(&self) -> StructDef<'_> { T::definition(&**self) } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } valuable-0.1.1/src/tuplable.rs000064400000000000000000000222151046102023000143650ustar 00000000000000use crate::{Valuable, Value, Visit}; use core::fmt; /// A tuple-like [`Valuable`] sub-type. /// /// Implemented by [`Valuable`] types that have a tuple-like shape. Fields are /// always unnamed. Values that implement `Tuplable` must return /// [`Value::Tuplable`] from their [`Valuable::as_value`] implementation. /// /// It is uncommon for users to implement this type as the crate provides /// implementations of `Tuplable` for Rust tuples. /// /// # Inspecting /// /// Inspecting fields contained by a `Tuplable` instance is done by visiting the /// tuple. When visiting a `Tuple`, the `visit_unnamed_fields()` method is /// called. When the tuple is statically defined, `visit_unnamed_fields()` is /// called once with the values of all the fields. A dynamic tuple /// implementation may call `visit_unnamed_fields()` multiple times. pub trait Tuplable: Valuable { /// Returns the tuple's definition. /// /// See [`TupleDef`] documentation for more details. /// /// # Examples /// /// ``` /// use valuable::{Tuplable, TupleDef}; /// /// let tuple = (123, "hello"); /// /// if let TupleDef::Static { fields, .. } = tuple.definition() { /// assert_eq!(2, fields); /// } /// ``` fn definition(&self) -> TupleDef; } /// The number of fields and other tuple-level information. /// /// Returned by [`Tuplable::definition()`], `TupleDef` provides the caller with /// information about the tuple's definition. /// /// This includes the number of fields contained by the tuple. #[derive(Debug)] #[non_exhaustive] pub enum TupleDef { /// The tuple is statically-defined, all fields are known ahead of time. /// /// Static tuple implementations are provided by the crate. /// /// # Examples /// /// A statically defined tuple. /// /// ``` /// use valuable::{Tuplable, TupleDef}; /// /// let tuple = (123, "hello"); /// /// match tuple.definition() { /// TupleDef::Static { fields, .. } => { /// assert_eq!(2, fields); /// } /// _ => unreachable!(), /// }; /// ``` #[non_exhaustive] Static { /// The number of fields contained by the tuple. fields: usize, }, /// The tuple is dynamically-defined, not all fields are known ahead of /// time. /// /// # Examples /// /// ``` /// use valuable::{Tuplable, TupleDef, Valuable, Value, Visit}; /// /// struct MyTuple; /// /// impl Valuable for MyTuple { /// fn as_value(&self) -> Value<'_> { /// Value::Tuplable(self) /// } /// /// fn visit(&self, visit: &mut dyn Visit) { /// visit.visit_unnamed_fields(&[Value::I32(123)]); /// visit.visit_unnamed_fields(&[Value::String("hello world")]); /// } /// } /// /// impl Tuplable for MyTuple { /// fn definition(&self) -> TupleDef { /// TupleDef::new_dynamic((1, Some(3))) /// } /// } /// ``` #[non_exhaustive] Dynamic { /// Returns the bounds on the number of tuple fields. /// /// Specifically, the first element is the lower bound, and the second /// element is the upper bound. fields: (usize, Option), }, } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Tuplable for $ty { fn definition(&self) -> TupleDef { T::definition(&**self) } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } impl Tuplable for () { fn definition(&self) -> TupleDef { TupleDef::Static { fields: 0 } } } macro_rules! tuple_impls { ( $( $len:expr => ( $($n:tt $name:ident)+ ) )+ ) => { $( impl<$($name),+> Valuable for ($($name,)+) where $($name: Valuable,)+ { fn as_value(&self) -> Value<'_> { Value::Tuplable(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_unnamed_fields(&[ $( self.$n.as_value(), )+ ]); } } impl<$($name),+> Tuplable for ($($name,)+) where $($name: Valuable,)+ { fn definition(&self) -> TupleDef { TupleDef::Static { fields: $len } } } )+ } } tuple_impls! { 1 => (0 T0) 2 => (0 T0 1 T1) 3 => (0 T0 1 T1 2 T2) 4 => (0 T0 1 T1 2 T2 3 T3) 5 => (0 T0 1 T1 2 T2 3 T3 4 T4) 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) 12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11) 13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12) 14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13) 15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14) 16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15) } impl fmt::Debug for dyn Tuplable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { if self.definition().is_unit() { ().fmt(fmt) } else { struct DebugTuple<'a, 'b> { fmt: fmt::DebugTuple<'a, 'b>, } impl Visit for DebugTuple<'_, '_> { fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { for value in values { self.fmt.field(value); } } fn visit_value(&mut self, _: Value<'_>) { unimplemented!() } } let mut debug = DebugTuple { fmt: fmt.debug_tuple(""), }; self.visit(&mut debug); debug.fmt.finish() } } } impl TupleDef { /// Create a new [`TupleDef::Static`] instance /// /// This should be used when the tuple's fields are fixed and known ahead of time. /// /// # Examples /// /// ``` /// use valuable::TupleDef; /// /// let def = TupleDef::new_static(2); /// ``` pub const fn new_static(fields: usize) -> TupleDef { TupleDef::Static { fields } } /// Create a new [`TupleDef::Dynamic`] instance. /// /// This is used when the tuple's fields may vary at runtime. /// /// # Examples /// /// ``` /// use valuable::TupleDef; /// /// let def = TupleDef::new_dynamic((2, Some(10))); /// ``` pub const fn new_dynamic(fields: (usize, Option)) -> TupleDef { TupleDef::Dynamic { fields } } /// Returns `true` if `self` represents the [unit][primitive@unit] tuple. /// /// # Examples /// /// With the unit tuple /// /// ``` /// use valuable::Tuplable; /// /// let tuple: &dyn Tuplable = &(); /// assert!(tuple.definition().is_unit()); /// ``` /// /// When not the unit tuple. /// /// ``` /// use valuable::Tuplable; /// /// let tuple: &dyn Tuplable = &(123,456); /// assert!(!tuple.definition().is_unit()); /// ``` pub fn is_unit(&self) -> bool { match *self { TupleDef::Static { fields } => fields == 0, TupleDef::Dynamic { fields } => fields == (0, Some(0)), } } /// Returns `true` if the tuple is [statically defined](TupleDef::Static). /// /// # Examples /// /// With a static tuple /// /// ``` /// use valuable::TupleDef; /// /// let def = TupleDef::new_static(2); /// assert!(def.is_static()); /// ``` /// /// With a dynamic tuple /// /// ``` /// use valuable::TupleDef; /// /// let def = TupleDef::new_dynamic((2, None)); /// assert!(!def.is_static()); /// ``` pub fn is_static(&self) -> bool { matches!(self, TupleDef::Static { .. }) } /// Returns `true` if the tuple is [dynamically defined](TupleDef::Dynamic). /// /// # Examples /// /// With a static tuple /// /// ``` /// use valuable::TupleDef; /// /// let def = TupleDef::new_static(2); /// assert!(!def.is_dynamic()); /// ``` /// /// With a dynamic tuple /// /// ``` /// use valuable::TupleDef; /// /// let def = TupleDef::new_dynamic((2, None)); /// assert!(def.is_dynamic()); /// ``` pub fn is_dynamic(&self) -> bool { matches!(self, TupleDef::Dynamic { .. }) } } valuable-0.1.1/src/valuable.rs000064400000000000000000000203031046102023000143440ustar 00000000000000use crate::{Slice, Value, Visit}; use core::fmt; use core::num::Wrapping; /// A type that can be converted to a [`Value`]. /// /// `Valuable` types are inspected by defining a [`Visit`] implementation and /// using it when calling [`Valuable::visit`]. See [`Visit`] documentation for /// more details. /// /// The `Valuable` procedural macro makes implementing `Valuable` easy. Users /// can add add [`#[derive(Valuable)]`][macro] to their types. /// /// `Valuable` provides implementations for many Rust primitives and standard /// library types. /// /// Types implementing `Valuable` may also implement one of the more specific /// traits: [`Structable`], [`Enumerable`], [`Listable`], and [`Mappable`]. These traits /// should be implemented when the type is a nested container of other `Valuable` types. /// /// [`Value`]: Value /// [`Visit`]: Visit /// [`Valuable::visit`]: Valuable::visit /// [`Structable`]: crate::Structable /// [`Enumerable`]: crate::Enumerable /// [`Listable`]: crate::Listable /// [`Mappable`]: crate::Mappable /// [macro]: macro@crate::Valuable pub trait Valuable { /// Converts self into a [`Value`] instance. /// /// # Examples /// /// ``` /// use valuable::Valuable; /// /// let _ = "hello".as_value(); /// ``` fn as_value(&self) -> Value<'_>; /// Calls the relevant method on [`Visit`] to extract data from `self`. /// /// This method is used to extract type-specific data from the value and is /// intended to be an implementation detail. For example, `Vec` implements /// `visit` by calling [`visit_value()`] on each of its elements. Structs /// implement `visit` by calling [`visit_named_fields()`] or /// [`visit_unnamed_fields()`]. /// /// Usually, users will call the [`visit`] function instead. /// /// [`Visit`]: Visit /// [`visit`]: visit() /// [`visit_value()`]: Visit::visit_value() /// [`visit_named_fields()`]: Visit::visit_named_fields() /// [`visit_unnamed_fields()`]: Visit::visit_unnamed_fields() fn visit(&self, visit: &mut dyn Visit); /// Calls [`Visit::visit_primitive_slice()`] with `self`. /// /// This method is an implementation detail used to optimize visiting /// primitive slices. /// /// [`Visit::visit_primitive_slice()`]: Visit::visit_primitive_slice fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { for item in slice { visit.visit_value(item.as_value()); } } } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Valuable for $ty { fn as_value(&self) -> Value<'_> { T::as_value(&**self) } fn visit(&self, visit: &mut dyn Visit) { T::visit(&**self, visit); } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } macro_rules! valuable { ( $( $variant:ident($ty:ty), )* ) => { $( impl Valuable for $ty { fn as_value(&self) -> Value<'_> { Value::$variant(*self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { visit.visit_primitive_slice(Slice::$variant(slice)); } } )* }; } valuable! { Bool(bool), Char(char), F32(f32), F64(f64), I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), Isize(isize), U8(u8), U16(u16), U32(u32), U64(u64), U128(u128), Usize(usize), } macro_rules! nonzero { ( $( $variant:ident($ty:ident), )* ) => { $( impl Valuable for core::num::$ty { fn as_value(&self) -> Value<'_> { Value::$variant(self.get()) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } )* }; } nonzero! { I8(NonZeroI8), I16(NonZeroI16), I32(NonZeroI32), I64(NonZeroI64), I128(NonZeroI128), Isize(NonZeroIsize), U8(NonZeroU8), U16(NonZeroU16), U32(NonZeroU32), U64(NonZeroU64), U128(NonZeroU128), Usize(NonZeroUsize), } #[cfg(not(valuable_no_atomic))] macro_rules! atomic { ( $( $(#[$attrs:meta])* $variant:ident($ty:ident), )* ) => { $( $(#[$attrs])* impl Valuable for core::sync::atomic::$ty { fn as_value(&self) -> Value<'_> { // Use SeqCst to match Debug and serde which use SeqCst. // https://github.com/rust-lang/rust/blob/1.52.1/library/core/src/sync/atomic.rs#L1361-L1366 // https://github.com/serde-rs/serde/issues/1496 Value::$variant(self.load(core::sync::atomic::Ordering::SeqCst)) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } )* }; } #[cfg(not(valuable_no_atomic))] atomic! { Bool(AtomicBool), I8(AtomicI8), I16(AtomicI16), I32(AtomicI32), #[cfg(not(valuable_no_atomic_64))] I64(AtomicI64), Isize(AtomicIsize), U8(AtomicU8), U16(AtomicU16), U32(AtomicU32), #[cfg(not(valuable_no_atomic_64))] U64(AtomicU64), Usize(AtomicUsize), } impl Valuable for Wrapping { fn as_value(&self) -> Value<'_> { self.0.as_value() } fn visit(&self, visit: &mut dyn Visit) { self.0.visit(visit); } } impl Valuable for () { fn as_value(&self) -> Value<'_> { Value::Tuplable(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_unnamed_fields(&[]); } } impl Valuable for Option { fn as_value(&self) -> Value<'_> { match self { Some(v) => v.as_value(), None => Value::Unit, } } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } impl Valuable for &'_ str { fn as_value(&self) -> Value<'_> { Value::String(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::String(self)); } fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { visit.visit_primitive_slice(Slice::Str(slice)); } } #[cfg(feature = "alloc")] impl Valuable for alloc::string::String { fn as_value(&self) -> Value<'_> { Value::String(&self[..]) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::String(self)); } fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { visit.visit_primitive_slice(Slice::String(slice)); } } #[cfg(feature = "std")] impl Valuable for &std::path::Path { fn as_value(&self) -> Value<'_> { Value::Path(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::Path(self)); } } #[cfg(feature = "std")] impl Valuable for std::path::PathBuf { fn as_value(&self) -> Value<'_> { Value::Path(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::Path(self)); } } #[cfg(feature = "std")] impl Valuable for dyn std::error::Error + 'static { fn as_value(&self) -> Value<'_> { Value::Error(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } impl fmt::Debug for dyn Valuable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let value = self.as_value(); value.fmt(fmt) } } valuable-0.1.1/src/value.rs000064400000000000000000000534261046102023000137010ustar 00000000000000use crate::{Enumerable, Listable, Mappable, Structable, Tuplable, Valuable, Visit}; use core::fmt; macro_rules! value { ( $( $(#[$attrs:meta])* $variant:ident($ty:ty), )* ) => { /// Any Rust value /// /// The `Value` enum is used to pass single values to the /// [visitor][`Visit`]. Primitive types are enumerated and other types /// are represented at trait objects. /// /// Values are converted to `Value` instances using /// [`Valuable::as_value()`]. /// /// # Examples /// /// Convert a primitive type /// /// ``` /// use valuable::{Value, Valuable}; /// /// let num = 123; /// let val = num.as_value(); /// /// assert!(matches!(val, Value::I32(v) if v == 123)); /// ``` /// /// Converting a struct /// /// ``` /// use valuable::{Value, Valuable}; /// /// #[derive(Valuable, Debug)] /// struct HelloWorld { /// message: String, /// } /// /// let hello = HelloWorld { /// message: "greetings".to_string(), /// }; /// /// let val = hello.as_value(); /// /// assert!(matches!(val, Value::Structable(_v))); /// /// // The Value `Debug` output matches the struct's /// assert_eq!( /// format!("{:?}", val), /// format!("{:?}", hello), /// ); /// ``` /// /// [visitor]: Visit #[non_exhaustive] #[derive(Clone, Copy)] pub enum Value<'a> { $( $(#[$attrs])* $variant($ty), )* /// A Rust `()` or `None` value. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::Unit; /// ``` Unit, } $( $(#[$attrs])* impl<'a> From<$ty> for Value<'a> { fn from(src: $ty) -> Value<'a> { Value::$variant(src) } } )* impl<'a> From<()> for Value<'a> { fn from(_: ()) -> Value<'a> { Value::Tuplable(&()) } } impl fmt::Debug for Value<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { use Value::*; // Doc comments are expanded into the branch arms, which results // in a warning. It isn't a big deal, so silence it. #[allow(unused_doc_comments)] match self { $( $(#[$attrs])* $variant(v) => fmt::Debug::fmt(v, fmt), )* Unit => ().fmt(fmt), } } } } } value! { /// A Rust `bool` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::Bool(true); /// ``` Bool(bool), /// A Rust `char` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::Char('h'); /// ``` Char(char), /// A Rust `f32` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::F32(3.1415); /// ``` F32(f32), /// A Rust `f64` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::F64(3.1415); /// ``` F64(f64), /// A Rust `i8` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::I8(42); /// ``` I8(i8), /// A Rust `i16` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::I16(42); /// ``` I16(i16), /// A Rust `i32` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::I32(42); /// ``` I32(i32), /// A Rust `i64` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::I64(42); /// ``` I64(i64), /// A Rust `i128` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::I128(42); /// ``` I128(i128), /// A Rust `isize` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::Isize(42); /// ``` Isize(isize), /// A Rust `&str` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::String("hello"); /// ``` String(&'a str), /// A Rust `u8` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::U8(42); /// ``` U8(u8), /// A Rust `u16` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::U16(42); /// ``` U16(u16), /// A Rust `u32` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::U32(42); /// ``` U32(u32), /// A Rust `u64` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::U64(42); /// ``` U64(u64), /// A Rust `u128` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::U128(42); /// ``` U128(u128), /// A Rust `usize` value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let v = Value::Usize(42); /// ``` Usize(usize), /// A Rust `&Path` value /// /// # Examples /// /// ``` /// use valuable::Value; /// use std::path::Path; /// /// let path = Path::new("a.txt"); /// let v = Value::Path(path); /// ``` #[cfg(feature = "std")] Path(&'a std::path::Path), /// A Rust error value /// /// # Examples /// /// ``` /// use valuable::Value; /// use std::io; /// /// let err: io::Error = io::ErrorKind::Other.into(); /// let v = Value::Error(&err); /// ``` #[cfg(feature = "std")] Error(&'a (dyn std::error::Error +'static)), /// A Rust list value /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let vals = vec![1, 2, 3, 4, 5]; /// let v = Value::Listable(&vals); /// ``` Listable(&'a dyn Listable), /// A Rust map value /// /// # Examples /// /// ``` /// use valuable::Value; /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); /// map.insert("foo", 1); /// map.insert("bar", 2); /// /// let v = Value::Mappable(&map); /// ``` Mappable(&'a dyn Mappable), /// A Rust struct value /// /// # Examples /// /// ``` /// use valuable::{Value, Valuable}; /// /// #[derive(Valuable)] /// struct MyStruct { /// field: u32, /// } /// /// let my_struct = MyStruct { /// field: 123, /// }; /// /// let v = Value::Structable(&my_struct); /// ``` Structable(&'a dyn Structable), /// A Rust enum value /// /// # Examples /// /// ``` /// use valuable::{Value, Valuable}; /// /// #[derive(Valuable)] /// enum MyEnum { /// Foo, /// Bar, /// } /// /// let my_enum = MyEnum::Foo; /// let v = Value::Enumerable(&my_enum); /// ``` Enumerable(&'a dyn Enumerable), /// A tuple value /// /// # Examples /// /// ``` /// use valuable::{Value, Valuable}; /// /// let my_tuple = (123, 456); /// let v = Value::Tuplable(&my_tuple); /// ``` Tuplable(&'a dyn Tuplable), } impl Valuable for Value<'_> { fn as_value(&self) -> Value<'_> { *self } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(*self); } } impl Default for Value<'_> { fn default() -> Self { Value::Unit } } macro_rules! convert { ( $( $(#[$attrs:meta])* $ty:ty => $as:ident, )* ) => { impl<'a> Value<'a> { /// Return a `bool` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::Bool(true).as_bool(), Some(true)); /// assert_eq!(Value::Char('c').as_bool(), None); /// ``` pub fn as_bool(&self) -> Option { match *self { Value::Bool(v) => Some(v), _ => None, } } /// Return a `char` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::Char('c').as_char(), Some('c')); /// assert_eq!(Value::Bool(true).as_char(), None); /// ``` pub fn as_char(&self) -> Option { match *self { Value::Char(v) => Some(v), _ => None, } } /// Return a `f32` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::F32(3.1415).as_f32(), Some(3.1415)); /// assert_eq!(Value::Bool(true).as_f32(), None); /// ``` pub fn as_f32(&self) -> Option { match *self { Value::F32(v) => Some(v), _ => None, } } /// Return a `f64` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::F64(3.1415).as_f64(), Some(3.1415)); /// assert_eq!(Value::Bool(true).as_f64(), None); /// ``` pub fn as_f64(&self) -> Option { match *self { Value::F64(v) => Some(v), _ => None, } } $( $(#[$attrs])* pub fn $as(&self) -> Option<$ty> { use Value::*; match *self { I8(v) => v.try_into().ok(), I16(v) => v.try_into().ok(), I32(v) => v.try_into().ok(), I64(v) => v.try_into().ok(), I128(v) => v.try_into().ok(), Isize(v) => v.try_into().ok(), U8(v) => v.try_into().ok(), U16(v) => v.try_into().ok(), U32(v) => v.try_into().ok(), U64(v) => v.try_into().ok(), U128(v) => v.try_into().ok(), Usize(v) => v.try_into().ok(), _ => None, } } )* /// Return a `&str` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::String("hello").as_str(), Some("hello")); /// assert_eq!(Value::Bool(true).as_str(), None); /// ``` pub fn as_str(&self) -> Option<&str> { match *self { Value::String(v) => Some(v), _ => None, } } /// Return a `&Path` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// use std::path::Path; /// /// let path = Path::new("a.txt"); /// /// assert!(Value::Path(path).as_path().is_some()); /// assert!(Value::Bool(true).as_path().is_none()); /// ``` #[cfg(feature = "std")] pub fn as_path(&self) -> Option<&std::path::Path> { match *self { Value::Path(v) => Some(v), _ => None, } } /// Return a `&dyn Error` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// use std::io; /// /// let err: io::Error = io::ErrorKind::Other.into(); /// /// assert!(Value::Error(&err).as_error().is_some()); /// assert!(Value::Bool(true).as_error().is_none()); /// ``` #[cfg(feature = "std")] pub fn as_error(&self) -> Option<&(dyn std::error::Error + 'static)> { match *self { Value::Error(v) => Some(v), _ => None, } } /// Return a `&dyn Listable` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let list = vec![1, 2, 3, 4]; /// /// assert!(Value::Listable(&list).as_listable().is_some()); /// assert!(Value::Bool(true).as_listable().is_none()); /// ``` pub fn as_listable(&self) -> Option<&dyn Listable> { match *self { Value::Listable(v) => Some(v), _ => None, } } /// Return a `&dyn Mappable` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); /// map.insert("foo", 123); /// map.insert("bar", 456); /// /// assert!(Value::Mappable(&map).as_mappable().is_some()); /// assert!(Value::Bool(true).as_mappable().is_none()); /// ``` pub fn as_mappable(&self) -> Option<&dyn Mappable> { match *self { Value::Mappable(v) => Some(v), _ => None, } } /// Return a `&dyn Structable` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::{Value, Valuable}; /// /// #[derive(Valuable)] /// struct Hello { /// message: &'static str, /// } /// /// let hello = Hello { message: "Hello world" }; /// /// assert!(Value::Structable(&hello).as_structable().is_some()); /// assert!(Value::Bool(true).as_structable().is_none()); /// ``` pub fn as_structable(&self) -> Option<&dyn Structable> { match *self { Value::Structable(v) => Some(v), _ => None, } } /// Return a `&dyn Enumerable` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::{Value, Valuable}; /// /// #[derive(Valuable)] /// enum Greet { /// Hello, /// World, /// } /// /// let greet = Greet::Hello; /// /// assert!(Value::Enumerable(&greet).as_enumerable().is_some()); /// assert!(Value::Bool(true).as_enumerable().is_none()); /// ``` pub fn as_enumerable(&self) -> Option<&dyn Enumerable> { match *self { Value::Enumerable(v) => Some(v), _ => None, } } /// Return a `&dyn Tuplable` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// let my_tuple = (123, 456); /// /// assert!(Value::Tuplable(&my_tuple).as_tuplable().is_some()); /// assert!(Value::Bool(true).as_tuplable().is_none()); /// ``` pub fn as_tuplable(&self) -> Option<&dyn Tuplable> { match *self { Value::Tuplable(v) => Some(v), _ => None, } } } } } convert! { /// Return a `i8` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::I8(42).as_i8(), Some(42)); /// assert_eq!(Value::I32(42).as_i8(), Some(42)); /// /// assert_eq!(Value::I64(i64::MAX).as_i8(), None); /// assert_eq!(Value::Bool(true).as_i8(), None); /// ``` i8 => as_i8, /// Return a `i16` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::I16(42).as_i16(), Some(42)); /// assert_eq!(Value::I32(42).as_i16(), Some(42)); /// /// assert_eq!(Value::I64(i64::MAX).as_i16(), None); /// assert_eq!(Value::Bool(true).as_i16(), None); /// ``` i16 => as_i16, /// Return a `i32` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::I32(42).as_i32(), Some(42)); /// assert_eq!(Value::I64(42).as_i32(), Some(42)); /// /// assert_eq!(Value::I64(i64::MAX).as_i32(), None); /// assert_eq!(Value::Bool(true).as_i32(), None); /// ``` i32 => as_i32, /// Return a `i64` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::I64(42).as_i64(), Some(42)); /// assert_eq!(Value::I128(42).as_i64(), Some(42)); /// /// assert_eq!(Value::I128(i128::MAX).as_i64(), None); /// assert_eq!(Value::Bool(true).as_i64(), None); /// ``` i64 => as_i64, /// Return a `i128` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::I128(42).as_i128(), Some(42)); /// assert_eq!(Value::U128(42).as_i128(), Some(42)); /// /// assert_eq!(Value::U128(u128::MAX).as_i128(), None); /// assert_eq!(Value::Bool(true).as_i128(), None); /// ``` i128 => as_i128, /// Return a `isize` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::Isize(42).as_isize(), Some(42)); /// assert_eq!(Value::Usize(42).as_isize(), Some(42)); /// /// assert_eq!(Value::Usize(usize::MAX).as_isize(), None); /// assert_eq!(Value::Bool(true).as_isize(), None); /// ``` isize => as_isize, /// Return a `u8` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::U8(42).as_u8(), Some(42)); /// assert_eq!(Value::U32(42).as_u8(), Some(42)); /// /// assert_eq!(Value::U32(u32::MAX).as_u8(), None); /// assert_eq!(Value::Bool(true).as_u8(), None); /// ``` u8 => as_u8, /// Return a `u16` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::U16(42).as_u16(), Some(42)); /// assert_eq!(Value::U32(42).as_u16(), Some(42)); /// /// assert_eq!(Value::U32(u32::MAX).as_u16(), None); /// assert_eq!(Value::Bool(true).as_u16(), None); /// ``` u16 => as_u16, /// Return a `u32` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::U32(42).as_u32(), Some(42)); /// assert_eq!(Value::U64(42).as_u32(), Some(42)); /// /// assert_eq!(Value::U64(u64::MAX).as_u32(), None); /// assert_eq!(Value::Bool(true).as_u32(), None); /// ``` u32 => as_u32, /// Return a `u64` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::U64(42).as_u64(), Some(42)); /// assert_eq!(Value::U128(42).as_u64(), Some(42)); /// /// assert_eq!(Value::U128(u128::MAX).as_u64(), None); /// assert_eq!(Value::Bool(true).as_u64(), None); /// ``` u64 => as_u64, /// Return a `u128` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::U128(42).as_u128(), Some(42)); /// assert_eq!(Value::I32(42).as_u128(), Some(42)); /// /// assert_eq!(Value::I32(-5).as_u128(), None); /// assert_eq!(Value::Bool(true).as_u128(), None); /// ``` u128 => as_u128, /// Return a `usize` representation of `self`, if possible. /// /// # Examples /// /// ``` /// use valuable::Value; /// /// assert_eq!(Value::Usize(42).as_usize(), Some(42)); /// assert_eq!(Value::I8(42).as_usize(), Some(42)); /// /// assert_eq!(Value::I8(-5).as_usize(), None); /// assert_eq!(Value::Bool(true).as_usize(), None); /// ``` usize => as_usize, } valuable-0.1.1/src/visit.rs000064400000000000000000000324651046102023000137230ustar 00000000000000use crate::*; /// Traverse a value's fields and variants. /// /// Each method of the `Visit` trait is a hook that enables the implementor to /// observe value fields. By default, most methods are implemented as a no-op. /// The `visit_primitive_slice` default implementation will iterate the slice, /// calling `visit_value` with each item. /// /// To recurse, the implementor must implement methods to visit the arguments. /// /// # Examples /// /// Recursively printing a Rust value. /// /// ``` /// use valuable::{NamedValues, Valuable, Value, Visit}; /// /// struct Print(String); /// /// impl Print { /// fn indent(&self) -> Print { /// Print(format!("{} ", self.0)) /// } /// } /// /// impl Visit for Print { /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Structable(v) => { /// let def = v.definition(); /// // Print the struct name /// println!("{}{}:", self.0, def.name()); /// /// // Visit fields /// let mut visit = self.indent(); /// v.visit(&mut visit); /// } /// Value::Enumerable(v) => { /// let def = v.definition(); /// let variant = v.variant(); /// // Print the enum name /// println!("{}{}::{}:", self.0, def.name(), variant.name()); /// /// // Visit fields /// let mut visit = self.indent(); /// v.visit(&mut visit); /// } /// Value::Listable(v) => { /// println!("{}", self.0); /// /// // Visit fields /// let mut visit = self.indent(); /// v.visit(&mut visit); /// } /// Value::Mappable(v) => { /// println!("{}", self.0); /// /// // Visit fields /// let mut visit = self.indent(); /// v.visit(&mut visit); /// } /// // Primitive or unknown type, just render Debug /// v => println!("{:?}", v), /// } /// } /// /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { /// for (field, value) in named_values { /// print!("{}- {}: ", self.0, field.name()); /// value.visit(self); /// } /// } /// /// fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { /// for value in values { /// print!("{}- ", self.0); /// value.visit(self); /// } /// } /// /// fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { /// print!("{}- {:?}: ", self.0, key); /// value.visit(self); /// } /// } /// /// #[derive(Valuable)] /// struct Person { /// name: String, /// age: u32, /// addresses: Vec
, /// } /// /// #[derive(Valuable)] /// struct Address { /// street: String, /// city: String, /// zip: String, /// } /// /// let person = Person { /// name: "Angela Ashton".to_string(), /// age: 31, /// addresses: vec![ /// Address { /// street: "123 1st Ave".to_string(), /// city: "Townsville".to_string(), /// zip: "12345".to_string(), /// }, /// Address { /// street: "555 Main St.".to_string(), /// city: "New Old Town".to_string(), /// zip: "55555".to_string(), /// }, /// ], /// }; /// /// let mut print = Print("".to_string()); /// valuable::visit(&person, &mut print); /// ``` pub trait Visit { /// Visit a single value. /// /// The `visit_value` method is called once when visiting single primitive /// values. When visiting `Listable` types, the `visit_value` method is /// called once per item in the listable type. /// /// Note, in the case of Listable types containing primitive types, /// `visit_primitive_slice` can be implemented instead for less overhead. /// /// # Examples /// /// Visiting a single value. /// /// ``` /// use valuable::{Valuable, Visit, Value}; /// /// struct Print; /// /// impl Visit for Print { /// fn visit_value(&mut self, value: Value<'_>) { /// println!("{:?}", value); /// } /// } /// /// let my_val = 123; /// my_val.visit(&mut Print); /// ``` /// /// Visiting multiple values in a list. /// /// ``` /// use valuable::{Valuable, Value, Visit}; /// /// struct PrintList { comma: bool }; /// /// impl Visit for PrintList { /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Listable(v) => v.visit(self), /// value => { /// if self.comma { /// println!(", {:?}", value); /// } else { /// print!("{:?}", value); /// self.comma = true; /// } /// } /// } /// } /// } /// /// let my_list = vec![1, 2, 3, 4, 5]; /// valuable::visit(&my_list, &mut PrintList { comma: false }); /// ``` fn visit_value(&mut self, value: Value<'_>); /// Visit a struct or enum's named fields. /// /// When the struct/enum is statically defined, all fields are known ahead /// of time and `visit_named_fields` is called once with all field values. /// When the struct/enum is dynamic, then the `visit_named_fields` method /// may be called multiple times. /// /// See [`Structable`] and [`Enumerable`] for static vs. dynamic details. /// /// # Examples /// /// Visiting all fields in a struct. /// /// ``` /// use valuable::{NamedValues, Valuable, Value, Visit}; /// /// #[derive(Valuable)] /// struct MyStruct { /// hello: String, /// world: u32, /// } /// /// struct Print; /// /// impl Visit for Print { /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { /// for (field, value) in named_values { /// println!("{:?}: {:?}", field, value); /// } /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Structable(v) => v.visit(self), /// _ => {} // do nothing for other types /// } /// } /// } /// /// let my_struct = MyStruct { /// hello: "Hello world".to_string(), /// world: 42, /// }; /// /// valuable::visit(&my_struct, &mut Print); /// ``` fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { let _ = named_values; } /// Visit a struct or enum's unnamed fields. /// /// When the struct/enum is statically defined, all fields are known ahead /// of time and `visit_unnamed_fields` is called once with all field values. /// When the struct/enum is dynamic, then the `visit_unnamed_fields` method /// may be called multiple times. /// /// See [`Structable`] and [`Enumerable`] for static vs. dynamic details. /// /// # Examples /// /// Visiting all fields in a struct. /// /// ``` /// use valuable::{Valuable, Value, Visit}; /// /// #[derive(Valuable)] /// struct MyStruct(String, u32); /// /// struct Print; /// /// impl Visit for Print { /// fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { /// for value in values { /// println!("{:?}", value); /// } /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Structable(v) => v.visit(self), /// _ => {} // do nothing for other types /// } /// } /// } /// /// let my_struct = MyStruct("Hello world".to_string(), 42); /// /// valuable::visit(&my_struct, &mut Print); /// ``` fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { let _ = values; } /// Visit a primitive slice. /// /// This method exists as an optimization when visiting [`Listable`] types. /// By default, `Listable` types are visited by passing each item to /// `visit_value`. However, if the listable stores a **primitive** type /// within contiguous memory, then `visit_primitive_slice` is called /// instead. /// /// When implementing `visit_primitive_slice`, be aware that the method may /// be called multiple times for a single `Listable` type. /// /// # Examples /// /// A vec calls `visit_primitive_slice` one time, but a `VecDeque` will call /// `visit_primitive_slice` twice. /// /// ``` /// use valuable::{Valuable, Value, Visit, Slice}; /// use std::collections::VecDeque; /// /// struct Count(u32); /// /// impl Visit for Count { /// fn visit_primitive_slice(&mut self, slice: Slice<'_>) { /// self.0 += 1; /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Listable(v) => v.visit(self), /// _ => {} // do nothing for other types /// } /// } /// } /// /// let vec = vec![1, 2, 3, 4, 5]; /// /// let mut count = Count(0); /// valuable::visit(&vec, &mut count); /// assert_eq!(1, count.0); /// /// let mut vec_deque = VecDeque::from(vec); /// /// let mut count = Count(0); /// valuable::visit(&vec_deque, &mut count); /// /// assert_eq!(2, count.0); /// ``` fn visit_primitive_slice(&mut self, slice: Slice<'_>) { for value in slice { self.visit_value(value); } } /// Visit a `Mappable`'s entries. /// /// The `visit_entry` method is called once for each entry contained by a /// `Mappable.` /// /// # Examples /// /// Visit a map's entries /// /// ``` /// use valuable::{Valuable, Value, Visit}; /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); /// map.insert("hello", 123); /// map.insert("world", 456); /// /// struct Print; /// /// impl Visit for Print { /// fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { /// println!("{:?} => {:?}", key, value); /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Mappable(v) => v.visit(self), /// _ => {} // do nothing for other types /// } /// } /// } /// /// valuable::visit(&map, &mut Print); /// ``` fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { let _ = (key, value); } } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Visit for $ty { fn visit_value(&mut self, value: Value<'_>) { T::visit_value(&mut **self, value) } fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { T::visit_named_fields(&mut **self, named_values) } fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { T::visit_unnamed_fields(&mut **self, values) } fn visit_primitive_slice(&mut self, slice: Slice<'_>) { T::visit_primitive_slice(&mut **self, slice) } fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { T::visit_entry(&mut **self, key, value) } } )* }; } deref! { &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, } /// Inspects a value by calling the relevant [`Visit`] methods with `value`'s /// data. /// /// This method calls [`Visit::visit_value()`] with the provided [`Valuable`] /// instance. See [`Visit`] documentation for more details. /// /// # Examples /// /// Extract a single field from a struct. Note: if the same field is repeatedly /// extracted from a struct, it is preferable to obtain the associated /// [`NamedField`] once and use it repeatedly. /// /// ``` /// use valuable::{NamedValues, Valuable, Value, Visit}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: usize, /// bar: usize, /// } /// /// struct GetFoo(usize); /// /// impl Visit for GetFoo { /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { /// if let Some(foo) = named_values.get_by_name("foo") { /// if let Some(val) = foo.as_usize() { /// self.0 = val; /// } /// } /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// if let Value::Structable(v) = value { /// v.visit(self); /// } /// } /// } /// /// let my_struct = MyStruct { /// foo: 123, /// bar: 456, /// }; /// /// let mut get_foo = GetFoo(0); /// valuable::visit(&my_struct, &mut get_foo); /// /// assert_eq!(123, get_foo.0); /// ``` /// /// [`Visit`]: Visit [`NamedField`]: crate::NamedField pub fn visit(value: &impl Valuable, visit: &mut dyn Visit) { visit.visit_value(value.as_value()); }