├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── by_ref.rs ├── capture.rs ├── owned.rs └── seq.rs ├── meta ├── serde1 │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ └── src │ │ └── lib.rs └── sval2 │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ └── src │ └── lib.rs └── src ├── error.rs ├── fill.rs ├── impls.rs ├── internal ├── cast │ ├── mod.rs │ └── primitive.rs ├── error.rs ├── fmt.rs ├── mod.rs ├── owned.rs ├── seq.rs ├── serde │ ├── mod.rs │ └── v1.rs └── sval │ ├── mod.rs │ └── v2.rs ├── lib.rs ├── owned.rs ├── test.rs └── visit.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Test 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | - windows-latest 18 | steps: 19 | - name: Checkout sources 20 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 21 | 22 | - name: Install Rust toolchain 23 | run: rustup default nightly 24 | 25 | - name: Install cargo-hack 26 | run: cargo install cargo-hack 27 | 28 | - name: All 29 | run: cargo test --all-features 30 | 31 | - name: Powerset 32 | run: cargo hack test --feature-powerset --depth 4 --lib 33 | 34 | embedded: 35 | name: Build (embedded) 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout sources 39 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 40 | 41 | - name: Install Rust toolchain 42 | run: | 43 | rustup default nightly 44 | rustup target add thumbv6m-none-eabi 45 | 46 | - name: Install cargo-hack 47 | run: cargo install cargo-hack 48 | 49 | - name: Powerset 50 | run: cargo hack check --each-feature --exclude-features std,test,error,owned -Z avoid-dev-deps --target thumbv6m-none-eabi 51 | 52 | nodeps: 53 | name: Build (no dev deps) 54 | runs-on: ubuntu-latest 55 | steps: 56 | - name: Checkout sources 57 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 58 | 59 | - name: Install Rust toolchain 60 | run: rustup default nightly 61 | 62 | - name: Install cargo-hack 63 | run: cargo install cargo-hack 64 | 65 | - name: Powerset 66 | run: cargo hack check --feature-powerset --depth 4 -Z avoid-dev-deps 67 | 68 | minimaldeps: 69 | name: Build (minimal versions) 70 | runs-on: ubuntu-latest 71 | steps: 72 | - name: Checkout sources 73 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 74 | 75 | - name: Install Rust toolchain 76 | run: rustup default nightly 77 | 78 | - name: Install cargo-hack 79 | run: cargo install cargo-hack 80 | 81 | - name: Powerset 82 | run: cargo hack check --each-feature --lib -Z minimal-versions -Z avoid-dev-deps 83 | 84 | benches: 85 | name: Build (benches) 86 | runs-on: ubuntu-latest 87 | steps: 88 | - name: Checkout sources 89 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 90 | 91 | - name: Install Rust toolchain 92 | run: rustup default nightly 93 | 94 | - name: Benches 95 | run: cargo bench --no-run --features "error sval2 serde1" 96 | 97 | wasm: 98 | name: Test (wasm) 99 | runs-on: ubuntu-latest 100 | steps: 101 | - name: Checkout sources 102 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 103 | 104 | - name: Install 105 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 106 | 107 | - name: All features 108 | run: wasm-pack test --node -- --all-features 109 | 110 | msrv: 111 | name: Test (MSRV) 112 | runs-on: ubuntu-latest 113 | env: 114 | MSRV_TOOLCHAIN: 1.61.0 115 | steps: 116 | - name: Checkout sources 117 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 118 | 119 | - name: Default features 120 | run: cargo +$MSRV_TOOLCHAIN build --features "std owned seq sval2 serde1" 121 | 122 | miri: 123 | name: Test (miri) 124 | runs-on: ubuntu-latest 125 | env: 126 | MIRI_TOOLCHAIN: nightly-2023-03-26 127 | steps: 128 | - name: Checkout sources 129 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab 130 | 131 | - name: Install miri 132 | run: rustup +$MIRI_TOOLCHAIN component add miri 133 | 134 | - name: Default features 135 | run: cargo +$MIRI_TOOLCHAIN miri test 136 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "value-bag" 3 | version = "1.11.1" 4 | authors = ["Ashley Mannix "] 5 | edition = "2021" 6 | license = "Apache-2.0 OR MIT" 7 | documentation = "https://docs.rs/value-bag" 8 | description = "Anonymous structured values" 9 | repository = "https://github.com/sval-rs/value-bag" 10 | readme = "README.md" 11 | keywords = ["serialization", "no_std"] 12 | categories = ["encoding", "no-std"] 13 | exclude = [ 14 | ".github/*", 15 | ] 16 | 17 | [package.metadata.docs.rs] 18 | features = ["std", "error", "sval", "serde", "test", "owned", "seq"] 19 | 20 | [workspace] 21 | members = [ 22 | "meta/serde1", 23 | "meta/sval2", 24 | ] 25 | 26 | [features] 27 | # Store 128bit numbers inline instead of as references 28 | # This may increase the size of `ValueBag` on some platforms 29 | inline-i128 = [] 30 | # Store small strings inline instead of as references 31 | # This requires a more recent Rust toolchain 32 | inline-str = [] 33 | 34 | # Use the standard library 35 | std = [ 36 | "alloc", 37 | "value-bag-sval2?/std", 38 | "value-bag-serde1?/std", 39 | ] 40 | 41 | # Assume an allocator 42 | alloc = [ 43 | "value-bag-sval2?/alloc", 44 | "value-bag-serde1?/alloc", 45 | ] 46 | 47 | # Support owned values 48 | owned = [ 49 | "alloc", 50 | "value-bag-serde1?/owned", 51 | ] 52 | 53 | # Utilities for working with sequences 54 | seq = [] 55 | 56 | # Add support for `sval` 57 | sval = ["sval2"] 58 | sval2 = [ 59 | "value-bag-sval2", 60 | ] 61 | 62 | # Add support for `serde` 63 | serde = ["serde1"] 64 | serde1 = [ 65 | "alloc", 66 | "value-bag-serde1", 67 | "value-bag-sval2?/serde1", 68 | ] 69 | 70 | # Add support for `std::error` 71 | error = [ 72 | "std", 73 | ] 74 | 75 | # Add support for testing the contents of a value bag 76 | test = ["std"] 77 | 78 | [dependencies.value-bag-sval2] 79 | version = "1.11.1" 80 | path = "meta/sval2" 81 | optional = true 82 | 83 | [dependencies.value-bag-serde1] 84 | version = "1.11.1" 85 | path = "meta/serde1" 86 | optional = true 87 | 88 | [dev-dependencies.value-bag-sval2] 89 | path = "meta/sval2" 90 | features = ["test", "json"] 91 | 92 | [dev-dependencies.value-bag-serde1] 93 | path = "meta/serde1" 94 | features = ["test", "json"] 95 | 96 | [target.'cfg(target_arch = "wasm32")'.dev-dependencies.wasm-bindgen] 97 | version = "0.2" 98 | 99 | [target.'cfg(target_arch = "wasm32")'.dev-dependencies.wasm-bindgen-test] 100 | version = "0.3" 101 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 sval-rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `value-bag` 2 | 3 | [![Rust](https://github.com/sval-rs/value-bag/workflows/Rust/badge.svg)](https://github.com/sval-rs/value-bag/actions) 4 | [![Latest version](https://img.shields.io/crates/v/value-bag.svg)](https://crates.io/crates/value-bag) 5 | [![Documentation Latest](https://docs.rs/value-bag/badge.svg)](https://docs.rs/value-bag) 6 | 7 | ## What is a value bag? 8 | 9 | A `ValueBag` is an anonymous structured value that supports casting, downcasting, formatting, and serializing. The producer of a `ValueBag` and its eventual consumer don't need to agree on a serialization contract. Any translation is handled internally by `ValueBag`. 10 | 11 | Say we capture an `i32` using its `Display` implementation as a `ValueBag`: 12 | 13 | ```rust 14 | let bag = ValueBag::capture_display(42); 15 | ``` 16 | 17 | That value can then be cast to a concrete integer type, like `u64`: 18 | 19 | ```rust 20 | let num = bag.as_u64().unwrap(); 21 | 22 | assert_eq!(42, num); 23 | ``` 24 | 25 | It could also be serialized as a number using `serde`: 26 | 27 | ```rust 28 | let num = serde_json::to_value(&bag).unwrap(); 29 | 30 | assert!(num.is_number()); 31 | ``` 32 | 33 | It works for more complex types too. Say we derive `sval::Value` on a type and capture it as a `ValueBag`: 34 | 35 | ```rust 36 | #[derive(Value)] 37 | struct Work { 38 | id: u64, 39 | description: String, 40 | } 41 | 42 | let work = Work { 43 | id: 123, 44 | description: String::from("do the work"), 45 | } 46 | 47 | let bag = ValueBag::capture_sval2(&work); 48 | ``` 49 | 50 | We could still serialize that value using `serde` without losing structure: 51 | 52 | ```rust 53 | let obj = serde_json::to_value(&bag).unwrap(); 54 | 55 | assert!(obj.is_object()); 56 | ``` 57 | 58 | It could also be formatted using `Display`: 59 | 60 | ```rust 61 | assert_eq!("Work { id: 123, description: \"do the work\" }", bag.to_string()); 62 | ``` 63 | 64 | The tradeoff in all this is that `ValueBag` needs to depend on the serialization frameworks (`sval`, `serde`, and `std::fmt`) that it supports, instead of just providing an API of its own for others to plug into. Doing this lets `ValueBag` guarantee everything will always line up, and keep its own public API narrow. 65 | 66 | ## Getting started 67 | 68 | Add the `value-bag` crate to your `Cargo.toml`: 69 | 70 | ```rust 71 | [dependencies.value-bag] 72 | version = "1.11.1" 73 | ``` 74 | 75 | You'll probably also want to add a feature for either `sval` (if you're in a no-std environment) or `serde` (if you need to integrate with other code that uses `serde`): 76 | 77 | ```rust 78 | [dependencies.value-bag] 79 | version = "1.11.1" 80 | features = ["sval2"] 81 | ``` 82 | 83 | ```rust 84 | [dependencies.value-bag] 85 | version = "1.11.1" 86 | features = ["serde1"] 87 | ``` 88 | 89 | Then you're ready to capture anonymous values! 90 | 91 | ```rust 92 | #[derive(Serialize)] 93 | struct MyValue { 94 | title: String, 95 | description: String, 96 | version: u32, 97 | } 98 | 99 | // Capture a value that implements `serde::Serialize` 100 | let bag = ValueBag::capture_serde1(&my_value); 101 | 102 | // Print the contents of the value bag 103 | println!("{:?}", bag); 104 | ``` 105 | 106 | ## Cargo features 107 | 108 | The `value-bag` crate is no-std by default, and offers the following Cargo features: 109 | 110 | - `std`: Enable support for the standard library. This allows more types to be captured in a `ValueBag`. 111 | - `error`: Enable support for capturing `std::error::Error`s. Implies `std`. 112 | - `sval`: Enable support for using the [`sval`](https://github.com/sval-rs/sval) serialization framework for inspecting `ValueBag`s by implementing `sval::value::Value`. Implies `sval2`. 113 | - `sval2`: Enable support for the stable `2.x.x` version of `sval`. 114 | - `serde`: Enable support for using the [`serde`](https://github.com/serde-rs/serde) serialization framework for inspecting `ValueBag`s by implementing `serde::Serialize`. Implies `std` and `serde1`. 115 | - `serde1`: Enable support for the stable `1.x.x` version of `serde`. 116 | - `owned`: Add support for buffering `ValueBag`s into an owned `Send + Sync` variant. 117 | - `seq`: Add support for working with sequences without needing to go through a full serialization framework. 118 | - `test`: Add test helpers for inspecting the shape of the value inside a `ValueBag`. 119 | -------------------------------------------------------------------------------- /benches/by_ref.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use value_bag::ValueBag; 6 | 7 | #[bench] 8 | fn u8_by_ref(b: &mut test::Bencher) { 9 | let v = ValueBag::from(1u8); 10 | b.iter(|| v.by_ref()) 11 | } 12 | -------------------------------------------------------------------------------- /benches/capture.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use value_bag::ValueBag; 6 | 7 | #[bench] 8 | fn u8_capture_from(b: &mut test::Bencher) { 9 | b.iter(|| ValueBag::from(1u8)) 10 | } 11 | 12 | #[bench] 13 | fn u8_capture_debug(b: &mut test::Bencher) { 14 | b.iter(|| ValueBag::capture_debug(&1u8)) 15 | } 16 | 17 | #[bench] 18 | fn str_capture_debug(b: &mut test::Bencher) { 19 | // Currently at the top of the linear list of types in `cast::primitive` 20 | b.iter(|| ValueBag::capture_debug(&"a string")) 21 | } 22 | 23 | #[bench] 24 | fn bool_capture_debug(b: &mut test::Bencher) { 25 | // Currently at the bottom of the linear list of types in `cast::primitive` 26 | b.iter(|| ValueBag::capture_debug(&true)) 27 | } 28 | 29 | #[bench] 30 | fn custom_capture_debug(b: &mut test::Bencher) { 31 | #[derive(Debug)] 32 | struct A; 33 | 34 | b.iter(|| ValueBag::capture_debug(&A)) 35 | } 36 | 37 | #[bench] 38 | fn fill_debug(b: &mut test::Bencher) { 39 | b.iter(|| { 40 | ValueBag::from_fill(&|slot: value_bag::fill::Slot| { 41 | #[derive(Debug)] 42 | struct A; 43 | 44 | slot.fill_debug(&A) 45 | }) 46 | }) 47 | } 48 | 49 | #[bench] 50 | fn u8_capture_from_to_u64(b: &mut test::Bencher) { 51 | let v = ValueBag::from(1u8); 52 | b.iter(|| v.to_u64()) 53 | } 54 | 55 | #[bench] 56 | fn u8_capture_debug_to_u64(b: &mut test::Bencher) { 57 | let v = ValueBag::capture_debug(&1u8); 58 | b.iter(|| v.to_u64()) 59 | } 60 | 61 | #[bench] 62 | fn u8_fill_to_u64(b: &mut test::Bencher) { 63 | let v = ValueBag::from_fill(&|slot: value_bag::fill::Slot| slot.fill_any(1u8)); 64 | 65 | b.iter(|| v.to_u64()) 66 | } 67 | 68 | #[bench] 69 | #[cfg(feature = "sval2")] 70 | fn u8_from_sval_to_u64(b: &mut test::Bencher) { 71 | let v = ValueBag::from_sval2(&1u8); 72 | 73 | b.iter(|| v.to_u64()) 74 | } 75 | 76 | #[bench] 77 | #[cfg(feature = "sval2")] 78 | fn u8_fill_sval_to_u64(b: &mut test::Bencher) { 79 | let v = ValueBag::from_fill(&|slot: value_bag::fill::Slot| slot.fill_sval2(&1u8)); 80 | 81 | b.iter(|| v.to_u64()) 82 | } 83 | 84 | #[bench] 85 | fn u8_capture_debug_to_borrowed_str(b: &mut test::Bencher) { 86 | let v = ValueBag::capture_debug(&1u8); 87 | b.iter(|| v.to_borrowed_str()) 88 | } 89 | 90 | #[bench] 91 | fn str_capture_debug_to_borrowed_str(b: &mut test::Bencher) { 92 | let v = ValueBag::capture_debug(&"a string"); 93 | b.iter(|| v.to_borrowed_str()) 94 | } 95 | 96 | #[bench] 97 | fn str_capture_debug_to_u64(b: &mut test::Bencher) { 98 | let v = ValueBag::capture_debug(&"a string"); 99 | b.iter(|| v.to_u64()) 100 | } 101 | 102 | #[bench] 103 | fn custom_capture_debug_to_str(b: &mut test::Bencher) { 104 | #[derive(Debug)] 105 | struct A; 106 | 107 | let v = ValueBag::capture_debug(&A); 108 | b.iter(|| v.to_borrowed_str()) 109 | } 110 | -------------------------------------------------------------------------------- /benches/owned.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "owned")] 2 | #![feature(test)] 3 | 4 | extern crate test; 5 | 6 | use value_bag::ValueBag; 7 | 8 | #[bench] 9 | fn u8_to_owned(b: &mut test::Bencher) { 10 | let bag = ValueBag::from(1u8); 11 | 12 | b.iter(|| bag.to_owned()); 13 | } 14 | 15 | #[bench] 16 | fn str_to_owned(b: &mut test::Bencher) { 17 | let bag = ValueBag::from("a string"); 18 | 19 | b.iter(|| bag.to_owned()); 20 | } 21 | 22 | #[bench] 23 | fn str_to_owned_clone(b: &mut test::Bencher) { 24 | let bag = ValueBag::from("a string").to_owned(); 25 | 26 | b.iter(|| bag.clone()); 27 | } 28 | 29 | #[bench] 30 | fn str_to_shared(b: &mut test::Bencher) { 31 | let bag = ValueBag::from("a string"); 32 | 33 | b.iter(|| bag.to_shared()); 34 | } 35 | 36 | #[bench] 37 | fn str_to_shared_clone(b: &mut test::Bencher) { 38 | let bag = ValueBag::from("a string").to_shared(); 39 | 40 | b.iter(|| bag.clone()); 41 | } 42 | 43 | #[bench] 44 | fn display_to_owned(b: &mut test::Bencher) { 45 | let bag = ValueBag::from_display(&42); 46 | 47 | b.iter(|| bag.to_owned()); 48 | } 49 | 50 | #[cfg(feature = "serde1")] 51 | #[bench] 52 | fn serde1_to_owned(b: &mut test::Bencher) { 53 | use value_bag_serde1::lib::ser::{Serialize, SerializeStruct, Serializer}; 54 | 55 | pub struct MyData<'a> { 56 | a: i32, 57 | b: &'a str, 58 | } 59 | 60 | impl<'a> Serialize for MyData<'a> { 61 | fn serialize(&self, serializer: S) -> Result { 62 | let mut serializer = serializer.serialize_struct("MyData", 2)?; 63 | 64 | serializer.serialize_field("a", &self.a)?; 65 | serializer.serialize_field("b", &self.b)?; 66 | 67 | serializer.end() 68 | } 69 | } 70 | 71 | let bag = ValueBag::from_serde1(&MyData { 72 | a: 42, 73 | b: "a string", 74 | }); 75 | 76 | b.iter(|| bag.to_owned()); 77 | } 78 | 79 | #[cfg(feature = "sval2")] 80 | #[bench] 81 | fn sval2_to_owned(b: &mut test::Bencher) { 82 | use value_bag_sval2::lib::{Label, Result, Stream, Value}; 83 | 84 | pub struct MyData<'a> { 85 | a: i32, 86 | b: &'a str, 87 | } 88 | 89 | impl<'a> Value for MyData<'a> { 90 | fn stream<'sval, S: Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> Result { 91 | stream.record_begin(None, Some(&Label::new("MyData")), None, Some(2))?; 92 | 93 | stream.record_value_begin(None, &Label::new("a"))?; 94 | stream.value(&self.a)?; 95 | stream.record_value_end(None, &Label::new("a"))?; 96 | 97 | stream.record_value_begin(None, &Label::new("b"))?; 98 | stream.value(&self.b)?; 99 | stream.record_value_end(None, &Label::new("b"))?; 100 | 101 | stream.record_end(None, Some(&Label::new("MyData")), None) 102 | } 103 | } 104 | 105 | let bag = ValueBag::from_sval2(&MyData { 106 | a: 42, 107 | b: "a string", 108 | }); 109 | 110 | b.iter(|| bag.to_owned()); 111 | } 112 | -------------------------------------------------------------------------------- /benches/seq.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "seq")] 2 | #![feature(test)] 3 | 4 | extern crate test; 5 | 6 | use value_bag::ValueBag; 7 | 8 | #[cfg(feature = "serde1")] 9 | #[bench] 10 | fn serde1_to_seq_5(b: &mut test::Bencher) { 11 | let v = ValueBag::from_serde1(&[1.0, 2.0, 3.0, 4.0, 5.0]); 12 | 13 | b.iter(|| v.to_f64_seq::>>()) 14 | } 15 | 16 | #[cfg(feature = "sval2")] 17 | #[bench] 18 | fn sval2_to_seq_5(b: &mut test::Bencher) { 19 | let v = ValueBag::from_sval2(&[1.0, 2.0, 3.0, 4.0, 5.0]); 20 | 21 | b.iter(|| v.to_f64_seq::>>()) 22 | } 23 | -------------------------------------------------------------------------------- /meta/serde1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "value-bag-serde1" 3 | version = "1.11.1" 4 | edition = "2021" 5 | authors = ["Ashley Mannix "] 6 | license = "Apache-2.0 OR MIT" 7 | description = "Implementation detail for value-bag" 8 | 9 | [features] 10 | std = [ 11 | "serde/std", 12 | "erased-serde/std", 13 | ] 14 | 15 | alloc = [] 16 | 17 | owned = [ 18 | "serde_buf" 19 | ] 20 | 21 | json = [ 22 | "serde_json", 23 | ] 24 | 25 | test = [ 26 | "serde_test", 27 | ] 28 | 29 | [dependencies.serde] 30 | version = "1" 31 | features = ["alloc"] 32 | default-features = false 33 | 34 | [dependencies.erased-serde] 35 | version = "0.4" 36 | features = ["alloc"] 37 | default-features = false 38 | 39 | [dependencies.serde_fmt] 40 | version = "1" 41 | default-features = false 42 | 43 | [dependencies.serde_buf] 44 | version = "0.1" 45 | default-features = false 46 | optional = true 47 | 48 | [dependencies.serde_json] 49 | version = "1" 50 | optional = true 51 | 52 | [dependencies.serde_test] 53 | version = "1" 54 | optional = true 55 | -------------------------------------------------------------------------------- /meta/serde1/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /meta/serde1/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /meta/serde1/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Implementation detail for `value-bag`; it should not be depended on directly. 3 | */ 4 | 5 | #![no_std] 6 | 7 | pub use erased_serde as erased; 8 | pub use serde as lib; 9 | pub use serde_fmt as fmt; 10 | 11 | #[cfg(feature = "owned")] 12 | pub use serde_buf as buf; 13 | 14 | #[cfg(feature = "json")] 15 | pub use serde_json as json; 16 | 17 | #[cfg(feature = "test")] 18 | pub use serde_test as test; 19 | -------------------------------------------------------------------------------- /meta/sval2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "value-bag-sval2" 3 | version = "1.11.1" 4 | edition = "2021" 5 | authors = ["Ashley Mannix "] 6 | license = "Apache-2.0 OR MIT" 7 | description = "Implementation detail for value-bag" 8 | 9 | [features] 10 | std = [ 11 | "sval/std", 12 | "sval_ref/std", 13 | "sval_buffer/std", 14 | "sval_serde?/std", 15 | "sval_json?/std", 16 | ] 17 | 18 | alloc = [ 19 | "sval/alloc", 20 | "sval_ref/alloc", 21 | "sval_buffer/alloc", 22 | "sval_serde?/alloc", 23 | "sval_json?/alloc", 24 | ] 25 | 26 | serde1 = [ 27 | "sval_serde", 28 | ] 29 | 30 | json = [ 31 | "sval_json", 32 | ] 33 | 34 | test = [ 35 | "std", 36 | "sval_test", 37 | ] 38 | 39 | [dependencies.sval] 40 | version = "2.1" 41 | default-features = false 42 | 43 | [dependencies.sval_ref] 44 | version = "2.1" 45 | default-features = false 46 | 47 | [dependencies.sval_dynamic] 48 | version = "2.1" 49 | default-features = false 50 | 51 | [dependencies.sval_buffer] 52 | version = "2.3" 53 | default-features = false 54 | 55 | [dependencies.sval_fmt] 56 | version = "2.1" 57 | default-features = false 58 | 59 | [dependencies.sval_serde] 60 | version = "2.1" 61 | default-features = false 62 | optional = true 63 | 64 | [dependencies.sval_json] 65 | version = "2.1" 66 | optional = true 67 | 68 | [dependencies.sval_test] 69 | version = "2.1" 70 | optional = true 71 | -------------------------------------------------------------------------------- /meta/sval2/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /meta/sval2/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /meta/sval2/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Implementation detail for `value-bag`; it should not be depended on directly. 3 | */ 4 | 5 | #![no_std] 6 | 7 | pub use sval as lib; 8 | pub use sval_buffer as buffer; 9 | pub use sval_dynamic as dynamic; 10 | pub use sval_fmt as fmt; 11 | pub use sval_ref as lib_ref; 12 | 13 | #[cfg(feature = "serde1")] 14 | pub use sval_serde as serde1; 15 | 16 | #[cfg(feature = "json")] 17 | pub use sval_json as json; 18 | 19 | #[cfg(feature = "test")] 20 | pub use sval_test as test; 21 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::std::fmt; 2 | 3 | /// An error encountered while working with structured data. 4 | #[derive(Debug)] 5 | pub struct Error { 6 | inner: Inner, 7 | } 8 | 9 | #[derive(Debug)] 10 | enum Inner { 11 | #[cfg(feature = "std")] 12 | Boxed(std_support::BoxedError), 13 | Msg(&'static str), 14 | Fmt, 15 | } 16 | 17 | impl Error { 18 | /// Create an error from a message. 19 | pub fn msg(msg: &'static str) -> Self { 20 | Error { 21 | inner: Inner::Msg(msg), 22 | } 23 | } 24 | 25 | #[cfg(feature = "serde1")] 26 | pub(crate) fn try_boxed(msg: &'static str, e: impl fmt::Display) -> Self { 27 | #[cfg(feature = "std")] 28 | { 29 | Error::boxed(format!("{msg}: {e}")) 30 | } 31 | #[cfg(not(feature = "std"))] 32 | { 33 | let _ = e; 34 | Error::msg(msg) 35 | } 36 | } 37 | } 38 | 39 | impl fmt::Display for Error { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | use self::Inner::*; 42 | match self.inner { 43 | #[cfg(feature = "std")] 44 | Boxed(ref err) => err.fmt(f), 45 | Msg(ref msg) => msg.fmt(f), 46 | Fmt => fmt::Error.fmt(f), 47 | } 48 | } 49 | } 50 | 51 | impl From for Error { 52 | fn from(_: fmt::Error) -> Self { 53 | Error { inner: Inner::Fmt } 54 | } 55 | } 56 | 57 | #[cfg(feature = "std")] 58 | mod std_support { 59 | use super::*; 60 | use crate::std::{boxed::Box, error, io}; 61 | 62 | pub(crate) type BoxedError = Box; 63 | 64 | impl Error { 65 | /// Create an error from a standard error type. 66 | pub fn boxed(err: E) -> Self 67 | where 68 | E: Into, 69 | { 70 | Error { 71 | inner: Inner::Boxed(err.into()), 72 | } 73 | } 74 | } 75 | 76 | impl error::Error for Error {} 77 | 78 | impl From for Error { 79 | fn from(err: io::Error) -> Self { 80 | Error::boxed(err) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/fill.rs: -------------------------------------------------------------------------------- 1 | //! Deferred value initialization. 2 | //! 3 | //! The [`Fill`] trait is a way to bridge APIs that may not be directly 4 | //! compatible with other constructor methods. 5 | //! 6 | //! The `Fill` trait is automatically implemented for closures, so can usually 7 | //! be used in libraries that can't implement the trait themselves. 8 | //! 9 | //! ``` 10 | //! use value_bag::{ValueBag, fill::Slot}; 11 | //! 12 | //! let value = ValueBag::from_fill(&|slot: Slot| { 13 | //! #[derive(Debug)] 14 | //! struct MyShortLivedValue; 15 | //! 16 | //! slot.fill_debug(&MyShortLivedValue) 17 | //! }); 18 | //! 19 | //! assert_eq!("MyShortLivedValue", format!("{:?}", value)); 20 | //! ``` 21 | //! 22 | //! The trait can also be implemented manually: 23 | //! 24 | //! ``` 25 | //! # use std::fmt::Debug; 26 | //! use value_bag::{ValueBag, Error, fill::{Slot, Fill}}; 27 | //! 28 | //! struct FillDebug; 29 | //! 30 | //! impl Fill for FillDebug { 31 | //! fn fill(&self, slot: Slot) -> Result<(), Error> { 32 | //! slot.fill_debug(&42i64 as &dyn Debug) 33 | //! } 34 | //! } 35 | //! 36 | //! let value = ValueBag::from_fill(&FillDebug); 37 | //! 38 | //! assert_eq!(None, value.to_i64()); 39 | //! ``` 40 | 41 | use crate::std::fmt; 42 | 43 | use super::internal::{Internal, InternalVisitor}; 44 | use super::{Error, ValueBag}; 45 | 46 | impl<'v> ValueBag<'v> { 47 | /// Get a value from a fillable slot. 48 | pub const fn from_fill(value: &'v T) -> Self 49 | where 50 | T: Fill, 51 | { 52 | ValueBag { 53 | inner: Internal::Fill(value), 54 | } 55 | } 56 | } 57 | 58 | /// A type that requires extra work to convert into a [`ValueBag`](../struct.ValueBag.html). 59 | /// 60 | /// This trait is an advanced initialization API. 61 | /// It's intended for erased values coming from other logging frameworks that may need 62 | /// to perform extra work to determine the concrete type to use. 63 | pub trait Fill { 64 | /// Fill a value. 65 | fn fill(&self, slot: Slot) -> Result<(), Error>; 66 | } 67 | 68 | impl Fill for F 69 | where 70 | F: Fn(Slot) -> Result<(), Error>, 71 | { 72 | fn fill(&self, slot: Slot) -> Result<(), Error> { 73 | (self)(slot) 74 | } 75 | } 76 | 77 | /// A value slot to fill using the [`Fill`](trait.Fill.html) trait. 78 | pub struct Slot<'s, 'f> { 79 | visitor: &'s mut dyn InternalVisitor<'f>, 80 | } 81 | 82 | impl<'s, 'f> fmt::Debug for Slot<'s, 'f> { 83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 84 | f.debug_struct("Slot").finish() 85 | } 86 | } 87 | 88 | impl<'s, 'f> Slot<'s, 'f> { 89 | pub(crate) fn new(visitor: &'s mut dyn InternalVisitor<'f>) -> Self { 90 | Slot { visitor } 91 | } 92 | 93 | pub(crate) fn fill(self, f: F) -> Result<(), Error> 94 | where 95 | F: FnOnce(&mut dyn InternalVisitor<'f>) -> Result<(), Error>, 96 | { 97 | f(self.visitor) 98 | } 99 | 100 | /// Fill the slot with a value. 101 | /// 102 | /// The given value doesn't need to satisfy any particular lifetime constraints. 103 | pub fn fill_any(self, value: T) -> Result<(), Error> 104 | where 105 | T: Into>, 106 | { 107 | self.fill(|visitor| value.into().inner.internal_visit(visitor)) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | #[cfg(target_arch = "wasm32")] 114 | use wasm_bindgen_test::*; 115 | 116 | use super::*; 117 | use crate::std::string::ToString; 118 | 119 | #[test] 120 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 121 | fn fill_value_borrowed() { 122 | struct TestFill; 123 | 124 | impl Fill for TestFill { 125 | fn fill(&self, slot: Slot) -> Result<(), Error> { 126 | let dbg = &1 as &dyn fmt::Debug; 127 | 128 | slot.fill_debug(dbg) 129 | } 130 | } 131 | 132 | assert_eq!("1", ValueBag::from_fill(&TestFill).to_string()); 133 | } 134 | 135 | #[test] 136 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 137 | fn fill_cast() { 138 | struct TestFill; 139 | 140 | impl Fill for TestFill { 141 | fn fill(&self, slot: Slot) -> Result<(), Error> { 142 | slot.fill_any("a string") 143 | } 144 | } 145 | 146 | assert_eq!( 147 | "a string", 148 | ValueBag::from_fill(&TestFill) 149 | .to_borrowed_str() 150 | .expect("invalid value") 151 | ); 152 | } 153 | 154 | #[test] 155 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 156 | fn fill_fn_cast() { 157 | assert_eq!( 158 | 42u64, 159 | ValueBag::from_fill(&|slot: Slot| slot.fill_any(42u64)) 160 | .to_u64() 161 | .unwrap() 162 | ); 163 | } 164 | 165 | #[test] 166 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 167 | fn fill_fn_borrowed() { 168 | #[derive(Debug)] 169 | struct MyValue; 170 | 171 | let value = MyValue; 172 | assert_eq!( 173 | format!("{:?}", value), 174 | format!( 175 | "{:?}", 176 | ValueBag::from_fill(&|slot: Slot| slot.fill_debug(&value)) 177 | ) 178 | ); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/impls.rs: -------------------------------------------------------------------------------- 1 | //! Converting standard types into `ValueBag`s. 2 | 3 | use super::{Error, ValueBag}; 4 | 5 | macro_rules! convert_primitive { 6 | ($($t:ty: $from:ident, $to:ident,)*) => { 7 | $( 8 | impl<'v> From<$t> for ValueBag<'v> { 9 | #[inline] 10 | fn from(v: $t) -> Self { 11 | ValueBag::$from(v) 12 | } 13 | } 14 | 15 | impl<'a, 'v> From<&'a $t> for ValueBag<'v> { 16 | #[inline] 17 | fn from(v: &'a $t) -> Self { 18 | ValueBag::$from(*v) 19 | } 20 | } 21 | 22 | impl<'v> From> for ValueBag<'v> { 23 | #[inline] 24 | fn from(v: Option<$t>) -> Self { 25 | ValueBag::from_option(v) 26 | } 27 | } 28 | 29 | impl<'v> TryFrom> for $t { 30 | type Error = Error; 31 | 32 | #[inline] 33 | fn try_from(v: ValueBag<'v>) -> Result { 34 | v.$to() 35 | .ok_or_else(|| Error::msg("conversion failed"))? 36 | .try_into() 37 | .map_err(|_| Error::msg("conversion failed")) 38 | } 39 | } 40 | )* 41 | }; 42 | } 43 | 44 | impl<'v> From<()> for ValueBag<'v> { 45 | #[inline] 46 | fn from(_: ()) -> Self { 47 | ValueBag::empty() 48 | } 49 | } 50 | 51 | impl<'a, 'v> From<&'a ()> for ValueBag<'v> { 52 | #[inline] 53 | fn from(_: &'a ()) -> Self { 54 | ValueBag::empty() 55 | } 56 | } 57 | 58 | convert_primitive!( 59 | u8: from_u8, to_u64, 60 | u16: from_u16, to_u64, 61 | u32: from_u32, to_u64, 62 | u64: from_u64, to_u64, 63 | usize: from_usize, to_u64, 64 | i8: from_i8, to_i64, 65 | i16: from_i16, to_i64, 66 | i32: from_i32, to_i64, 67 | i64: from_i64, to_i64, 68 | isize: from_isize, to_i64, 69 | f64: from_f64, to_f64, 70 | bool: from_bool, to_bool, 71 | char: from_char, to_char, 72 | ); 73 | 74 | impl<'v> From for ValueBag<'v> { 75 | #[inline] 76 | fn from(v: f32) -> Self { 77 | ValueBag::from_f32(v) 78 | } 79 | } 80 | 81 | impl<'v> From> for ValueBag<'v> { 82 | #[inline] 83 | fn from(v: Option) -> Self { 84 | ValueBag::from_option(v) 85 | } 86 | } 87 | 88 | impl<'a, 'v> From<&'a f32> for ValueBag<'v> { 89 | #[inline] 90 | fn from(v: &'a f32) -> Self { 91 | ValueBag::from_f32(*v) 92 | } 93 | } 94 | 95 | #[cfg(feature = "inline-i128")] 96 | impl<'a, 'v> From<&'a u128> for ValueBag<'v> { 97 | #[inline] 98 | fn from(v: &'a u128) -> Self { 99 | ValueBag::from_u128(*v) 100 | } 101 | } 102 | 103 | #[cfg(not(feature = "inline-i128"))] 104 | impl<'v> From<&'v u128> for ValueBag<'v> { 105 | #[inline] 106 | fn from(v: &'v u128) -> Self { 107 | ValueBag::from_u128_ref(v) 108 | } 109 | } 110 | 111 | #[cfg(feature = "inline-i128")] 112 | impl<'v> From for ValueBag<'v> { 113 | #[inline] 114 | fn from(v: u128) -> Self { 115 | ValueBag::from_u128(v) 116 | } 117 | } 118 | 119 | impl<'v> TryFrom> for u128 { 120 | type Error = Error; 121 | 122 | #[inline] 123 | fn try_from(v: ValueBag<'v>) -> Result { 124 | v.to_u128().ok_or_else(|| Error::msg("conversion failed")) 125 | } 126 | } 127 | 128 | #[cfg(feature = "inline-i128")] 129 | impl<'a, 'v> From<&'a i128> for ValueBag<'v> { 130 | #[inline] 131 | fn from(v: &'a i128) -> Self { 132 | ValueBag::from_i128(*v) 133 | } 134 | } 135 | 136 | #[cfg(not(feature = "inline-i128"))] 137 | impl<'v> From<&'v i128> for ValueBag<'v> { 138 | #[inline] 139 | fn from(v: &'v i128) -> Self { 140 | ValueBag::from_i128_ref(v) 141 | } 142 | } 143 | 144 | #[cfg(feature = "inline-i128")] 145 | impl<'v> From for ValueBag<'v> { 146 | #[inline] 147 | fn from(v: i128) -> Self { 148 | ValueBag::from_i128(v) 149 | } 150 | } 151 | 152 | impl<'v> TryFrom> for i128 { 153 | type Error = Error; 154 | 155 | #[inline] 156 | fn try_from(v: ValueBag<'v>) -> Result { 157 | v.to_i128().ok_or_else(|| Error::msg("conversion failed")) 158 | } 159 | } 160 | 161 | impl<'v> From<&'v str> for ValueBag<'v> { 162 | #[inline] 163 | fn from(v: &'v str) -> Self { 164 | ValueBag::from_str(v) 165 | } 166 | } 167 | 168 | impl<'v> From> for ValueBag<'v> { 169 | #[inline] 170 | fn from(v: Option<&'v str>) -> Self { 171 | ValueBag::from_option(v) 172 | } 173 | } 174 | 175 | impl<'v> TryFrom> for &'v str { 176 | type Error = Error; 177 | 178 | #[inline] 179 | fn try_from(v: ValueBag<'v>) -> Result { 180 | v.to_borrowed_str() 181 | .ok_or_else(|| Error::msg("conversion failed")) 182 | } 183 | } 184 | 185 | impl<'v, 'u> From<&'v &'u str> for ValueBag<'v> 186 | where 187 | 'u: 'v, 188 | { 189 | #[inline] 190 | fn from(v: &'v &'u str) -> Self { 191 | ValueBag::from_str(v) 192 | } 193 | } 194 | 195 | #[cfg(feature = "alloc")] 196 | mod alloc_support { 197 | use super::*; 198 | 199 | use crate::std::{borrow::Cow, string::String}; 200 | 201 | impl<'v> From<&'v String> for ValueBag<'v> { 202 | #[inline] 203 | fn from(v: &'v String) -> Self { 204 | ValueBag::from_str(v) 205 | } 206 | } 207 | 208 | impl<'v> TryFrom> for String { 209 | type Error = Error; 210 | 211 | #[inline] 212 | fn try_from(v: ValueBag<'v>) -> Result { 213 | Ok(v.to_str() 214 | .ok_or_else(|| Error::msg("conversion failed"))? 215 | .into_owned()) 216 | } 217 | } 218 | 219 | impl<'v> From<&'v Cow<'v, str>> for ValueBag<'v> { 220 | #[inline] 221 | fn from(v: &'v Cow<'v, str>) -> Self { 222 | ValueBag::from_str(v) 223 | } 224 | } 225 | 226 | impl<'v> TryFrom> for Cow<'v, str> { 227 | type Error = Error; 228 | 229 | #[inline] 230 | fn try_from(v: ValueBag<'v>) -> Result { 231 | v.to_str().ok_or_else(|| Error::msg("conversion failed")) 232 | } 233 | } 234 | } 235 | 236 | #[cfg(feature = "owned")] 237 | mod owned_support { 238 | use super::*; 239 | 240 | use crate::OwnedValueBag; 241 | 242 | impl<'v> From<&'v OwnedValueBag> for ValueBag<'v> { 243 | #[inline] 244 | fn from(v: &'v OwnedValueBag) -> ValueBag<'v> { 245 | v.by_ref() 246 | } 247 | } 248 | } 249 | 250 | #[cfg(test)] 251 | mod tests { 252 | #[cfg(target_arch = "wasm32")] 253 | use wasm_bindgen_test::*; 254 | 255 | use crate::{ 256 | std::{borrow::ToOwned, string::ToString}, 257 | test::{IntoValueBag, TestToken}, 258 | }; 259 | 260 | #[test] 261 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 262 | fn test_into_display() { 263 | assert_eq!(42u64.into_value_bag().by_ref().to_string(), "42"); 264 | assert_eq!(42i64.into_value_bag().by_ref().to_string(), "42"); 265 | assert_eq!(42.01f64.into_value_bag().by_ref().to_string(), "42.01"); 266 | assert_eq!(true.into_value_bag().by_ref().to_string(), "true"); 267 | assert_eq!('a'.into_value_bag().by_ref().to_string(), "a"); 268 | assert_eq!( 269 | "a loong string".into_value_bag().by_ref().to_string(), 270 | "a loong string" 271 | ); 272 | assert_eq!(().into_value_bag().by_ref().to_string(), "None"); 273 | } 274 | 275 | #[test] 276 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 277 | fn test_into_structured() { 278 | assert_eq!( 279 | 42u64.into_value_bag().by_ref().to_test_token(), 280 | TestToken::U64(42) 281 | ); 282 | assert_eq!( 283 | 42i64.into_value_bag().by_ref().to_test_token(), 284 | TestToken::I64(42) 285 | ); 286 | assert_eq!( 287 | 42.01f64.into_value_bag().by_ref().to_test_token(), 288 | TestToken::F64(42.01) 289 | ); 290 | assert_eq!( 291 | true.into_value_bag().by_ref().to_test_token(), 292 | TestToken::Bool(true) 293 | ); 294 | assert_eq!( 295 | 'a'.into_value_bag().by_ref().to_test_token(), 296 | TestToken::Char('a') 297 | ); 298 | assert_eq!( 299 | "a loong string".into_value_bag().by_ref().to_test_token(), 300 | TestToken::Str("a loong string".to_owned()) 301 | ); 302 | assert_eq!( 303 | ().into_value_bag().by_ref().to_test_token(), 304 | TestToken::None 305 | ); 306 | 307 | #[cfg(feature = "inline-i128")] 308 | { 309 | assert_eq!( 310 | 42u128.into_value_bag().by_ref().to_test_token(), 311 | TestToken::U128(42) 312 | ); 313 | assert_eq!( 314 | 42i128.into_value_bag().by_ref().to_test_token(), 315 | TestToken::I128(42) 316 | ); 317 | } 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/internal/cast/mod.rs: -------------------------------------------------------------------------------- 1 | //! Coerce a `Value` into some concrete types. 2 | //! 3 | //! These operations are cheap when the captured value is a simple primitive, 4 | //! but may end up executing arbitrary caller code if the value is complex. 5 | //! They will also attempt to downcast erased types into a primitive where possible. 6 | 7 | use crate::std::fmt; 8 | 9 | #[cfg(feature = "alloc")] 10 | use crate::std::{borrow::ToOwned, string::String}; 11 | 12 | use super::{Internal, InternalVisitor}; 13 | use crate::{Error, ValueBag}; 14 | 15 | mod primitive; 16 | 17 | impl ValueBag<'static> { 18 | /// Try capture an owned raw value. 19 | /// 20 | /// This method will return `Some` if the value is a simple primitive 21 | /// that can be captured without losing its structure. In other cases 22 | /// this method will return `None`. 23 | #[cfg(feature = "owned")] 24 | pub fn try_capture_owned(value: &'_ T) -> Option 25 | where 26 | T: ?Sized + 'static, 27 | { 28 | primitive::from_owned_any(value) 29 | } 30 | } 31 | 32 | impl<'v> ValueBag<'v> { 33 | /// Try capture a raw value. 34 | /// 35 | /// This method will return `Some` if the value is a simple primitive 36 | /// that can be captured without losing its structure. In other cases 37 | /// this method will return `None`. 38 | pub fn try_capture(value: &'v T) -> Option 39 | where 40 | T: ?Sized + 'static, 41 | { 42 | primitive::from_any(value) 43 | } 44 | 45 | /// Try get a `u64` from this value. 46 | /// 47 | /// This method is cheap for primitive types, but may call arbitrary 48 | /// serialization implementations for complex ones. 49 | pub fn to_u64(&self) -> Option { 50 | self.inner.cast().into_u64() 51 | } 52 | 53 | /// Try get a `i64` from this value. 54 | /// 55 | /// This method is cheap for primitive types, but may call arbitrary 56 | /// serialization implementations for complex ones. 57 | pub fn to_i64(&self) -> Option { 58 | self.inner.cast().into_i64() 59 | } 60 | 61 | /// Try get a `u128` from this value. 62 | /// 63 | /// This method is cheap for primitive types, but may call arbitrary 64 | /// serialization implementations for complex ones. 65 | pub fn to_u128(&self) -> Option { 66 | self.inner.cast().into_u128() 67 | } 68 | 69 | /// Try get a `i128` from this value. 70 | /// 71 | /// This method is cheap for primitive types, but may call arbitrary 72 | /// serialization implementations for complex ones. 73 | pub fn to_i128(&self) -> Option { 74 | self.inner.cast().into_i128() 75 | } 76 | 77 | /// Try get a `f64` from this value. 78 | /// 79 | /// This method is cheap for primitive types, but may call arbitrary 80 | /// serialization implementations for complex ones. 81 | /// 82 | /// This method is based on standard `TryInto` conversions, and will 83 | /// only return `Some` if there's a guaranteed lossless conversion between 84 | /// the source and destination types. For a more lenient alternative, see 85 | /// [`ValueBag::as_f64`]. 86 | pub fn to_f64(&self) -> Option { 87 | self.inner.cast().into_f64() 88 | } 89 | 90 | /// Get a `f64` from this value. 91 | /// 92 | /// This method is cheap for primitive types, but may call arbitrary 93 | /// serialization implementations for complex ones. 94 | /// 95 | /// This method is like [`ValueBag::to_f64`] except will always return 96 | /// a `f64`, regardless of the actual type of underlying value. For 97 | /// numeric types, it will use a regular `as` conversion, which may be lossy. 98 | /// For non-numeric types it will return `NaN`. 99 | pub fn as_f64(&self) -> f64 { 100 | self.inner.cast().as_f64() 101 | } 102 | 103 | /// Try get a `bool` from this value. 104 | /// 105 | /// This method is cheap for primitive types, but may call arbitrary 106 | /// serialization implementations for complex ones. 107 | pub fn to_bool(&self) -> Option { 108 | self.inner.cast().into_bool() 109 | } 110 | 111 | /// Try get a `char` from this value. 112 | /// 113 | /// This method is cheap for primitive types, but may call arbitrary 114 | /// serialization implementations for complex ones. 115 | pub fn to_char(&self) -> Option { 116 | self.inner.cast().into_char() 117 | } 118 | 119 | /// Try get a `str` from this value. 120 | /// 121 | /// This method is cheap for primitive types. It won't allocate an owned 122 | /// `String` if the value is a complex type. 123 | pub fn to_borrowed_str(&self) -> Option<&'v str> { 124 | self.inner.cast().into_borrowed_str() 125 | } 126 | 127 | /// Check whether this value is empty. 128 | pub fn is_empty(&self) -> bool { 129 | matches!(self.inner, Internal::None) 130 | } 131 | 132 | /// Check whether this value can be downcast to `T`. 133 | pub fn is(&self) -> bool { 134 | self.downcast_ref::().is_some() 135 | } 136 | 137 | /// Try downcast this value to `T`. 138 | pub fn downcast_ref(&self) -> Option<&T> { 139 | match self.inner { 140 | Internal::Debug(value) => value.as_any().downcast_ref(), 141 | Internal::Display(value) => value.as_any().downcast_ref(), 142 | #[cfg(feature = "error")] 143 | Internal::Error(value) => value.as_any().downcast_ref(), 144 | #[cfg(feature = "sval2")] 145 | Internal::Sval2(value) => value.as_any().downcast_ref(), 146 | #[cfg(feature = "serde1")] 147 | Internal::Serde1(value) => value.as_any().downcast_ref(), 148 | 149 | #[cfg(feature = "owned")] 150 | Internal::SharedDebug(ref value) => value.as_any().downcast_ref(), 151 | #[cfg(feature = "owned")] 152 | Internal::SharedDisplay(ref value) => value.as_any().downcast_ref(), 153 | #[cfg(all(feature = "error", feature = "owned"))] 154 | Internal::SharedError(ref value) => value.as_any().downcast_ref(), 155 | #[cfg(all(feature = "serde1", feature = "owned"))] 156 | Internal::SharedSerde1(ref value) => value.as_any().downcast_ref(), 157 | #[cfg(all(feature = "sval2", feature = "owned"))] 158 | Internal::SharedSval2(ref value) => value.as_any().downcast_ref(), 159 | #[cfg(all(feature = "seq", feature = "owned"))] 160 | Internal::SharedSeq(ref value) => value.as_any().downcast_ref(), 161 | 162 | #[cfg(feature = "owned")] 163 | Internal::SharedRefDebug(value) => value.as_any().downcast_ref(), 164 | #[cfg(feature = "owned")] 165 | Internal::SharedRefDisplay(value) => value.as_any().downcast_ref(), 166 | #[cfg(all(feature = "error", feature = "owned"))] 167 | Internal::SharedRefError(value) => value.as_any().downcast_ref(), 168 | #[cfg(all(feature = "serde1", feature = "owned"))] 169 | Internal::SharedRefSerde1(value) => value.as_any().downcast_ref(), 170 | #[cfg(all(feature = "sval2", feature = "owned"))] 171 | Internal::SharedRefSval2(value) => value.as_any().downcast_ref(), 172 | #[cfg(all(feature = "seq", feature = "owned"))] 173 | Internal::SharedRefSeq(value) => value.as_any().downcast_ref(), 174 | 175 | _ => None, 176 | } 177 | } 178 | } 179 | 180 | impl<'v> Internal<'v> { 181 | /// Cast the inner value to another type. 182 | #[inline] 183 | fn cast(&self) -> Cast<'v> { 184 | struct CastVisitor<'v>(Cast<'v>); 185 | 186 | impl<'v> InternalVisitor<'v> for CastVisitor<'v> { 187 | #[inline] 188 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { 189 | v.fill(crate::fill::Slot::new(self)) 190 | } 191 | 192 | #[inline] 193 | fn debug(&mut self, _: &dyn fmt::Debug) -> Result<(), Error> { 194 | Ok(()) 195 | } 196 | 197 | #[inline] 198 | fn display(&mut self, _: &dyn fmt::Display) -> Result<(), Error> { 199 | Ok(()) 200 | } 201 | 202 | #[inline] 203 | fn u64(&mut self, v: u64) -> Result<(), Error> { 204 | self.0 = Cast::Unsigned(v); 205 | Ok(()) 206 | } 207 | 208 | #[inline] 209 | fn i64(&mut self, v: i64) -> Result<(), Error> { 210 | self.0 = Cast::Signed(v); 211 | Ok(()) 212 | } 213 | 214 | #[inline] 215 | fn u128(&mut self, v: &u128) -> Result<(), Error> { 216 | self.0 = Cast::BigUnsigned(*v); 217 | Ok(()) 218 | } 219 | 220 | #[inline] 221 | fn i128(&mut self, v: &i128) -> Result<(), Error> { 222 | self.0 = Cast::BigSigned(*v); 223 | Ok(()) 224 | } 225 | 226 | #[inline] 227 | fn f64(&mut self, v: f64) -> Result<(), Error> { 228 | self.0 = Cast::Float(v); 229 | Ok(()) 230 | } 231 | 232 | #[inline] 233 | fn bool(&mut self, v: bool) -> Result<(), Error> { 234 | self.0 = Cast::Bool(v); 235 | Ok(()) 236 | } 237 | 238 | #[inline] 239 | fn char(&mut self, v: char) -> Result<(), Error> { 240 | self.0 = Cast::Char(v); 241 | Ok(()) 242 | } 243 | 244 | #[cfg(feature = "alloc")] 245 | #[inline] 246 | fn str(&mut self, s: &str) -> Result<(), Error> { 247 | self.0 = Cast::String(s.to_owned()); 248 | Ok(()) 249 | } 250 | 251 | #[cfg(not(feature = "alloc"))] 252 | #[inline] 253 | fn str(&mut self, _: &str) -> Result<(), Error> { 254 | Ok(()) 255 | } 256 | 257 | #[inline] 258 | fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { 259 | self.0 = Cast::Str(v); 260 | Ok(()) 261 | } 262 | 263 | #[inline] 264 | fn none(&mut self) -> Result<(), Error> { 265 | self.0 = Cast::None; 266 | Ok(()) 267 | } 268 | 269 | #[cfg(feature = "error")] 270 | #[inline] 271 | fn error(&mut self, _: &dyn super::error::Error) -> Result<(), Error> { 272 | Ok(()) 273 | } 274 | 275 | #[cfg(feature = "sval2")] 276 | #[inline] 277 | fn sval2(&mut self, v: &dyn super::sval::v2::Value) -> Result<(), Error> { 278 | if super::sval::v2::internal_visit(v, self) { 279 | Ok(()) 280 | } else { 281 | Err(Error::msg("invalid cast")) 282 | } 283 | } 284 | 285 | #[cfg(feature = "sval2")] 286 | fn borrowed_sval2(&mut self, v: &'v dyn super::sval::v2::Value) -> Result<(), Error> { 287 | if super::sval::v2::borrowed_internal_visit(v, self) { 288 | Ok(()) 289 | } else { 290 | Err(Error::msg("invalid cast")) 291 | } 292 | } 293 | 294 | #[cfg(feature = "serde1")] 295 | #[inline] 296 | fn serde1(&mut self, v: &dyn super::serde::v1::Serialize) -> Result<(), Error> { 297 | if super::serde::v1::internal_visit(v, self) { 298 | Ok(()) 299 | } else { 300 | Err(Error::msg("invalid cast")) 301 | } 302 | } 303 | 304 | #[cfg(feature = "seq")] 305 | fn seq(&mut self, _: &dyn super::seq::Seq) -> Result<(), Error> { 306 | self.0 = Cast::None; 307 | Ok(()) 308 | } 309 | 310 | fn poisoned(&mut self, _: &'static str) -> Result<(), Error> { 311 | self.0 = Cast::None; 312 | Ok(()) 313 | } 314 | } 315 | 316 | match &self { 317 | Internal::Signed(value) => Cast::Signed(*value), 318 | Internal::Unsigned(value) => Cast::Unsigned(*value), 319 | #[cfg(feature = "inline-i128")] 320 | Internal::BigSigned(value) => Cast::BigSigned(*value), 321 | #[cfg(not(feature = "inline-i128"))] 322 | Internal::BigSigned(value) => Cast::BigSigned(**value), 323 | #[cfg(feature = "inline-i128")] 324 | Internal::BigUnsigned(value) => Cast::BigUnsigned(*value), 325 | #[cfg(not(feature = "inline-i128"))] 326 | Internal::BigUnsigned(value) => Cast::BigUnsigned(**value), 327 | Internal::Float(value) => Cast::Float(*value), 328 | Internal::Bool(value) => Cast::Bool(*value), 329 | Internal::Char(value) => Cast::Char(*value), 330 | Internal::Str(value) => Cast::Str(value), 331 | Internal::None => Cast::None, 332 | other => { 333 | // If the erased value isn't a primitive then we visit it 334 | let mut cast = CastVisitor(Cast::None); 335 | let _ = other.internal_visit(&mut cast); 336 | cast.0 337 | } 338 | } 339 | } 340 | } 341 | 342 | pub(in crate::internal) enum Cast<'v> { 343 | Signed(i64), 344 | Unsigned(u64), 345 | BigSigned(i128), 346 | BigUnsigned(u128), 347 | Float(f64), 348 | Bool(bool), 349 | Char(char), 350 | Str(&'v str), 351 | None, 352 | #[cfg(feature = "alloc")] 353 | String(String), 354 | } 355 | 356 | impl<'v> Cast<'v> { 357 | #[inline] 358 | fn into_borrowed_str(self) -> Option<&'v str> { 359 | if let Cast::Str(value) = self { 360 | Some(value) 361 | } else { 362 | None 363 | } 364 | } 365 | 366 | #[inline] 367 | fn into_u64(self) -> Option { 368 | match self { 369 | Cast::Unsigned(value) => Some(value), 370 | Cast::BigUnsigned(value) => value.try_into().ok(), 371 | Cast::Signed(value) => value.try_into().ok(), 372 | Cast::BigSigned(value) => value.try_into().ok(), 373 | _ => None, 374 | } 375 | } 376 | 377 | #[inline] 378 | fn into_i64(self) -> Option { 379 | match self { 380 | Cast::Signed(value) => Some(value), 381 | Cast::BigSigned(value) => value.try_into().ok(), 382 | Cast::Unsigned(value) => value.try_into().ok(), 383 | Cast::BigUnsigned(value) => value.try_into().ok(), 384 | _ => None, 385 | } 386 | } 387 | 388 | #[inline] 389 | fn into_u128(self) -> Option { 390 | match self { 391 | Cast::BigUnsigned(value) => Some(value), 392 | Cast::Unsigned(value) => Some(value.into()), 393 | Cast::Signed(value) => value.try_into().ok(), 394 | Cast::BigSigned(value) => value.try_into().ok(), 395 | _ => None, 396 | } 397 | } 398 | 399 | #[inline] 400 | fn into_i128(self) -> Option { 401 | match self { 402 | Cast::BigSigned(value) => Some(value), 403 | Cast::Signed(value) => Some(value.into()), 404 | Cast::Unsigned(value) => value.try_into().ok(), 405 | Cast::BigUnsigned(value) => value.try_into().ok(), 406 | _ => None, 407 | } 408 | } 409 | 410 | #[inline] 411 | fn into_f64(self) -> Option { 412 | match self { 413 | Cast::Float(value) => Some(value), 414 | Cast::Unsigned(value) => u32::try_from(value) 415 | .ok() 416 | .and_then(|value| value.try_into().ok()), 417 | Cast::Signed(value) => i32::try_from(value) 418 | .ok() 419 | .and_then(|value| value.try_into().ok()), 420 | Cast::BigUnsigned(value) => u32::try_from(value) 421 | .ok() 422 | .and_then(|value| value.try_into().ok()), 423 | Cast::BigSigned(value) => i32::try_from(value) 424 | .ok() 425 | .and_then(|value| value.try_into().ok()), 426 | _ => None, 427 | } 428 | } 429 | 430 | #[inline] 431 | fn as_f64(self) -> f64 { 432 | match self { 433 | Cast::Float(value) => value, 434 | Cast::Unsigned(value) => value as f64, 435 | Cast::Signed(value) => value as f64, 436 | Cast::BigUnsigned(value) => value as f64, 437 | Cast::BigSigned(value) => value as f64, 438 | _ => f64::NAN, 439 | } 440 | } 441 | 442 | #[inline] 443 | fn into_char(self) -> Option { 444 | if let Cast::Char(value) = self { 445 | Some(value) 446 | } else { 447 | None 448 | } 449 | } 450 | 451 | #[inline] 452 | fn into_bool(self) -> Option { 453 | if let Cast::Bool(value) = self { 454 | Some(value) 455 | } else { 456 | None 457 | } 458 | } 459 | } 460 | 461 | #[cfg(feature = "alloc")] 462 | mod alloc_support { 463 | use super::*; 464 | 465 | use crate::std::borrow::Cow; 466 | 467 | impl<'v> ValueBag<'v> { 468 | /// Try get a `str` from this value. 469 | /// 470 | /// This method is cheap for primitive types, but may call arbitrary 471 | /// serialization implementations for complex ones. If the serialization 472 | /// implementation produces a short lived string it will be allocated. 473 | #[inline] 474 | pub fn to_str(&self) -> Option> { 475 | self.inner.cast().into_str() 476 | } 477 | } 478 | 479 | impl<'v> Cast<'v> { 480 | #[inline] 481 | pub(in crate::internal) fn into_str(self) -> Option> { 482 | match self { 483 | Cast::Str(value) => Some(value.into()), 484 | Cast::String(value) => Some(value.into()), 485 | _ => None, 486 | } 487 | } 488 | } 489 | 490 | #[cfg(test)] 491 | mod tests { 492 | #[cfg(target_arch = "wasm32")] 493 | use wasm_bindgen_test::*; 494 | 495 | use crate::{std::borrow::ToOwned, test::IntoValueBag, ValueBag}; 496 | 497 | #[test] 498 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 499 | fn primitive_cast() { 500 | let short_lived = "a string".to_owned(); 501 | assert_eq!( 502 | "a string", 503 | (&*short_lived) 504 | .into_value_bag() 505 | .to_borrowed_str() 506 | .expect("invalid value") 507 | ); 508 | assert_eq!( 509 | "a string", 510 | &*"a string".into_value_bag().to_str().expect("invalid value") 511 | ); 512 | assert_eq!( 513 | "a string", 514 | (&*short_lived) 515 | .into_value_bag() 516 | .to_borrowed_str() 517 | .expect("invalid value") 518 | ); 519 | assert_eq!( 520 | "a string", 521 | ValueBag::try_capture(&short_lived) 522 | .expect("invalid value") 523 | .to_borrowed_str() 524 | .expect("invalid value") 525 | ); 526 | } 527 | } 528 | } 529 | 530 | #[cfg(test)] 531 | mod tests { 532 | #[cfg(target_arch = "wasm32")] 533 | use wasm_bindgen_test::*; 534 | 535 | use super::*; 536 | 537 | use crate::test::IntoValueBag; 538 | 539 | #[test] 540 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 541 | fn is_empty() { 542 | assert!(ValueBag::from(None::).is_empty(),); 543 | 544 | assert!(ValueBag::try_capture(&None::).unwrap().is_empty(),); 545 | } 546 | 547 | #[test] 548 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 549 | fn primitive_capture_str() { 550 | let s: &str = "short lived"; 551 | assert_eq!( 552 | "short lived", 553 | ValueBag::try_capture(s) 554 | .unwrap() 555 | .to_borrowed_str() 556 | .expect("invalid value") 557 | ); 558 | } 559 | 560 | #[test] 561 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 562 | fn primitive_cast() { 563 | assert_eq!( 564 | "a string", 565 | "a string" 566 | .into_value_bag() 567 | .by_ref() 568 | .to_borrowed_str() 569 | .expect("invalid value") 570 | ); 571 | 572 | assert_eq!( 573 | 1u64, 574 | 1u8.into_value_bag() 575 | .by_ref() 576 | .to_u64() 577 | .expect("invalid value") 578 | ); 579 | assert_eq!( 580 | 1u64, 581 | 1u16.into_value_bag() 582 | .by_ref() 583 | .to_u64() 584 | .expect("invalid value") 585 | ); 586 | assert_eq!( 587 | 1u64, 588 | 1u32.into_value_bag() 589 | .by_ref() 590 | .to_u64() 591 | .expect("invalid value") 592 | ); 593 | assert_eq!( 594 | 1u64, 595 | 1u64.into_value_bag() 596 | .by_ref() 597 | .to_u64() 598 | .expect("invalid value") 599 | ); 600 | assert_eq!( 601 | 1u64, 602 | 1usize 603 | .into_value_bag() 604 | .by_ref() 605 | .to_u64() 606 | .expect("invalid value") 607 | ); 608 | assert_eq!( 609 | 1u128, 610 | 1u128 611 | .into_value_bag() 612 | .by_ref() 613 | .to_u128() 614 | .expect("invalid value") 615 | ); 616 | 617 | assert_eq!( 618 | -1i64, 619 | -(1i8 620 | .into_value_bag() 621 | .by_ref() 622 | .to_i64() 623 | .expect("invalid value")) 624 | ); 625 | assert_eq!( 626 | -1i64, 627 | -(1i8 628 | .into_value_bag() 629 | .by_ref() 630 | .to_i64() 631 | .expect("invalid value")) 632 | ); 633 | assert_eq!( 634 | -1i64, 635 | -(1i8 636 | .into_value_bag() 637 | .by_ref() 638 | .to_i64() 639 | .expect("invalid value")) 640 | ); 641 | assert_eq!( 642 | -1i64, 643 | -(1i64 644 | .into_value_bag() 645 | .by_ref() 646 | .to_i64() 647 | .expect("invalid value")) 648 | ); 649 | assert_eq!( 650 | -1i64, 651 | -(1isize 652 | .into_value_bag() 653 | .by_ref() 654 | .to_i64() 655 | .expect("invalid value")) 656 | ); 657 | assert_eq!( 658 | -1i128, 659 | -(1i128 660 | .into_value_bag() 661 | .by_ref() 662 | .to_i128() 663 | .expect("invalid value")) 664 | ); 665 | 666 | assert!(1f64.into_value_bag().by_ref().to_f64().is_some()); 667 | assert!(1u64.into_value_bag().by_ref().to_f64().is_some()); 668 | assert!((-1i64).into_value_bag().by_ref().to_f64().is_some()); 669 | assert!(1u128.into_value_bag().by_ref().to_f64().is_some()); 670 | assert!((-1i128).into_value_bag().by_ref().to_f64().is_some()); 671 | 672 | assert!(u64::MAX.into_value_bag().by_ref().to_u128().is_some()); 673 | assert!(i64::MIN.into_value_bag().by_ref().to_i128().is_some()); 674 | assert!(i64::MAX.into_value_bag().by_ref().to_u64().is_some()); 675 | 676 | assert!((-1i64).into_value_bag().by_ref().to_u64().is_none()); 677 | assert!(u64::MAX.into_value_bag().by_ref().to_i64().is_none()); 678 | assert!(u64::MAX.into_value_bag().by_ref().to_f64().is_none()); 679 | 680 | assert!(i128::MAX.into_value_bag().by_ref().to_i64().is_none()); 681 | assert!(u128::MAX.into_value_bag().by_ref().to_u64().is_none()); 682 | 683 | assert!(1f64.into_value_bag().by_ref().to_u64().is_none()); 684 | 685 | assert_eq!( 686 | 'a', 687 | 'a'.into_value_bag() 688 | .by_ref() 689 | .to_char() 690 | .expect("invalid value") 691 | ); 692 | assert!(true 693 | .into_value_bag() 694 | .by_ref() 695 | .to_bool() 696 | .expect("invalid value")); 697 | } 698 | 699 | #[test] 700 | fn as_cast() { 701 | assert_eq!(1.0, 1f64.into_value_bag().as_f64()); 702 | assert_eq!(1.0, 1u64.into_value_bag().as_f64()); 703 | assert_eq!(-1.0, -(1i64.into_value_bag().as_f64())); 704 | assert!(true.into_value_bag().as_f64().is_nan()); 705 | } 706 | } 707 | -------------------------------------------------------------------------------- /src/internal/cast/primitive.rs: -------------------------------------------------------------------------------- 1 | /* 2 | This module generates code to try efficiently convert some arbitrary `T: 'static` into 3 | a `Internal`. 4 | */ 5 | 6 | #[cfg(feature = "alloc")] 7 | use crate::std::string::String; 8 | 9 | use crate::ValueBag; 10 | 11 | // NOTE: The casts for unsized values (str) are dubious here. To really do this properly 12 | // we need https://github.com/rust-lang/rust/issues/81513 13 | // NOTE: With some kind of const `Any::is` we could do all this at compile-time 14 | // Older versions of `value-bag` did this, but the infrastructure just wasn't worth 15 | // the tiny performance improvement 16 | use crate::std::any::TypeId; 17 | 18 | enum Void {} 19 | 20 | #[repr(transparent)] 21 | struct VoidRef<'a>(*const &'a Void); 22 | 23 | macro_rules! check_type_ids { 24 | (&$l:lifetime $v:ident => $( 25 | $(#[cfg($($cfg:tt)*)])* 26 | $ty:ty, 27 | )* 28 | ) => { 29 | $( 30 | $(#[cfg($($cfg)*)])* 31 | if TypeId::of::() == TypeId::of::<$ty>() { 32 | // SAFETY: We verify the value is $ty before casting 33 | let v = unsafe { *($v.0 as *const & $l $ty) }; 34 | 35 | return Some(ValueBag::from(v)); 36 | } 37 | )* 38 | $( 39 | $(#[cfg($($cfg)*)])* 40 | if TypeId::of::() == TypeId::of::>() { 41 | // SAFETY: We verify the value is Option<$ty> before casting 42 | let v = unsafe { *($v.0 as *const & $l Option<$ty>) }; 43 | 44 | if let Some(v) = v { 45 | return Some(ValueBag::from(v)); 46 | } else { 47 | return Some(ValueBag::empty()); 48 | } 49 | } 50 | )* 51 | }; 52 | } 53 | 54 | pub(in crate::internal) fn from_any<'v, T: ?Sized + 'static>(value: &'v T) -> Option> { 55 | let type_ids = |v: VoidRef<'v>| { 56 | if TypeId::of::() == TypeId::of::() { 57 | // SAFETY: We verify the value is str before casting 58 | let v = unsafe { *(v.0 as *const &'v str) }; 59 | 60 | return Some(ValueBag::from(v)); 61 | } 62 | 63 | check_type_ids!( 64 | &'v v => 65 | usize, 66 | u8, 67 | u16, 68 | u32, 69 | u64, 70 | u128, 71 | isize, 72 | i8, 73 | i16, 74 | i32, 75 | i64, 76 | i128, 77 | f32, 78 | f64, 79 | char, 80 | bool, 81 | &'static str, 82 | // We deal with `str` separately because it's unsized 83 | // str, 84 | #[cfg(feature = "alloc")] 85 | String, 86 | ); 87 | 88 | None 89 | }; 90 | 91 | (type_ids)(VoidRef(&(value) as *const &'v T as *const &'v Void)) 92 | } 93 | 94 | #[cfg(feature = "owned")] 95 | pub(in crate::internal) fn from_owned_any<'a, T: ?Sized + 'static>( 96 | value: &'a T, 97 | ) -> Option> { 98 | let type_ids = |v: VoidRef<'a>| { 99 | check_type_ids!( 100 | &'a v => 101 | usize, 102 | u8, 103 | u16, 104 | u32, 105 | u64, 106 | #[cfg(feature = "inline-i128")] 107 | u128, 108 | isize, 109 | i8, 110 | i16, 111 | i32, 112 | i64, 113 | #[cfg(feature = "inline-i128")] 114 | i128, 115 | f32, 116 | f64, 117 | char, 118 | bool, 119 | ); 120 | 121 | None 122 | }; 123 | 124 | (type_ids)(VoidRef(&(value) as *const &'a T as *const &'a Void)) 125 | } 126 | -------------------------------------------------------------------------------- /src/internal/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fill::Slot, 3 | std::{any::Any, error}, 4 | ValueBag, 5 | }; 6 | 7 | use super::Internal; 8 | 9 | impl<'v> ValueBag<'v> { 10 | /// Get a value from an error. 11 | pub fn capture_error(value: &'v T) -> Self 12 | where 13 | T: error::Error + 'static, 14 | { 15 | ValueBag { 16 | inner: Internal::Error(value), 17 | } 18 | } 19 | 20 | /// Get a value from an erased value. 21 | #[inline] 22 | pub const fn from_dyn_error(value: &'v (dyn error::Error + 'static)) -> Self { 23 | ValueBag { 24 | inner: Internal::AnonError(value), 25 | } 26 | } 27 | 28 | /// Try get an error from this value. 29 | #[inline] 30 | pub fn to_borrowed_error(&self) -> Option<&'v (dyn Error + 'static)> { 31 | match self.inner { 32 | Internal::Error(value) => Some(value.as_super()), 33 | Internal::AnonError(value) => Some(value), 34 | _ => None, 35 | } 36 | } 37 | } 38 | 39 | #[cfg(feature = "error")] 40 | pub(crate) trait DowncastError { 41 | fn as_any(&self) -> &dyn Any; 42 | fn as_super(&self) -> &(dyn error::Error + 'static); 43 | } 44 | 45 | #[cfg(feature = "error")] 46 | impl DowncastError for T { 47 | fn as_any(&self) -> &dyn Any { 48 | self 49 | } 50 | 51 | fn as_super(&self) -> &(dyn error::Error + 'static) { 52 | self 53 | } 54 | } 55 | 56 | impl<'s, 'f> Slot<'s, 'f> { 57 | /// Fill the slot with an error. 58 | /// 59 | /// The given value doesn't need to satisfy any particular lifetime constraints. 60 | pub fn fill_error(self, value: T) -> Result<(), crate::Error> 61 | where 62 | T: error::Error + 'static, 63 | { 64 | self.fill(|visitor| visitor.error(&value)) 65 | } 66 | 67 | /// Fill the slot with an error. 68 | pub fn fill_dyn_error(self, value: &(dyn error::Error + 'static)) -> Result<(), crate::Error> { 69 | self.fill(|visitor| visitor.error(value)) 70 | } 71 | } 72 | 73 | pub use self::error::Error; 74 | 75 | #[cfg(feature = "owned")] 76 | pub(crate) mod owned { 77 | use crate::std::{boxed::Box, error, fmt, string::ToString}; 78 | 79 | #[derive(Clone, Debug)] 80 | pub(crate) struct OwnedError { 81 | display: Box, 82 | source: Option>, 83 | } 84 | 85 | impl fmt::Display for OwnedError { 86 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 87 | fmt::Display::fmt(&self.display, f) 88 | } 89 | } 90 | 91 | impl error::Error for OwnedError { 92 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { 93 | if let Some(ref source) = self.source { 94 | Some(&**source) 95 | } else { 96 | None 97 | } 98 | } 99 | } 100 | 101 | pub(crate) fn buffer(err: &(dyn error::Error + 'static)) -> OwnedError { 102 | OwnedError { 103 | display: err.to_string().into(), 104 | source: err.source().map(buffer).map(Box::new), 105 | } 106 | } 107 | } 108 | 109 | impl<'v> From<&'v (dyn error::Error + 'static)> for ValueBag<'v> { 110 | #[inline] 111 | fn from(v: &'v (dyn error::Error + 'static)) -> Self { 112 | ValueBag::from_dyn_error(v) 113 | } 114 | } 115 | 116 | impl<'v> From> for ValueBag<'v> { 117 | #[inline] 118 | fn from(v: Option<&'v (dyn error::Error + 'static)>) -> Self { 119 | ValueBag::from_option(v) 120 | } 121 | } 122 | 123 | impl<'v> TryFrom> for &'v (dyn error::Error + 'static) { 124 | type Error = crate::Error; 125 | 126 | #[inline] 127 | fn try_from(v: ValueBag<'v>) -> Result { 128 | v.to_borrowed_error() 129 | .ok_or_else(|| Self::Error::msg("conversion failed")) 130 | } 131 | } 132 | 133 | impl<'v, 'u> From<&'v &'u (dyn error::Error + 'static)> for ValueBag<'v> 134 | where 135 | 'u: 'v, 136 | { 137 | #[inline] 138 | fn from(v: &'v &'u (dyn error::Error + 'static)) -> Self { 139 | ValueBag::from_dyn_error(*v) 140 | } 141 | } 142 | 143 | #[cfg(test)] 144 | mod tests { 145 | #[cfg(target_arch = "wasm32")] 146 | use wasm_bindgen_test::*; 147 | 148 | use super::*; 149 | 150 | use crate::{ 151 | std::{io, string::ToString}, 152 | test::*, 153 | }; 154 | 155 | #[test] 156 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 157 | fn error_capture() { 158 | let err = io::Error::from(io::ErrorKind::Other); 159 | 160 | assert_eq!( 161 | err.to_string(), 162 | ValueBag::capture_error(&err) 163 | .to_borrowed_error() 164 | .expect("invalid value") 165 | .to_string() 166 | ); 167 | 168 | assert_eq!( 169 | err.to_string(), 170 | ValueBag::from_dyn_error(&err) 171 | .to_borrowed_error() 172 | .expect("invalid value") 173 | .to_string() 174 | ); 175 | } 176 | 177 | #[test] 178 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 179 | fn error_downcast() { 180 | let err = io::Error::from(io::ErrorKind::Other); 181 | 182 | assert!(ValueBag::capture_error(&err) 183 | .downcast_ref::() 184 | .is_some()); 185 | } 186 | 187 | #[test] 188 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 189 | fn error_visit() { 190 | let err = io::Error::from(io::ErrorKind::Other); 191 | 192 | ValueBag::from_dyn_error(&err) 193 | .visit(TestVisit::default()) 194 | .expect("failed to visit value"); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/internal/fmt.rs: -------------------------------------------------------------------------------- 1 | //! Integration between `Value` and `std::fmt`. 2 | //! 3 | //! This module allows any `Value` to implement the `Debug` and `Display` traits, 4 | //! and for any `Debug` or `Display` to be captured as a `Value`. 5 | 6 | use crate::{ 7 | fill::Slot, 8 | std::{any::Any, fmt}, 9 | Error, ValueBag, 10 | }; 11 | 12 | use super::{Internal, InternalVisitor}; 13 | 14 | impl<'v> ValueBag<'v> { 15 | /// Get a value from a debuggable type. 16 | /// 17 | /// This method will attempt to capture the given value as a well-known primitive 18 | /// before resorting to using its `Debug` implementation. 19 | pub fn capture_debug(value: &'v T) -> Self 20 | where 21 | T: Debug + 'static, 22 | { 23 | Self::try_capture(value).unwrap_or(ValueBag { 24 | inner: Internal::Debug(value), 25 | }) 26 | } 27 | 28 | /// Get a value from a displayable type. 29 | /// 30 | /// This method will attempt to capture the given value as a well-known primitive 31 | /// before resorting to using its `Display` implementation. 32 | pub fn capture_display(value: &'v T) -> Self 33 | where 34 | T: Display + 'static, 35 | { 36 | Self::try_capture(value).unwrap_or(ValueBag { 37 | inner: Internal::Display(value), 38 | }) 39 | } 40 | 41 | /// Get a value from a debuggable type without capturing support. 42 | pub const fn from_debug(value: &'v T) -> Self 43 | where 44 | T: Debug, 45 | { 46 | ValueBag { 47 | inner: Internal::AnonDebug(value), 48 | } 49 | } 50 | 51 | /// Get a value from a displayable type without capturing support. 52 | pub const fn from_display(value: &'v T) -> Self 53 | where 54 | T: Display, 55 | { 56 | ValueBag { 57 | inner: Internal::AnonDisplay(value), 58 | } 59 | } 60 | 61 | /// Get a value from a debuggable type without capturing support. 62 | #[inline] 63 | pub const fn from_dyn_debug(value: &'v dyn Debug) -> Self { 64 | ValueBag { 65 | inner: Internal::AnonDebug(value), 66 | } 67 | } 68 | 69 | /// Get a value from a displayable type without capturing support. 70 | #[inline] 71 | pub const fn from_dyn_display(value: &'v dyn Display) -> Self { 72 | ValueBag { 73 | inner: Internal::AnonDisplay(value), 74 | } 75 | } 76 | } 77 | 78 | pub(crate) trait DowncastDisplay { 79 | fn as_any(&self) -> &dyn Any; 80 | fn as_super(&self) -> &dyn fmt::Display; 81 | } 82 | 83 | impl DowncastDisplay for T { 84 | fn as_any(&self) -> &dyn Any { 85 | self 86 | } 87 | 88 | fn as_super(&self) -> &dyn fmt::Display { 89 | self 90 | } 91 | } 92 | 93 | impl<'a> fmt::Display for dyn DowncastDisplay + Send + Sync + 'a { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | self.as_super().fmt(f) 96 | } 97 | } 98 | 99 | pub(crate) trait DowncastDebug { 100 | fn as_any(&self) -> &dyn Any; 101 | fn as_super(&self) -> &dyn fmt::Debug; 102 | } 103 | 104 | impl DowncastDebug for T { 105 | fn as_any(&self) -> &dyn Any { 106 | self 107 | } 108 | 109 | fn as_super(&self) -> &dyn fmt::Debug { 110 | self 111 | } 112 | } 113 | 114 | impl<'a> fmt::Debug for dyn DowncastDebug + Send + Sync + 'a { 115 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 116 | self.as_super().fmt(f) 117 | } 118 | } 119 | 120 | impl<'s, 'f> Slot<'s, 'f> { 121 | /// Fill the slot with a debuggable value. 122 | /// 123 | /// The given value doesn't need to satisfy any particular lifetime constraints. 124 | pub fn fill_debug(self, value: T) -> Result<(), Error> 125 | where 126 | T: Debug, 127 | { 128 | self.fill(|visitor| visitor.debug(&value)) 129 | } 130 | 131 | /// Fill the slot with a displayable value. 132 | /// 133 | /// The given value doesn't need to satisfy any particular lifetime constraints. 134 | pub fn fill_display(self, value: T) -> Result<(), Error> 135 | where 136 | T: Display, 137 | { 138 | self.fill(|visitor| visitor.display(&value)) 139 | } 140 | } 141 | 142 | pub use self::fmt::{Debug, Display}; 143 | 144 | impl<'v> Debug for ValueBag<'v> { 145 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 146 | struct DebugVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>); 147 | 148 | impl<'a, 'b: 'a, 'v> InternalVisitor<'v> for DebugVisitor<'a, 'b> { 149 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { 150 | v.fill(crate::fill::Slot::new(self)) 151 | } 152 | 153 | fn debug(&mut self, v: &dyn Debug) -> Result<(), Error> { 154 | Debug::fmt(v, self.0)?; 155 | 156 | Ok(()) 157 | } 158 | 159 | fn display(&mut self, v: &dyn Display) -> Result<(), Error> { 160 | Display::fmt(v, self.0)?; 161 | 162 | Ok(()) 163 | } 164 | 165 | fn u64(&mut self, v: u64) -> Result<(), Error> { 166 | Debug::fmt(&v, self.0)?; 167 | 168 | Ok(()) 169 | } 170 | 171 | fn i64(&mut self, v: i64) -> Result<(), Error> { 172 | Debug::fmt(&v, self.0)?; 173 | 174 | Ok(()) 175 | } 176 | 177 | fn u128(&mut self, v: &u128) -> Result<(), Error> { 178 | Debug::fmt(&v, self.0)?; 179 | 180 | Ok(()) 181 | } 182 | 183 | fn i128(&mut self, v: &i128) -> Result<(), Error> { 184 | Debug::fmt(&v, self.0)?; 185 | 186 | Ok(()) 187 | } 188 | 189 | fn f64(&mut self, v: f64) -> Result<(), Error> { 190 | Debug::fmt(&v, self.0)?; 191 | 192 | Ok(()) 193 | } 194 | 195 | fn bool(&mut self, v: bool) -> Result<(), Error> { 196 | Debug::fmt(&v, self.0)?; 197 | 198 | Ok(()) 199 | } 200 | 201 | fn char(&mut self, v: char) -> Result<(), Error> { 202 | Debug::fmt(&v, self.0)?; 203 | 204 | Ok(()) 205 | } 206 | 207 | fn str(&mut self, v: &str) -> Result<(), Error> { 208 | Debug::fmt(&v, self.0)?; 209 | 210 | Ok(()) 211 | } 212 | 213 | fn none(&mut self) -> Result<(), Error> { 214 | self.debug(&format_args!("None")) 215 | } 216 | 217 | #[cfg(feature = "error")] 218 | fn error(&mut self, v: &(dyn std::error::Error + 'static)) -> Result<(), Error> { 219 | Debug::fmt(v, self.0)?; 220 | 221 | Ok(()) 222 | } 223 | 224 | #[cfg(feature = "sval2")] 225 | fn sval2(&mut self, v: &dyn crate::internal::sval::v2::Value) -> Result<(), Error> { 226 | crate::internal::sval::v2::fmt(self.0, v) 227 | } 228 | 229 | #[cfg(feature = "serde1")] 230 | fn serde1( 231 | &mut self, 232 | v: &dyn crate::internal::serde::v1::Serialize, 233 | ) -> Result<(), Error> { 234 | crate::internal::serde::v1::fmt(self.0, v) 235 | } 236 | 237 | #[cfg(feature = "seq")] 238 | fn seq(&mut self, seq: &dyn crate::internal::seq::Seq) -> Result<(), Error> { 239 | let mut visitor = seq::FmtSeq(self.0.debug_list()); 240 | seq.visit(&mut visitor); 241 | visitor.0.finish()?; 242 | 243 | Ok(()) 244 | } 245 | 246 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> { 247 | write!(self.0, "<{msg}>")?; 248 | 249 | Ok(()) 250 | } 251 | } 252 | 253 | self.internal_visit(&mut DebugVisitor(f)) 254 | .map_err(|_| fmt::Error)?; 255 | 256 | Ok(()) 257 | } 258 | } 259 | 260 | impl<'v> Display for ValueBag<'v> { 261 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 262 | struct DisplayVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>); 263 | 264 | impl<'a, 'b: 'a, 'v> InternalVisitor<'v> for DisplayVisitor<'a, 'b> { 265 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { 266 | v.fill(crate::fill::Slot::new(self)) 267 | } 268 | 269 | fn debug(&mut self, v: &dyn Debug) -> Result<(), Error> { 270 | Debug::fmt(v, self.0)?; 271 | 272 | Ok(()) 273 | } 274 | 275 | fn display(&mut self, v: &dyn Display) -> Result<(), Error> { 276 | Display::fmt(v, self.0)?; 277 | 278 | Ok(()) 279 | } 280 | 281 | fn u64(&mut self, v: u64) -> Result<(), Error> { 282 | Display::fmt(&v, self.0)?; 283 | 284 | Ok(()) 285 | } 286 | 287 | fn i64(&mut self, v: i64) -> Result<(), Error> { 288 | Display::fmt(&v, self.0)?; 289 | 290 | Ok(()) 291 | } 292 | 293 | fn u128(&mut self, v: &u128) -> Result<(), Error> { 294 | Display::fmt(&v, self.0)?; 295 | 296 | Ok(()) 297 | } 298 | 299 | fn i128(&mut self, v: &i128) -> Result<(), Error> { 300 | Display::fmt(&v, self.0)?; 301 | 302 | Ok(()) 303 | } 304 | 305 | fn f64(&mut self, v: f64) -> Result<(), Error> { 306 | Display::fmt(&v, self.0)?; 307 | 308 | Ok(()) 309 | } 310 | 311 | fn bool(&mut self, v: bool) -> Result<(), Error> { 312 | Display::fmt(&v, self.0)?; 313 | 314 | Ok(()) 315 | } 316 | 317 | fn char(&mut self, v: char) -> Result<(), Error> { 318 | Display::fmt(&v, self.0)?; 319 | 320 | Ok(()) 321 | } 322 | 323 | fn str(&mut self, v: &str) -> Result<(), Error> { 324 | Display::fmt(&v, self.0)?; 325 | 326 | Ok(()) 327 | } 328 | 329 | fn none(&mut self) -> Result<(), Error> { 330 | self.debug(&format_args!("None")) 331 | } 332 | 333 | #[cfg(feature = "error")] 334 | fn error(&mut self, v: &(dyn std::error::Error + 'static)) -> Result<(), Error> { 335 | Display::fmt(v, self.0)?; 336 | 337 | Ok(()) 338 | } 339 | 340 | #[cfg(feature = "sval2")] 341 | fn sval2(&mut self, v: &dyn crate::internal::sval::v2::Value) -> Result<(), Error> { 342 | crate::internal::sval::v2::fmt(self.0, v) 343 | } 344 | 345 | #[cfg(feature = "serde1")] 346 | fn serde1( 347 | &mut self, 348 | v: &dyn crate::internal::serde::v1::Serialize, 349 | ) -> Result<(), Error> { 350 | crate::internal::serde::v1::fmt(self.0, v) 351 | } 352 | 353 | #[cfg(feature = "seq")] 354 | fn seq(&mut self, seq: &dyn crate::internal::seq::Seq) -> Result<(), Error> { 355 | let mut visitor = seq::FmtSeq(self.0.debug_list()); 356 | seq.visit(&mut visitor); 357 | visitor.0.finish()?; 358 | 359 | Ok(()) 360 | } 361 | 362 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> { 363 | write!(self.0, "<{msg}>")?; 364 | 365 | Ok(()) 366 | } 367 | } 368 | 369 | self.internal_visit(&mut DisplayVisitor(f)) 370 | .map_err(|_| fmt::Error)?; 371 | 372 | Ok(()) 373 | } 374 | } 375 | 376 | #[cfg(feature = "seq")] 377 | mod seq { 378 | use super::*; 379 | use core::ops::ControlFlow; 380 | 381 | pub(super) struct FmtSeq<'a, 'b>(pub(super) fmt::DebugList<'b, 'a>); 382 | 383 | impl<'a, 'b, 'c> crate::internal::seq::Visitor<'c> for FmtSeq<'a, 'b> { 384 | fn element(&mut self, inner: ValueBag) -> ControlFlow<()> { 385 | self.0.entry(&inner); 386 | ControlFlow::Continue(()) 387 | } 388 | } 389 | } 390 | 391 | #[cfg(feature = "owned")] 392 | pub(crate) mod owned { 393 | use crate::std::{boxed::Box, fmt, string::ToString}; 394 | 395 | impl fmt::Debug for crate::OwnedValueBag { 396 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 397 | fmt::Debug::fmt(&self.by_ref(), f) 398 | } 399 | } 400 | 401 | impl fmt::Display for crate::OwnedValueBag { 402 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 403 | fmt::Display::fmt(&self.by_ref(), f) 404 | } 405 | } 406 | 407 | #[derive(Clone)] 408 | pub(crate) struct OwnedFmt(Box); 409 | 410 | pub(crate) fn buffer_debug(v: impl fmt::Debug) -> OwnedFmt { 411 | OwnedFmt(format!("{:?}", v).into()) 412 | } 413 | 414 | pub(crate) fn buffer_display(v: impl fmt::Display) -> OwnedFmt { 415 | OwnedFmt(v.to_string().into()) 416 | } 417 | 418 | impl fmt::Debug for OwnedFmt { 419 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 420 | fmt::Display::fmt(self, f) 421 | } 422 | } 423 | 424 | impl fmt::Display for OwnedFmt { 425 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 426 | fmt::Display::fmt(&self.0, f) 427 | } 428 | } 429 | } 430 | 431 | impl<'v> From<&'v dyn Debug> for ValueBag<'v> { 432 | #[inline] 433 | fn from(v: &'v dyn Debug) -> Self { 434 | ValueBag::from_dyn_debug(v) 435 | } 436 | } 437 | 438 | impl<'v> From> for ValueBag<'v> { 439 | #[inline] 440 | fn from(v: Option<&'v dyn Debug>) -> Self { 441 | ValueBag::from_option(v) 442 | } 443 | } 444 | 445 | impl<'v, 'u> From<&'v &'u dyn Debug> for ValueBag<'v> 446 | where 447 | 'u: 'v, 448 | { 449 | #[inline] 450 | fn from(v: &'v &'u dyn Debug) -> Self { 451 | ValueBag::from_dyn_debug(*v) 452 | } 453 | } 454 | 455 | impl<'v> From<&'v dyn Display> for ValueBag<'v> { 456 | #[inline] 457 | fn from(v: &'v dyn Display) -> Self { 458 | ValueBag::from_dyn_display(v) 459 | } 460 | } 461 | 462 | impl<'v> From> for ValueBag<'v> { 463 | #[inline] 464 | fn from(v: Option<&'v dyn Display>) -> Self { 465 | ValueBag::from_option(v) 466 | } 467 | } 468 | 469 | impl<'v, 'u> From<&'v &'u dyn Display> for ValueBag<'v> 470 | where 471 | 'u: 'v, 472 | { 473 | #[inline] 474 | fn from(v: &'v &'u dyn Display) -> Self { 475 | ValueBag::from_dyn_display(*v) 476 | } 477 | } 478 | 479 | #[cfg(test)] 480 | mod tests { 481 | #[cfg(target_arch = "wasm32")] 482 | use wasm_bindgen_test::*; 483 | 484 | use super::*; 485 | use crate::{ 486 | std::string::ToString, 487 | test::{IntoValueBag, TestToken}, 488 | }; 489 | 490 | #[test] 491 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 492 | fn fmt_capture() { 493 | assert_eq!( 494 | ValueBag::capture_debug(&1u16).to_test_token(), 495 | TestToken::U64(1) 496 | ); 497 | assert_eq!( 498 | ValueBag::capture_display(&1u16).to_test_token(), 499 | TestToken::U64(1) 500 | ); 501 | 502 | assert_eq!( 503 | ValueBag::capture_debug(&Some(1u16)).to_test_token(), 504 | TestToken::U64(1) 505 | ); 506 | } 507 | 508 | #[test] 509 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 510 | fn fmt_fill() { 511 | assert_eq!( 512 | ValueBag::from_fill(&|slot: Slot| slot.fill_debug(1u16)).to_test_token(), 513 | TestToken::Str("1".into()) 514 | ); 515 | assert_eq!( 516 | ValueBag::from_fill(&|slot: Slot| slot.fill_display(1u16)).to_test_token(), 517 | TestToken::Str("1".into()) 518 | ); 519 | } 520 | 521 | #[test] 522 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 523 | fn fmt_capture_args() { 524 | assert_eq!( 525 | ValueBag::from_debug(&format_args!("a {}", "value")).to_string(), 526 | "a value" 527 | ); 528 | } 529 | 530 | #[test] 531 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 532 | fn fmt_cast() { 533 | assert_eq!( 534 | 42u64, 535 | ValueBag::capture_debug(&42u64) 536 | .to_u64() 537 | .expect("invalid value") 538 | ); 539 | 540 | assert_eq!( 541 | "a string", 542 | ValueBag::capture_display(&"a string") 543 | .to_borrowed_str() 544 | .expect("invalid value") 545 | ); 546 | } 547 | 548 | #[test] 549 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 550 | fn fmt_downcast() { 551 | #[derive(Debug, PartialEq, Eq)] 552 | struct Timestamp(usize); 553 | 554 | impl Display for Timestamp { 555 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 556 | write!(f, "time is {}", self.0) 557 | } 558 | } 559 | 560 | let ts = Timestamp(42); 561 | 562 | assert_eq!( 563 | &ts, 564 | ValueBag::capture_debug(&ts) 565 | .downcast_ref::() 566 | .expect("invalid value") 567 | ); 568 | 569 | assert_eq!( 570 | &ts, 571 | ValueBag::capture_display(&ts) 572 | .downcast_ref::() 573 | .expect("invalid value") 574 | ); 575 | } 576 | 577 | #[test] 578 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 579 | fn fmt_debug() { 580 | assert_eq!( 581 | format!("{:?}", "a string"), 582 | format!("{:?}", "a string".into_value_bag().by_ref()), 583 | ); 584 | 585 | assert_eq!( 586 | format!("{:04?}", 42u64), 587 | format!("{:04?}", 42u64.into_value_bag().by_ref()), 588 | ); 589 | } 590 | 591 | #[test] 592 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 593 | fn fmt_display() { 594 | assert_eq!( 595 | format!("{}", "a string"), 596 | format!("{}", "a string".into_value_bag().by_ref()), 597 | ); 598 | 599 | assert_eq!( 600 | format!("{:04}", 42u64), 601 | format!("{:04}", 42u64.into_value_bag().by_ref()), 602 | ); 603 | } 604 | 605 | #[cfg(feature = "seq")] 606 | mod seq_support { 607 | use super::*; 608 | 609 | #[test] 610 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 611 | fn fmt_debug_seq() { 612 | assert_eq!( 613 | "[01, 02, 03]", 614 | format!("{:>02?}", ValueBag::from_seq_slice(&[1, 2, 3])) 615 | ); 616 | } 617 | 618 | #[test] 619 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 620 | fn fmt_display_seq() { 621 | assert_eq!( 622 | "[1, 2, 3]", 623 | format!("{}", ValueBag::from_seq_slice(&[1, 2, 3])) 624 | ); 625 | } 626 | } 627 | } 628 | -------------------------------------------------------------------------------- /src/internal/mod.rs: -------------------------------------------------------------------------------- 1 | //! The internal `Value` serialization API. 2 | //! 3 | //! This implementation isn't intended to be public. It may need to change 4 | //! for optimizations or to support new external serialization frameworks. 5 | 6 | use crate::{fill::Fill, Error, ValueBag}; 7 | 8 | pub(crate) mod cast; 9 | #[cfg(feature = "error")] 10 | pub(crate) mod error; 11 | pub(crate) mod fmt; 12 | #[cfg(feature = "seq")] 13 | pub(crate) mod seq; 14 | #[cfg(feature = "serde1")] 15 | pub(crate) mod serde; 16 | #[cfg(feature = "sval2")] 17 | pub(crate) mod sval; 18 | 19 | #[cfg(feature = "owned")] 20 | pub(crate) mod owned; 21 | 22 | #[cfg(feature = "owned")] 23 | use crate::std::sync::Arc; 24 | 25 | // NOTE: It takes less space to have separate variants for the presence 26 | // of a `TypeId` instead of using `Option`, because `TypeId` doesn't 27 | // have a niche value 28 | /// A container for a structured value for a specific kind of visitor. 29 | #[derive(Clone)] 30 | pub(crate) enum Internal<'v> { 31 | // Primitive values 32 | Signed(i64), 33 | Unsigned(u64), 34 | #[cfg(not(feature = "inline-i128"))] 35 | BigSigned(&'v i128), 36 | #[cfg(feature = "inline-i128")] 37 | BigSigned(i128), 38 | #[cfg(not(feature = "inline-i128"))] 39 | BigUnsigned(&'v u128), 40 | #[cfg(feature = "inline-i128")] 41 | BigUnsigned(u128), 42 | Float(f64), 43 | Bool(bool), 44 | Char(char), 45 | Str(&'v str), 46 | None, 47 | 48 | // Captured values 49 | Fill(&'v dyn Fill), 50 | Debug(&'v dyn fmt::DowncastDebug), 51 | Display(&'v dyn fmt::DowncastDisplay), 52 | #[cfg(feature = "error")] 53 | Error(&'v dyn error::DowncastError), 54 | #[cfg(feature = "sval2")] 55 | Sval2(&'v dyn sval::v2::DowncastValue), 56 | #[cfg(feature = "serde1")] 57 | Serde1(&'v dyn serde::v1::DowncastSerialize), 58 | 59 | // Anonymous values 60 | AnonDebug(&'v dyn fmt::Debug), 61 | AnonDisplay(&'v dyn fmt::Display), 62 | #[cfg(feature = "error")] 63 | AnonError(&'v (dyn error::Error + 'static)), 64 | #[cfg(feature = "sval2")] 65 | AnonSval2(&'v dyn sval::v2::Value), 66 | #[cfg(feature = "serde1")] 67 | AnonSerde1(&'v dyn serde::v1::Serialize), 68 | #[cfg(feature = "seq")] 69 | AnonSeq(&'v dyn seq::Seq), 70 | 71 | // Shared values 72 | #[cfg(feature = "owned")] 73 | SharedDebug(Arc), 74 | #[cfg(feature = "owned")] 75 | SharedDisplay(Arc), 76 | #[cfg(all(feature = "error", feature = "owned"))] 77 | SharedError(Arc), 78 | #[cfg(all(feature = "serde1", feature = "owned"))] 79 | SharedSerde1(Arc), 80 | #[cfg(all(feature = "sval2", feature = "owned"))] 81 | SharedSval2(Arc), 82 | #[cfg(all(feature = "seq", feature = "owned"))] 83 | SharedSeq(Arc), 84 | 85 | // NOTE: These variants exist because we can't clone an `Arc` in `const` fns 86 | // (plus we may not want to anyways) 87 | #[cfg(feature = "owned")] 88 | SharedRefDebug(&'v Arc), 89 | #[cfg(feature = "owned")] 90 | SharedRefDisplay(&'v Arc), 91 | #[cfg(all(feature = "error", feature = "owned"))] 92 | SharedRefError(&'v Arc), 93 | #[cfg(all(feature = "serde1", feature = "owned"))] 94 | SharedRefSerde1(&'v Arc), 95 | #[cfg(all(feature = "sval2", feature = "owned"))] 96 | SharedRefSval2(&'v Arc), 97 | #[cfg(all(feature = "seq", feature = "owned"))] 98 | SharedRefSeq(&'v Arc), 99 | 100 | // Poisoned value 101 | #[cfg_attr(not(feature = "owned"), allow(dead_code))] 102 | Poisoned(&'static str), 103 | } 104 | 105 | /// The internal serialization contract. 106 | pub(crate) trait InternalVisitor<'v> { 107 | fn fill(&mut self, v: &dyn Fill) -> Result<(), Error>; 108 | 109 | fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error>; 110 | fn borrowed_debug(&mut self, v: &'v dyn fmt::Debug) -> Result<(), Error> { 111 | self.debug(v) 112 | } 113 | #[cfg(feature = "owned")] 114 | fn shared_debug(&mut self, v: &Arc) -> Result<(), Error> { 115 | self.debug(v) 116 | } 117 | fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error>; 118 | fn borrowed_display(&mut self, v: &'v dyn fmt::Display) -> Result<(), Error> { 119 | self.display(v) 120 | } 121 | #[cfg(feature = "owned")] 122 | fn shared_display( 123 | &mut self, 124 | v: &Arc, 125 | ) -> Result<(), Error> { 126 | self.display(v) 127 | } 128 | 129 | fn u64(&mut self, v: u64) -> Result<(), Error>; 130 | fn i64(&mut self, v: i64) -> Result<(), Error>; 131 | fn u128(&mut self, v: &u128) -> Result<(), Error>; 132 | #[cfg(not(feature = "inline-i128"))] 133 | fn borrowed_u128(&mut self, v: &'v u128) -> Result<(), Error> { 134 | self.u128(v) 135 | } 136 | fn i128(&mut self, v: &i128) -> Result<(), Error>; 137 | #[cfg(not(feature = "inline-i128"))] 138 | fn borrowed_i128(&mut self, v: &'v i128) -> Result<(), Error> { 139 | self.i128(v) 140 | } 141 | fn f64(&mut self, v: f64) -> Result<(), Error>; 142 | fn bool(&mut self, v: bool) -> Result<(), Error>; 143 | fn char(&mut self, v: char) -> Result<(), Error>; 144 | 145 | fn str(&mut self, v: &str) -> Result<(), Error>; 146 | fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { 147 | self.str(v) 148 | } 149 | 150 | fn none(&mut self) -> Result<(), Error>; 151 | 152 | #[cfg(feature = "error")] 153 | fn error(&mut self, v: &(dyn error::Error + 'static)) -> Result<(), Error>; 154 | #[cfg(feature = "error")] 155 | fn borrowed_error(&mut self, v: &'v (dyn error::Error + 'static)) -> Result<(), Error> { 156 | self.error(v) 157 | } 158 | #[cfg(all(feature = "error", feature = "owned"))] 159 | fn shared_error( 160 | &mut self, 161 | v: &Arc, 162 | ) -> Result<(), Error> { 163 | self.error(v.as_super()) 164 | } 165 | 166 | #[cfg(feature = "sval2")] 167 | fn sval2(&mut self, v: &dyn sval::v2::Value) -> Result<(), Error>; 168 | #[cfg(feature = "sval2")] 169 | fn borrowed_sval2(&mut self, v: &'v dyn sval::v2::Value) -> Result<(), Error> { 170 | self.sval2(v) 171 | } 172 | #[cfg(all(feature = "sval2", feature = "owned"))] 173 | fn shared_sval2( 174 | &mut self, 175 | v: &Arc, 176 | ) -> Result<(), Error> { 177 | self.sval2(v.as_super()) 178 | } 179 | 180 | #[cfg(feature = "serde1")] 181 | fn serde1(&mut self, v: &dyn serde::v1::Serialize) -> Result<(), Error>; 182 | #[cfg(feature = "serde1")] 183 | fn borrowed_serde1(&mut self, v: &'v dyn serde::v1::Serialize) -> Result<(), Error> { 184 | self.serde1(v) 185 | } 186 | #[cfg(all(feature = "serde1", feature = "owned"))] 187 | fn shared_serde1( 188 | &mut self, 189 | v: &Arc, 190 | ) -> Result<(), Error> { 191 | self.serde1(v.as_super()) 192 | } 193 | 194 | #[cfg(feature = "seq")] 195 | fn seq(&mut self, v: &dyn seq::Seq) -> Result<(), Error>; 196 | 197 | #[cfg(feature = "seq")] 198 | fn borrowed_seq(&mut self, v: &'v dyn seq::Seq) -> Result<(), Error> { 199 | self.seq(v) 200 | } 201 | 202 | #[cfg(all(feature = "seq", feature = "owned"))] 203 | fn shared_seq(&mut self, v: &Arc) -> Result<(), Error> { 204 | self.seq(v.as_super()) 205 | } 206 | 207 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error>; 208 | } 209 | 210 | impl<'a, 'v, V: InternalVisitor<'v> + ?Sized> InternalVisitor<'v> for &'a mut V { 211 | fn fill(&mut self, v: &dyn Fill) -> Result<(), Error> { 212 | (**self).fill(v) 213 | } 214 | 215 | fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> { 216 | (**self).debug(v) 217 | } 218 | 219 | fn borrowed_debug(&mut self, v: &'v dyn fmt::Debug) -> Result<(), Error> { 220 | (**self).borrowed_debug(v) 221 | } 222 | 223 | #[cfg(feature = "owned")] 224 | fn shared_debug(&mut self, v: &Arc) -> Result<(), Error> { 225 | (**self).shared_debug(v) 226 | } 227 | 228 | fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error> { 229 | (**self).display(v) 230 | } 231 | 232 | fn borrowed_display(&mut self, v: &'v dyn fmt::Display) -> Result<(), Error> { 233 | (**self).borrowed_display(v) 234 | } 235 | 236 | #[cfg(feature = "owned")] 237 | fn shared_display( 238 | &mut self, 239 | v: &Arc, 240 | ) -> Result<(), Error> { 241 | (**self).shared_display(v) 242 | } 243 | 244 | fn u64(&mut self, v: u64) -> Result<(), Error> { 245 | (**self).u64(v) 246 | } 247 | 248 | fn i64(&mut self, v: i64) -> Result<(), Error> { 249 | (**self).i64(v) 250 | } 251 | 252 | fn u128(&mut self, v: &u128) -> Result<(), Error> { 253 | (**self).u128(v) 254 | } 255 | 256 | #[cfg(not(feature = "inline-i128"))] 257 | fn borrowed_u128(&mut self, v: &'v u128) -> Result<(), Error> { 258 | (**self).borrowed_u128(v) 259 | } 260 | 261 | fn i128(&mut self, v: &i128) -> Result<(), Error> { 262 | (**self).i128(v) 263 | } 264 | 265 | #[cfg(not(feature = "inline-i128"))] 266 | fn borrowed_i128(&mut self, v: &'v i128) -> Result<(), Error> { 267 | (**self).borrowed_i128(v) 268 | } 269 | 270 | fn f64(&mut self, v: f64) -> Result<(), Error> { 271 | (**self).f64(v) 272 | } 273 | 274 | fn bool(&mut self, v: bool) -> Result<(), Error> { 275 | (**self).bool(v) 276 | } 277 | 278 | fn char(&mut self, v: char) -> Result<(), Error> { 279 | (**self).char(v) 280 | } 281 | 282 | fn str(&mut self, v: &str) -> Result<(), Error> { 283 | (**self).str(v) 284 | } 285 | 286 | fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { 287 | (**self).borrowed_str(v) 288 | } 289 | 290 | fn none(&mut self) -> Result<(), Error> { 291 | (**self).none() 292 | } 293 | 294 | #[cfg(feature = "error")] 295 | fn error(&mut self, v: &(dyn error::Error + 'static)) -> Result<(), Error> { 296 | (**self).error(v) 297 | } 298 | 299 | #[cfg(feature = "error")] 300 | fn borrowed_error(&mut self, v: &'v (dyn error::Error + 'static)) -> Result<(), Error> { 301 | (**self).borrowed_error(v) 302 | } 303 | 304 | #[cfg(all(feature = "error", feature = "owned"))] 305 | fn shared_error( 306 | &mut self, 307 | v: &Arc, 308 | ) -> Result<(), Error> { 309 | (**self).shared_error(v) 310 | } 311 | 312 | #[cfg(feature = "sval2")] 313 | fn sval2(&mut self, v: &dyn sval::v2::Value) -> Result<(), Error> { 314 | (**self).sval2(v) 315 | } 316 | 317 | #[cfg(feature = "sval2")] 318 | fn borrowed_sval2(&mut self, v: &'v dyn sval::v2::Value) -> Result<(), Error> { 319 | (**self).borrowed_sval2(v) 320 | } 321 | 322 | #[cfg(all(feature = "sval2", feature = "owned"))] 323 | fn shared_sval2( 324 | &mut self, 325 | v: &Arc, 326 | ) -> Result<(), Error> { 327 | (**self).shared_sval2(v) 328 | } 329 | 330 | #[cfg(feature = "serde1")] 331 | fn serde1(&mut self, v: &dyn serde::v1::Serialize) -> Result<(), Error> { 332 | (**self).serde1(v) 333 | } 334 | 335 | #[cfg(feature = "serde1")] 336 | fn borrowed_serde1(&mut self, v: &'v dyn serde::v1::Serialize) -> Result<(), Error> { 337 | (**self).borrowed_serde1(v) 338 | } 339 | 340 | #[cfg(all(feature = "serde1", feature = "owned"))] 341 | fn shared_serde1( 342 | &mut self, 343 | v: &Arc, 344 | ) -> Result<(), Error> { 345 | (**self).shared_serde1(v) 346 | } 347 | 348 | #[cfg(feature = "seq")] 349 | fn seq(&mut self, seq: &dyn seq::Seq) -> Result<(), Error> { 350 | (**self).seq(seq) 351 | } 352 | 353 | #[cfg(feature = "seq")] 354 | fn borrowed_seq(&mut self, seq: &'v dyn seq::Seq) -> Result<(), Error> { 355 | (**self).borrowed_seq(seq) 356 | } 357 | 358 | #[cfg(all(feature = "seq", feature = "owned"))] 359 | fn shared_seq(&mut self, seq: &Arc) -> Result<(), Error> { 360 | (**self).shared_seq(seq) 361 | } 362 | 363 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> { 364 | (**self).poisoned(msg) 365 | } 366 | } 367 | 368 | impl<'v> ValueBag<'v> { 369 | /// Visit the value using an internal visitor. 370 | #[inline] 371 | pub(crate) fn internal_visit(&self, visitor: impl InternalVisitor<'v>) -> Result<(), Error> { 372 | self.inner.internal_visit(visitor) 373 | } 374 | } 375 | 376 | impl<'v> Internal<'v> { 377 | #[inline] 378 | pub(crate) const fn by_ref(&self) -> Internal<'_> { 379 | match self { 380 | Internal::Signed(value) => Internal::Signed(*value), 381 | Internal::Unsigned(value) => Internal::Unsigned(*value), 382 | Internal::BigSigned(value) => Internal::BigSigned(*value), 383 | Internal::BigUnsigned(value) => Internal::BigUnsigned(*value), 384 | Internal::Float(value) => Internal::Float(*value), 385 | Internal::Bool(value) => Internal::Bool(*value), 386 | Internal::Char(value) => Internal::Char(*value), 387 | Internal::Str(value) => Internal::Str(value), 388 | Internal::None => Internal::None, 389 | 390 | Internal::Fill(value) => Internal::Fill(*value), 391 | 392 | Internal::AnonDebug(value) => Internal::AnonDebug(*value), 393 | Internal::Debug(value) => Internal::Debug(*value), 394 | 395 | Internal::AnonDisplay(value) => Internal::AnonDisplay(*value), 396 | Internal::Display(value) => Internal::Display(*value), 397 | 398 | #[cfg(feature = "error")] 399 | Internal::AnonError(value) => Internal::AnonError(*value), 400 | #[cfg(feature = "error")] 401 | Internal::Error(value) => Internal::Error(*value), 402 | 403 | #[cfg(feature = "sval2")] 404 | Internal::AnonSval2(value) => Internal::AnonSval2(*value), 405 | #[cfg(feature = "sval2")] 406 | Internal::Sval2(value) => Internal::Sval2(*value), 407 | 408 | #[cfg(feature = "serde1")] 409 | Internal::AnonSerde1(value) => Internal::AnonSerde1(*value), 410 | #[cfg(feature = "serde1")] 411 | Internal::Serde1(value) => Internal::Serde1(*value), 412 | 413 | #[cfg(feature = "seq")] 414 | Internal::AnonSeq(value) => Internal::AnonSeq(*value), 415 | 416 | #[cfg(feature = "owned")] 417 | Internal::SharedDebug(ref value) => Internal::SharedRefDebug(value), 418 | #[cfg(feature = "owned")] 419 | Internal::SharedDisplay(ref value) => Internal::SharedRefDisplay(value), 420 | #[cfg(all(feature = "error", feature = "owned"))] 421 | Internal::SharedError(ref value) => Internal::SharedRefError(value), 422 | #[cfg(all(feature = "serde1", feature = "owned"))] 423 | Internal::SharedSerde1(ref value) => Internal::SharedRefSerde1(value), 424 | #[cfg(all(feature = "sval2", feature = "owned"))] 425 | Internal::SharedSval2(ref value) => Internal::SharedRefSval2(value), 426 | #[cfg(all(feature = "seq", feature = "owned"))] 427 | Internal::SharedSeq(ref value) => Internal::SharedRefSeq(value), 428 | 429 | #[cfg(feature = "owned")] 430 | Internal::SharedRefDebug(value) => Internal::SharedRefDebug(*value), 431 | #[cfg(feature = "owned")] 432 | Internal::SharedRefDisplay(value) => Internal::SharedRefDisplay(*value), 433 | #[cfg(all(feature = "error", feature = "owned"))] 434 | Internal::SharedRefError(value) => Internal::SharedRefError(*value), 435 | #[cfg(all(feature = "serde1", feature = "owned"))] 436 | Internal::SharedRefSerde1(value) => Internal::SharedRefSerde1(*value), 437 | #[cfg(all(feature = "sval2", feature = "owned"))] 438 | Internal::SharedRefSval2(value) => Internal::SharedRefSval2(*value), 439 | #[cfg(all(feature = "seq", feature = "owned"))] 440 | Internal::SharedRefSeq(value) => Internal::SharedRefSeq(*value), 441 | 442 | Internal::Poisoned(msg) => Internal::Poisoned(msg), 443 | } 444 | } 445 | 446 | #[inline] 447 | pub(crate) fn internal_visit( 448 | &self, 449 | mut visitor: impl InternalVisitor<'v>, 450 | ) -> Result<(), Error> { 451 | match self { 452 | Internal::Signed(value) => visitor.i64(*value), 453 | Internal::Unsigned(value) => visitor.u64(*value), 454 | #[cfg(feature = "inline-i128")] 455 | Internal::BigSigned(value) => visitor.i128(value), 456 | #[cfg(feature = "inline-i128")] 457 | Internal::BigUnsigned(value) => visitor.u128(value), 458 | #[cfg(not(feature = "inline-i128"))] 459 | Internal::BigSigned(value) => visitor.borrowed_i128(value), 460 | #[cfg(not(feature = "inline-i128"))] 461 | Internal::BigUnsigned(value) => visitor.borrowed_u128(value), 462 | Internal::Float(value) => visitor.f64(*value), 463 | Internal::Bool(value) => visitor.bool(*value), 464 | Internal::Char(value) => visitor.char(*value), 465 | Internal::Str(value) => visitor.borrowed_str(value), 466 | Internal::None => visitor.none(), 467 | 468 | Internal::Fill(value) => visitor.fill(*value), 469 | 470 | Internal::AnonDebug(value) => visitor.borrowed_debug(*value), 471 | Internal::Debug(value) => visitor.borrowed_debug(value.as_super()), 472 | 473 | Internal::AnonDisplay(value) => visitor.borrowed_display(*value), 474 | Internal::Display(value) => visitor.borrowed_display(value.as_super()), 475 | 476 | #[cfg(feature = "error")] 477 | Internal::AnonError(value) => visitor.borrowed_error(*value), 478 | #[cfg(feature = "error")] 479 | Internal::Error(value) => visitor.borrowed_error(value.as_super()), 480 | 481 | #[cfg(feature = "sval2")] 482 | Internal::AnonSval2(value) => visitor.borrowed_sval2(*value), 483 | #[cfg(feature = "sval2")] 484 | Internal::Sval2(value) => visitor.borrowed_sval2(value.as_super()), 485 | 486 | #[cfg(feature = "serde1")] 487 | Internal::AnonSerde1(value) => visitor.borrowed_serde1(*value), 488 | #[cfg(feature = "serde1")] 489 | Internal::Serde1(value) => visitor.borrowed_serde1(value.as_super()), 490 | 491 | #[cfg(feature = "seq")] 492 | Internal::AnonSeq(value) => visitor.borrowed_seq(*value), 493 | 494 | #[cfg(feature = "owned")] 495 | Internal::SharedDebug(ref value) => visitor.shared_debug(value), 496 | #[cfg(feature = "owned")] 497 | Internal::SharedDisplay(ref value) => visitor.shared_display(value), 498 | #[cfg(all(feature = "error", feature = "owned"))] 499 | Internal::SharedError(ref value) => visitor.shared_error(value), 500 | #[cfg(all(feature = "serde1", feature = "owned"))] 501 | Internal::SharedSerde1(ref value) => visitor.shared_serde1(value), 502 | #[cfg(all(feature = "sval2", feature = "owned"))] 503 | Internal::SharedSval2(ref value) => visitor.shared_sval2(value), 504 | #[cfg(all(feature = "seq", feature = "owned"))] 505 | Internal::SharedSeq(value) => visitor.shared_seq(value), 506 | 507 | #[cfg(feature = "owned")] 508 | Internal::SharedRefDebug(value) => visitor.shared_debug(value), 509 | #[cfg(feature = "owned")] 510 | Internal::SharedRefDisplay(value) => visitor.shared_display(value), 511 | #[cfg(all(feature = "error", feature = "owned"))] 512 | Internal::SharedRefError(value) => visitor.shared_error(value), 513 | #[cfg(all(feature = "serde1", feature = "owned"))] 514 | Internal::SharedRefSerde1(value) => visitor.shared_serde1(value), 515 | #[cfg(all(feature = "sval2", feature = "owned"))] 516 | Internal::SharedRefSval2(value) => visitor.shared_sval2(value), 517 | #[cfg(all(feature = "seq", feature = "owned"))] 518 | Internal::SharedRefSeq(value) => visitor.shared_seq(value), 519 | 520 | Internal::Poisoned(msg) => visitor.poisoned(msg), 521 | } 522 | } 523 | } 524 | -------------------------------------------------------------------------------- /src/internal/owned.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | internal::{self, Internal, InternalVisitor}, 3 | std::{boxed::Box, sync::Arc}, 4 | Error, 5 | }; 6 | 7 | #[derive(Clone)] 8 | pub(crate) enum OwnedInternal { 9 | // Primitive values 10 | BigSigned(i128), 11 | BigUnsigned(u128), 12 | Float(f64), 13 | Bool(bool), 14 | Char(char), 15 | #[cfg(feature = "inline-str")] 16 | StrSmall(inline_str::InlineStr), 17 | Str(Box), 18 | None, 19 | 20 | // Buffered values 21 | Debug(internal::fmt::owned::OwnedFmt), 22 | Display(internal::fmt::owned::OwnedFmt), 23 | #[cfg(feature = "error")] 24 | Error(internal::error::owned::OwnedError), 25 | #[cfg(feature = "serde1")] 26 | Serde1(internal::serde::v1::owned::OwnedSerialize), 27 | #[cfg(feature = "sval2")] 28 | Sval2(internal::sval::v2::owned::OwnedValue), 29 | #[cfg(feature = "seq")] 30 | Seq(internal::seq::owned::OwnedSeq), 31 | 32 | // Shared values 33 | SharedDebug(Arc), 34 | SharedDisplay(Arc), 35 | #[cfg(feature = "error")] 36 | SharedError(Arc), 37 | #[cfg(feature = "serde1")] 38 | SharedSerde1(Arc), 39 | #[cfg(feature = "sval2")] 40 | SharedSval2(Arc), 41 | #[cfg(feature = "seq")] 42 | SharedSeq(Arc), 43 | 44 | // Poisoned value 45 | Poisoned(&'static str), 46 | } 47 | 48 | impl OwnedInternal { 49 | #[inline] 50 | pub(crate) const fn by_ref(&self) -> Internal { 51 | match self { 52 | #[cfg(not(feature = "inline-i128"))] 53 | OwnedInternal::BigSigned(v) => Internal::BigSigned(v), 54 | #[cfg(feature = "inline-i128")] 55 | OwnedInternal::BigSigned(v) => Internal::BigSigned(*v), 56 | #[cfg(not(feature = "inline-i128"))] 57 | OwnedInternal::BigUnsigned(v) => Internal::BigUnsigned(v), 58 | #[cfg(feature = "inline-i128")] 59 | OwnedInternal::BigUnsigned(v) => Internal::BigUnsigned(*v), 60 | OwnedInternal::Float(v) => Internal::Float(*v), 61 | OwnedInternal::Bool(v) => Internal::Bool(*v), 62 | OwnedInternal::Char(v) => Internal::Char(*v), 63 | OwnedInternal::Str(v) => Internal::Str(v), 64 | #[cfg(feature = "inline-str")] 65 | OwnedInternal::StrSmall(v) => Internal::Str(v.get()), 66 | OwnedInternal::None => Internal::None, 67 | 68 | OwnedInternal::Debug(v) => Internal::AnonDebug(v), 69 | OwnedInternal::Display(v) => Internal::AnonDisplay(v), 70 | #[cfg(feature = "error")] 71 | OwnedInternal::Error(v) => Internal::AnonError(v), 72 | #[cfg(feature = "serde1")] 73 | OwnedInternal::Serde1(v) => Internal::AnonSerde1(v), 74 | #[cfg(feature = "sval2")] 75 | OwnedInternal::Sval2(v) => Internal::AnonSval2(v), 76 | #[cfg(feature = "seq")] 77 | OwnedInternal::Seq(v) => Internal::AnonSeq(v), 78 | 79 | OwnedInternal::SharedDebug(ref value) => Internal::SharedRefDebug(value), 80 | OwnedInternal::SharedDisplay(ref value) => Internal::SharedRefDisplay(value), 81 | #[cfg(feature = "error")] 82 | OwnedInternal::SharedError(ref value) => Internal::SharedRefError(value), 83 | #[cfg(feature = "serde1")] 84 | OwnedInternal::SharedSerde1(ref value) => Internal::SharedRefSerde1(value), 85 | #[cfg(feature = "sval2")] 86 | OwnedInternal::SharedSval2(ref value) => Internal::SharedRefSval2(value), 87 | #[cfg(feature = "seq")] 88 | OwnedInternal::SharedSeq(ref value) => Internal::SharedRefSeq(value), 89 | 90 | OwnedInternal::Poisoned(msg) => Internal::Poisoned(msg), 91 | } 92 | } 93 | 94 | #[inline] 95 | pub(crate) fn into_shared(self) -> Self { 96 | match self { 97 | OwnedInternal::BigSigned(v) => OwnedInternal::BigSigned(v), 98 | OwnedInternal::BigUnsigned(v) => OwnedInternal::BigUnsigned(v), 99 | OwnedInternal::Float(v) => OwnedInternal::Float(v), 100 | OwnedInternal::Bool(v) => OwnedInternal::Bool(v), 101 | OwnedInternal::Char(v) => OwnedInternal::Char(v), 102 | OwnedInternal::Str(v) => OwnedInternal::Str(v), 103 | #[cfg(feature = "inline-str")] 104 | OwnedInternal::StrSmall(v) => OwnedInternal::StrSmall(v), 105 | OwnedInternal::None => OwnedInternal::None, 106 | 107 | OwnedInternal::Debug(v) => OwnedInternal::SharedDebug(Arc::new(v)), 108 | OwnedInternal::Display(v) => OwnedInternal::SharedDisplay(Arc::new(v)), 109 | #[cfg(feature = "error")] 110 | OwnedInternal::Error(v) => OwnedInternal::SharedError(Arc::new(v)), 111 | #[cfg(feature = "serde1")] 112 | OwnedInternal::Serde1(v) => OwnedInternal::SharedSerde1(Arc::new(v)), 113 | #[cfg(feature = "sval2")] 114 | OwnedInternal::Sval2(v) => OwnedInternal::SharedSval2(Arc::new(v)), 115 | #[cfg(feature = "seq")] 116 | OwnedInternal::Seq(v) => OwnedInternal::SharedSeq(Arc::new(v)), 117 | 118 | OwnedInternal::SharedDebug(v) => OwnedInternal::SharedDebug(v), 119 | OwnedInternal::SharedDisplay(v) => OwnedInternal::SharedDisplay(v), 120 | #[cfg(feature = "error")] 121 | OwnedInternal::SharedError(v) => OwnedInternal::SharedError(v), 122 | #[cfg(feature = "serde1")] 123 | OwnedInternal::SharedSerde1(v) => OwnedInternal::SharedSerde1(v), 124 | #[cfg(feature = "sval2")] 125 | OwnedInternal::SharedSval2(v) => OwnedInternal::SharedSval2(v), 126 | #[cfg(feature = "seq")] 127 | OwnedInternal::SharedSeq(v) => OwnedInternal::SharedSeq(v), 128 | 129 | OwnedInternal::Poisoned(msg) => OwnedInternal::Poisoned(msg), 130 | } 131 | } 132 | } 133 | 134 | impl<'v> Internal<'v> { 135 | pub(crate) fn to_owned(&self) -> OwnedInternal { 136 | struct OwnedVisitor(OwnedInternal); 137 | 138 | impl<'v> InternalVisitor<'v> for OwnedVisitor { 139 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { 140 | v.fill(crate::fill::Slot::new(self)) 141 | } 142 | 143 | fn debug(&mut self, v: &dyn internal::fmt::Debug) -> Result<(), Error> { 144 | self.0 = OwnedInternal::Debug(internal::fmt::owned::buffer_debug(v)); 145 | Ok(()) 146 | } 147 | 148 | fn shared_debug( 149 | &mut self, 150 | v: &Arc, 151 | ) -> Result<(), Error> { 152 | self.0 = OwnedInternal::SharedDebug(v.clone()); 153 | Ok(()) 154 | } 155 | 156 | fn display(&mut self, v: &dyn internal::fmt::Display) -> Result<(), Error> { 157 | self.0 = OwnedInternal::Display(internal::fmt::owned::buffer_display(v)); 158 | Ok(()) 159 | } 160 | 161 | fn shared_display( 162 | &mut self, 163 | v: &Arc, 164 | ) -> Result<(), Error> { 165 | self.0 = OwnedInternal::SharedDisplay(v.clone()); 166 | Ok(()) 167 | } 168 | 169 | fn u64(&mut self, v: u64) -> Result<(), Error> { 170 | self.0 = OwnedInternal::BigUnsigned(v as u128); 171 | Ok(()) 172 | } 173 | 174 | fn i64(&mut self, v: i64) -> Result<(), Error> { 175 | self.0 = OwnedInternal::BigSigned(v as i128); 176 | Ok(()) 177 | } 178 | 179 | fn u128(&mut self, v: &u128) -> Result<(), Error> { 180 | self.0 = OwnedInternal::BigUnsigned(*v); 181 | Ok(()) 182 | } 183 | 184 | fn i128(&mut self, v: &i128) -> Result<(), Error> { 185 | self.0 = OwnedInternal::BigSigned(*v); 186 | Ok(()) 187 | } 188 | 189 | fn f64(&mut self, v: f64) -> Result<(), Error> { 190 | self.0 = OwnedInternal::Float(v); 191 | Ok(()) 192 | } 193 | 194 | fn bool(&mut self, v: bool) -> Result<(), Error> { 195 | self.0 = OwnedInternal::Bool(v); 196 | Ok(()) 197 | } 198 | 199 | fn char(&mut self, v: char) -> Result<(), Error> { 200 | self.0 = OwnedInternal::Char(v); 201 | Ok(()) 202 | } 203 | 204 | fn str(&mut self, v: &str) -> Result<(), Error> { 205 | #[cfg(feature = "inline-str")] 206 | { 207 | if v.len() <= inline_str::MAX_INLINE_LEN { 208 | self.0 = OwnedInternal::StrSmall(inline_str::InlineStr::copy_from(v)); 209 | return Ok(()); 210 | } 211 | } 212 | 213 | self.0 = OwnedInternal::Str(v.into()); 214 | Ok(()) 215 | } 216 | 217 | fn none(&mut self) -> Result<(), Error> { 218 | self.0 = OwnedInternal::None; 219 | Ok(()) 220 | } 221 | 222 | #[cfg(feature = "error")] 223 | fn error(&mut self, v: &(dyn internal::error::Error + 'static)) -> Result<(), Error> { 224 | self.0 = OwnedInternal::Error(internal::error::owned::buffer(v)); 225 | Ok(()) 226 | } 227 | 228 | #[cfg(feature = "error")] 229 | fn shared_error( 230 | &mut self, 231 | v: &Arc, 232 | ) -> Result<(), Error> { 233 | self.0 = OwnedInternal::SharedError(v.clone()); 234 | Ok(()) 235 | } 236 | 237 | #[cfg(feature = "sval2")] 238 | fn sval2(&mut self, v: &dyn internal::sval::v2::Value) -> Result<(), Error> { 239 | self.0 = internal::sval::v2::owned::buffer(v) 240 | .map(OwnedInternal::Sval2) 241 | .unwrap_or(OwnedInternal::Poisoned("failed to buffer the value")); 242 | Ok(()) 243 | } 244 | 245 | #[cfg(feature = "sval2")] 246 | fn shared_sval2( 247 | &mut self, 248 | v: &Arc, 249 | ) -> Result<(), Error> { 250 | self.0 = OwnedInternal::SharedSval2(v.clone()); 251 | Ok(()) 252 | } 253 | 254 | #[cfg(feature = "serde1")] 255 | fn serde1(&mut self, v: &dyn internal::serde::v1::Serialize) -> Result<(), Error> { 256 | self.0 = internal::serde::v1::owned::buffer(v) 257 | .map(OwnedInternal::Serde1) 258 | .unwrap_or(OwnedInternal::Poisoned("failed to buffer the value")); 259 | Ok(()) 260 | } 261 | 262 | #[cfg(feature = "serde1")] 263 | fn shared_serde1( 264 | &mut self, 265 | v: &Arc, 266 | ) -> Result<(), Error> { 267 | self.0 = OwnedInternal::SharedSerde1(v.clone()); 268 | Ok(()) 269 | } 270 | 271 | #[cfg(feature = "seq")] 272 | fn seq(&mut self, v: &dyn internal::seq::Seq) -> Result<(), Error> { 273 | self.0 = internal::seq::owned::buffer(v) 274 | .map(OwnedInternal::Seq) 275 | .unwrap_or(OwnedInternal::Poisoned("failed to buffer the value")); 276 | Ok(()) 277 | } 278 | 279 | #[cfg(feature = "seq")] 280 | fn shared_seq( 281 | &mut self, 282 | v: &Arc, 283 | ) -> Result<(), Error> { 284 | self.0 = OwnedInternal::SharedSeq(v.clone()); 285 | Ok(()) 286 | } 287 | 288 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> { 289 | self.0 = OwnedInternal::Poisoned(msg); 290 | Ok(()) 291 | } 292 | } 293 | 294 | let mut visitor = OwnedVisitor(OwnedInternal::None); 295 | 296 | let _ = self.internal_visit(&mut visitor); 297 | 298 | visitor.0 299 | } 300 | } 301 | 302 | #[cfg(feature = "inline-str")] 303 | mod inline_str { 304 | use crate::std::{fmt, slice, str}; 305 | 306 | pub(super) const MAX_INLINE_LEN: usize = 22; 307 | 308 | #[derive(Clone, Copy)] 309 | pub(crate) struct InlineStr { 310 | data: [u8; MAX_INLINE_LEN], 311 | len: u8, 312 | } 313 | 314 | impl fmt::Debug for InlineStr { 315 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 316 | fmt::Debug::fmt(self.get(), f) 317 | } 318 | } 319 | 320 | impl fmt::Display for InlineStr { 321 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 322 | fmt::Display::fmt(self.get(), f) 323 | } 324 | } 325 | 326 | impl InlineStr { 327 | pub(super) fn copy_from(str: &str) -> Self { 328 | let str = str.as_bytes(); 329 | 330 | // NOTE: This will panic if the string is too big 331 | let mut data = [0; MAX_INLINE_LEN]; 332 | data[..str.len()].copy_from_slice(str); 333 | 334 | InlineStr { 335 | data, 336 | len: str.len() as u8, 337 | } 338 | } 339 | 340 | pub(super) const fn get(&self) -> &str { 341 | // NOTE: We can't slice data in `const` fns yet, so we do it this way 342 | // SAFETY: `data` contains valid UTF8, and `len` points within `data` 343 | unsafe { 344 | str::from_utf8_unchecked(slice::from_raw_parts( 345 | &self.data as *const u8, 346 | self.len as usize, 347 | )) 348 | } 349 | } 350 | } 351 | 352 | #[cfg(test)] 353 | mod tests { 354 | use super::*; 355 | 356 | use std::string::ToString; 357 | 358 | #[test] 359 | fn inline_str() { 360 | let s = InlineStr::copy_from("abc"); 361 | 362 | assert_eq!("abc", s.get()); 363 | assert_eq!("abc", s.to_string()); 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /src/internal/seq.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fill::Slot, 3 | internal::{Internal, InternalVisitor}, 4 | std::{any::Any, fmt, marker::PhantomData, mem, ops::ControlFlow}, 5 | Error, ValueBag, 6 | }; 7 | 8 | #[cfg(feature = "alloc")] 9 | use crate::std::{string::String, vec::Vec}; 10 | 11 | impl<'v> ValueBag<'v> { 12 | /// Get a value from a sequence of values without capturing support. 13 | pub fn from_seq_slice(value: &'v I) -> Self 14 | where 15 | I: AsRef<[T]>, 16 | &'v T: Into> + 'v, 17 | { 18 | ValueBag { 19 | inner: Internal::AnonSeq(SeqSlice::new_ref(value)), 20 | } 21 | } 22 | 23 | pub(crate) const fn from_dyn_seq(value: &'v dyn Seq) -> Self { 24 | ValueBag { 25 | inner: Internal::AnonSeq(value), 26 | } 27 | } 28 | 29 | /// Try get a collection `S` of `u64`s from this value. 30 | /// 31 | /// If this value is a sequence then the collection `S` will be extended 32 | /// with the attempted conversion of each of its elements. 33 | /// 34 | /// If this value is not a sequence then this method will return `None`. 35 | pub fn to_u64_seq>>(&self) -> Option { 36 | self.inner 37 | .extend::>() 38 | .map(|seq| seq.into_inner()) 39 | } 40 | 41 | /// Try get a collection `S` of `i64`s from this value. 42 | /// 43 | /// If this value is a sequence then the collection `S` will be extended 44 | /// with the attempted conversion of each of its elements. 45 | /// 46 | /// If this value is not a sequence then this method will return `None`. 47 | pub fn to_i64_seq>>(&self) -> Option { 48 | self.inner 49 | .extend::>() 50 | .map(|seq| seq.into_inner()) 51 | } 52 | 53 | /// Try get a collection `S` of `u128`s from this value. 54 | /// 55 | /// If this value is a sequence then the collection `S` will be extended 56 | /// with the attempted conversion of each of its elements. 57 | /// 58 | /// If this value is not a sequence then this method will return `None`. 59 | pub fn to_u128_seq>>(&self) -> Option { 60 | self.inner 61 | .extend::>() 62 | .map(|seq| seq.into_inner()) 63 | } 64 | 65 | /// Try get a collection `S` of `i128`s from this value. 66 | /// 67 | /// If this value is a sequence then the collection `S` will be extended 68 | /// with the attempted conversion of each of its elements. 69 | /// 70 | /// If this value is not a sequence then this method will return `None`. 71 | pub fn to_i128_seq>>(&self) -> Option { 72 | self.inner 73 | .extend::>() 74 | .map(|seq| seq.into_inner()) 75 | } 76 | 77 | /// Try get a collection `S` of `f64`s from this value. 78 | /// 79 | /// If this value is a sequence then the collection `S` will be extended 80 | /// with the attempted conversion of each of its elements. 81 | /// 82 | /// If this value is not a sequence then this method will return `None`. 83 | pub fn to_f64_seq>>(&self) -> Option { 84 | self.inner 85 | .extend::>() 86 | .map(|seq| seq.into_inner()) 87 | } 88 | 89 | /// Get a collection `S` of `f64`s from this value. 90 | /// 91 | /// If this value is a sequence then the collection `S` will be extended 92 | /// with the conversion of each of its elements. The conversion is the 93 | /// same as [`ValueBag::as_f64`]. 94 | /// 95 | /// If this value is not a sequence then this method will return an 96 | /// empty collection. 97 | /// 98 | /// This is similar to [`ValueBag::to_f64_seq`], but can be more 99 | /// convenient when there's no need to distinguish between an empty 100 | /// collection and a non-collection, or between `f64` and non-`f64` elements. 101 | pub fn as_f64_seq>(&self) -> S { 102 | #[derive(Default)] 103 | struct ExtendF64(S); 104 | 105 | impl<'a, S: Extend> ExtendValue<'a> for ExtendF64 { 106 | fn extend(&mut self, inner: Internal<'_>) { 107 | self.0.extend(Some(ValueBag { inner }.as_f64())) 108 | } 109 | } 110 | 111 | self.inner 112 | .extend::>() 113 | .map(|seq| seq.0) 114 | .unwrap_or_default() 115 | } 116 | 117 | /// Try get a collection `S` of `bool`s from this value. 118 | /// 119 | /// If this value is a sequence then the collection `S` will be extended 120 | /// with the attempted conversion of each of its elements. 121 | /// 122 | /// If this value is not a sequence then this method will return `None`. 123 | pub fn to_bool_seq>>(&self) -> Option { 124 | self.inner 125 | .extend::>() 126 | .map(|seq| seq.into_inner()) 127 | } 128 | 129 | /// Try get a collection `S` of `char`s from this value. 130 | /// 131 | /// If this value is a sequence then the collection `S` will be extended 132 | /// with the attempted conversion of each of its elements. 133 | /// 134 | /// If this value is not a sequence then this method will return `None`. 135 | pub fn to_char_seq>>(&self) -> Option { 136 | self.inner 137 | .extend::>() 138 | .map(|seq| seq.into_inner()) 139 | } 140 | 141 | /// Try get a collection `S` of strings from this value. 142 | /// 143 | /// If this value is a sequence then the collection `S` will be extended 144 | /// with the attempted conversion of each of its elements. 145 | /// 146 | /// If this value is not a sequence then this method will return `None`. 147 | #[inline] 148 | pub fn to_borrowed_str_seq>>(&self) -> Option { 149 | #[derive(Default)] 150 | struct ExtendStr<'a, S>(S, PhantomData<&'a str>); 151 | 152 | impl<'a, S: Extend>> ExtendValue<'a> for ExtendStr<'a, S> { 153 | fn extend<'b>(&mut self, _: Internal<'b>) { 154 | self.0.extend(Some(None::<&'a str>)) 155 | } 156 | 157 | fn extend_borrowed(&mut self, inner: Internal<'a>) { 158 | self.0.extend(Some(ValueBag { inner }.to_borrowed_str())) 159 | } 160 | } 161 | 162 | self.inner.extend::>().map(|seq| seq.0) 163 | } 164 | } 165 | 166 | impl<'s, 'f> Slot<'s, 'f> { 167 | /// Fill the slot with a sequence of values. 168 | /// 169 | /// The given value doesn't need to satisfy any particular lifetime constraints. 170 | pub fn fill_seq_slice(self, value: &'f I) -> Result<(), Error> 171 | where 172 | I: AsRef<[T]>, 173 | &'f T: Into> + 'f, 174 | { 175 | self.fill(|visitor| visitor.seq(SeqSlice::new_ref(value))) 176 | } 177 | } 178 | 179 | /* 180 | This is a bit of an ugly way of working around the gulf between 181 | lifetimes expressed externally as bounds, and lifetimes implied 182 | on methods. 183 | */ 184 | 185 | #[repr(transparent)] 186 | struct SeqSlice<'a, I: ?Sized, T>(PhantomData<&'a [T]>, I); 187 | 188 | impl<'a, I: AsRef<[T]> + ?Sized + 'a, T> SeqSlice<'a, I, T> { 189 | fn new_ref(v: &'a I) -> &'a SeqSlice<'a, I, T> { 190 | // SAFETY: `SeqSlice<'a, I, T>` and `I` have the same ABI 191 | unsafe { &*(v as *const I as *const SeqSlice<'a, I, T>) } 192 | } 193 | 194 | fn as_ref<'b>(&'b self) -> &'a [T] { 195 | // SAFETY: `new_ref` requires there's a borrow of `&'a I` 196 | // on the borrow stack, so we can safely borrow it for `'a` here 197 | let inner = unsafe { mem::transmute::<&'b I, &'a I>(&self.1) }; 198 | 199 | inner.as_ref() 200 | } 201 | } 202 | 203 | impl<'a, I, T> Seq for SeqSlice<'a, I, T> 204 | where 205 | I: AsRef<[T]> + ?Sized + 'a, 206 | &'a T: Into>, 207 | { 208 | fn visit(&self, visitor: &mut dyn Visitor<'_>) { 209 | for v in self.as_ref().iter() { 210 | if let ControlFlow::Break(()) = visitor.element(v.into()) { 211 | return; 212 | } 213 | } 214 | } 215 | 216 | fn borrowed_visit<'v>(&'v self, visitor: &mut dyn Visitor<'v>) { 217 | for v in self.as_ref().iter() { 218 | if let ControlFlow::Break(()) = visitor.borrowed_element(v.into()) { 219 | return; 220 | } 221 | } 222 | } 223 | } 224 | 225 | pub(crate) trait Seq { 226 | fn visit(&self, visitor: &mut dyn Visitor<'_>); 227 | 228 | fn borrowed_visit<'v>(&'v self, visitor: &mut dyn Visitor<'v>) { 229 | self.visit(visitor) 230 | } 231 | } 232 | 233 | impl<'a, S: Seq + ?Sized> Seq for &'a S { 234 | fn visit(&self, visitor: &mut dyn Visitor<'_>) { 235 | (**self).visit(visitor) 236 | } 237 | 238 | fn borrowed_visit<'v>(&'v self, visitor: &mut dyn Visitor<'v>) { 239 | (**self).borrowed_visit(visitor) 240 | } 241 | } 242 | 243 | pub(crate) trait Visitor<'v> { 244 | fn element(&mut self, v: ValueBag) -> ControlFlow<()>; 245 | 246 | fn borrowed_element(&mut self, v: ValueBag<'v>) -> ControlFlow<()> { 247 | self.element(v) 248 | } 249 | } 250 | 251 | impl<'a, 'v, T: Visitor<'v> + ?Sized> Visitor<'v> for &'a mut T { 252 | fn element(&mut self, v: ValueBag) -> ControlFlow<()> { 253 | (**self).element(v) 254 | } 255 | 256 | fn borrowed_element(&mut self, v: ValueBag<'v>) -> ControlFlow<()> { 257 | (**self).borrowed_element(v) 258 | } 259 | } 260 | 261 | pub(crate) trait DowncastSeq { 262 | // Currently only used when `owned` is also available 263 | #[allow(dead_code)] 264 | fn as_any(&self) -> &dyn Any; 265 | fn as_super(&self) -> &dyn Seq; 266 | } 267 | 268 | impl DowncastSeq for T { 269 | fn as_any(&self) -> &dyn Any { 270 | self 271 | } 272 | 273 | fn as_super(&self) -> &dyn Seq { 274 | self 275 | } 276 | } 277 | 278 | impl<'a> Seq for dyn DowncastSeq + Send + Sync + 'a { 279 | fn visit(&self, visitor: &mut dyn Visitor<'_>) { 280 | self.as_super().visit(visitor) 281 | } 282 | } 283 | 284 | macro_rules! convert_primitive( 285 | ($($t:ty,)*) => { 286 | $( 287 | impl<'v, const N: usize> From<&'v [$t; N]> for ValueBag<'v> { 288 | fn from(v: &'v [$t; N]) -> Self { 289 | ValueBag::from_seq_slice(v) 290 | } 291 | } 292 | 293 | impl<'v, const N: usize> From> for ValueBag<'v> { 294 | fn from(v: Option<&'v [$t; N]>) -> Self { 295 | ValueBag::from_option(v) 296 | } 297 | } 298 | 299 | impl<'a, 'v> From<&'v &'a [$t]> for ValueBag<'v> { 300 | fn from(v: &'v &'a [$t]) -> Self { 301 | ValueBag::from_seq_slice(v) 302 | } 303 | } 304 | 305 | impl<'a, 'v> From> for ValueBag<'v> { 306 | fn from(v: Option<&'v &'a [$t]>) -> Self { 307 | ValueBag::from_option(v) 308 | } 309 | } 310 | 311 | #[cfg(feature = "alloc")] 312 | impl<'v> From<&'v Vec<$t>> for ValueBag<'v> { 313 | fn from(v: &'v Vec<$t>) -> Self { 314 | ValueBag::from_seq_slice(v) 315 | } 316 | } 317 | 318 | #[cfg(feature = "alloc")] 319 | impl<'v> From>> for ValueBag<'v> { 320 | fn from(v: Option<&'v Vec<$t>>) -> Self { 321 | ValueBag::from_option(v) 322 | } 323 | } 324 | )* 325 | } 326 | ); 327 | 328 | convert_primitive![ 329 | u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, char, 330 | ]; 331 | 332 | impl<'v, 'a, const N: usize> From<&'v [&'a str; N]> for ValueBag<'v> { 333 | fn from(v: &'v [&'a str; N]) -> Self { 334 | ValueBag::from_seq_slice(v) 335 | } 336 | } 337 | 338 | impl<'v, 'a, const N: usize> From> for ValueBag<'v> { 339 | fn from(v: Option<&'v [&'a str; N]>) -> Self { 340 | ValueBag::from_option(v) 341 | } 342 | } 343 | 344 | impl<'v, 'a, 'b> From<&'v &'a [&'b str]> for ValueBag<'v> { 345 | fn from(v: &'v &'a [&'b str]) -> Self { 346 | ValueBag::from_seq_slice(v) 347 | } 348 | } 349 | 350 | impl<'v, 'a, 'b> From> for ValueBag<'v> { 351 | fn from(v: Option<&'v &'a [&'b str]>) -> Self { 352 | ValueBag::from_option(v) 353 | } 354 | } 355 | 356 | #[cfg(feature = "alloc")] 357 | impl<'v> From<&'v Vec> for ValueBag<'v> { 358 | fn from(v: &'v Vec) -> Self { 359 | ValueBag::from_seq_slice(v) 360 | } 361 | } 362 | 363 | #[cfg(feature = "alloc")] 364 | impl<'v> From>> for ValueBag<'v> { 365 | fn from(v: Option<&'v Vec>) -> Self { 366 | ValueBag::from_option(v) 367 | } 368 | } 369 | 370 | #[derive(Default)] 371 | pub(crate) struct ExtendPrimitive(S, PhantomData); 372 | 373 | impl ExtendPrimitive { 374 | pub fn into_inner(self) -> S { 375 | self.0 376 | } 377 | } 378 | 379 | impl<'a, S: Extend>, T: for<'b> TryFrom>> ExtendValue<'a> 380 | for ExtendPrimitive 381 | { 382 | fn extend(&mut self, inner: Internal) { 383 | self.0.extend(Some(ValueBag { inner }.try_into().ok())) 384 | } 385 | } 386 | 387 | #[allow(dead_code)] 388 | pub(crate) trait ExtendValue<'v> { 389 | fn extend(&mut self, v: Internal); 390 | 391 | fn extend_borrowed(&mut self, v: Internal<'v>) { 392 | self.extend(v); 393 | } 394 | } 395 | 396 | struct ExtendVisitor(S); 397 | 398 | impl<'v, S: ExtendValue<'v>> Visitor<'v> for ExtendVisitor { 399 | fn element(&mut self, v: ValueBag) -> ControlFlow<()> { 400 | self.0.extend(v.inner); 401 | ControlFlow::Continue(()) 402 | } 403 | 404 | fn borrowed_element(&mut self, v: ValueBag<'v>) -> ControlFlow<()> { 405 | self.0.extend_borrowed(v.inner); 406 | ControlFlow::Continue(()) 407 | } 408 | } 409 | 410 | impl<'v> Internal<'v> { 411 | #[inline] 412 | pub(crate) fn extend>(&self) -> Option { 413 | struct SeqVisitor(Option); 414 | 415 | impl<'v, S: Default + ExtendValue<'v>> InternalVisitor<'v> for SeqVisitor { 416 | #[inline] 417 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { 418 | v.fill(Slot::new(self)) 419 | } 420 | 421 | #[inline] 422 | fn debug(&mut self, _: &dyn fmt::Debug) -> Result<(), Error> { 423 | Ok(()) 424 | } 425 | 426 | #[inline] 427 | fn display(&mut self, _: &dyn fmt::Display) -> Result<(), Error> { 428 | Ok(()) 429 | } 430 | 431 | #[inline] 432 | fn u64(&mut self, _: u64) -> Result<(), Error> { 433 | Ok(()) 434 | } 435 | 436 | #[inline] 437 | fn i64(&mut self, _: i64) -> Result<(), Error> { 438 | Ok(()) 439 | } 440 | 441 | #[inline] 442 | fn u128(&mut self, _: &u128) -> Result<(), Error> { 443 | Ok(()) 444 | } 445 | 446 | #[inline] 447 | fn i128(&mut self, _: &i128) -> Result<(), Error> { 448 | Ok(()) 449 | } 450 | 451 | #[inline] 452 | fn f64(&mut self, _: f64) -> Result<(), Error> { 453 | Ok(()) 454 | } 455 | 456 | #[inline] 457 | fn bool(&mut self, _: bool) -> Result<(), Error> { 458 | Ok(()) 459 | } 460 | 461 | #[inline] 462 | fn char(&mut self, _: char) -> Result<(), Error> { 463 | Ok(()) 464 | } 465 | 466 | #[inline] 467 | fn str(&mut self, _: &str) -> Result<(), Error> { 468 | Ok(()) 469 | } 470 | 471 | #[inline] 472 | fn none(&mut self) -> Result<(), Error> { 473 | Ok(()) 474 | } 475 | 476 | #[cfg(feature = "error")] 477 | #[inline] 478 | fn error(&mut self, _: &dyn crate::internal::error::Error) -> Result<(), Error> { 479 | Ok(()) 480 | } 481 | 482 | #[cfg(feature = "sval2")] 483 | #[inline] 484 | fn sval2(&mut self, v: &dyn crate::internal::sval::v2::Value) -> Result<(), Error> { 485 | self.0 = crate::internal::sval::v2::seq::extend(v); 486 | 487 | Ok(()) 488 | } 489 | 490 | #[cfg(feature = "sval2")] 491 | #[inline] 492 | fn borrowed_sval2( 493 | &mut self, 494 | v: &'v dyn crate::internal::sval::v2::Value, 495 | ) -> Result<(), Error> { 496 | self.0 = crate::internal::sval::v2::seq::extend_borrowed(v); 497 | 498 | Ok(()) 499 | } 500 | 501 | #[cfg(feature = "serde1")] 502 | #[inline] 503 | fn serde1( 504 | &mut self, 505 | v: &dyn crate::internal::serde::v1::Serialize, 506 | ) -> Result<(), Error> { 507 | self.0 = crate::internal::serde::v1::seq::extend(v); 508 | 509 | Ok(()) 510 | } 511 | 512 | fn seq(&mut self, seq: &dyn Seq) -> Result<(), Error> { 513 | let mut s = ExtendVisitor(S::default()); 514 | seq.visit(&mut s); 515 | self.0 = Some(s.0); 516 | 517 | Ok(()) 518 | } 519 | 520 | fn borrowed_seq(&mut self, seq: &'v dyn Seq) -> Result<(), Error> { 521 | let mut s = ExtendVisitor(S::default()); 522 | seq.borrowed_visit(&mut s); 523 | self.0 = Some(s.0); 524 | 525 | Ok(()) 526 | } 527 | 528 | fn poisoned(&mut self, _: &'static str) -> Result<(), Error> { 529 | Ok(()) 530 | } 531 | } 532 | 533 | let mut visitor = SeqVisitor(None); 534 | let _ = self.internal_visit(&mut visitor); 535 | 536 | visitor.0 537 | } 538 | } 539 | 540 | #[cfg(feature = "alloc")] 541 | mod alloc_support { 542 | use super::*; 543 | 544 | use crate::std::borrow::Cow; 545 | 546 | impl<'v> ValueBag<'v> { 547 | /// Try get a collection `S` of strings from this value. 548 | /// 549 | /// If this value is a sequence then the collection `S` will be extended 550 | /// with the attempted conversion of each of its elements. 551 | /// 552 | /// If this value is not a sequence then this method will return `None`. 553 | #[inline] 554 | pub fn to_str_seq>>>(&self) -> Option { 555 | #[derive(Default)] 556 | struct ExtendStr<'a, S>(S, PhantomData>); 557 | 558 | impl<'a, S: Extend>>> ExtendValue<'a> for ExtendStr<'a, S> { 559 | fn extend(&mut self, inner: Internal<'_>) { 560 | self.0.extend(Some( 561 | ValueBag { inner } 562 | .to_str() 563 | .map(|s| Cow::Owned(s.into_owned())), 564 | )) 565 | } 566 | 567 | fn extend_borrowed(&mut self, inner: Internal<'a>) { 568 | self.0.extend(Some(ValueBag { inner }.to_str())) 569 | } 570 | } 571 | 572 | self.inner.extend::>().map(|seq| seq.0) 573 | } 574 | } 575 | } 576 | 577 | #[cfg(feature = "owned")] 578 | pub(crate) mod owned { 579 | use super::*; 580 | 581 | use crate::{owned::OwnedValueBag, std::boxed::Box}; 582 | 583 | #[derive(Clone)] 584 | pub(crate) struct OwnedSeq(Box<[OwnedValueBag]>); 585 | 586 | impl Seq for OwnedSeq { 587 | fn visit(&self, visitor: &mut dyn Visitor<'_>) { 588 | for item in self.0.iter() { 589 | if let ControlFlow::Break(()) = visitor.element(item.by_ref()) { 590 | return; 591 | } 592 | } 593 | } 594 | } 595 | 596 | pub(crate) fn buffer(v: &dyn Seq) -> Result { 597 | struct BufferVisitor(Vec); 598 | 599 | impl<'v> Visitor<'v> for BufferVisitor { 600 | fn element(&mut self, v: ValueBag) -> ControlFlow<()> { 601 | self.0.push(v.to_owned()); 602 | ControlFlow::Continue(()) 603 | } 604 | } 605 | 606 | let mut buf = BufferVisitor(Vec::new()); 607 | v.visit(&mut buf); 608 | Ok(OwnedSeq(buf.0.into_boxed_slice())) 609 | } 610 | } 611 | 612 | #[cfg(test)] 613 | mod tests { 614 | #[cfg(target_arch = "wasm32")] 615 | use wasm_bindgen_test::*; 616 | 617 | use std::vec::Vec; 618 | 619 | use super::*; 620 | 621 | #[test] 622 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 623 | fn to_u64_seq() { 624 | assert_eq!( 625 | Some(vec![Some(1u64), Some(2u64), Some(3u64)]), 626 | ValueBag::from(&[1u8, 2u8, 3u8]).to_u64_seq::>>() 627 | ); 628 | 629 | assert_eq!( 630 | Some(vec![Some(1u64), Some(2u64), Some(3u64)]), 631 | ValueBag::from(&[1u16, 2u16, 3u16]).to_u64_seq::>>() 632 | ); 633 | 634 | assert_eq!( 635 | Some(vec![Some(1u64), Some(2u64), Some(3u64)]), 636 | ValueBag::from(&[1u32, 2u32, 3u32]).to_u64_seq::>>() 637 | ); 638 | 639 | assert_eq!( 640 | Some(vec![Some(1u64), Some(2u64), Some(3u64)]), 641 | ValueBag::from(&[1u64, 2u64, 3u64]).to_u64_seq::>>() 642 | ); 643 | } 644 | 645 | #[test] 646 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 647 | fn to_i64_seq() { 648 | assert_eq!( 649 | Some(vec![Some(1i64), Some(2i64), Some(3i64)]), 650 | ValueBag::from(&[1i8, 2i8, 3i8]).to_i64_seq::>>() 651 | ); 652 | 653 | assert_eq!( 654 | Some(vec![Some(1i64), Some(2i64), Some(3i64)]), 655 | ValueBag::from(&[1i16, 2i16, 3i16]).to_i64_seq::>>() 656 | ); 657 | 658 | assert_eq!( 659 | Some(vec![Some(1i64), Some(2i64), Some(3i64)]), 660 | ValueBag::from(&[1i32, 2i32, 3i32]).to_i64_seq::>>() 661 | ); 662 | 663 | assert_eq!( 664 | Some(vec![Some(1i64), Some(2i64), Some(3i64)]), 665 | ValueBag::from(&[1i64, 2i64, 3i64]).to_i64_seq::>>() 666 | ); 667 | } 668 | 669 | #[test] 670 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 671 | fn to_f64_seq() { 672 | assert_eq!( 673 | Some(vec![Some(1.0f64), Some(2.0f64), Some(3.0f64)]), 674 | ValueBag::from(&[1.0f64, 2.0f64, 3.0f64]).to_f64_seq::>>() 675 | ); 676 | } 677 | 678 | #[test] 679 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 680 | fn as_f64_seq() { 681 | assert_eq!( 682 | Vec::::new(), 683 | ValueBag::from(1.0f64).as_f64_seq::>() 684 | ); 685 | 686 | assert_eq!( 687 | vec![1.0f64, 2.0f64, 3.0f64], 688 | ValueBag::from(&[1, 2, 3]).as_f64_seq::>() 689 | ); 690 | } 691 | 692 | #[test] 693 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 694 | fn to_u128_seq() { 695 | assert_eq!( 696 | Some(vec![Some(1u128), Some(2u128), Some(3u128)]), 697 | ValueBag::from(&[1u128, 2u128, 3u128]).to_u128_seq::>>() 698 | ); 699 | } 700 | 701 | #[test] 702 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 703 | fn to_i128_seq() { 704 | assert_eq!( 705 | Some(vec![Some(1i128), Some(2i128), Some(3i128)]), 706 | ValueBag::from(&[1i128, 2i128, 3i128]).to_i128_seq::>>() 707 | ); 708 | } 709 | 710 | #[test] 711 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 712 | fn to_bool_seq() { 713 | assert_eq!( 714 | Some(vec![Some(true), Some(false)]), 715 | ValueBag::from(&[true, false]).to_bool_seq::>>() 716 | ); 717 | } 718 | 719 | #[test] 720 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 721 | fn to_char_seq() { 722 | assert_eq!( 723 | Some(vec![Some('a'), Some('b'), Some('c')]), 724 | ValueBag::from(&['a', 'b', 'c']).to_char_seq::>>() 725 | ); 726 | } 727 | 728 | #[test] 729 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 730 | fn to_borrowed_str_seq() { 731 | let v = ["a", "b", "c"]; 732 | let v = ValueBag::from(&v); 733 | 734 | assert_eq!( 735 | Some(vec![Some("a"), Some("b"), Some("c")]), 736 | v.to_borrowed_str_seq::>>() 737 | ); 738 | 739 | let v = ValueBag::from_fill(&|slot: Slot| slot.fill_seq_slice(&["a", "b", "c"])); 740 | 741 | assert_eq!( 742 | Some(vec![None, None, None]), 743 | v.to_borrowed_str_seq::>>() 744 | ); 745 | } 746 | 747 | #[cfg(feature = "alloc")] 748 | mod alloc_support { 749 | use super::*; 750 | 751 | use crate::std::borrow::Cow; 752 | 753 | #[test] 754 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 755 | fn to_str_seq() { 756 | let v = ["a", "b", "c"]; 757 | let v = ValueBag::from(&v); 758 | 759 | assert_eq!( 760 | Some(vec![ 761 | Some(Cow::Borrowed("a")), 762 | Some(Cow::Borrowed("b")), 763 | Some(Cow::Borrowed("c")) 764 | ]), 765 | v.to_str_seq::>>>() 766 | ); 767 | 768 | let v = ValueBag::from_fill(&|slot: Slot| slot.fill_seq_slice(&["a", "b", "c"])); 769 | 770 | assert_eq!( 771 | Some(vec![ 772 | Some(Cow::Owned("a".into())), 773 | Some(Cow::Owned("b".into())), 774 | Some(Cow::Owned("c".into())) 775 | ]), 776 | v.to_str_seq::>>>() 777 | ); 778 | } 779 | } 780 | } 781 | -------------------------------------------------------------------------------- /src/internal/serde/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod v1; 2 | -------------------------------------------------------------------------------- /src/internal/sval/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod v2; 2 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Structured values. 2 | //! 3 | //! This crate contains the [`ValueBag`] type, a container for an anonymous structured value. 4 | //! `ValueBag`s can be captured in various ways and then formatted, inspected, and serialized 5 | //! without losing their original structure. 6 | //! 7 | //! The producer of a [`ValueBag`] may use a different strategy for capturing than the eventual 8 | //! consumer. They don't need to coordinate directly. 9 | 10 | #![doc(html_root_url = "https://docs.rs/value-bag/1.11.1")] 11 | #![no_std] 12 | #![allow( 13 | clippy::unnecessary_fallible_conversions, 14 | clippy::explicit_auto_deref, 15 | clippy::wrong_self_convention 16 | )] 17 | 18 | /* 19 | # Crate design 20 | 21 | This library internally ties several frameworks together. The details of how 22 | this is done are hidden from end-users. It looks roughly like this: 23 | 24 | ┌─────┐ ┌──────┐ 25 | │sval2│ │serde1│ 1. libs on crates.io 26 | └──┬──┘ └─┬─┬──┘ 27 | ├──────────┘ │ 28 | ┌───────▼──┐ ┌───▼───────┐ 29 | │meta/sval2│ │meta/serde1│ 2. meta crates with features 30 | └───────┬──┘ └───┬───────┘ 31 | │ │ 32 | ┌─────────────▼──┐ ┌───▼─────────────┐ 33 | │internal/sval/v2◄─────┤internal/serde/v1│ 3. internal modules with `InternalVisitor` 34 | └─────────────┬──┘ └───┬─────────────┘ 35 | │ │ 36 | ┌──────▼────────┬───▼────────────┐ 37 | │Internal::Sval2│Internal::Serde1│ 4. variants in `Internal` enum 38 | └───────────────┼────────────────┘ 39 | │ 40 | ┌───────────────────────▼────────────────────────┐ 41 | │ValueBag::capture_sval2│ValueBag::capture_serde1│ 5. ctors on `ValueBag` 42 | └───────────────────────┼────────────────────────┘ 43 | │ 44 | ┌───────────────────────▼───────────────────────────┐ 45 | │impl Value for ValueBag│impl Serialize for ValueBag│ 6. trait impls on `ValueBag` 46 | └───────────────────────┴───────────────────────────┘ 47 | 48 | ## 1. libs on crates.io 49 | 50 | These are the frameworks like `serde` or `sval`. 51 | 52 | ## 2. meta crates with features 53 | 54 | These are crates that are internal to `value-bag`. They depend on the public 55 | framework and any utility crates that come along with it. They also expose 56 | features for any other framework. This is done this way so `value-bag` can use 57 | Cargo's `crate?/feature` syntax to conditionally add framework support. 58 | 59 | ## 3. internal modules with `InternalVisitor` 60 | 61 | These are modules in `value-bag` that integrate the framework using the 62 | `InternalVisitor` trait. This makes it possible for that framework to cast 63 | primitive values and pass-through any other framework. 64 | 65 | ## 4. variants in `Internal` enum 66 | 67 | These are individual variants on the `Internal` enum that the `ValueBag` 68 | type wraps. Each framework has one or more variants in this enum. 69 | 70 | ## 5. ctors on `ValueBag` 71 | 72 | These are constructors for producers of `ValueBag`s that accept a value 73 | implementing a serialization trait from a specific framework, like 74 | `serde::Serialize` or `sval::Value`. 75 | 76 | ## 7. trait impls on `ValueBag` 77 | 78 | These are trait impls for consumers of `ValueBag`s that serialize the 79 | underlying value, bridging it if it was produced for a different framework. 80 | */ 81 | 82 | #[cfg(any(feature = "std", test))] 83 | #[macro_use] 84 | #[allow(unused_imports)] 85 | extern crate std; 86 | 87 | #[cfg(all(not(test), feature = "alloc", not(feature = "std")))] 88 | #[macro_use] 89 | #[allow(unused_imports)] 90 | extern crate core; 91 | 92 | #[cfg(all(not(test), feature = "alloc", not(feature = "std")))] 93 | #[macro_use] 94 | #[allow(unused_imports)] 95 | extern crate alloc; 96 | 97 | #[cfg(all(not(test), feature = "alloc", not(feature = "std")))] 98 | #[allow(unused_imports)] 99 | mod std { 100 | pub use crate::{ 101 | alloc::{borrow, boxed, string, vec}, 102 | core::*, 103 | }; 104 | 105 | #[cfg(feature = "owned")] 106 | pub use crate::alloc::sync; 107 | } 108 | 109 | #[cfg(not(any(feature = "alloc", feature = "std", test)))] 110 | #[macro_use] 111 | #[allow(unused_imports)] 112 | extern crate core as std; 113 | 114 | mod error; 115 | pub mod fill; 116 | mod impls; 117 | mod internal; 118 | pub mod visit; 119 | 120 | #[cfg(any(test, feature = "test"))] 121 | pub mod test; 122 | 123 | #[cfg(feature = "owned")] 124 | mod owned; 125 | #[cfg(feature = "owned")] 126 | pub use self::owned::*; 127 | 128 | pub use self::error::Error; 129 | 130 | /// A dynamic structured value. 131 | /// 132 | /// # Capturing values 133 | /// 134 | /// There are a few ways to capture a value: 135 | /// 136 | /// - Using the `ValueBag::capture_*` and `ValueBag::from_*` methods. 137 | /// - Using the standard `From` trait. 138 | /// - Using the `Fill` API. 139 | /// 140 | /// ## Using the `ValueBag::capture_*` methods 141 | /// 142 | /// `ValueBag` offers a few constructor methods that capture values of different kinds. 143 | /// These methods require a `T: 'static` to support downcasting. 144 | /// 145 | /// ``` 146 | /// use value_bag::ValueBag; 147 | /// 148 | /// let value = ValueBag::capture_debug(&42i32); 149 | /// 150 | /// assert_eq!(Some(42), value.to_i64()); 151 | /// ``` 152 | /// 153 | /// Capturing a value using these methods will retain type information so that 154 | /// the contents of the bag can be serialized using an appropriate type. 155 | /// 156 | /// For cases where the `'static` bound can't be satisfied, there's also a few 157 | /// constructors that exclude it. 158 | /// 159 | /// ``` 160 | /// # use std::fmt::Debug; 161 | /// use value_bag::ValueBag; 162 | /// 163 | /// let value = ValueBag::from_debug(&42i32); 164 | /// 165 | /// assert_eq!(None, value.to_i64()); 166 | /// ``` 167 | /// 168 | /// These `ValueBag::from_*` methods are lossy though and `ValueBag::capture_*` should be preferred. 169 | /// 170 | /// ## Using the standard `From` trait 171 | /// 172 | /// Primitive types can be converted into a `ValueBag` using the standard `From` trait. 173 | /// 174 | /// ``` 175 | /// use value_bag::ValueBag; 176 | /// 177 | /// let value = ValueBag::from(42i32); 178 | /// 179 | /// assert_eq!(Some(42), value.to_i64()); 180 | /// ``` 181 | /// 182 | /// ## Using the `Fill` API 183 | /// 184 | /// The [`fill`] module provides a way to bridge APIs that may not be directly 185 | /// compatible with other constructor methods. 186 | /// 187 | /// The `Fill` trait is automatically implemented for closures, so can usually 188 | /// be used in libraries that can't implement the trait themselves. 189 | /// 190 | /// ``` 191 | /// use value_bag::{ValueBag, fill::Slot}; 192 | /// 193 | /// let value = ValueBag::from_fill(&|slot: Slot| { 194 | /// #[derive(Debug)] 195 | /// struct MyShortLivedValue; 196 | /// 197 | /// slot.fill_debug(&MyShortLivedValue) 198 | /// }); 199 | /// 200 | /// assert_eq!("MyShortLivedValue", format!("{:?}", value)); 201 | /// ``` 202 | /// 203 | /// The trait can also be implemented manually: 204 | /// 205 | /// ``` 206 | /// # use std::fmt::Debug; 207 | /// use value_bag::{ValueBag, Error, fill::{Slot, Fill}}; 208 | /// 209 | /// struct FillDebug; 210 | /// 211 | /// impl Fill for FillDebug { 212 | /// fn fill(&self, slot: Slot) -> Result<(), Error> { 213 | /// slot.fill_debug(&42i32 as &dyn Debug) 214 | /// } 215 | /// } 216 | /// 217 | /// let value = ValueBag::from_fill(&FillDebug); 218 | /// 219 | /// assert_eq!(None, value.to_i64()); 220 | /// ``` 221 | /// 222 | /// # Inspecting values 223 | /// 224 | /// Once you have a `ValueBag` there are also a few ways to inspect it: 225 | /// 226 | /// - Using `std::fmt` 227 | /// - Using `sval` 228 | /// - Using `serde` 229 | /// - Using the `ValueBag::visit` method. 230 | /// - Using the `ValueBag::to_*` methods. 231 | /// - Using the `ValueBag::downcast_ref` method. 232 | /// 233 | /// ## Using the `ValueBag::visit` method 234 | /// 235 | /// The [`visit`] module provides a simple visitor API that can be used to inspect 236 | /// the structure of primitives stored in a `ValueBag`. 237 | /// More complex datatypes can then be handled using `std::fmt`, `sval`, or `serde`. 238 | /// 239 | /// ``` 240 | /// #[cfg(not(feature = "std"))] fn main() {} 241 | /// #[cfg(feature = "std")] 242 | /// # fn main() -> Result<(), Box> { 243 | /// # fn escape(buf: &[u8]) -> &[u8] { buf } 244 | /// # fn itoa_fmt(num: T) -> Vec { vec![] } 245 | /// # fn ryu_fmt(num: T) -> Vec { vec![] } 246 | /// # use std::io::Write; 247 | /// use value_bag::{ValueBag, Error, visit::Visit}; 248 | /// 249 | /// // Implement some simple custom serialization 250 | /// struct MyVisit(Vec); 251 | /// impl<'v> Visit<'v> for MyVisit { 252 | /// fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { 253 | /// // Fallback to `Debug` if we didn't visit the value specially 254 | /// write!(&mut self.0, "{:?}", v).map_err(|_| Error::msg("failed to write value")) 255 | /// } 256 | /// 257 | /// fn visit_u64(&mut self, v: u64) -> Result<(), Error> { 258 | /// self.0.extend_from_slice(itoa_fmt(v).as_slice()); 259 | /// Ok(()) 260 | /// } 261 | /// 262 | /// fn visit_i64(&mut self, v: i64) -> Result<(), Error> { 263 | /// self.0.extend_from_slice(itoa_fmt(v).as_slice()); 264 | /// Ok(()) 265 | /// } 266 | /// 267 | /// fn visit_f64(&mut self, v: f64) -> Result<(), Error> { 268 | /// self.0.extend_from_slice(ryu_fmt(v).as_slice()); 269 | /// Ok(()) 270 | /// } 271 | /// 272 | /// fn visit_str(&mut self, v: &str) -> Result<(), Error> { 273 | /// self.0.push(b'\"'); 274 | /// self.0.extend_from_slice(escape(v.as_bytes())); 275 | /// self.0.push(b'\"'); 276 | /// Ok(()) 277 | /// } 278 | /// 279 | /// fn visit_bool(&mut self, v: bool) -> Result<(), Error> { 280 | /// self.0.extend_from_slice(if v { b"true" } else { b"false" }); 281 | /// Ok(()) 282 | /// } 283 | /// } 284 | /// 285 | /// let value = ValueBag::from(42i64); 286 | /// 287 | /// let mut visitor = MyVisit(vec![]); 288 | /// value.visit(&mut visitor)?; 289 | /// # Ok(()) 290 | /// # } 291 | /// ``` 292 | /// 293 | /// ## Using `std::fmt` 294 | /// 295 | /// Any `ValueBag` can be formatted using the `std::fmt` machinery as either `Debug` 296 | /// or `Display`. 297 | /// 298 | /// ``` 299 | /// use value_bag::ValueBag; 300 | /// 301 | /// let value = ValueBag::from(true); 302 | /// 303 | /// assert_eq!("true", format!("{:?}", value)); 304 | /// ``` 305 | /// 306 | /// ## Using `sval` 307 | /// 308 | /// When the `sval2` feature is enabled, any `ValueBag` can be serialized using `sval`. 309 | /// This makes it possible to visit any typed structure captured in the `ValueBag`, 310 | /// including complex datatypes like maps and sequences. 311 | /// 312 | /// `sval` doesn't need to allocate so can be used in no-std environments. 313 | /// 314 | /// First, enable the `sval2` feature in your `Cargo.toml`: 315 | /// 316 | /// ```toml 317 | /// [dependencies.value-bag] 318 | /// features = ["sval2"] 319 | /// ``` 320 | /// 321 | /// Then stream the contents of the `ValueBag` using `sval`. 322 | /// 323 | /// ``` 324 | /// # #[cfg(not(all(feature = "std", feature = "sval2")))] fn main() {} 325 | /// # #[cfg(all(feature = "std", feature = "sval2"))] 326 | /// # fn main() -> Result<(), Box> { 327 | /// # use value_bag_sval2::json as sval_json; 328 | /// use value_bag::ValueBag; 329 | /// 330 | /// let value = ValueBag::from(42i64); 331 | /// let json = sval_json::stream_to_string(value)?; 332 | /// # Ok(()) 333 | /// # } 334 | /// ``` 335 | /// 336 | /// ## Using `serde` 337 | /// 338 | /// When the `serde1` feature is enabled, any `ValueBag` can be serialized using `serde`. 339 | /// This makes it possible to visit any typed structure captured in the `ValueBag`, 340 | /// including complex datatypes like maps and sequences. 341 | /// 342 | /// `serde` needs a few temporary allocations, so also brings in the `std` feature. 343 | /// 344 | /// First, enable the `serde1` feature in your `Cargo.toml`: 345 | /// 346 | /// ```toml 347 | /// [dependencies.value-bag] 348 | /// features = ["serde1"] 349 | /// ``` 350 | /// 351 | /// Then stream the contents of the `ValueBag` using `serde`. 352 | /// 353 | /// ``` 354 | /// # #[cfg(not(all(feature = "std", feature = "serde1")))] fn main() {} 355 | /// # #[cfg(all(feature = "std", feature = "serde1"))] 356 | /// # fn main() -> Result<(), Box> { 357 | /// # use value_bag_serde1::json as serde_json; 358 | /// use value_bag::ValueBag; 359 | /// 360 | /// let value = ValueBag::from(42i64); 361 | /// let json = serde_json::to_string(&value)?; 362 | /// # Ok(()) 363 | /// # } 364 | /// ``` 365 | /// 366 | /// Also see [`serde.rs`](https://serde.rs) for more examples of writing your own serializers. 367 | /// 368 | /// ## Using the `ValueBag::to_*` methods 369 | /// 370 | /// `ValueBag` provides a set of methods for attempting to pull a concrete value out. 371 | /// These are useful for ad-hoc analysis but aren't intended for exhaustively serializing 372 | /// the contents of a `ValueBag`. 373 | /// 374 | /// ``` 375 | /// use value_bag::ValueBag; 376 | /// 377 | /// let value = ValueBag::capture_display(&42u64); 378 | /// 379 | /// assert_eq!(Some(42u64), value.to_u64()); 380 | /// ``` 381 | /// 382 | /// ## Using the `ValueBag::downcast_ref` method 383 | /// 384 | /// When a `ValueBag` is created using one of the `capture_*` constructors, it can be downcast 385 | /// back to its original value. 386 | /// This can also be useful for ad-hoc analysis where there's a common possible non-primitive 387 | /// type that could be captured. 388 | /// 389 | /// ``` 390 | /// # #[derive(Debug)] struct SystemTime; 391 | /// # fn now() -> SystemTime { SystemTime } 392 | /// use value_bag::ValueBag; 393 | /// 394 | /// let timestamp = now(); 395 | /// let value = ValueBag::capture_debug(×tamp); 396 | /// 397 | /// assert!(value.downcast_ref::().is_some()); 398 | /// ``` 399 | /// 400 | /// # Working with sequences 401 | /// 402 | /// The `seq` feature of `value-bag` enables utilities for working with values that are sequences. 403 | /// First, enable the `seq` feature in your `Cargo.toml`: 404 | /// 405 | /// ```toml 406 | /// [dependencies.value-bag] 407 | /// features = ["seq"] 408 | /// ``` 409 | /// 410 | /// Slices and arrays can be captured as sequences: 411 | /// 412 | /// ``` 413 | /// # #[cfg(not(all(feature = "serde1", feature = "seq")))] fn main() {} 414 | /// # #[cfg(all(feature = "serde1", feature = "seq"))] 415 | /// # fn main() -> Result<(), Box> { 416 | /// # use value_bag_serde1::json as serde_json; 417 | /// use value_bag::ValueBag; 418 | /// 419 | /// let value = ValueBag::from_seq_slice(&[1, 2, 3]); 420 | /// 421 | /// assert_eq!("[1,2,3]", serde_json::to_string(&value)?); 422 | /// # Ok(()) 423 | /// # } 424 | /// ``` 425 | /// 426 | /// A sequence captured with either `sval` or `serde` can have its elements extracted: 427 | /// 428 | /// ``` 429 | /// # #[cfg(not(all(feature = "serde1", feature = "seq")))] fn main() {} 430 | /// # #[cfg(all(feature = "serde1", feature = "seq"))] 431 | /// # fn main() -> Result<(), Box> { 432 | /// use value_bag::ValueBag; 433 | /// 434 | /// let value = ValueBag::from_serde1(&[1.0, 2.0, 3.0]); 435 | /// 436 | /// let seq = value.to_f64_seq::>>().ok_or("not a sequence")?; 437 | /// 438 | /// assert_eq!(vec![Some(1.0), Some(2.0), Some(3.0)], seq); 439 | /// # Ok(()) 440 | /// # } 441 | /// ``` 442 | #[derive(Clone)] 443 | pub struct ValueBag<'v> { 444 | inner: internal::Internal<'v>, 445 | } 446 | 447 | impl<'v> ValueBag<'v> { 448 | /// Get an empty `ValueBag`. 449 | #[inline] 450 | pub const fn empty() -> ValueBag<'v> { 451 | ValueBag { 452 | inner: internal::Internal::None, 453 | } 454 | } 455 | 456 | /// Get a `ValueBag` from an `Option`. 457 | /// 458 | /// This method will return `ValueBag::empty` if the value is `None`. 459 | #[inline] 460 | pub fn from_option(v: Option>>) -> ValueBag<'v> { 461 | match v { 462 | Some(v) => v.into(), 463 | None => ValueBag::empty(), 464 | } 465 | } 466 | 467 | /// Get a `ValueBag` from a `u8`. 468 | #[inline] 469 | pub const fn from_u8(v: u8) -> ValueBag<'v> { 470 | ValueBag { 471 | inner: internal::Internal::Unsigned(v as u64), 472 | } 473 | } 474 | 475 | /// Get a `ValueBag` from a `u16`. 476 | #[inline] 477 | pub const fn from_u16(v: u16) -> ValueBag<'v> { 478 | ValueBag { 479 | inner: internal::Internal::Unsigned(v as u64), 480 | } 481 | } 482 | 483 | /// Get a `ValueBag` from a `u32`. 484 | #[inline] 485 | pub const fn from_u32(v: u32) -> ValueBag<'v> { 486 | ValueBag { 487 | inner: internal::Internal::Unsigned(v as u64), 488 | } 489 | } 490 | 491 | /// Get a `ValueBag` from a `u64`. 492 | #[inline] 493 | pub const fn from_u64(v: u64) -> ValueBag<'v> { 494 | ValueBag { 495 | inner: internal::Internal::Unsigned(v), 496 | } 497 | } 498 | 499 | /// Get a `ValueBag` from a `usize`. 500 | #[inline] 501 | pub const fn from_usize(v: usize) -> ValueBag<'v> { 502 | ValueBag { 503 | inner: internal::Internal::Unsigned(v as u64), 504 | } 505 | } 506 | 507 | /// Get a `ValueBag` from a `u128`. 508 | #[inline] 509 | pub const fn from_u128_ref(v: &'v u128) -> ValueBag<'v> { 510 | ValueBag { 511 | #[cfg(not(feature = "inline-i128"))] 512 | inner: internal::Internal::BigUnsigned(v), 513 | #[cfg(feature = "inline-i128")] 514 | inner: internal::Internal::BigUnsigned(*v), 515 | } 516 | } 517 | 518 | /// Get a `ValueBag` from a `u128`. 519 | #[inline] 520 | #[cfg(feature = "inline-i128")] 521 | pub const fn from_u128(v: u128) -> ValueBag<'v> { 522 | ValueBag { 523 | inner: internal::Internal::BigUnsigned(v), 524 | } 525 | } 526 | 527 | /// Get a `ValueBag` from a `i8`. 528 | #[inline] 529 | pub const fn from_i8(v: i8) -> ValueBag<'v> { 530 | ValueBag { 531 | inner: internal::Internal::Signed(v as i64), 532 | } 533 | } 534 | 535 | /// Get a `ValueBag` from a `i16`. 536 | #[inline] 537 | pub const fn from_i16(v: i16) -> ValueBag<'v> { 538 | ValueBag { 539 | inner: internal::Internal::Signed(v as i64), 540 | } 541 | } 542 | 543 | /// Get a `ValueBag` from a `i32`. 544 | #[inline] 545 | pub const fn from_i32(v: i32) -> ValueBag<'v> { 546 | ValueBag { 547 | inner: internal::Internal::Signed(v as i64), 548 | } 549 | } 550 | 551 | /// Get a `ValueBag` from a `i64`. 552 | #[inline] 553 | pub const fn from_i64(v: i64) -> ValueBag<'v> { 554 | ValueBag { 555 | inner: internal::Internal::Signed(v), 556 | } 557 | } 558 | 559 | /// Get a `ValueBag` from a `isize`. 560 | #[inline] 561 | pub const fn from_isize(v: isize) -> ValueBag<'v> { 562 | ValueBag { 563 | inner: internal::Internal::Signed(v as i64), 564 | } 565 | } 566 | 567 | /// Get a `ValueBag` from a `i128`. 568 | #[inline] 569 | pub const fn from_i128_ref(v: &'v i128) -> ValueBag<'v> { 570 | ValueBag { 571 | #[cfg(not(feature = "inline-i128"))] 572 | inner: internal::Internal::BigSigned(v), 573 | #[cfg(feature = "inline-i128")] 574 | inner: internal::Internal::BigSigned(*v), 575 | } 576 | } 577 | 578 | /// Get a `ValueBag` from a `i128`. 579 | #[inline] 580 | #[cfg(feature = "inline-i128")] 581 | pub const fn from_i128(v: i128) -> ValueBag<'v> { 582 | ValueBag { 583 | inner: internal::Internal::BigSigned(v), 584 | } 585 | } 586 | 587 | /// Get a `ValueBag` from a `f32`. 588 | #[inline] 589 | pub const fn from_f32(v: f32) -> ValueBag<'v> { 590 | ValueBag { 591 | inner: internal::Internal::Float(v as f64), 592 | } 593 | } 594 | 595 | /// Get a `ValueBag` from a `f64`. 596 | #[inline] 597 | pub const fn from_f64(v: f64) -> ValueBag<'v> { 598 | ValueBag { 599 | inner: internal::Internal::Float(v), 600 | } 601 | } 602 | 603 | /// Get a `ValueBag` from a `bool`. 604 | #[inline] 605 | pub const fn from_bool(v: bool) -> ValueBag<'v> { 606 | ValueBag { 607 | inner: internal::Internal::Bool(v), 608 | } 609 | } 610 | 611 | /// Get a `ValueBag` from a `str`. 612 | #[inline] 613 | pub const fn from_str(v: &'v str) -> ValueBag<'v> { 614 | ValueBag { 615 | inner: internal::Internal::Str(v), 616 | } 617 | } 618 | 619 | /// Get a `ValueBag` from a `char`. 620 | #[inline] 621 | pub const fn from_char(v: char) -> ValueBag<'v> { 622 | ValueBag { 623 | inner: internal::Internal::Char(v), 624 | } 625 | } 626 | 627 | /// Get a `ValueBag` from a reference to a `ValueBag`. 628 | #[inline] 629 | pub const fn by_ref(&self) -> ValueBag<'_> { 630 | ValueBag { 631 | inner: self.inner.by_ref(), 632 | } 633 | } 634 | } 635 | 636 | #[cfg(test)] 637 | mod tests { 638 | use super::*; 639 | use crate::std::mem; 640 | 641 | #[cfg(feature = "inline-i128")] 642 | const SIZE_LIMIT_U64: usize = 4; 643 | #[cfg(not(feature = "inline-i128"))] 644 | const SIZE_LIMIT_U64: usize = 3; 645 | 646 | #[test] 647 | fn value_bag_size() { 648 | let size = mem::size_of::>(); 649 | let limit = mem::size_of::() * SIZE_LIMIT_U64; 650 | 651 | if size > limit { 652 | panic!( 653 | "`ValueBag` size ({} bytes) is too large (expected up to {} bytes)", 654 | size, limit, 655 | ); 656 | } 657 | } 658 | } 659 | -------------------------------------------------------------------------------- /src/owned.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | internal::{self, Internal}, 3 | std::sync::Arc, 4 | ValueBag, 5 | }; 6 | 7 | /// A dynamic structured value. 8 | /// 9 | /// This type is an owned variant of [`ValueBag`] that can be 10 | /// constructed using its [`to_owned`](struct.ValueBag.html#method.to_owned) method. 11 | /// `OwnedValueBag`s are suitable for storing and sharing across threads. 12 | /// 13 | /// `OwnedValueBag`s can be inspected by converting back into a regular `ValueBag` 14 | /// using the [`by_ref`](#method.by_ref) method. 15 | #[derive(Clone)] 16 | pub struct OwnedValueBag { 17 | inner: internal::owned::OwnedInternal, 18 | } 19 | 20 | impl<'v> ValueBag<'v> { 21 | /// Buffer this value into an [`OwnedValueBag`]. 22 | pub fn to_owned(&self) -> OwnedValueBag { 23 | OwnedValueBag { 24 | inner: self.inner.to_owned(), 25 | } 26 | } 27 | 28 | /// Buffer this value into an [`OwnedValueBag`], internally storing it 29 | /// in an `Arc` for cheap cloning. 30 | pub fn to_shared(&self) -> OwnedValueBag { 31 | self.to_owned().into_shared() 32 | } 33 | } 34 | 35 | impl ValueBag<'static> { 36 | /// Get a value from an owned, sharable, debuggable type. 37 | /// 38 | /// This method will attempt to capture the given value as a well-known primitive 39 | /// before resorting to using its `Debug` implementation. 40 | /// 41 | /// The value will be stored in an `Arc` for cheap cloning. 42 | pub fn capture_shared_debug(value: T) -> Self 43 | where 44 | T: internal::fmt::Debug + Send + Sync + 'static, 45 | { 46 | Self::try_capture_owned(&value).unwrap_or_else(|| ValueBag { 47 | inner: Internal::SharedDebug(Arc::new(value)), 48 | }) 49 | } 50 | 51 | /// Get a value from an owned, sharable, displayable type. 52 | /// 53 | /// This method will attempt to capture the given value as a well-known primitive 54 | /// before resorting to using its `Display` implementation. 55 | /// 56 | /// The value will be stored in an `Arc` for cheap cloning. 57 | pub fn capture_shared_display(value: T) -> Self 58 | where 59 | T: internal::fmt::Display + Send + Sync + 'static, 60 | { 61 | Self::try_capture_owned(&value).unwrap_or_else(|| ValueBag { 62 | inner: Internal::SharedDisplay(Arc::new(value)), 63 | }) 64 | } 65 | 66 | /// Get a value from an owned, shared error. 67 | /// 68 | /// The value will be stored in an `Arc` for cheap cloning. 69 | #[cfg(feature = "error")] 70 | pub fn capture_shared_error(value: T) -> Self 71 | where 72 | T: internal::error::Error + Send + Sync + 'static, 73 | { 74 | ValueBag { 75 | inner: Internal::SharedError(Arc::new(value)), 76 | } 77 | } 78 | 79 | /// Get a value from an owned, shared, structured type. 80 | /// 81 | /// This method will attempt to capture the given value as a well-known primitive 82 | /// before resorting to using its `Value` implementation. 83 | /// 84 | /// The value will be stored in an `Arc` for cheap cloning. 85 | #[cfg(feature = "sval2")] 86 | pub fn capture_shared_sval2(value: T) -> Self 87 | where 88 | T: value_bag_sval2::lib::Value + Send + Sync + 'static, 89 | { 90 | Self::try_capture_owned(&value).unwrap_or(ValueBag { 91 | inner: Internal::SharedSval2(Arc::new(value)), 92 | }) 93 | } 94 | 95 | /// Get a value from an owned, shared, structured type. 96 | /// 97 | /// This method will attempt to capture the given value as a well-known primitive 98 | /// before resorting to using its `Value` implementation. 99 | /// 100 | /// The value will be stored in an `Arc` for cheap cloning. 101 | #[cfg(feature = "serde1")] 102 | pub fn capture_shared_serde1(value: T) -> Self 103 | where 104 | T: value_bag_serde1::lib::Serialize + Send + Sync + 'static, 105 | { 106 | Self::try_capture_owned(&value).unwrap_or(ValueBag { 107 | inner: Internal::SharedSerde1(Arc::new(value)), 108 | }) 109 | } 110 | 111 | /// Get a value from an owned, shared, sequence. 112 | /// 113 | /// The value will be stored in an `Arc` for cheap cloning. 114 | #[cfg(feature = "seq")] 115 | pub fn capture_shared_seq_slice(value: I) -> Self 116 | where 117 | I: AsRef<[T]> + Send + Sync + 'static, 118 | T: Send + Sync + 'static, 119 | for<'v> &'v T: Into>, 120 | { 121 | use crate::{ 122 | internal::seq::Visitor, 123 | std::{marker::PhantomData, ops::ControlFlow}, 124 | }; 125 | 126 | struct OwnedSeqSlice(PhantomData<[T]>, I); 127 | 128 | impl internal::seq::Seq for OwnedSeqSlice 129 | where 130 | I: AsRef<[T]> + ?Sized, 131 | for<'v> &'v T: Into>, 132 | { 133 | fn visit(&self, visitor: &mut dyn Visitor<'_>) { 134 | for v in self.1.as_ref().iter() { 135 | if let ControlFlow::Break(()) = visitor.element(v.into()) { 136 | return; 137 | } 138 | } 139 | } 140 | } 141 | 142 | Self::try_capture_owned(&value).unwrap_or(ValueBag { 143 | inner: Internal::SharedSeq(Arc::new(OwnedSeqSlice(PhantomData, value))), 144 | }) 145 | } 146 | } 147 | 148 | impl OwnedValueBag { 149 | /// Get a regular [`ValueBag`] from this type. 150 | /// 151 | /// Once a `ValueBag` has been buffered, it will behave 152 | /// slightly differently when converted back: 153 | /// 154 | /// - `fmt::Debug` won't use formatting flags. 155 | /// - `serde::Serialize` will use the text-based representation. 156 | /// - The original type may change, so downcasting can stop producing results. 157 | pub const fn by_ref(&self) -> ValueBag { 158 | ValueBag { 159 | inner: self.inner.by_ref(), 160 | } 161 | } 162 | 163 | /// Make this value cheap to clone and share by internally storing it in an `Arc`. 164 | /// 165 | /// If the value is already shared then this method will simply clone it. 166 | pub fn into_shared(self) -> Self { 167 | OwnedValueBag { 168 | inner: self.inner.into_shared(), 169 | } 170 | } 171 | } 172 | 173 | #[cfg(test)] 174 | mod tests { 175 | #[cfg(target_arch = "wasm32")] 176 | use wasm_bindgen_test::*; 177 | 178 | use super::*; 179 | 180 | use crate::{ 181 | fill, 182 | std::{mem, string::ToString}, 183 | }; 184 | 185 | const SIZE_LIMIT_U64: usize = 4; 186 | 187 | #[test] 188 | fn is_send_sync() { 189 | fn assert() {} 190 | 191 | assert::(); 192 | } 193 | 194 | #[test] 195 | fn owned_value_bag_size() { 196 | let size = mem::size_of::(); 197 | let limit = mem::size_of::() * SIZE_LIMIT_U64; 198 | 199 | if size > limit { 200 | panic!( 201 | "`OwnedValueBag` size ({} bytes) is too large (expected up to {} bytes)", 202 | size, limit, 203 | ); 204 | } 205 | } 206 | 207 | #[test] 208 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 209 | fn fill_to_owned() { 210 | let value = ValueBag::from_fill(&|slot: fill::Slot| slot.fill_any(42u64)).to_owned(); 211 | 212 | assert!(matches!( 213 | value.inner, 214 | internal::owned::OwnedInternal::BigUnsigned(42) 215 | )); 216 | } 217 | 218 | #[test] 219 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 220 | fn fill_to_shared() { 221 | let value = ValueBag::from_fill(&|slot: fill::Slot| slot.fill_any(42u64)).to_shared(); 222 | 223 | assert!(matches!( 224 | value.inner, 225 | internal::owned::OwnedInternal::BigUnsigned(42) 226 | )); 227 | } 228 | 229 | #[test] 230 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 231 | fn fmt_to_owned() { 232 | let debug = ValueBag::from_debug(&"a value").to_owned(); 233 | let display = ValueBag::from_display(&"a value").to_owned(); 234 | 235 | assert!(matches!( 236 | debug.inner, 237 | internal::owned::OwnedInternal::Debug(_) 238 | )); 239 | assert!(matches!( 240 | display.inner, 241 | internal::owned::OwnedInternal::Display(_) 242 | )); 243 | 244 | assert_eq!("\"a value\"", debug.to_string()); 245 | assert_eq!("a value", display.to_string()); 246 | 247 | let debug = debug.by_ref(); 248 | let display = display.by_ref(); 249 | 250 | assert!(matches!(debug.inner, internal::Internal::AnonDebug(_))); 251 | assert!(matches!(display.inner, internal::Internal::AnonDisplay(_))); 252 | } 253 | 254 | #[test] 255 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 256 | fn fmt_to_shared() { 257 | let debug = ValueBag::from_debug(&"a value").to_shared(); 258 | let display = ValueBag::from_display(&"a value").to_shared(); 259 | 260 | assert!(matches!( 261 | debug.inner, 262 | internal::owned::OwnedInternal::SharedDebug(_) 263 | )); 264 | assert!(matches!( 265 | display.inner, 266 | internal::owned::OwnedInternal::SharedDisplay(_) 267 | )); 268 | } 269 | 270 | #[test] 271 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 272 | fn owned_fmt_to_owned() { 273 | let debug = ValueBag::capture_shared_debug("a value".to_string()).to_owned(); 274 | let display = ValueBag::capture_shared_display("a value".to_string()).to_owned(); 275 | 276 | assert!(matches!( 277 | debug.inner, 278 | internal::owned::OwnedInternal::SharedDebug(_) 279 | )); 280 | assert!(matches!( 281 | display.inner, 282 | internal::owned::OwnedInternal::SharedDisplay(_) 283 | )); 284 | 285 | assert_eq!("\"a value\"", debug.to_string()); 286 | assert_eq!("a value", display.to_string()); 287 | 288 | let debug = debug.by_ref(); 289 | let display = display.by_ref(); 290 | 291 | assert!(matches!(debug.inner, internal::Internal::SharedRefDebug(_))); 292 | assert!(matches!( 293 | display.inner, 294 | internal::Internal::SharedRefDisplay(_) 295 | )); 296 | } 297 | 298 | #[test] 299 | #[cfg(feature = "error")] 300 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 301 | fn error_to_owned() { 302 | use crate::std::io; 303 | 304 | let value = 305 | ValueBag::from_dyn_error(&io::Error::new(io::ErrorKind::Other, "something failed!")) 306 | .to_owned(); 307 | 308 | assert!(matches!( 309 | value.inner, 310 | internal::owned::OwnedInternal::Error(_) 311 | )); 312 | 313 | let value = value.by_ref(); 314 | 315 | assert!(matches!(value.inner, internal::Internal::AnonError(_))); 316 | 317 | assert!(value.to_borrowed_error().is_some()); 318 | } 319 | 320 | #[test] 321 | #[cfg(feature = "error")] 322 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 323 | fn error_to_shared() { 324 | use crate::std::io; 325 | 326 | let value = 327 | ValueBag::from_dyn_error(&io::Error::new(io::ErrorKind::Other, "something failed!")) 328 | .to_shared(); 329 | 330 | assert!(matches!( 331 | value.inner, 332 | internal::owned::OwnedInternal::SharedError(_) 333 | )); 334 | } 335 | 336 | #[test] 337 | #[cfg(feature = "error")] 338 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 339 | fn owned_error_to_owned() { 340 | use crate::std::io; 341 | 342 | let value = ValueBag::capture_shared_error(io::Error::new( 343 | io::ErrorKind::Other, 344 | "something failed!", 345 | )) 346 | .to_owned(); 347 | 348 | assert!(matches!( 349 | value.inner, 350 | internal::owned::OwnedInternal::SharedError(_) 351 | )); 352 | 353 | let value = value.by_ref(); 354 | 355 | assert!(matches!(value.inner, internal::Internal::SharedRefError(_))); 356 | } 357 | 358 | #[test] 359 | #[cfg(feature = "serde1")] 360 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 361 | fn serde1_to_owned() { 362 | let value = ValueBag::from_serde1(&42u64).to_owned(); 363 | 364 | assert!(matches!( 365 | value.inner, 366 | internal::owned::OwnedInternal::Serde1(_) 367 | )); 368 | 369 | let value = value.by_ref(); 370 | 371 | assert!(matches!(value.inner, internal::Internal::AnonSerde1(_))); 372 | } 373 | 374 | #[test] 375 | #[cfg(feature = "serde1")] 376 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 377 | fn serde1_to_shared() { 378 | let value = ValueBag::from_serde1(&42u64).to_shared(); 379 | 380 | assert!(matches!( 381 | value.inner, 382 | internal::owned::OwnedInternal::SharedSerde1(_) 383 | )); 384 | } 385 | 386 | #[test] 387 | #[cfg(feature = "serde1")] 388 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 389 | fn owned_serde1_to_owned() { 390 | let value = ValueBag::capture_shared_serde1("a value".to_string()).to_owned(); 391 | 392 | assert!(matches!( 393 | value.inner, 394 | internal::owned::OwnedInternal::SharedSerde1(_) 395 | )); 396 | 397 | let value = value.by_ref(); 398 | 399 | assert!(matches!( 400 | value.inner, 401 | internal::Internal::SharedRefSerde1(_) 402 | )); 403 | } 404 | 405 | #[test] 406 | #[cfg(feature = "sval2")] 407 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 408 | fn sval2_to_owned() { 409 | let value = ValueBag::from_sval2(&42u64).to_owned(); 410 | 411 | assert!(matches!( 412 | value.inner, 413 | internal::owned::OwnedInternal::Sval2(_) 414 | )); 415 | 416 | let value = value.by_ref(); 417 | 418 | assert!(matches!(value.inner, internal::Internal::AnonSval2(_))); 419 | } 420 | 421 | #[test] 422 | #[cfg(feature = "sval2")] 423 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 424 | fn sval2_to_shared() { 425 | let value = ValueBag::from_sval2(&42u64).to_shared(); 426 | 427 | assert!(matches!( 428 | value.inner, 429 | internal::owned::OwnedInternal::SharedSval2(_) 430 | )); 431 | } 432 | 433 | #[test] 434 | #[cfg(feature = "sval2")] 435 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 436 | fn owned_sval2_to_owned() { 437 | let value = ValueBag::capture_shared_sval2("a value".to_string()).to_owned(); 438 | 439 | assert!(matches!( 440 | value.inner, 441 | internal::owned::OwnedInternal::SharedSval2(_) 442 | )); 443 | 444 | let value = value.by_ref(); 445 | 446 | assert!(matches!(value.inner, internal::Internal::SharedRefSval2(_))); 447 | } 448 | 449 | #[test] 450 | #[cfg(feature = "seq")] 451 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 452 | fn seq_to_owned() { 453 | let value = ValueBag::from_seq_slice(&[1, 2, 3]).to_owned(); 454 | 455 | assert!(matches!( 456 | value.inner, 457 | internal::owned::OwnedInternal::Seq(_) 458 | )); 459 | 460 | let value = value.by_ref(); 461 | 462 | assert!(matches!(value.inner, internal::Internal::AnonSeq(_))); 463 | } 464 | 465 | #[test] 466 | #[cfg(feature = "seq")] 467 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 468 | fn seq_to_shared() { 469 | let value = ValueBag::from_seq_slice(&[1, 2, 3]).to_shared(); 470 | 471 | assert!(matches!( 472 | value.inner, 473 | internal::owned::OwnedInternal::SharedSeq(_) 474 | )); 475 | } 476 | 477 | #[test] 478 | #[cfg(feature = "seq")] 479 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 480 | fn owned_seq_to_owned() { 481 | let value = ValueBag::capture_shared_seq_slice(vec![1, 2, 3]).to_owned(); 482 | 483 | assert!(matches!( 484 | value.inner, 485 | internal::owned::OwnedInternal::SharedSeq(_) 486 | )); 487 | 488 | let value = value.by_ref(); 489 | 490 | assert!(matches!(value.inner, internal::Internal::SharedRefSeq(_))); 491 | } 492 | } 493 | -------------------------------------------------------------------------------- /src/test.rs: -------------------------------------------------------------------------------- 1 | //! Test support for inspecting values. 2 | 3 | use crate::{ 4 | internal, 5 | std::{fmt, str, string::String}, 6 | visit::Visit, 7 | Error, ValueBag, 8 | }; 9 | 10 | #[cfg(test)] 11 | pub(crate) trait IntoValueBag<'v> { 12 | fn into_value_bag(self) -> ValueBag<'v>; 13 | } 14 | 15 | #[cfg(test)] 16 | impl<'v, T> IntoValueBag<'v> for T 17 | where 18 | T: Into>, 19 | { 20 | fn into_value_bag(self) -> ValueBag<'v> { 21 | self.into() 22 | } 23 | } 24 | 25 | /** 26 | A tokenized representation of the captured value for testing. 27 | */ 28 | #[derive(Debug, PartialEq)] 29 | #[non_exhaustive] 30 | pub enum TestToken { 31 | U64(u64), 32 | I64(i64), 33 | F64(f64), 34 | U128(u128), 35 | I128(i128), 36 | Char(char), 37 | Bool(bool), 38 | Str(String), 39 | None, 40 | 41 | #[cfg(feature = "error")] 42 | Error, 43 | 44 | #[cfg(feature = "sval2")] 45 | Sval { 46 | version: u32, 47 | }, 48 | 49 | #[cfg(feature = "serde1")] 50 | Serde { 51 | version: u32, 52 | }, 53 | 54 | #[cfg(feature = "seq")] 55 | Seq, 56 | 57 | Poisoned(String), 58 | } 59 | 60 | impl<'v> ValueBag<'v> { 61 | /** 62 | Convert the value bag into a token for testing. 63 | 64 | This _isn't_ a general-purpose API for working with values outside of testing. 65 | */ 66 | pub fn to_test_token(&self) -> TestToken { 67 | struct TestVisitor(Option); 68 | 69 | impl<'v> internal::InternalVisitor<'v> for TestVisitor { 70 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { 71 | v.fill(crate::fill::Slot::new(self)) 72 | } 73 | 74 | fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> { 75 | self.0 = Some(TestToken::Str(format!("{:?}", v))); 76 | Ok(()) 77 | } 78 | 79 | fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error> { 80 | self.0 = Some(TestToken::Str(format!("{}", v))); 81 | Ok(()) 82 | } 83 | 84 | fn u64(&mut self, v: u64) -> Result<(), Error> { 85 | self.0 = Some(TestToken::U64(v)); 86 | Ok(()) 87 | } 88 | 89 | fn i64(&mut self, v: i64) -> Result<(), Error> { 90 | self.0 = Some(TestToken::I64(v)); 91 | Ok(()) 92 | } 93 | 94 | fn u128(&mut self, v: &u128) -> Result<(), Error> { 95 | self.0 = Some(TestToken::U128(*v)); 96 | Ok(()) 97 | } 98 | 99 | fn i128(&mut self, v: &i128) -> Result<(), Error> { 100 | self.0 = Some(TestToken::I128(*v)); 101 | Ok(()) 102 | } 103 | 104 | fn f64(&mut self, v: f64) -> Result<(), Error> { 105 | self.0 = Some(TestToken::F64(v)); 106 | Ok(()) 107 | } 108 | 109 | fn bool(&mut self, v: bool) -> Result<(), Error> { 110 | self.0 = Some(TestToken::Bool(v)); 111 | Ok(()) 112 | } 113 | 114 | fn char(&mut self, v: char) -> Result<(), Error> { 115 | self.0 = Some(TestToken::Char(v)); 116 | Ok(()) 117 | } 118 | 119 | fn str(&mut self, v: &str) -> Result<(), Error> { 120 | self.0 = Some(TestToken::Str(v.into())); 121 | Ok(()) 122 | } 123 | 124 | fn none(&mut self) -> Result<(), Error> { 125 | self.0 = Some(TestToken::None); 126 | Ok(()) 127 | } 128 | 129 | #[cfg(feature = "error")] 130 | fn error(&mut self, _: &dyn internal::error::Error) -> Result<(), Error> { 131 | self.0 = Some(TestToken::Error); 132 | Ok(()) 133 | } 134 | 135 | #[cfg(feature = "sval2")] 136 | fn sval2(&mut self, _: &dyn internal::sval::v2::Value) -> Result<(), Error> { 137 | self.0 = Some(TestToken::Sval { version: 2 }); 138 | Ok(()) 139 | } 140 | 141 | #[cfg(feature = "serde1")] 142 | fn serde1(&mut self, _: &dyn internal::serde::v1::Serialize) -> Result<(), Error> { 143 | self.0 = Some(TestToken::Serde { version: 1 }); 144 | Ok(()) 145 | } 146 | 147 | #[cfg(feature = "seq")] 148 | fn seq(&mut self, _: &dyn internal::seq::Seq) -> Result<(), Error> { 149 | self.0 = Some(TestToken::Seq); 150 | Ok(()) 151 | } 152 | 153 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> { 154 | self.0 = Some(TestToken::Poisoned(msg.into())); 155 | Ok(()) 156 | } 157 | } 158 | 159 | let mut visitor = TestVisitor(None); 160 | self.internal_visit(&mut visitor).unwrap(); 161 | 162 | visitor.0.unwrap() 163 | } 164 | } 165 | 166 | pub(crate) struct TestVisit { 167 | pub i64: i64, 168 | pub u64: u64, 169 | pub i128: i128, 170 | pub u128: u128, 171 | pub f64: f64, 172 | pub bool: bool, 173 | pub str: &'static str, 174 | pub borrowed_str: &'static str, 175 | pub char: char, 176 | } 177 | 178 | impl Default for TestVisit { 179 | fn default() -> Self { 180 | TestVisit { 181 | i64: -42, 182 | u64: 42, 183 | i128: -42, 184 | u128: 42, 185 | f64: 11.0, 186 | bool: true, 187 | str: "some string", 188 | borrowed_str: "some borrowed string", 189 | char: 'n', 190 | } 191 | } 192 | } 193 | 194 | impl<'v> Visit<'v> for TestVisit { 195 | fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { 196 | panic!("unexpected value: {}", v) 197 | } 198 | 199 | fn visit_i64(&mut self, v: i64) -> Result<(), Error> { 200 | assert_eq!(self.i64, v); 201 | Ok(()) 202 | } 203 | 204 | fn visit_u64(&mut self, v: u64) -> Result<(), Error> { 205 | assert_eq!(self.u64, v); 206 | Ok(()) 207 | } 208 | 209 | fn visit_i128(&mut self, v: i128) -> Result<(), Error> { 210 | assert_eq!(self.i128, v); 211 | Ok(()) 212 | } 213 | 214 | fn visit_u128(&mut self, v: u128) -> Result<(), Error> { 215 | assert_eq!(self.u128, v); 216 | Ok(()) 217 | } 218 | 219 | fn visit_f64(&mut self, v: f64) -> Result<(), Error> { 220 | assert_eq!(self.f64, v); 221 | Ok(()) 222 | } 223 | 224 | fn visit_bool(&mut self, v: bool) -> Result<(), Error> { 225 | assert_eq!(self.bool, v); 226 | Ok(()) 227 | } 228 | 229 | fn visit_str(&mut self, v: &str) -> Result<(), Error> { 230 | assert_eq!(self.str, v); 231 | Ok(()) 232 | } 233 | 234 | fn visit_borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { 235 | assert_eq!(self.borrowed_str, v); 236 | Ok(()) 237 | } 238 | 239 | fn visit_char(&mut self, v: char) -> Result<(), Error> { 240 | assert_eq!(self.char, v); 241 | Ok(()) 242 | } 243 | 244 | #[cfg(feature = "error")] 245 | fn visit_error(&mut self, err: &(dyn crate::std::error::Error + 'static)) -> Result<(), Error> { 246 | assert!(err.downcast_ref::().is_some()); 247 | Ok(()) 248 | } 249 | 250 | #[cfg(feature = "error")] 251 | fn visit_borrowed_error( 252 | &mut self, 253 | err: &'v (dyn crate::std::error::Error + 'static), 254 | ) -> Result<(), Error> { 255 | assert!(err.downcast_ref::().is_some()); 256 | Ok(()) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/visit.rs: -------------------------------------------------------------------------------- 1 | //! Value inspection. 2 | //! 3 | //! The [`Visit`] trait provides a simple visitor API that can be used to inspect 4 | //! the structure of primitives stored in a [`ValueBag`](../struct.ValueBag.html). 5 | //! More complex datatypes can then be handled using `std::fmt`, `sval`, or `serde`. 6 | //! 7 | //! ``` 8 | //! # #[cfg(not(feature = "std"))] fn main() {} 9 | //! # #[cfg(feature = "std")] 10 | //! # fn main() -> Result<(), Box> { 11 | //! # fn escape(buf: &[u8]) -> &[u8] { buf } 12 | //! # fn itoa_fmt(num: T) -> Vec { vec![] } 13 | //! # fn ryu_fmt(num: T) -> Vec { vec![] } 14 | //! # use std::io::Write; 15 | //! use value_bag::{ValueBag, Error, visit::Visit}; 16 | //! 17 | //! // Implement some simple custom serialization 18 | //! struct MyVisit(Vec); 19 | //! impl<'v> Visit<'v> for MyVisit { 20 | //! fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { 21 | //! // Fallback to `Debug` if we didn't visit the value specially 22 | //! write!(&mut self.0, "{:?}", v).map_err(|_| Error::msg("failed to write value")) 23 | //! } 24 | //! 25 | //! fn visit_u64(&mut self, v: u64) -> Result<(), Error> { 26 | //! self.0.extend_from_slice(itoa_fmt(v).as_slice()); 27 | //! Ok(()) 28 | //! } 29 | //! 30 | //! fn visit_i64(&mut self, v: i64) -> Result<(), Error> { 31 | //! self.0.extend_from_slice(itoa_fmt(v).as_slice()); 32 | //! Ok(()) 33 | //! } 34 | //! 35 | //! fn visit_f64(&mut self, v: f64) -> Result<(), Error> { 36 | //! self.0.extend_from_slice(ryu_fmt(v).as_slice()); 37 | //! Ok(()) 38 | //! } 39 | //! 40 | //! fn visit_str(&mut self, v: &str) -> Result<(), Error> { 41 | //! self.0.push(b'\"'); 42 | //! self.0.extend_from_slice(escape(v.as_bytes())); 43 | //! self.0.push(b'\"'); 44 | //! Ok(()) 45 | //! } 46 | //! 47 | //! fn visit_bool(&mut self, v: bool) -> Result<(), Error> { 48 | //! self.0.extend_from_slice(if v { b"true" } else { b"false" }); 49 | //! Ok(()) 50 | //! } 51 | //! } 52 | //! 53 | //! let value = ValueBag::from(42i64); 54 | //! 55 | //! let mut visitor = MyVisit(vec![]); 56 | //! value.visit(&mut visitor)?; 57 | //! # Ok(()) 58 | //! # } 59 | //! ``` 60 | 61 | use crate::{ 62 | internal::{self, InternalVisitor}, 63 | Error, ValueBag, 64 | }; 65 | 66 | /// A visitor for a `ValueBag`. 67 | pub trait Visit<'v> { 68 | /// Visit a `ValueBag`. 69 | /// 70 | /// This is the only required method on `Visit` and acts as a fallback for any 71 | /// more specific methods that aren't overridden. 72 | /// The `ValueBag` may be formatted using its `fmt::Debug` or `fmt::Display` implementation, 73 | /// or serialized using its `sval::Value` or `serde::Serialize` implementation. 74 | fn visit_any(&mut self, value: ValueBag) -> Result<(), Error>; 75 | 76 | /// Visit an empty value. 77 | #[inline] 78 | fn visit_empty(&mut self) -> Result<(), Error> { 79 | self.visit_any(ValueBag::empty()) 80 | } 81 | 82 | /// Visit an unsigned integer. 83 | #[inline] 84 | fn visit_u64(&mut self, value: u64) -> Result<(), Error> { 85 | self.visit_any(value.into()) 86 | } 87 | 88 | /// Visit a signed integer. 89 | #[inline] 90 | fn visit_i64(&mut self, value: i64) -> Result<(), Error> { 91 | self.visit_any(value.into()) 92 | } 93 | 94 | /// Visit a big unsigned integer. 95 | #[inline] 96 | fn visit_u128(&mut self, value: u128) -> Result<(), Error> { 97 | self.visit_any((&value).into()) 98 | } 99 | 100 | /// Visit a big signed integer. 101 | #[inline] 102 | fn visit_i128(&mut self, value: i128) -> Result<(), Error> { 103 | self.visit_any((&value).into()) 104 | } 105 | 106 | /// Visit a floating point. 107 | #[inline] 108 | fn visit_f64(&mut self, value: f64) -> Result<(), Error> { 109 | self.visit_any(value.into()) 110 | } 111 | 112 | /// Visit a boolean. 113 | #[inline] 114 | fn visit_bool(&mut self, value: bool) -> Result<(), Error> { 115 | self.visit_any(value.into()) 116 | } 117 | 118 | /// Visit a string. 119 | #[inline] 120 | fn visit_str(&mut self, value: &str) -> Result<(), Error> { 121 | self.visit_any(value.into()) 122 | } 123 | 124 | /// Visit a string. 125 | #[inline] 126 | fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { 127 | self.visit_str(value) 128 | } 129 | 130 | /// Visit a Unicode character. 131 | #[inline] 132 | fn visit_char(&mut self, value: char) -> Result<(), Error> { 133 | let mut b = [0; 4]; 134 | self.visit_str(&*value.encode_utf8(&mut b)) 135 | } 136 | 137 | /// Visit an error. 138 | #[inline] 139 | #[cfg(feature = "error")] 140 | fn visit_error(&mut self, err: &(dyn crate::std::error::Error + 'static)) -> Result<(), Error> { 141 | self.visit_any(ValueBag::from_dyn_error(err)) 142 | } 143 | 144 | /// Visit an error. 145 | #[inline] 146 | #[cfg(feature = "error")] 147 | fn visit_borrowed_error( 148 | &mut self, 149 | err: &'v (dyn crate::std::error::Error + 'static), 150 | ) -> Result<(), Error> { 151 | self.visit_any(ValueBag::from_dyn_error(err)) 152 | } 153 | } 154 | 155 | impl<'a, 'v, T: ?Sized> Visit<'v> for &'a mut T 156 | where 157 | T: Visit<'v>, 158 | { 159 | #[inline] 160 | fn visit_any(&mut self, value: ValueBag) -> Result<(), Error> { 161 | (**self).visit_any(value) 162 | } 163 | 164 | #[inline] 165 | fn visit_empty(&mut self) -> Result<(), Error> { 166 | (**self).visit_empty() 167 | } 168 | 169 | #[inline] 170 | fn visit_u64(&mut self, value: u64) -> Result<(), Error> { 171 | (**self).visit_u64(value) 172 | } 173 | 174 | #[inline] 175 | fn visit_i64(&mut self, value: i64) -> Result<(), Error> { 176 | (**self).visit_i64(value) 177 | } 178 | 179 | #[inline] 180 | fn visit_u128(&mut self, value: u128) -> Result<(), Error> { 181 | (**self).visit_u128(value) 182 | } 183 | 184 | #[inline] 185 | fn visit_i128(&mut self, value: i128) -> Result<(), Error> { 186 | (**self).visit_i128(value) 187 | } 188 | 189 | #[inline] 190 | fn visit_f64(&mut self, value: f64) -> Result<(), Error> { 191 | (**self).visit_f64(value) 192 | } 193 | 194 | #[inline] 195 | fn visit_bool(&mut self, value: bool) -> Result<(), Error> { 196 | (**self).visit_bool(value) 197 | } 198 | 199 | #[inline] 200 | fn visit_str(&mut self, value: &str) -> Result<(), Error> { 201 | (**self).visit_str(value) 202 | } 203 | 204 | #[inline] 205 | fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { 206 | (**self).visit_borrowed_str(value) 207 | } 208 | 209 | #[inline] 210 | fn visit_char(&mut self, value: char) -> Result<(), Error> { 211 | (**self).visit_char(value) 212 | } 213 | 214 | #[inline] 215 | #[cfg(feature = "error")] 216 | fn visit_error(&mut self, err: &(dyn crate::std::error::Error + 'static)) -> Result<(), Error> { 217 | (**self).visit_error(err) 218 | } 219 | 220 | #[inline] 221 | #[cfg(feature = "error")] 222 | fn visit_borrowed_error( 223 | &mut self, 224 | err: &'v (dyn crate::std::error::Error + 'static), 225 | ) -> Result<(), Error> { 226 | (**self).visit_borrowed_error(err) 227 | } 228 | } 229 | 230 | impl<'v> ValueBag<'v> { 231 | /// Visit this value using a simple visitor. 232 | /// 233 | /// The visitor isn't strictly required to inspect the contents of a value bag. 234 | /// It's useful for simple cases where a full framework like `serde` or `sval` 235 | /// isn't necessary. 236 | pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> { 237 | struct Visitor(V); 238 | 239 | impl<'v, V> InternalVisitor<'v> for Visitor 240 | where 241 | V: Visit<'v>, 242 | { 243 | fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> { 244 | v.fill(crate::fill::Slot::new(self)) 245 | } 246 | 247 | fn debug(&mut self, v: &dyn internal::fmt::Debug) -> Result<(), Error> { 248 | self.0.visit_any(ValueBag::from_dyn_debug(v)) 249 | } 250 | 251 | fn display(&mut self, v: &dyn internal::fmt::Display) -> Result<(), Error> { 252 | self.0.visit_any(ValueBag::from_dyn_display(v)) 253 | } 254 | 255 | fn u64(&mut self, v: u64) -> Result<(), Error> { 256 | self.0.visit_u64(v) 257 | } 258 | 259 | fn i64(&mut self, v: i64) -> Result<(), Error> { 260 | self.0.visit_i64(v) 261 | } 262 | 263 | fn u128(&mut self, v: &u128) -> Result<(), Error> { 264 | self.0.visit_u128(*v) 265 | } 266 | 267 | fn i128(&mut self, v: &i128) -> Result<(), Error> { 268 | self.0.visit_i128(*v) 269 | } 270 | 271 | fn f64(&mut self, v: f64) -> Result<(), Error> { 272 | self.0.visit_f64(v) 273 | } 274 | 275 | fn bool(&mut self, v: bool) -> Result<(), Error> { 276 | self.0.visit_bool(v) 277 | } 278 | 279 | fn char(&mut self, v: char) -> Result<(), Error> { 280 | self.0.visit_char(v) 281 | } 282 | 283 | fn str(&mut self, v: &str) -> Result<(), Error> { 284 | self.0.visit_str(v) 285 | } 286 | 287 | fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { 288 | self.0.visit_borrowed_str(v) 289 | } 290 | 291 | fn none(&mut self) -> Result<(), Error> { 292 | self.0.visit_empty() 293 | } 294 | 295 | #[cfg(feature = "error")] 296 | fn error(&mut self, v: &(dyn internal::error::Error + 'static)) -> Result<(), Error> { 297 | self.0.visit_error(v) 298 | } 299 | 300 | #[cfg(feature = "error")] 301 | fn borrowed_error( 302 | &mut self, 303 | v: &'v (dyn internal::error::Error + 'static), 304 | ) -> Result<(), Error> { 305 | self.0.visit_borrowed_error(v) 306 | } 307 | 308 | #[cfg(feature = "sval2")] 309 | fn sval2(&mut self, v: &dyn internal::sval::v2::Value) -> Result<(), Error> { 310 | if internal::sval::v2::internal_visit(v, self) { 311 | Ok(()) 312 | } else { 313 | self.0.visit_any(ValueBag::from_dyn_sval2(v)) 314 | } 315 | } 316 | 317 | #[cfg(feature = "sval2")] 318 | fn borrowed_sval2( 319 | &mut self, 320 | v: &'v dyn internal::sval::v2::Value, 321 | ) -> Result<(), Error> { 322 | if internal::sval::v2::borrowed_internal_visit(v, self) { 323 | Ok(()) 324 | } else { 325 | self.0.visit_any(ValueBag::from_dyn_sval2(v)) 326 | } 327 | } 328 | 329 | #[cfg(feature = "serde1")] 330 | fn serde1(&mut self, v: &dyn internal::serde::v1::Serialize) -> Result<(), Error> { 331 | if internal::serde::v1::internal_visit(v, self) { 332 | Ok(()) 333 | } else { 334 | self.0.visit_any(ValueBag::from_dyn_serde1(v)) 335 | } 336 | } 337 | 338 | #[cfg(feature = "seq")] 339 | fn seq(&mut self, v: &dyn internal::seq::Seq) -> Result<(), Error> { 340 | self.0.visit_any(ValueBag::from_dyn_seq(v)) 341 | } 342 | 343 | fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> { 344 | Err(Error::msg(msg)) 345 | } 346 | } 347 | 348 | self.internal_visit(&mut Visitor(visitor)) 349 | } 350 | } 351 | 352 | #[cfg(test)] 353 | mod tests { 354 | use super::*; 355 | use crate::test::*; 356 | 357 | #[cfg(target_arch = "wasm32")] 358 | use wasm_bindgen_test::*; 359 | 360 | #[test] 361 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 362 | fn visit_structured() { 363 | ValueBag::from(42u64) 364 | .visit(TestVisit::default()) 365 | .expect("failed to visit value"); 366 | ValueBag::from(-42i64) 367 | .visit(TestVisit::default()) 368 | .expect("failed to visit value"); 369 | ValueBag::from(&42u128) 370 | .visit(TestVisit::default()) 371 | .expect("failed to visit value"); 372 | ValueBag::from(&-42i128) 373 | .visit(TestVisit::default()) 374 | .expect("failed to visit value"); 375 | ValueBag::from(11f64) 376 | .visit(TestVisit::default()) 377 | .expect("failed to visit value"); 378 | ValueBag::from(true) 379 | .visit(TestVisit::default()) 380 | .expect("failed to visit value"); 381 | ValueBag::from("some borrowed string") 382 | .visit(TestVisit::default()) 383 | .expect("failed to visit value"); 384 | ValueBag::from('n') 385 | .visit(TestVisit::default()) 386 | .expect("failed to visit value"); 387 | } 388 | 389 | #[test] 390 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 391 | fn visit_empty() { 392 | struct Visitor(bool); 393 | 394 | impl<'v> Visit<'v> for Visitor { 395 | fn visit_any(&mut self, _: ValueBag) -> Result<(), Error> { 396 | Ok(()) 397 | } 398 | 399 | fn visit_empty(&mut self) -> Result<(), Error> { 400 | self.0 = true; 401 | Ok(()) 402 | } 403 | } 404 | 405 | let mut visitor = Visitor(false); 406 | ValueBag::empty().visit(&mut visitor).unwrap(); 407 | 408 | assert!(visitor.0); 409 | } 410 | 411 | #[test] 412 | #[cfg(feature = "serde1")] 413 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 414 | fn visit_serde1() { 415 | use crate::std::string::{String, ToString}; 416 | 417 | struct Data; 418 | 419 | impl value_bag_serde1::lib::Serialize for Data { 420 | fn serialize( 421 | &self, 422 | serializer: S, 423 | ) -> Result { 424 | use value_bag_serde1::lib::ser::SerializeStruct; 425 | 426 | let mut s = serializer.serialize_struct("Data", 3)?; 427 | s.serialize_field("a", &1)?; 428 | s.serialize_field("b", &2)?; 429 | s.serialize_field("c", &3)?; 430 | s.end() 431 | } 432 | } 433 | 434 | struct Visitor(String); 435 | 436 | impl<'v> Visit<'v> for Visitor { 437 | fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { 438 | self.0 = v.to_string(); 439 | 440 | Ok(()) 441 | } 442 | } 443 | 444 | let mut visitor = Visitor("".into()); 445 | ValueBag::from_serde1(&Data).visit(&mut visitor).unwrap(); 446 | 447 | assert_eq!("Data { a: 1, b: 2, c: 3 }", visitor.0); 448 | } 449 | 450 | #[test] 451 | #[cfg(feature = "sval2")] 452 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] 453 | fn visit_sval2() { 454 | use crate::std::string::{String, ToString}; 455 | 456 | struct Data; 457 | 458 | impl value_bag_sval2::lib::Value for Data { 459 | fn stream<'sval, S: value_bag_sval2::lib::Stream<'sval> + ?Sized>( 460 | &'sval self, 461 | stream: &mut S, 462 | ) -> value_bag_sval2::lib::Result { 463 | stream.map_begin(Some(3))?; 464 | 465 | stream.map_key_begin()?; 466 | stream.value("a")?; 467 | stream.map_key_end()?; 468 | 469 | stream.map_value_begin()?; 470 | stream.value(&1)?; 471 | stream.map_value_end()?; 472 | 473 | stream.map_key_begin()?; 474 | stream.value("b")?; 475 | stream.map_key_end()?; 476 | 477 | stream.map_value_begin()?; 478 | stream.value(&2)?; 479 | stream.map_value_end()?; 480 | 481 | stream.map_key_begin()?; 482 | stream.value("c")?; 483 | stream.map_key_end()?; 484 | 485 | stream.map_value_begin()?; 486 | stream.value(&3)?; 487 | stream.map_value_end()?; 488 | 489 | stream.map_end() 490 | } 491 | } 492 | 493 | struct Visitor(String); 494 | 495 | impl<'v> Visit<'v> for Visitor { 496 | fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> { 497 | self.0 = v.to_string(); 498 | 499 | Ok(()) 500 | } 501 | } 502 | 503 | let mut visitor = Visitor("".into()); 504 | ValueBag::from_sval2(&Data).visit(&mut visitor).unwrap(); 505 | 506 | assert_eq!("{ \"a\": 1, \"b\": 2, \"c\": 3 }", visitor.0); 507 | } 508 | } 509 | --------------------------------------------------------------------------------