├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── ci └── no_atomic.sh ├── tests ├── Cargo.toml ├── src │ ├── hello_world.rs │ ├── lib.rs │ └── visit_count.rs └── tests │ ├── derive.rs │ ├── enumerable.rs │ ├── field.rs │ ├── listable.rs │ ├── mappable.rs │ ├── named_values.rs │ ├── slice.rs │ ├── structable.rs │ ├── tuplable.rs │ ├── ui │ ├── not_valuable.rs │ ├── not_valuable.stderr │ ├── unexpected.rs │ └── unexpected.stderr │ └── value.rs ├── valuable-derive ├── Cargo.toml └── src │ ├── attr.rs │ ├── error.rs │ ├── expand.rs │ └── lib.rs ├── valuable-serde ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── test.rs └── valuable ├── Cargo.toml ├── benches └── structable.rs ├── build.rs ├── examples ├── derive.rs ├── hello_world.rs └── print.rs ├── no_atomic.rs └── src ├── enumerable.rs ├── field.rs ├── lib.rs ├── listable.rs ├── mappable.rs ├── named_values.rs ├── slice.rs ├── structable.rs ├── tuplable.rs ├── valuable.rs ├── value.rs └── visit.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | schedule: 11 | - cron: '0 2 * * 0' 12 | 13 | env: 14 | RUSTFLAGS: -Dwarnings 15 | RUST_BACKTRACE: 1 16 | 17 | defaults: 18 | run: 19 | shell: bash 20 | 21 | concurrency: 22 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} 23 | cancel-in-progress: true 24 | 25 | jobs: 26 | test: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: Install Rust 31 | run: rustup update stable 32 | - run: cargo test --all-features --workspace 33 | - run: cargo build --all-features --all-targets --workspace 34 | 35 | minrust: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v4 39 | - name: Install cargo-hack 40 | uses: taiki-e/install-action@cargo-hack 41 | - run: cargo hack check --workspace --all-features --ignore-private --rust-version 42 | 43 | no-std: 44 | strategy: 45 | fail-fast: false 46 | matrix: 47 | # thumbv7m-none-eabi supports atomic CAS. 48 | # thumbv6m-none-eabi supports atomic load/store, but not atomic CAS. 49 | target: 50 | - thumbv6m-none-eabi 51 | - thumbv7m-none-eabi 52 | runs-on: ubuntu-latest 53 | steps: 54 | - uses: actions/checkout@v4 55 | - name: Install Rust 56 | run: rustup update stable 57 | - name: Install cargo-hack 58 | uses: taiki-e/install-action@cargo-hack 59 | - run: rustup target add ${{ matrix.target }} 60 | - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --skip std,default 61 | working-directory: valuable 62 | - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --skip std,default 63 | working-directory: valuable-serde 64 | 65 | features: 66 | runs-on: ubuntu-latest 67 | steps: 68 | - uses: actions/checkout@v4 69 | - name: Install Rust 70 | run: rustup update stable 71 | - name: Install cargo-hack 72 | uses: taiki-e/install-action@cargo-hack 73 | - run: cargo hack build --workspace --feature-powerset 74 | 75 | # When this job failed, run ci/no_atomic.sh and commit result changes. 76 | codegen: 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v4 80 | - name: Install Rust 81 | run: rustup update stable 82 | - run: ci/no_atomic.sh 83 | - run: git add -N . && git diff --exit-code 84 | if: github.repository_owner != 'tokio-rs' || github.event_name != 'schedule' 85 | - id: diff 86 | run: | 87 | git config user.name "Taiki Endo" 88 | git config user.email "te316e89@gmail.com" 89 | git add -N . 90 | if ! git diff --exit-code; then 91 | git add . 92 | git commit -m "Update no_atomic.rs" 93 | echo "::set-output name=success::false" 94 | fi 95 | if: github.repository_owner == 'tokio-rs' && github.event_name == 'schedule' 96 | - uses: peter-evans/create-pull-request@v6 97 | with: 98 | title: Update no_atomic.rs 99 | body: | 100 | Auto-generated by [create-pull-request][1] 101 | [Please close and immediately reopen this pull request to run CI.][2] 102 | 103 | [1]: https://github.com/peter-evans/create-pull-request 104 | [2]: https://github.com/peter-evans/create-pull-request/blob/HEAD/docs/concepts-guidelines.md#workarounds-to-trigger-further-workflow-runs 105 | branch: update-no-atomic-rs 106 | if: github.repository_owner == 'tokio-rs' && github.event_name == 'schedule' && steps.diff.outputs.success == 'false' 107 | 108 | fmt: 109 | runs-on: ubuntu-latest 110 | steps: 111 | - uses: actions/checkout@v4 112 | - name: Install Rust 113 | run: rustup update stable 114 | - run: cargo fmt --all --check 115 | 116 | docs: 117 | runs-on: ubuntu-latest 118 | steps: 119 | - uses: actions/checkout@v4 120 | - name: Install Rust 121 | run: rustup update nightly && rustup default nightly 122 | - run: cargo doc --no-deps --all-features 123 | env: 124 | RUSTDOCFLAGS: -Dwarnings --cfg docsrs 125 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.1 (2025-01-17) 2 | 3 | - Make `Valuable` implementation for `HashMap` more generic (#112) 4 | - Bump MSRV to 1.56 (#118) 5 | - derive: update syn to 2.0 (#118) 6 | - derive: implement `#[valuable]` attributes (#75) 7 | - derive: use `#[automatically_derived]` on generated impls to improve coverage support (#136) 8 | 9 | # 0.1.0 (2022-01-03) 10 | 11 | - Initial release 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "valuable", 5 | "valuable-derive", 6 | "valuable-serde", 7 | "tests", 8 | ] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Valuable Contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Valuable 2 | 3 | Valuable provides object-safe value inspection. Use cases include passing 4 | structured data to trait objects and object-safe serialization. 5 | 6 | ## License 7 | 8 | This project is licensed under the [MIT license](LICENSE). 9 | 10 | ### Contribution 11 | 12 | Unless you explicitly state otherwise, any contribution intentionally submitted 13 | for inclusion in Valuable by you, shall be licensed as MIT, without any additional 14 | terms or conditions. -------------------------------------------------------------------------------- /ci/no_atomic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | cd "$(dirname "$0")"/.. 5 | 6 | # Update the list of targets that do not support atomic/CAS operations. 7 | # 8 | # Usage: 9 | # ./ci/no_atomic.sh 10 | 11 | file="valuable/no_atomic.rs" 12 | 13 | no_atomic_cas=() 14 | no_atomic_64=() 15 | no_atomic=() 16 | for target_spec in $(RUSTC_BOOTSTRAP=1 rustc +stable -Z unstable-options --print all-target-specs-json | jq -c '. | to_entries | .[]'); do 17 | target=$(jq <<<"${target_spec}" -r '.key') 18 | target_spec=$(jq <<<"${target_spec}" -c '.value') 19 | res=$(jq <<<"${target_spec}" -r 'select(."atomic-cas" == false)') 20 | [[ -z "${res}" ]] || no_atomic_cas+=("${target}") 21 | max_atomic_width=$(jq <<<"${target_spec}" -r '."max-atomic-width"') 22 | min_atomic_width=$(jq <<<"${target_spec}" -r '."min-atomic-width"') 23 | case "${max_atomic_width}" in 24 | # It is not clear exactly what `"max-atomic-width" == null` means, but they 25 | # actually seem to have the same max-atomic-width as the target-pointer-width. 26 | # The targets currently included in this group are "mipsel-sony-psp", 27 | # "thumbv4t-none-eabi", "thumbv6m-none-eabi", all of which are 28 | # `"target-pointer-width" == "32"`, so assuming them `"max-atomic-width" == 32` 29 | # for now. 30 | 32 | null) no_atomic_64+=("${target}") ;; 31 | # `"max-atomic-width" == 0` means that atomic is not supported at all. 32 | # We do not have a cfg for targets with {8,16}-bit atomic only, so 33 | # for now we treat them the same as targets that do not support atomic. 34 | 0 | 8 | 16) no_atomic+=("${target}") ;; 35 | 64 | 128) ;; 36 | *) exit 1 ;; 37 | esac 38 | case "${min_atomic_width}" in 39 | 8 | null) ;; 40 | *) no_atomic+=("${target}") ;; 41 | esac 42 | done 43 | 44 | cat >"${file}" <>"${file}" 52 | done 53 | cat >>"${file}" <>"${file}" 60 | done 61 | cat >>"${file}" <>"${file}" 68 | done 69 | cat >>"${file}" <"] 5 | edition = "2021" 6 | license = "MIT" 7 | publish = false 8 | 9 | [features] 10 | std = ["valuable/std"] 11 | derive = ["valuable/derive"] 12 | 13 | [dependencies] 14 | valuable = { path = "../valuable", default-features = false } 15 | 16 | [dev-dependencies] 17 | rustversion = "1" 18 | trybuild = "1" 19 | -------------------------------------------------------------------------------- /tests/src/hello_world.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | #[derive(Default, Debug)] 4 | pub struct HelloWorld { 5 | pub id: u32, 6 | } 7 | 8 | pub static HELLO_WORLD_FIELDS: &[NamedField<'static>] = &[NamedField::new("id")]; 9 | 10 | impl Valuable for HelloWorld { 11 | fn as_value(&self) -> Value<'_> { 12 | Value::Structable(self) 13 | } 14 | 15 | fn visit(&self, visit: &mut dyn Visit) { 16 | visit.visit_named_fields(&NamedValues::new( 17 | HELLO_WORLD_FIELDS, 18 | &[Value::U32(self.id)], 19 | )); 20 | } 21 | } 22 | 23 | impl Structable for HelloWorld { 24 | fn definition(&self) -> StructDef<'_> { 25 | StructDef::new_static("HelloWorld", Fields::Named(HELLO_WORLD_FIELDS)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod hello_world; 2 | pub use hello_world::{HelloWorld, HELLO_WORLD_FIELDS}; 3 | 4 | mod visit_count; 5 | pub use visit_count::{visit_counts, VisitCount}; 6 | -------------------------------------------------------------------------------- /tests/src/visit_count.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | #[derive(Default, Debug, PartialEq)] 4 | pub struct VisitCount { 5 | pub visit_value: u32, 6 | pub visit_named_fields: u32, 7 | pub visit_unnamed_fields: u32, 8 | pub visit_primitive_slice: u32, 9 | pub visit_entry: u32, 10 | } 11 | 12 | pub fn visit_counts(val: &impl Valuable) -> VisitCount { 13 | let mut visit = VisitCount::default(); 14 | val.visit(&mut visit); 15 | visit 16 | } 17 | 18 | impl Visit for VisitCount { 19 | fn visit_value(&mut self, _: Value<'_>) { 20 | self.visit_value += 1; 21 | } 22 | 23 | fn visit_named_fields(&mut self, _: &NamedValues<'_>) { 24 | self.visit_named_fields += 1; 25 | } 26 | 27 | fn visit_unnamed_fields(&mut self, _: &[Value<'_>]) { 28 | self.visit_unnamed_fields += 1; 29 | } 30 | 31 | fn visit_primitive_slice(&mut self, _: Slice<'_>) { 32 | self.visit_primitive_slice += 1; 33 | } 34 | 35 | fn visit_entry(&mut self, _: Value<'_>, _: Value<'_>) { 36 | self.visit_entry += 1; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/tests/derive.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | #![allow(dead_code)] 3 | 4 | use valuable::*; 5 | 6 | use std::collections::HashMap; 7 | use std::env; 8 | 9 | #[test] 10 | fn test_derive_struct() { 11 | #[derive(Valuable)] 12 | struct Struct { 13 | x: &'static str, 14 | } 15 | 16 | #[derive(Valuable)] 17 | struct Tuple(u8); 18 | 19 | #[derive(Valuable)] 20 | struct Unit; 21 | 22 | let v = Struct { x: "a" }; 23 | assert_eq!(format!("{:?}", v.as_value()), r#"Struct { x: "a" }"#); 24 | let v = Tuple(0); 25 | assert_eq!(format!("{:?}", v.as_value()), r#"Tuple(0)"#); 26 | let v = Unit; 27 | assert_eq!(format!("{:?}", v.as_value()), r#"Unit"#); 28 | } 29 | 30 | #[test] 31 | fn test_derive_enum() { 32 | #[derive(Valuable)] 33 | enum Enum { 34 | Struct { x: &'static str }, 35 | Tuple(u8), 36 | Unit, 37 | } 38 | 39 | let v = Enum::Struct { x: "a" }; 40 | assert_eq!(format!("{:?}", v.as_value()), r#"Enum::Struct { x: "a" }"#); 41 | let v = Enum::Tuple(0); 42 | assert_eq!(format!("{:?}", v.as_value()), r#"Enum::Tuple(0)"#); 43 | let v = Enum::Unit; 44 | assert_eq!(format!("{:?}", v.as_value()), r#"Enum::Unit"#); 45 | } 46 | 47 | #[test] 48 | fn test_derive_mut() { 49 | #[derive(Valuable)] 50 | struct S { 51 | _f: (), 52 | } 53 | 54 | #[derive(Valuable)] 55 | enum E { 56 | _V, 57 | } 58 | 59 | #[derive(Valuable)] 60 | struct Test<'a> { 61 | string: &'a mut String, 62 | list: &'a mut Vec, 63 | map: &'a mut HashMap, 64 | struct_: &'a mut S, 65 | enum_: &'a mut E, 66 | } 67 | } 68 | 69 | #[test] 70 | fn test_rename() { 71 | #[derive(Valuable)] 72 | #[valuable(rename = "A")] 73 | struct S { 74 | #[valuable(rename = "b")] 75 | f: (), 76 | } 77 | 78 | #[derive(Valuable)] 79 | #[valuable(rename = "C")] 80 | enum E { 81 | #[valuable(rename = "D")] 82 | S { 83 | #[valuable(rename = "e")] 84 | f: (), 85 | }, 86 | #[valuable(rename = "F")] 87 | T(()), 88 | #[valuable(rename = "G")] 89 | U, 90 | } 91 | 92 | let s = Structable::definition(&S { f: () }); 93 | assert_eq!(s.name(), "A"); 94 | assert!(matches!(s.fields(), Fields::Named(f) if f[0].name() == "b")); 95 | let e = Enumerable::definition(&E::S { f: () }); 96 | assert_eq!(e.name(), "C"); 97 | assert_eq!(e.variants()[0].name(), "D"); 98 | assert!(matches!(e.variants()[0].fields(), Fields::Named(f) if f[0].name() == "e")); 99 | let e = Enumerable::definition(&E::T(())); 100 | assert_eq!(e.variants()[1].name(), "F"); 101 | let e = Enumerable::definition(&E::U); 102 | assert_eq!(e.variants()[2].name(), "G"); 103 | } 104 | 105 | #[test] 106 | fn test_skip_empty() { 107 | struct NotValuable; 108 | 109 | #[derive(Valuable)] 110 | struct S { 111 | #[valuable(skip)] 112 | f: NotValuable, 113 | } 114 | 115 | #[derive(Valuable)] 116 | struct T(#[valuable(skip)] NotValuable); 117 | 118 | #[derive(Valuable)] 119 | enum E { 120 | S { 121 | #[valuable(skip)] 122 | f: NotValuable, 123 | }, 124 | T(#[valuable(skip)] NotValuable), 125 | } 126 | 127 | let s = Structable::definition(&S { f: NotValuable }); 128 | assert!(matches!(s.fields(), Fields::Named(f) if f.is_empty())); 129 | let s = Structable::definition(&T(NotValuable)); 130 | assert!(matches!(s.fields(), Fields::Unnamed(f) if *f == 0)); 131 | let e = Enumerable::definition(&E::S { f: NotValuable }); 132 | assert_eq!(e.variants().len(), 2); 133 | assert!(matches!(e.variants()[0].fields(), Fields::Named(f) if f.is_empty())); 134 | assert!(matches!(e.variants()[1].fields(), Fields::Unnamed(f) if *f == 0)); 135 | } 136 | 137 | #[test] 138 | fn test_skip() { 139 | struct NotValuable; 140 | 141 | #[derive(Valuable)] 142 | struct S { 143 | f1: (), 144 | #[valuable(skip)] 145 | f2: NotValuable, 146 | f3: (), 147 | } 148 | 149 | #[derive(Valuable)] 150 | struct T((), #[valuable(skip)] NotValuable, ()); 151 | 152 | #[derive(Valuable)] 153 | enum E { 154 | S { 155 | f1: (), 156 | #[valuable(skip)] 157 | f2: NotValuable, 158 | f3: (), 159 | }, 160 | T((), #[valuable(skip)] NotValuable, ()), 161 | } 162 | 163 | let s = Structable::definition(&S { 164 | f1: (), 165 | f2: NotValuable, 166 | f3: (), 167 | }); 168 | assert!(matches!(s.fields(), Fields::Named(f) if f.len() == 2)); 169 | let s = Structable::definition(&T((), NotValuable, ())); 170 | assert!(matches!(s.fields(), Fields::Unnamed(f) if *f == 2)); 171 | let e = Enumerable::definition(&E::S { 172 | f1: (), 173 | f2: NotValuable, 174 | f3: (), 175 | }); 176 | assert_eq!(e.variants().len(), 2); 177 | assert!(matches!(e.variants()[0].fields(), Fields::Named(f) if f.len() == 2)); 178 | assert!(matches!(e.variants()[1].fields(), Fields::Unnamed(f) if *f == 2)); 179 | } 180 | 181 | #[test] 182 | fn test_transparent() { 183 | #[derive(Valuable)] 184 | #[valuable(transparent)] 185 | struct S { 186 | f: u8, 187 | } 188 | 189 | #[derive(Valuable)] 190 | #[valuable(transparent)] 191 | struct T(char); 192 | 193 | assert!(matches!(Valuable::as_value(&S { f: 0 }), Value::U8(0))); 194 | assert!(matches!(Valuable::as_value(&T('a')), Value::Char('a'))); 195 | } 196 | 197 | #[rustversion::attr(not(stable), ignore)] 198 | #[test] 199 | fn ui() { 200 | // Do not require developers to manually set `TRYBUILD=overwrite`. 201 | if env::var_os("CI").is_none() { 202 | env::set_var("TRYBUILD", "overwrite"); 203 | } 204 | 205 | let t = trybuild::TestCases::new(); 206 | t.compile_fail("tests/ui/*.rs"); 207 | } 208 | -------------------------------------------------------------------------------- /tests/tests/enumerable.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | #[test] 4 | fn test_manual_static_impl() { 5 | enum Enum { 6 | Struct { x: &'static str }, 7 | Tuple(u8), 8 | Unit, 9 | } 10 | 11 | static ENUM_STRUCT_FIELDS: &[NamedField<'static>] = &[NamedField::new("x")]; 12 | static ENUM_VARIANTS: &[VariantDef<'static>] = &[ 13 | VariantDef::new("Struct", Fields::Named(ENUM_STRUCT_FIELDS)), 14 | VariantDef::new("Tuple", Fields::Unnamed(1)), 15 | VariantDef::new("Unit", Fields::Unnamed(0)), 16 | ]; 17 | 18 | impl Enumerable for Enum { 19 | fn definition(&self) -> EnumDef<'_> { 20 | EnumDef::new_static("Enum", ENUM_VARIANTS) 21 | } 22 | 23 | fn variant(&self) -> Variant<'_> { 24 | match *self { 25 | Enum::Struct { .. } => Variant::Static(&ENUM_VARIANTS[0]), 26 | Enum::Tuple(..) => Variant::Static(&ENUM_VARIANTS[1]), 27 | Enum::Unit => Variant::Static(&ENUM_VARIANTS[2]), 28 | } 29 | } 30 | } 31 | 32 | impl Valuable for Enum { 33 | fn as_value(&self) -> Value<'_> { 34 | Value::Enumerable(self) 35 | } 36 | 37 | fn visit(&self, visitor: &mut dyn Visit) { 38 | match self { 39 | Enum::Struct { x } => { 40 | visitor.visit_named_fields(&NamedValues::new( 41 | ENUM_STRUCT_FIELDS, 42 | &[Value::String(x)], 43 | )); 44 | } 45 | Enum::Tuple(y) => { 46 | visitor.visit_unnamed_fields(&[Value::U8(*y)]); 47 | } 48 | Enum::Unit => { 49 | visitor.visit_unnamed_fields(&[]); 50 | } 51 | } 52 | } 53 | } 54 | 55 | let v = Enum::Struct { x: "a" }; 56 | assert_eq!(format!("{:?}", v.as_value()), r#"Enum::Struct { x: "a" }"#); 57 | let v = Enum::Tuple(0); 58 | assert_eq!(format!("{:?}", v.as_value()), r#"Enum::Tuple(0)"#); 59 | let v = Enum::Unit; 60 | assert_eq!(format!("{:?}", v.as_value()), r#"Enum::Unit"#); 61 | } 62 | 63 | #[test] 64 | fn test_manual_dyn_impl() { 65 | struct MyEnum; 66 | 67 | impl Valuable for MyEnum { 68 | fn as_value(&self) -> Value<'_> { 69 | Value::Enumerable(self) 70 | } 71 | 72 | fn visit(&self, visitor: &mut dyn Visit) { 73 | visitor.visit_unnamed_fields(&[Value::String("hello")]); 74 | } 75 | } 76 | 77 | impl Enumerable for MyEnum { 78 | fn definition(&self) -> EnumDef<'_> { 79 | EnumDef::new_dynamic("MyEnum", &[]) 80 | } 81 | 82 | fn variant(&self) -> Variant<'_> { 83 | Variant::Dynamic(VariantDef::new("MyVariant", Fields::Unnamed(0))) 84 | } 85 | } 86 | 87 | let v = MyEnum; 88 | assert_eq!( 89 | format!("{:?}", v.as_value()), 90 | "MyEnum::MyVariant(\"hello\")" 91 | ); 92 | } 93 | 94 | #[test] 95 | fn test_variant_named_field() { 96 | let name = "my_field".to_string(); 97 | let fields = [NamedField::new(&name[..])]; 98 | let variant = VariantDef::new("Hello", Fields::Named(&fields[..])); 99 | 100 | assert_eq!(variant.name(), "Hello"); 101 | 102 | match *variant.fields() { 103 | Fields::Named(f) => { 104 | assert!(std::ptr::eq((&fields[..]).as_ptr(), f.as_ptr(),)); 105 | } 106 | _ => panic!(), 107 | } 108 | } 109 | 110 | #[test] 111 | fn test_variant_unnamed_field() { 112 | let variant = VariantDef::new("Hello", Fields::Unnamed(1)); 113 | 114 | assert_eq!(variant.name(), "Hello"); 115 | assert!(matches!(variant.fields(), Fields::Unnamed(1))); 116 | } 117 | 118 | #[test] 119 | fn test_enum_def() { 120 | let fields = [NamedField::new("foo")]; 121 | let a = VariantDef::new("A", Fields::Named(&fields[..])); 122 | let b = VariantDef::new("B", Fields::Unnamed(1)); 123 | let variants = [a, b]; 124 | let def = EnumDef::new_dynamic("Foo", &variants); 125 | 126 | assert_eq!(def.name(), "Foo"); 127 | assert!(std::ptr::eq(variants.as_ptr(), def.variants().as_ptr(),)); 128 | assert!(def.is_dynamic()); 129 | } 130 | -------------------------------------------------------------------------------- /tests/tests/field.rs: -------------------------------------------------------------------------------- 1 | use valuable::NamedField; 2 | 3 | fn assert_clone() {} 4 | fn assert_copy() {} 5 | 6 | #[test] 7 | fn is_clone_copy() { 8 | assert_clone::>(); 9 | assert_copy::>(); 10 | } 11 | -------------------------------------------------------------------------------- /tests/tests/listable.rs: -------------------------------------------------------------------------------- 1 | use tests::*; 2 | use valuable::*; 3 | 4 | struct VisitHello(u32); 5 | 6 | impl Visit for VisitHello { 7 | fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 8 | let id = &HELLO_WORLD_FIELDS[0]; 9 | assert_eq!("id", id.name()); 10 | assert_eq!(Some(self.0), named_values.get(id).unwrap().as_u32()); 11 | } 12 | 13 | fn visit_value(&mut self, _: Value<'_>) { 14 | unreachable!("not called in this test"); 15 | } 16 | } 17 | 18 | #[derive(Default)] 19 | struct VisitList(u32); 20 | 21 | impl Visit for VisitList { 22 | fn visit_value(&mut self, item: Value<'_>) { 23 | visit_hello(&item, self.0); 24 | self.0 += 1; 25 | } 26 | } 27 | 28 | fn visit_hello(value: &Value<'_>, expect: u32) { 29 | let value = value.as_structable().unwrap(); 30 | 31 | // Check only one visit method is called 32 | let counts = tests::visit_counts(&value); 33 | assert_eq!( 34 | counts, 35 | tests::VisitCount { 36 | visit_named_fields: 1, 37 | ..Default::default() 38 | } 39 | ); 40 | 41 | // Check the next ID 42 | let mut v = VisitHello(expect); 43 | value.visit(&mut v); 44 | } 45 | 46 | macro_rules! test_default { 47 | ( 48 | $( 49 | $name:ident => |$x:ident| $b:expr; 50 | )* 51 | ) => { 52 | $( 53 | mod $name { 54 | use super::*; 55 | #[test] 56 | fn test_default_visit_slice_empty() { 57 | let $x: Vec = vec![]; 58 | let empty = $b; 59 | 60 | assert_eq!(Listable::size_hint(&empty), (0, Some(0))); 61 | 62 | let counts = tests::visit_counts(&empty); 63 | assert_eq!( 64 | counts, 65 | Default::default(), 66 | ); 67 | 68 | let mut counts = tests::VisitCount::default(); 69 | valuable::visit(&empty, &mut counts); 70 | assert_eq!( 71 | counts, 72 | tests::VisitCount { 73 | visit_value: 1, 74 | ..Default::default() 75 | } 76 | ); 77 | } 78 | 79 | #[test] 80 | fn test_default_visit_slice_small() { 81 | let $x = (0..4).map(|i| HelloWorld { id: i }).collect::>(); 82 | let hellos = $b; 83 | 84 | assert_eq!(Listable::size_hint(&hellos), (4, Some(4))); 85 | 86 | let counts = tests::visit_counts(&hellos); 87 | assert_eq!( 88 | counts, 89 | tests::VisitCount { 90 | visit_value: 4, 91 | ..Default::default() 92 | } 93 | ); 94 | 95 | let mut counts = tests::VisitCount::default(); 96 | valuable::visit(&hellos, &mut counts); 97 | assert_eq!( 98 | counts, 99 | tests::VisitCount { 100 | visit_value: 1, 101 | ..Default::default() 102 | } 103 | ); 104 | 105 | let mut visit = VisitList::default(); 106 | hellos.visit(&mut visit); 107 | assert_eq!(visit.0, 4); 108 | } 109 | 110 | #[test] 111 | fn test_default_visit_slice_big_pow_2() { 112 | let $x = (0..1024).map(|i| HelloWorld { id: i }).collect::>(); 113 | let hellos = $b; 114 | 115 | assert_eq!(Listable::size_hint(&hellos), (1024, Some(1024))); 116 | 117 | let counts = tests::visit_counts(&hellos); 118 | assert_eq!( 119 | counts, 120 | tests::VisitCount { 121 | visit_value: 1024, 122 | ..Default::default() 123 | } 124 | ); 125 | 126 | let mut counts = tests::VisitCount::default(); 127 | valuable::visit(&hellos, &mut counts); 128 | assert_eq!( 129 | counts, 130 | tests::VisitCount { 131 | visit_value: 1, 132 | ..Default::default() 133 | } 134 | ); 135 | 136 | let mut visit = VisitList::default(); 137 | hellos.visit(&mut visit); 138 | assert_eq!(visit.0, 1024); 139 | } 140 | 141 | #[test] 142 | fn test_default_visit_slice_big_odd() { 143 | let $x = (0..63).map(|i| HelloWorld { id: i }).collect::>(); 144 | let hellos = $b; 145 | 146 | assert_eq!(Listable::size_hint(&hellos), (63, Some(63))); 147 | 148 | let counts = tests::visit_counts(&hellos); 149 | assert_eq!( 150 | counts, 151 | tests::VisitCount { 152 | visit_value: 63, 153 | ..Default::default() 154 | } 155 | ); 156 | 157 | let mut counts = tests::VisitCount::default(); 158 | valuable::visit(&hellos, &mut counts); 159 | assert_eq!( 160 | counts, 161 | tests::VisitCount { 162 | visit_value: 1, 163 | ..Default::default() 164 | } 165 | ); 166 | 167 | let mut visit = VisitList::default(); 168 | hellos.visit(&mut visit); 169 | assert_eq!(visit.0, 63); 170 | } 171 | } 172 | )* 173 | } 174 | } 175 | 176 | test_default! { 177 | test_vec => |x| x; 178 | test_slice => |x| &x[..]; 179 | } 180 | 181 | macro_rules! test_primitive { 182 | ( 183 | $( 184 | $name:ident, $variant:ident(Value::$vvariant:ident): $ty:ty => |$x:ident| $b:block; 185 | )* 186 | ) => { 187 | $( 188 | mod $name { 189 | use super::*; 190 | 191 | fn test_iter<'a>(mut i: impl Iterator>, expect: &[$ty]) { 192 | assert_eq!(i.size_hint(), (expect.len(), Some(expect.len()))); 193 | let mut expect = expect.iter(); 194 | 195 | loop { 196 | match (i.next(), expect.next()) { 197 | (Some(Value::$vvariant(actual)), Some(expect)) => { 198 | // When testing floating-point values, the 199 | // actual value will be the exact same float 200 | // value as the expected, if everything is 201 | // working correctly. So, it's not strictly 202 | // necessary to use epsilon comparisons here, 203 | // and modifying the macro to use epsilon 204 | // comparisons for floats would make it 205 | // significantly more complex... 206 | #[allow(clippy::float_cmp)] 207 | { 208 | assert_eq!(actual, *expect) 209 | } 210 | } 211 | (None, None) => break, 212 | _ => panic!(), 213 | } 214 | } 215 | } 216 | 217 | struct VisitPrimitive<'a>(&'a [$ty]); 218 | 219 | impl Visit for VisitPrimitive<'_> { 220 | fn visit_primitive_slice(&mut self, slice: Slice<'_>) { 221 | assert_eq!(slice.len(), self.0.len()); 222 | 223 | // fmt::Debug 224 | assert_eq!( 225 | format!("{:?}", slice), 226 | format!("{:?}", self.0), 227 | ); 228 | 229 | // Test the expected variant has been received 230 | match slice { 231 | Slice::$variant(slice) => { 232 | assert_eq!(slice, &self.0[..]); 233 | } 234 | _ => panic!(), 235 | } 236 | 237 | test_iter(slice.iter(), &self.0); 238 | test_iter(IntoIterator::into_iter(&slice), &self.0); 239 | test_iter(IntoIterator::into_iter(slice), &self.0); 240 | } 241 | 242 | fn visit_value(&mut self, _: Value<'_>) { 243 | unreachable!("not called in this test"); 244 | } 245 | } 246 | 247 | #[test] 248 | fn test_empty() { 249 | let empty: Vec<$ty> = vec![]; 250 | 251 | assert_eq!(Listable::size_hint(&empty), (0, Some(0))); 252 | 253 | let counts = tests::visit_counts(&empty); 254 | assert_eq!(counts, tests::VisitCount { visit_primitive_slice: 1, .. Default::default() }); 255 | } 256 | 257 | #[test] 258 | fn test_slices() { 259 | fn do_test(listable: &impl Listable, expect: &[$ty]) { 260 | assert_eq!(listable.size_hint(), expect.iter().size_hint()); 261 | 262 | let counts = tests::visit_counts(listable); 263 | assert_eq!(counts, tests::VisitCount { visit_primitive_slice: 1, .. Default::default() }); 264 | 265 | let mut visit = VisitPrimitive(expect); 266 | listable.visit(&mut visit); 267 | } 268 | 269 | for &len in &[4_usize, 10, 30, 32, 63, 64, 100, 1000, 1024] { 270 | let vec = (0..len).map(|$x| $b).collect::>(); 271 | do_test(&vec, &vec); 272 | 273 | let vec = vec.into_boxed_slice(); 274 | do_test(&vec, &vec); 275 | } 276 | } 277 | } 278 | )* 279 | }; 280 | } 281 | 282 | test_primitive! { 283 | test_bool, Bool(Value::Bool): bool => |x| { x % 2 == 0 }; 284 | test_char, Char(Value::Char): char => |x| { TryFrom::try_from(x as u32).unwrap_or('f') }; 285 | test_f32, F32(Value::F32): f32 => |x| { x as f32 }; 286 | test_f64, F64(Value::F64): f64 => |x| { x as f64 }; 287 | test_i8, I8(Value::I8): i8 => |x| { x as i8 }; 288 | test_i16, I16(Value::I16): i16 => |x| { x as i16 }; 289 | test_i32, I32(Value::I32): i32 => |x| { x as i32 }; 290 | test_i64, I64(Value::I64): i64 => |x| { x as i64 }; 291 | test_i128, I128(Value::I128): i128 => |x| { x as i128 }; 292 | test_isize, Isize(Value::Isize): isize => |x| { x as isize }; 293 | test_str, Str(Value::String): &'static str => |x| { crate::leak(format!("{}", x)) }; 294 | test_string, String(Value::String): String => |x| { format!("{}", x) }; 295 | test_u8, U8(Value::U8): u8 => |x| { x as u8 }; 296 | test_u16, U16(Value::U16): u16 => |x| { x as u16 }; 297 | test_u32, U32(Value::U32): u32 => |x| { x as u32 }; 298 | test_u64, U64(Value::U64): u64 => |x| { x as u64 }; 299 | test_u128, U128(Value::U128): u128 => |x| { x as u128 }; 300 | test_usize, Usize(Value::Usize): usize => |x| { x as usize }; 301 | // test_unit, Unit: () => |_x| { () }; 302 | } 303 | 304 | fn leak(s: String) -> &'static str { 305 | Box::leak(s.into_boxed_str()) 306 | } 307 | -------------------------------------------------------------------------------- /tests/tests/mappable.rs: -------------------------------------------------------------------------------- 1 | use tests::*; 2 | use valuable::*; 3 | 4 | macro_rules! test_mappable { 5 | ($($name:ident => $ty:ident,)*) => { 6 | $( 7 | mod $name { 8 | use super::*; 9 | 10 | #[test] 11 | fn test_empty() { 12 | let map = std::collections::$ty::<(), ()>::new(); 13 | 14 | assert_eq!(Mappable::size_hint(&map), (0, Some(0))); 15 | 16 | let counts = visit_counts(&map); 17 | assert_eq!( 18 | counts, 19 | Default::default() 20 | ); 21 | 22 | assert_eq!( 23 | format!("{:?}", map.as_value()), 24 | format!("{:?}", map) 25 | ); 26 | } 27 | 28 | #[test] 29 | fn test_full() { 30 | let mut map = std::collections::$ty::new(); 31 | map.insert("foo", 123); 32 | map.insert("bar", 456); 33 | 34 | assert_eq!(Mappable::size_hint(&map), (2, Some(2))); 35 | 36 | let counts = visit_counts(&map); 37 | assert_eq!( 38 | counts, 39 | tests::VisitCount { 40 | visit_entry: 2, 41 | ..Default::default() 42 | } 43 | ); 44 | 45 | assert_eq!( 46 | format!("{:?}", map.as_value()), 47 | format!("{:?}", map) 48 | ); 49 | } 50 | 51 | #[test] 52 | fn test_nested_structable() { 53 | let mut map = std::collections::$ty::new(); 54 | map.insert("foo", HelloWorld { id: 1 }); 55 | map.insert("bar", HelloWorld { id: 2 }); 56 | map.insert("baz", HelloWorld { id: 3 }); 57 | 58 | assert_eq!(Mappable::size_hint(&map), (3, Some(3))); 59 | 60 | let counts = visit_counts(&map); 61 | assert_eq!( 62 | counts, 63 | tests::VisitCount { 64 | visit_entry: 3, 65 | ..Default::default() 66 | } 67 | ); 68 | 69 | assert_eq!( 70 | format!("{:?}", map.as_value()), 71 | format!("{:?}", map) 72 | ); 73 | } 74 | } 75 | )* 76 | }; 77 | } 78 | 79 | test_mappable! { 80 | hash_map => HashMap, 81 | btree_map => BTreeMap, 82 | } 83 | -------------------------------------------------------------------------------- /tests/tests/named_values.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | #[test] 4 | fn test_iter() { 5 | let f = [NamedField::new("a"), NamedField::new("b")]; 6 | let v = NamedValues::new(&f, &[Value::I32(1), Value::I32(2)]); 7 | 8 | let iter = v.iter(); 9 | assert_eq!(iter.len(), 2); 10 | let v: Vec<_> = iter.map(|(f, v)| (f.name(), v.as_i32().unwrap())).collect(); 11 | assert_eq!(v, vec![("a", 1), ("b", 2)]); 12 | } 13 | 14 | #[test] 15 | fn test_iter_rev() { 16 | let f = [NamedField::new("a"), NamedField::new("b")]; 17 | let v = NamedValues::new(&f, &[Value::I32(1), Value::I32(2)]); 18 | 19 | let iter = v.iter().rev(); 20 | assert_eq!(iter.len(), 2); 21 | let v: Vec<_> = iter.map(|(f, v)| (f.name(), v.as_i32().unwrap())).collect(); 22 | assert_eq!(v, vec![("b", 2), ("a", 1)]); 23 | } 24 | 25 | #[test] 26 | fn test_get() { 27 | let f = [NamedField::new("a"), NamedField::new("b")]; 28 | let bad = NamedField::new("a"); 29 | let v = NamedValues::new(&f, &[Value::I32(1), Value::I32(2)]); 30 | 31 | assert!(matches!(v.get(&f[0]), Some(Value::I32(v)) if *v == 1)); 32 | assert!(matches!(v.get(&f[1]), Some(Value::I32(v)) if *v == 2)); 33 | assert!(v.get(&bad).is_none()); 34 | } 35 | 36 | #[test] 37 | fn test_get_by_name() { 38 | let f = [NamedField::new("a"), NamedField::new("b")]; 39 | let v = NamedValues::new(&f, &[Value::I32(1), Value::I32(2)]); 40 | 41 | assert!(matches!(v.get_by_name("a"), Some(Value::I32(v)) if *v == 1)); 42 | assert!(matches!(v.get_by_name("b"), Some(Value::I32(v)) if *v == 2)); 43 | assert!(v.get_by_name("c").is_none()); 44 | } 45 | -------------------------------------------------------------------------------- /tests/tests/slice.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | #[test] 4 | fn test_iter() { 5 | let slice = Slice::I32(&[1, 2, 3]); 6 | 7 | let iter = slice.iter(); 8 | assert_eq!(iter.len(), 3); 9 | let v: Vec<_> = iter.map(|v| v.as_i32().unwrap()).collect(); 10 | assert_eq!(v, vec![1, 2, 3]); 11 | } 12 | 13 | #[test] 14 | fn test_iter_rev() { 15 | let slice = Slice::I32(&[1, 2, 3]); 16 | 17 | let iter = slice.iter().rev(); 18 | assert_eq!(iter.len(), 3); 19 | let v: Vec<_> = iter.map(|v| v.as_i32().unwrap()).collect(); 20 | assert_eq!(v, vec![3, 2, 1]); 21 | } 22 | -------------------------------------------------------------------------------- /tests/tests/structable.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | #[test] 4 | fn test_manual_static_impl() { 5 | struct MyStruct { 6 | num: u32, 7 | list: Vec, 8 | sub: SubStruct, 9 | } 10 | 11 | static MY_STRUCT_FIELDS: &[NamedField<'static>] = &[ 12 | NamedField::new("num"), 13 | NamedField::new("list"), 14 | NamedField::new("sub"), 15 | ]; 16 | 17 | struct SubStruct { 18 | message: &'static str, 19 | } 20 | 21 | static SUB_STRUCT_FIELDS: &[NamedField<'static>] = &[NamedField::new("message")]; 22 | 23 | impl Valuable for MyStruct { 24 | fn as_value(&self) -> Value<'_> { 25 | Value::Structable(self) 26 | } 27 | 28 | fn visit(&self, visit: &mut dyn Visit) { 29 | visit.visit_named_fields(&NamedValues::new( 30 | MY_STRUCT_FIELDS, 31 | &[ 32 | Value::U32(self.num), 33 | Value::Listable(&self.list), 34 | Value::Structable(&self.sub), 35 | ], 36 | )); 37 | } 38 | } 39 | 40 | impl Structable for MyStruct { 41 | fn definition(&self) -> StructDef<'_> { 42 | StructDef::new_static("MyStruct", Fields::Named(MY_STRUCT_FIELDS)) 43 | } 44 | } 45 | 46 | impl Valuable for SubStruct { 47 | fn as_value(&self) -> Value<'_> { 48 | Value::Structable(self) 49 | } 50 | 51 | fn visit(&self, visit: &mut dyn Visit) { 52 | visit.visit_named_fields(&NamedValues::new( 53 | SUB_STRUCT_FIELDS, 54 | &[Value::String(self.message)], 55 | )); 56 | } 57 | } 58 | 59 | impl Structable for SubStruct { 60 | fn definition(&self) -> StructDef<'_> { 61 | StructDef::new_static("SubStruct", Fields::Named(SUB_STRUCT_FIELDS)) 62 | } 63 | } 64 | 65 | let my_struct = MyStruct { 66 | num: 12, 67 | list: vec!["hello".to_string()], 68 | sub: SubStruct { message: "world" }, 69 | }; 70 | 71 | assert_eq!( 72 | format!("{:?}", my_struct.as_value()), 73 | "MyStruct { num: 12, list: [\"hello\"], sub: SubStruct { message: \"world\" } }" 74 | ); 75 | 76 | let counts = tests::visit_counts(&my_struct); 77 | assert_eq!( 78 | counts, 79 | tests::VisitCount { 80 | visit_named_fields: 1, 81 | ..Default::default() 82 | } 83 | ); 84 | 85 | let mut counts = tests::VisitCount::default(); 86 | valuable::visit(&my_struct, &mut counts); 87 | assert_eq!( 88 | counts, 89 | tests::VisitCount { 90 | visit_value: 1, 91 | ..Default::default() 92 | } 93 | ); 94 | } 95 | 96 | #[test] 97 | fn test_manual_dyn_impl() { 98 | struct MyStruct; 99 | 100 | impl Valuable for MyStruct { 101 | fn as_value(&self) -> Value<'_> { 102 | Value::Structable(self) 103 | } 104 | 105 | fn visit(&self, visit: &mut dyn Visit) { 106 | visit.visit_named_fields(&NamedValues::new( 107 | &[NamedField::new("foo")], 108 | &[Value::U32(1)], 109 | )); 110 | visit.visit_named_fields(&NamedValues::new( 111 | &[NamedField::new("bar")], 112 | &[Value::String("two")], 113 | )); 114 | } 115 | } 116 | 117 | impl Structable for MyStruct { 118 | fn definition(&self) -> StructDef<'_> { 119 | StructDef::new_dynamic("MyStruct", Fields::Named(&[])) 120 | } 121 | } 122 | 123 | let my_struct = MyStruct; 124 | 125 | assert_eq!( 126 | format!("{:?}", my_struct.as_value()), 127 | "MyStruct { foo: 1, bar: \"two\" }" 128 | ); 129 | } 130 | 131 | #[test] 132 | fn test_named_field() { 133 | let name = "hello".to_string(); 134 | let field = NamedField::new(&name[..]); 135 | assert_eq!(field.name(), "hello"); 136 | 137 | let fields = [field]; 138 | 139 | let fields = Fields::Named(&fields[..]); 140 | assert!(fields.is_named()); 141 | assert!(!fields.is_unnamed()); 142 | 143 | match fields { 144 | Fields::Named(..) => {} 145 | _ => panic!(), 146 | } 147 | } 148 | 149 | #[test] 150 | fn test_fields_unnamed() { 151 | let fields = Fields::Unnamed(1); 152 | assert!(fields.is_unnamed()); 153 | assert!(!fields.is_named()); 154 | } 155 | 156 | #[test] 157 | fn test_struct_def() { 158 | let def = StructDef::new_static("hello", Fields::Unnamed(1)); 159 | 160 | assert_eq!(def.name(), "hello"); 161 | assert!(matches!(def.fields(), Fields::Unnamed(1))); 162 | assert!(!def.is_dynamic()); 163 | } 164 | 165 | #[test] 166 | fn test_named_values() { 167 | let fields = [NamedField::new("foo"), NamedField::new("bar")]; 168 | 169 | let vals = NamedValues::new(&fields[..], &[Value::U32(123), Value::String("hello")]); 170 | 171 | let other_field = NamedField::new("foo"); 172 | 173 | assert!(matches!(vals.get(&fields[0]), Some(Value::U32(v)) if *v == 123)); 174 | assert!(matches!(vals.get(&fields[1]), Some(Value::String(v)) if *v == "hello")); 175 | assert!(vals.get(&other_field).is_none()); 176 | 177 | let e = vals.iter().collect::>(); 178 | assert_eq!(2, e.len()); 179 | 180 | assert_eq!(e[0].0.name(), "foo"); 181 | assert!(matches!(e[0].1, Value::U32(v) if *v == 123)); 182 | 183 | assert_eq!(e[1].0.name(), "bar"); 184 | assert!(matches!(e[1].1, Value::String(v) if *v == "hello")); 185 | } 186 | 187 | #[test] 188 | #[should_panic] 189 | fn test_unbalanced_named_values() { 190 | NamedValues::new( 191 | &[NamedField::new("foo")], 192 | &[Value::U32(123), Value::U32(123)], 193 | ); 194 | } 195 | 196 | // Test the `NamedField` lifetime escapes correctly 197 | #[allow(dead_code)] 198 | fn extract_named_field(def: &StructDef<'static>) -> &'static [NamedField<'static>] { 199 | match def.fields() { 200 | Fields::Named(fields) => fields, 201 | _ => unreachable!(), 202 | } 203 | } 204 | 205 | // Test the name lifetime escapes correctly 206 | #[allow(dead_code)] 207 | fn extract_name(def: &StructDef<'static>) -> &'static str { 208 | def.name() 209 | } 210 | -------------------------------------------------------------------------------- /tests/tests/tuplable.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | macro_rules! test_tuple { 4 | ( 5 | $( 6 | $name:ident => $num:expr, $tuple:expr; 7 | )* 8 | ) => { 9 | $( 10 | #[test] 11 | fn $name() { 12 | let tuple = $tuple; 13 | 14 | assert_eq!(format!("{:?}", tuple.as_value()), format!("{:?}", tuple),); 15 | 16 | let def = tuple.definition(); 17 | 18 | assert_eq!(def.is_unit(), $num == 0); 19 | assert!(def.is_static()); 20 | assert!(matches!(def, TupleDef::Static { fields, .. } if fields == $num)); 21 | 22 | let counts = tests::visit_counts(&tuple); 23 | assert_eq!( 24 | counts, 25 | tests::VisitCount { 26 | visit_unnamed_fields: 1, 27 | ..Default::default() 28 | } 29 | ); 30 | } 31 | )* 32 | }; 33 | } 34 | 35 | #[derive(Valuable, Debug)] 36 | struct Foo { 37 | name: &'static str, 38 | } 39 | 40 | test_tuple! { 41 | test_0 => 0, (); 42 | test_1 => 1, (123,); 43 | test_2 => 2, (123, "foo"); 44 | test_3 => 3, (123, "foo", "bar".to_string()); 45 | test_4 => 4, ("bar".to_string(), 123, "foo", Foo { name: "Foo" }); 46 | test_10 => 10, ( 47 | 1, 48 | "two", 49 | 3_u64, 50 | 4_f32, 51 | "five".to_string(), 52 | 6, 53 | 7, 54 | 8, 55 | Foo { name: "nine" }, 56 | 10, 57 | ); 58 | } 59 | 60 | #[test] 61 | fn test_dyn_impl() { 62 | struct MyTuple; 63 | 64 | impl Valuable for MyTuple { 65 | fn as_value(&self) -> Value<'_> { 66 | Value::Tuplable(self) 67 | } 68 | 69 | fn visit(&self, visit: &mut dyn Visit) { 70 | visit.visit_unnamed_fields(&[Value::I32(123), Value::String("hello world")]); 71 | visit.visit_unnamed_fields(&[Value::String("j/k there is more")]); 72 | } 73 | } 74 | 75 | impl Tuplable for MyTuple { 76 | fn definition(&self) -> TupleDef { 77 | TupleDef::new_dynamic((0, None)) 78 | } 79 | } 80 | 81 | let def = MyTuple.definition(); 82 | assert!(!def.is_unit()); 83 | assert!(def.is_dynamic()); 84 | 85 | assert_eq!( 86 | format!("{:?}", MyTuple.as_value()), 87 | format!("{:?}", (123, "hello world", "j/k there is more")), 88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /tests/tests/ui/not_valuable.rs: -------------------------------------------------------------------------------- 1 | use valuable::Valuable; 2 | 3 | struct S; 4 | 5 | #[derive(Valuable)] 6 | struct Struct { 7 | f: Option, 8 | } 9 | 10 | #[derive(Valuable)] 11 | struct Tuple(Option); 12 | 13 | #[derive(Valuable)] 14 | enum Enum { 15 | Struct { f: Option }, 16 | Tuple(Option), 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /tests/tests/ui/not_valuable.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `S: Valuable` is not satisfied 2 | --> tests/ui/not_valuable.rs:7:8 3 | | 4 | 5 | #[derive(Valuable)] 5 | | -------- required by a bound introduced by this call 6 | 6 | struct Struct { 7 | 7 | f: Option, 8 | | ^^^^^^^^^ the trait `Valuable` is not implemented for `S` 9 | | 10 | = help: the following other types implement trait `Valuable`: 11 | &T 12 | &[T] 13 | &mut T 14 | &std::path::Path 15 | &str 16 | () 17 | (T0, T1) 18 | (T0, T1, T2) 19 | and $N others 20 | = note: required for `Option` to implement `Valuable` 21 | 22 | error[E0277]: the trait bound `S: Valuable` is not satisfied 23 | --> tests/ui/not_valuable.rs:11:14 24 | | 25 | 10 | #[derive(Valuable)] 26 | | -------- required by a bound introduced by this call 27 | 11 | struct Tuple(Option); 28 | | ^^^^^^^^^ the trait `Valuable` is not implemented for `S` 29 | | 30 | = help: the following other types implement trait `Valuable`: 31 | &T 32 | &[T] 33 | &mut T 34 | &std::path::Path 35 | &str 36 | () 37 | (T0, T1) 38 | (T0, T1, T2) 39 | and $N others 40 | = note: required for `Option` to implement `Valuable` 41 | 42 | error[E0277]: the trait bound `S: Valuable` is not satisfied 43 | --> tests/ui/not_valuable.rs:15:25 44 | | 45 | 13 | #[derive(Valuable)] 46 | | -------- required by a bound introduced by this call 47 | 14 | enum Enum { 48 | 15 | Struct { f: Option }, 49 | | ^ the trait `Valuable` is not implemented for `S` 50 | | 51 | = help: the following other types implement trait `Valuable`: 52 | &T 53 | &[T] 54 | &mut T 55 | &std::path::Path 56 | &str 57 | () 58 | (T0, T1) 59 | (T0, T1, T2) 60 | and $N others 61 | = note: required for `Option` to implement `Valuable` 62 | = note: 1 redundant requirement hidden 63 | = note: required for `&Option` to implement `Valuable` 64 | 65 | error[E0277]: the trait bound `S: Valuable` is not satisfied 66 | --> tests/ui/not_valuable.rs:16:19 67 | | 68 | 13 | #[derive(Valuable)] 69 | | -------- required by a bound introduced by this call 70 | ... 71 | 16 | Tuple(Option), 72 | | ^ the trait `Valuable` is not implemented for `S` 73 | | 74 | = help: the following other types implement trait `Valuable`: 75 | &T 76 | &[T] 77 | &mut T 78 | &std::path::Path 79 | &str 80 | () 81 | (T0, T1) 82 | (T0, T1, T2) 83 | and $N others 84 | = note: required for `Option` to implement `Valuable` 85 | = note: 1 redundant requirement hidden 86 | = note: required for `&Option` to implement `Valuable` 87 | -------------------------------------------------------------------------------- /tests/tests/ui/unexpected.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | #[derive(Valuable)] 4 | struct Rename1 { 5 | #[valuable(rename = b)] 6 | f: (), 7 | } 8 | #[derive(Valuable)] 9 | struct Rename2 { 10 | #[valuable(rename = 'b')] 11 | f: (), 12 | } 13 | 14 | #[derive(Valuable)] 15 | struct Transparent1 { 16 | #[valuable(transparent)] 17 | f: (), 18 | } 19 | #[derive(Valuable)] 20 | #[valuable(transparent)] 21 | enum Transparent2 { 22 | V(()), 23 | } 24 | #[derive(Valuable)] 25 | #[valuable(transparent)] 26 | struct Transparent3 { 27 | f1: (), 28 | f2: (), 29 | } 30 | #[derive(Valuable)] 31 | #[valuable(transparent, rename = "a")] 32 | struct Transparent4 { 33 | f: (), 34 | } 35 | 36 | #[derive(Valuable)] 37 | #[valuable(skip)] 38 | struct Skip1 { 39 | f: (), 40 | } 41 | #[derive(Valuable)] 42 | #[valuable(skip)] 43 | enum Skip2 { 44 | #[valuable(skip)] 45 | V(()), 46 | } 47 | #[derive(Valuable)] 48 | struct Skip3 { 49 | #[valuable(skip, rename = "a")] 50 | f: (), 51 | } 52 | 53 | fn main() {} 54 | -------------------------------------------------------------------------------- /tests/tests/ui/unexpected.stderr: -------------------------------------------------------------------------------- 1 | error: expected string literal 2 | --> tests/ui/unexpected.rs:5:25 3 | | 4 | 5 | #[valuable(rename = b)] 5 | | ^ 6 | 7 | error: expected string literal 8 | --> tests/ui/unexpected.rs:10:25 9 | | 10 | 10 | #[valuable(rename = 'b')] 11 | | ^^^ 12 | 13 | error: #[valuable(transparent)] may only be used on structs 14 | --> tests/ui/unexpected.rs:16:16 15 | | 16 | 16 | #[valuable(transparent)] 17 | | ^^^^^^^^^^^ 18 | 19 | error: #[valuable(transparent)] may only be used on structs 20 | --> tests/ui/unexpected.rs:20:12 21 | | 22 | 20 | #[valuable(transparent)] 23 | | ^^^^^^^^^^^ 24 | 25 | error: #[valuable(transparent)] struct needs exactly one field, but has 2 26 | --> tests/ui/unexpected.rs:25:1 27 | | 28 | 25 | / #[valuable(transparent)] 29 | 26 | | struct Transparent3 { 30 | 27 | | f1: (), 31 | 28 | | f2: (), 32 | 29 | | } 33 | | |_^ 34 | 35 | error: #[valuable(transparent)] may not be used together with #[valuable(rename)] 36 | --> tests/ui/unexpected.rs:31:25 37 | | 38 | 31 | #[valuable(transparent, rename = "a")] 39 | | ^^^^^^ 40 | 41 | error: #[valuable(skip)] may only be used on fields 42 | --> tests/ui/unexpected.rs:37:12 43 | | 44 | 37 | #[valuable(skip)] 45 | | ^^^^ 46 | 47 | error: #[valuable(skip)] may only be used on fields 48 | --> tests/ui/unexpected.rs:42:12 49 | | 50 | 42 | #[valuable(skip)] 51 | | ^^^^ 52 | 53 | error: #[valuable(skip)] may only be used on fields 54 | --> tests/ui/unexpected.rs:44:16 55 | | 56 | 44 | #[valuable(skip)] 57 | | ^^^^ 58 | 59 | error: #[valuable(skip)] may not be used together with #[valuable(rename)] 60 | --> tests/ui/unexpected.rs:49:22 61 | | 62 | 49 | #[valuable(skip, rename = "a")] 63 | | ^^^^^^ 64 | -------------------------------------------------------------------------------- /tests/tests/value.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | use core::sync::atomic; 4 | 5 | macro_rules! assert_visit_call { 6 | ($v:expr) => { 7 | let counts = tests::visit_counts($v); 8 | assert_eq!( 9 | counts, 10 | tests::VisitCount { 11 | visit_value: 1, 12 | ..Default::default() 13 | } 14 | ); 15 | 16 | let mut counts = tests::VisitCount::default(); 17 | valuable::visit(&$v, &mut counts); 18 | 19 | assert_eq!( 20 | counts, 21 | tests::VisitCount { 22 | visit_value: 1, 23 | ..Default::default() 24 | } 25 | ) 26 | }; 27 | } 28 | 29 | macro_rules! assert_as { 30 | ($val:expr, $src:expr, $variant:ident, $eq:ident, [$( $as:ident => $v:ident, )*]) => { 31 | $( 32 | if $val.$as().is_some() { 33 | let a = stringify!($variant); 34 | let b = stringify!($v); 35 | // numeric types convert between each other 36 | 37 | if !NUMERICS.contains(&a) || !NUMERICS.contains(&b) { 38 | assert_eq!(stringify!($variant), stringify!($v)); 39 | } 40 | } else { 41 | assert_ne!(stringify!($variant), stringify!($v)); 42 | } 43 | )* 44 | } 45 | } 46 | 47 | macro_rules! assert_value { 48 | ( 49 | $ty:ty: $variant:ident, $as:ident, $eq:ident => $( $values:expr ),* 50 | ) => {{ 51 | use Value::*; 52 | 53 | struct VisitValue<'a>($ty, std::marker::PhantomData<&'a ()>); 54 | 55 | impl<'a> Visit for VisitValue<'a> { 56 | fn visit_value(&mut self, val: Value<'_>) { 57 | assert!(matches!(val, $variant(v) if $eq(&v, &self.0))); 58 | } 59 | } 60 | 61 | for &src in &[ $( $values ),* ] { 62 | // Visit the raw value once 63 | assert_visit_call!(&src); 64 | let mut visit = VisitValue(src, std::marker::PhantomData); 65 | src.visit(&mut visit); 66 | 67 | let val = Value::from(src); 68 | 69 | // Visit the converted value 70 | assert_visit_call!(&val); 71 | let mut visit = VisitValue(src, std::marker::PhantomData); 72 | val.visit(&mut visit); 73 | 74 | // fmt::Debug 75 | assert_eq!( 76 | format!("{:?}", val), 77 | format!("{:?}", src), 78 | ); 79 | 80 | // Test conversion 81 | assert!(matches!(val, $variant(v) if $eq(&v, &src))); 82 | 83 | // Test `as_value()` 84 | assert!(matches!(Valuable::as_value(&val), $variant(v) if $eq(&v, &src))); 85 | 86 | // Test clone() 87 | assert!(matches!(val.clone(), $variant(v) if $eq(&v, &src))); 88 | 89 | // Test self as_*() conversion 90 | assert!($eq(&val.$as().unwrap(), &src)); 91 | 92 | // Test all `as_*()` conversion 93 | assert_as!(val, src, $variant, $eq, [ 94 | as_bool => Bool, 95 | as_char => Char, 96 | as_f32 => F32, 97 | as_f64 => F64, 98 | as_i8 => I8, 99 | as_i16 => I16, 100 | as_i32 => I32, 101 | as_i64 => I64, 102 | as_i128 => I128, 103 | as_isize => Isize, 104 | as_str => String, 105 | as_u8 => U8, 106 | as_u16 => U16, 107 | as_u32 => U32, 108 | as_u64 => U64, 109 | as_u128 => U128, 110 | as_usize => Usize, 111 | as_error => Error, 112 | as_listable => Listable, 113 | as_mappable => Mappable, 114 | as_structable => Structable, 115 | as_enumerable => Enumerable, 116 | ]); 117 | } 118 | }}; 119 | } 120 | 121 | macro_rules! ints { 122 | ( 123 | $( $n:expr ),* 124 | ) => {{ 125 | vec![ 126 | $( 127 | ::try_from($n).ok().map(Value::from), 128 | ::try_from($n).ok().map(Value::from), 129 | ::try_from($n).ok().map(Value::from), 130 | ::try_from($n).ok().map(Value::from), 131 | ::try_from($n).ok().map(Value::from), 132 | ::try_from($n).ok().map(Value::from), 133 | ::try_from($n).ok().map(Value::from), 134 | ::try_from($n).ok().map(Value::from), 135 | ::try_from($n).ok().map(Value::from), 136 | ::try_from($n).ok().map(Value::from), 137 | ::try_from($n).ok().map(Value::from), 138 | ::try_from($n).ok().map(Value::from), 139 | )* 140 | ] 141 | .into_iter() 142 | .filter_map(core::convert::identity) 143 | .collect::>() 144 | }} 145 | } 146 | 147 | macro_rules! test_num { 148 | ( 149 | $( 150 | $name:ident($as:ident, $ty:ty, $variant:ident); 151 | )* 152 | ) => { 153 | // Stringify all variants 154 | const NUMERICS: &[&str] = &[ 155 | $( 156 | stringify!($variant), 157 | )* 158 | ]; 159 | 160 | $( 161 | #[test] 162 | // We're not actually using 3.14 as the value of pi, it's just an 163 | // arbitrary float... 164 | #[allow(clippy::approx_constant)] 165 | fn $name() { 166 | let mut valid = vec![]; 167 | let mut invalid = vec![ 168 | Value::from(true), 169 | Value::from('h'), 170 | Value::from(3.14_f32), 171 | Value::from(3.1415_f64), 172 | Value::from("hello world"), 173 | ]; 174 | 175 | for &shift in &[ 176 | 0, 8, 16, 24, 32, 48, 64, 72, 80, 88, 96, 104, 112, 120, 126, 127 177 | ] { 178 | let actual = u128::MAX.checked_shr(shift).unwrap(); 179 | 180 | match <$ty>::try_from(actual) { 181 | Ok(v) => valid.push(v), 182 | Err(_) => invalid.push(Value::from(actual)), 183 | } 184 | } 185 | 186 | for &n in &valid { 187 | assert_value!($ty: $variant, $as, eq => n); 188 | 189 | for val in ints!(n) { 190 | assert_eq!(Some(n), val.$as()); 191 | } 192 | } 193 | 194 | for val in &invalid { 195 | assert!(val.$as().is_none()); 196 | } 197 | } 198 | )* 199 | } 200 | } 201 | 202 | #[test] 203 | fn test_default() { 204 | assert!(matches!(Value::default(), Value::Unit)); 205 | } 206 | 207 | #[test] 208 | fn test_bool() { 209 | assert_value!(bool: Bool, as_bool, eq => true, false); 210 | } 211 | 212 | #[test] 213 | fn test_char() { 214 | assert_value!(char: Char, as_char, eq => 'a', 'b', 'c'); 215 | } 216 | 217 | #[test] 218 | // We're not actually using 3.14 as the value of pi, it's just an 219 | // arbitrary float... 220 | #[allow(clippy::approx_constant)] 221 | fn test_f32() { 222 | assert_value!(f32: F32, as_f32, eq => 3.1415_f32, -1.234_f32, f32::MAX, f32::MIN); 223 | } 224 | 225 | #[test] 226 | // We're not actually using 3.14 as the value of pi, it's just an 227 | // arbitrary float... 228 | #[allow(clippy::approx_constant)] 229 | fn test_f64() { 230 | assert_value!(f64: F64, as_f64, eq => 3.1415_f64, -1.234_f64, f64::MAX, f64::MIN); 231 | } 232 | 233 | #[test] 234 | fn test_str() { 235 | let string = "in a string".to_string(); 236 | assert_value!(&'a str: String, as_str, eq => "hello world", &string); 237 | } 238 | 239 | #[test] 240 | fn test_path() { 241 | use std::path; 242 | 243 | let path = path::PathBuf::from("a.txt"); 244 | assert_value!(&'a path::Path: Path, as_path, eq => path::Path::new("b.txt"), &path); 245 | } 246 | 247 | #[test] 248 | fn test_error() { 249 | use std::{error, io}; 250 | 251 | let error: io::Error = io::ErrorKind::Other.into(); 252 | let error: &dyn error::Error = &error; 253 | assert_value!(&'a dyn error::Error: Error, as_error, yes => error); 254 | 255 | assert!(error 256 | .as_value() 257 | .as_error() 258 | .unwrap() 259 | .downcast_ref::() 260 | .is_some()); // Check that Value::Error downcast-able 261 | } 262 | 263 | test_num! { 264 | test_u8(as_u8, u8, U8); 265 | test_u16(as_u16, u16, U16); 266 | test_u32(as_u32, u32, U32); 267 | test_u64(as_u64, u64, U64); 268 | test_u128(as_u128, u128, U128); 269 | test_usize(as_usize, usize, Usize); 270 | test_i8(as_i8, i8, I8); 271 | test_i16(as_i16, i16, I16); 272 | test_i32(as_i32, i32, I32); 273 | test_i64(as_i64, i64, I64); 274 | test_i128(as_i128, i128, I128); 275 | test_isize(as_isize, isize, Isize); 276 | } 277 | 278 | #[test] 279 | fn test_valuable_ref() { 280 | let val = &123; 281 | let val = Valuable::as_value(&val); 282 | assert!(matches!(val, Value::I32(v) if v == 123)); 283 | } 284 | 285 | #[test] 286 | fn test_valuable_box() { 287 | let val = Box::new(123); 288 | let val = Valuable::as_value(&val); 289 | assert!(matches!(val, Value::I32(v) if v == 123)); 290 | } 291 | 292 | #[test] 293 | fn test_option() { 294 | let val = Some(1_i32); 295 | let val = Valuable::as_value(&val); 296 | assert!(matches!(val, Value::I32(v) if v == 1)); 297 | 298 | let val = None::; 299 | let val = Valuable::as_value(&val); 300 | assert!(matches!(val, Value::Unit)); 301 | } 302 | 303 | #[test] 304 | fn test_atomic() { 305 | let val = atomic::AtomicBool::new(true); 306 | assert!(matches!(val.as_value(), Value::Bool(v) if v)); 307 | let val = atomic::AtomicI8::new(i8::MAX); 308 | assert!(matches!(val.as_value(), Value::I8(v) if v == i8::MAX)); 309 | let val = atomic::AtomicI16::new(i16::MAX); 310 | assert!(matches!(val.as_value(), Value::I16(v) if v == i16::MAX)); 311 | let val = atomic::AtomicI32::new(i32::MAX); 312 | assert!(matches!(val.as_value(), Value::I32(v) if v == i32::MAX)); 313 | let val = atomic::AtomicI64::new(i64::MAX); 314 | assert!(matches!(val.as_value(), Value::I64(v) if v == i64::MAX)); 315 | let val = atomic::AtomicIsize::new(isize::MAX); 316 | assert!(matches!(val.as_value(), Value::Isize(v) if v == isize::MAX)); 317 | let val = atomic::AtomicU8::new(u8::MAX); 318 | assert!(matches!(val.as_value(), Value::U8(v) if v == u8::MAX)); 319 | let val = atomic::AtomicU16::new(u16::MAX); 320 | assert!(matches!(val.as_value(), Value::U16(v) if v == u16::MAX)); 321 | let val = atomic::AtomicU32::new(u32::MAX); 322 | assert!(matches!(val.as_value(), Value::U32(v) if v == u32::MAX)); 323 | let val = atomic::AtomicU64::new(u64::MAX); 324 | assert!(matches!(val.as_value(), Value::U64(v) if v == u64::MAX)); 325 | let val = atomic::AtomicUsize::new(usize::MAX); 326 | assert!(matches!(val.as_value(), Value::Usize(v) if v == usize::MAX)); 327 | } 328 | 329 | fn eq(a: &T, b: &T) -> bool { 330 | *a == *b 331 | } 332 | 333 | fn yes(_: &T, _: &T) -> bool { 334 | true 335 | } 336 | -------------------------------------------------------------------------------- /valuable-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "valuable-derive" 3 | version = "0.1.1" 4 | edition = "2021" 5 | license = "MIT" 6 | rust-version = "1.56" 7 | description = "Macros for the `valuable` crate." 8 | repository = "https://github.com/tokio-rs/valuable" 9 | categories = [ 10 | "development-tools::debugging", 11 | "encoding", 12 | ] 13 | keywords = [ 14 | "valuable", 15 | "serialization", 16 | "debugging", 17 | "no_std", 18 | ] 19 | [lib] 20 | proc-macro = true 21 | 22 | [dependencies] 23 | proc-macro2 = "1.0.60" 24 | quote = "1.0" 25 | syn = { version = "2.0", features = ["extra-traits"] } 26 | 27 | [dev-dependencies] 28 | valuable = { path = "../valuable", features = ["derive"] } 29 | -------------------------------------------------------------------------------- /valuable-derive/src/attr.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, fmt::Write as _, thread}; 2 | 3 | use proc_macro2::Span; 4 | use syn::{punctuated::Punctuated, spanned::Spanned, Error, Fields, Ident, Meta}; 5 | 6 | const ATTR_NAME: &str = "valuable"; 7 | 8 | // All #[valuable] attributes. 9 | static ATTRS: &[AttrDef] = &[ 10 | // #[valuable(rename = "...")] 11 | AttrDef { 12 | name: "rename", 13 | conflicts_with: &[], 14 | position: &[ 15 | Position::Struct, 16 | Position::Enum, 17 | Position::Variant, 18 | Position::NamedField, 19 | ], 20 | style: &[MetaStyle::NameValue], 21 | }, 22 | // #[valuable(transparent)] 23 | AttrDef { 24 | name: "transparent", 25 | conflicts_with: &["rename"], 26 | position: &[ 27 | Position::Struct, 28 | // TODO: We can probably support single-variant enum that has a single field 29 | // Position::Enum, 30 | ], 31 | style: &[MetaStyle::Ident], 32 | }, 33 | // #[valuable(skip)] 34 | AttrDef { 35 | name: "skip", 36 | conflicts_with: &["rename"], 37 | position: &[ 38 | // TODO: How do we implement Enumerable::variant and Valuable::as_value if a variant is skipped? 39 | // Position::Variant, 40 | Position::NamedField, 41 | Position::UnnamedField, 42 | ], 43 | style: &[MetaStyle::Ident], 44 | }, 45 | ]; 46 | 47 | pub(crate) struct Attrs { 48 | rename: Option<(syn::MetaNameValue, syn::LitStr)>, 49 | transparent: Option, 50 | skip: Option, 51 | } 52 | 53 | impl Attrs { 54 | pub(crate) fn rename(&self, original: &Ident) -> syn::LitStr { 55 | self.rename.as_ref().map_or_else( 56 | || syn::LitStr::new(&original.to_string(), original.span()), 57 | |(_, l)| l.clone(), 58 | ) 59 | } 60 | 61 | pub(crate) fn transparent(&self) -> bool { 62 | self.transparent.is_some() 63 | } 64 | 65 | pub(crate) fn skip(&self) -> bool { 66 | self.skip.is_some() 67 | } 68 | } 69 | 70 | pub(crate) fn parse_attrs(cx: &Context, attrs: &[syn::Attribute], pos: Position) -> Attrs { 71 | let mut rename = None; 72 | let mut transparent = None; 73 | let mut skip = None; 74 | 75 | let attrs = filter_attrs(cx, attrs, pos); 76 | for (def, meta) in &attrs { 77 | macro_rules! lit_str { 78 | ($field:ident) => {{ 79 | let m = match meta { 80 | Meta::NameValue(m) => m, 81 | _ => unreachable!(), 82 | }; 83 | let lit = match &m.value { 84 | syn::Expr::Lit(syn::ExprLit { 85 | lit: syn::Lit::Str(l), 86 | .. 87 | }) => l, 88 | l => { 89 | cx.error(format_err!(l, "expected string literal")); 90 | continue; 91 | } 92 | }; 93 | $field = Some((m.clone(), lit.clone())); 94 | }}; 95 | } 96 | 97 | if def.late_check(cx, &attrs) { 98 | continue; 99 | } 100 | match def.name { 101 | // #[valuable(rename = "...")] 102 | "rename" => lit_str!(rename), 103 | // #[valuable(transparent)] 104 | "transparent" => transparent = Some(meta.span()), 105 | // #[valuable(skip)] 106 | "skip" => skip = Some(meta.span()), 107 | 108 | _ => unreachable!("{}", def.name), 109 | } 110 | } 111 | 112 | Attrs { 113 | rename, 114 | transparent, 115 | skip, 116 | } 117 | } 118 | 119 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 120 | pub(crate) enum Position { 121 | // TODO: kind: struct, tuple, or unit 122 | Struct, 123 | Enum, 124 | // TODO: kind of variants: struct, tuple, or unit 125 | Variant, 126 | NamedField, 127 | UnnamedField, 128 | } 129 | 130 | impl Position { 131 | #[allow(clippy::trivially_copy_pass_by_ref)] 132 | fn as_str(&self) -> &'static str { 133 | match self { 134 | Position::Struct => "struct", 135 | Position::Enum => "enum", 136 | Position::Variant => "variant", 137 | Position::NamedField => "named field", 138 | Position::UnnamedField => "unnamed field", 139 | } 140 | } 141 | 142 | fn is_field(self) -> bool { 143 | self == Position::NamedField || self == Position::UnnamedField 144 | } 145 | } 146 | 147 | impl From<&Fields> for Position { 148 | fn from(meta: &Fields) -> Self { 149 | match meta { 150 | Fields::Named(..) => Position::NamedField, 151 | Fields::Unnamed(..) | Fields::Unit => Position::UnnamedField, 152 | } 153 | } 154 | } 155 | 156 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 157 | pub(crate) enum MetaStyle { 158 | // #[attr()] 159 | Ident, 160 | // #[attr( = ...)] 161 | NameValue, 162 | // #[attr((...))] 163 | List, 164 | } 165 | 166 | impl MetaStyle { 167 | pub(crate) fn format(self, name: &str) -> String { 168 | match self { 169 | MetaStyle::Ident => name.to_owned(), 170 | MetaStyle::List => format!("{}(...)", name), 171 | MetaStyle::NameValue => format!("{} = ...", name), 172 | } 173 | } 174 | } 175 | 176 | impl From<&Meta> for MetaStyle { 177 | fn from(meta: &Meta) -> Self { 178 | match meta { 179 | Meta::Path(..) => MetaStyle::Ident, 180 | Meta::List(..) => MetaStyle::List, 181 | Meta::NameValue(..) => MetaStyle::NameValue, 182 | } 183 | } 184 | } 185 | 186 | #[derive(Debug)] 187 | struct AttrDef { 188 | name: &'static str, 189 | conflicts_with: &'static [&'static str], 190 | // allowed positions. 191 | position: &'static [Position], 192 | // allowed styles. 193 | style: &'static [MetaStyle], 194 | } 195 | 196 | impl AttrDef { 197 | /// Performs checks that can be performed without parsing other attributes, 198 | /// and returns `true` if at least one error occurs. 199 | fn early_check(&self, cx: &Context, pos: Position, meta: &Meta) -> bool { 200 | let mut has_error = false; 201 | if let Err(msg) = self.check_position(pos) { 202 | cx.error(format_err!(meta, msg)); 203 | has_error = true; 204 | } 205 | if let Err(msg) = self.check_style(meta) { 206 | cx.error(format_err!(meta, msg)); 207 | has_error = true; 208 | } 209 | has_error 210 | } 211 | 212 | fn check_position(&self, pos: Position) -> Result<(), String> { 213 | if self.position.contains(&pos) { 214 | return Ok(()); 215 | } 216 | let mut msg = format!("#[{}({})] may only be used on ", ATTR_NAME, self.name); 217 | // TODO: simplify if possible 218 | // len == 1: a 219 | // len == 2: a and b 220 | // len > 2: a, b, and c 221 | let position = if self.position.contains(&Position::NamedField) 222 | && self.position.contains(&Position::UnnamedField) 223 | { 224 | let mut position: Vec<_> = self 225 | .position 226 | .iter() 227 | .filter(|p| !p.is_field()) 228 | .map(Position::as_str) 229 | .collect(); 230 | position.push("field"); 231 | position 232 | } else { 233 | self.position.iter().map(Position::as_str).collect() 234 | }; 235 | let len = position.len(); 236 | for (i, p) in position.iter().enumerate() { 237 | if i != 0 { 238 | if len == 2 { 239 | msg.push_str(" and "); 240 | } else { 241 | msg.push_str(", "); 242 | if i == len - 1 { 243 | msg.push_str("and "); 244 | } 245 | } 246 | } 247 | msg.push_str(p); 248 | msg.push('s'); 249 | } 250 | Err(msg) 251 | } 252 | 253 | fn check_style(&self, meta: &Meta) -> Result<(), String> { 254 | let meta = MetaStyle::from(meta); 255 | if self.style.contains(&meta) { 256 | return Ok(()); 257 | } 258 | let mut msg = "expected ".to_owned(); 259 | let mut first = true; 260 | for style in self.style { 261 | if first { 262 | first = false; 263 | } else { 264 | msg.push_str(" or "); 265 | } 266 | let _ = write!(msg, "`#[{}({})]`", ATTR_NAME, style.format(self.name)); 267 | } 268 | msg.push_str(", found "); 269 | let _ = write!(msg, "`#[{}({})]`", ATTR_NAME, meta.format(self.name)); 270 | Err(msg) 271 | } 272 | 273 | /// Performs checks that can be performed after parsing all attributes in 274 | /// the same scope and parent scopes, and returns `true` if at least one 275 | /// error occurs. 276 | fn late_check(&self, cx: &Context, attrs: &[(&AttrDef, Meta)]) -> bool { 277 | let mut has_error = false; 278 | for (def, meta) in attrs { 279 | if def.name != self.name && self.conflicts_with.contains(&def.name) { 280 | let msg = format!( 281 | "#[{0}({1})] may not be used together with #[{0}({2})]", 282 | ATTR_NAME, self.name, def.name 283 | ); 284 | cx.error(format_err!(meta.path(), msg)); 285 | has_error = true; 286 | } 287 | } 288 | has_error 289 | } 290 | } 291 | 292 | #[derive(Debug)] 293 | pub(crate) struct Context { 294 | // - `None`: during checking. 295 | // - `Some(None)`: there are no errors. 296 | // - `Some(Some)`: there are errors. 297 | #[allow(clippy::option_option)] 298 | error: RefCell>>, 299 | } 300 | 301 | impl Context { 302 | pub(crate) fn error(&self, e: Error) { 303 | match self.error.borrow_mut().as_mut().unwrap() { 304 | Some(base) => base.combine(e), 305 | error @ None => *error = Some(e), 306 | } 307 | } 308 | 309 | pub(crate) fn check(self) -> Result<(), Error> { 310 | match self.error.borrow_mut().take().unwrap() { 311 | Some(e) => Err(e), 312 | None => Ok(()), 313 | } 314 | } 315 | } 316 | 317 | impl Default for Context { 318 | fn default() -> Self { 319 | Self { 320 | error: RefCell::new(Some(None)), 321 | } 322 | } 323 | } 324 | 325 | impl Drop for Context { 326 | fn drop(&mut self) { 327 | if !thread::panicking() && self.error.borrow().is_some() { 328 | panic!("context need to be checked"); 329 | } 330 | } 331 | } 332 | 333 | fn filter_attrs<'a>( 334 | cx: &'a Context, 335 | attrs: &'a [syn::Attribute], 336 | pos: Position, 337 | ) -> Vec<(&'static AttrDef, Meta)> { 338 | let mut counter = vec![0; ATTRS.len()]; 339 | attrs 340 | .iter() 341 | .filter(|attr| attr.path().is_ident(ATTR_NAME)) 342 | .filter_map(move |attr| match &attr.meta { 343 | Meta::List(list) => match list 344 | .parse_args_with(Punctuated::::parse_terminated) 345 | { 346 | Ok(list) => Some(list), 347 | Err(e) => { 348 | cx.error(e); 349 | None 350 | } 351 | }, 352 | m => { 353 | cx.error(format_err!(m, "expected `#[{}(...)]`", ATTR_NAME)); 354 | None 355 | } 356 | }) 357 | .flatten() 358 | .filter_map(move |m| match m.path().get_ident() { 359 | Some(p) => match ATTRS.iter().position(|a| p == a.name) { 360 | Some(pos) => { 361 | counter[pos] += 1; 362 | if counter[pos] == 1 { 363 | Some((&ATTRS[pos], m)) 364 | } else { 365 | cx.error(format_err!( 366 | &m, 367 | "duplicate #[{}({})] attribute", 368 | ATTR_NAME, 369 | p 370 | )); 371 | None 372 | } 373 | } 374 | None => { 375 | cx.error(format_err!(p, "unknown {} attribute `{}`", ATTR_NAME, p)); 376 | None 377 | } 378 | }, 379 | None => { 380 | cx.error(format_err!(m, "expected identifier, found path")); 381 | None 382 | } 383 | }) 384 | .filter(|(def, meta)| !def.early_check(cx, pos, meta)) 385 | .collect() 386 | } 387 | -------------------------------------------------------------------------------- /valuable-derive/src/error.rs: -------------------------------------------------------------------------------- 1 | macro_rules! format_err { 2 | ($span:expr, $msg:expr $(,)?) => { 3 | syn::Error::new_spanned(&$span as &dyn quote::ToTokens, &$msg as &dyn std::fmt::Display) 4 | }; 5 | ($span:expr, $($tt:tt)*) => { 6 | format_err!($span, format!($($tt)*)) 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /valuable-derive/src/expand.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::{format_ident, quote, ToTokens}; 3 | use syn::{Error, Ident, Result}; 4 | 5 | use crate::attr::{parse_attrs, Attrs, Context, Position}; 6 | 7 | pub(crate) fn derive_valuable(input: &mut syn::DeriveInput) -> TokenStream { 8 | let cx = Context::default(); 9 | match &input.data { 10 | syn::Data::Struct(data) => derive_struct(cx, input, data), 11 | syn::Data::Enum(data) => derive_enum(cx, input, data), 12 | syn::Data::Union(data) => { 13 | // It's impossible to derive union because we cannot safely reference the field. 14 | Err(Error::new( 15 | data.union_token.span, 16 | "#[derive(Valuable)] may only be used on structs and enums", 17 | )) 18 | } 19 | } 20 | .unwrap_or_else(Error::into_compile_error) 21 | } 22 | 23 | fn derive_struct( 24 | cx: Context, 25 | input: &syn::DeriveInput, 26 | data: &syn::DataStruct, 27 | ) -> Result { 28 | let struct_attrs = parse_attrs(&cx, &input.attrs, Position::Struct); 29 | let field_attrs: Vec<_> = data 30 | .fields 31 | .iter() 32 | .map(|f| parse_attrs(&cx, &f.attrs, Position::from(&data.fields))) 33 | .collect(); 34 | if struct_attrs.transparent() && data.fields.len() != 1 { 35 | cx.error(Error::new_spanned( 36 | input, 37 | format!( 38 | "#[valuable(transparent)] struct needs exactly one field, but has {}", 39 | data.fields.len() 40 | ), 41 | )) 42 | } 43 | cx.check()?; 44 | 45 | let name = &input.ident; 46 | let name_literal = struct_attrs.rename(name); 47 | 48 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 49 | let allowed_lints = allowed_lints(); 50 | 51 | if struct_attrs.transparent() { 52 | let field = data.fields.iter().next().unwrap(); 53 | let access = field.ident.as_ref().map_or_else( 54 | || syn::Index::from(0).to_token_stream(), 55 | ToTokens::to_token_stream, 56 | ); 57 | let access = respan(quote! { &self.#access }, &field.ty); 58 | let valuable_impl = quote! { 59 | #[automatically_derived] 60 | impl #impl_generics ::valuable::Valuable for #name #ty_generics #where_clause { 61 | fn as_value(&self) -> ::valuable::Value<'_> { 62 | ::valuable::Valuable::as_value(#access) 63 | } 64 | 65 | fn visit(&self, visitor: &mut dyn ::valuable::Visit) { 66 | ::valuable::Valuable::visit(#access, visitor); 67 | } 68 | } 69 | }; 70 | 71 | return Ok(quote! { 72 | #allowed_lints 73 | const _: () = { 74 | #valuable_impl 75 | }; 76 | }); 77 | } 78 | 79 | let visit_fields; 80 | let struct_def; 81 | let mut named_fields_statics = None; 82 | 83 | match &data.fields { 84 | syn::Fields::Named(_) => { 85 | // _FIELDS 86 | let named_fields_static_name = format_ident!("{}_FIELDS", input.ident); 87 | named_fields_statics = Some(named_fields_static( 88 | &named_fields_static_name, 89 | &data.fields, 90 | &field_attrs, 91 | )); 92 | 93 | struct_def = quote! { 94 | ::valuable::StructDef::new_static( 95 | #name_literal, 96 | ::valuable::Fields::Named(#named_fields_static_name), 97 | ) 98 | }; 99 | 100 | let fields = data 101 | .fields 102 | .iter() 103 | .enumerate() 104 | .filter(|(i, _)| !field_attrs[*i].skip()) 105 | .map(|(_, field)| { 106 | let f = field.ident.as_ref(); 107 | let tokens = quote! { 108 | &self.#f 109 | }; 110 | respan(tokens, &field.ty) 111 | }); 112 | visit_fields = quote! { 113 | visitor.visit_named_fields(&::valuable::NamedValues::new( 114 | #named_fields_static_name, 115 | &[ 116 | #(::valuable::Valuable::as_value(#fields),)* 117 | ], 118 | )); 119 | } 120 | } 121 | syn::Fields::Unnamed(_) | syn::Fields::Unit => { 122 | let indices: Vec<_> = data 123 | .fields 124 | .iter() 125 | .enumerate() 126 | .filter(|(i, _)| !field_attrs[*i].skip()) 127 | .map(|(i, field)| { 128 | let index = syn::Index::from(i); 129 | let tokens = quote! { 130 | &self.#index 131 | }; 132 | respan(tokens, &field.ty) 133 | }) 134 | .collect(); 135 | 136 | let len = indices.len(); 137 | struct_def = quote! { 138 | ::valuable::StructDef::new_static( 139 | #name_literal, 140 | ::valuable::Fields::Unnamed(#len), 141 | ) 142 | }; 143 | 144 | visit_fields = quote! { 145 | visitor.visit_unnamed_fields( 146 | &[ 147 | #(::valuable::Valuable::as_value(#indices),)* 148 | ], 149 | ); 150 | }; 151 | } 152 | } 153 | 154 | let structable_impl = quote! { 155 | #[automatically_derived] 156 | impl #impl_generics ::valuable::Structable for #name #ty_generics #where_clause { 157 | fn definition(&self) -> ::valuable::StructDef<'_> { 158 | #struct_def 159 | } 160 | } 161 | }; 162 | 163 | let valuable_impl = quote! { 164 | #[automatically_derived] 165 | impl #impl_generics ::valuable::Valuable for #name #ty_generics #where_clause { 166 | fn as_value(&self) -> ::valuable::Value<'_> { 167 | ::valuable::Value::Structable(self) 168 | } 169 | 170 | fn visit(&self, visitor: &mut dyn ::valuable::Visit) { 171 | #visit_fields 172 | } 173 | } 174 | }; 175 | 176 | Ok(quote! { 177 | #allowed_lints 178 | const _: () = { 179 | #named_fields_statics 180 | #structable_impl 181 | #valuable_impl 182 | }; 183 | }) 184 | } 185 | 186 | fn derive_enum(cx: Context, input: &syn::DeriveInput, data: &syn::DataEnum) -> Result { 187 | let enum_attrs = parse_attrs(&cx, &input.attrs, Position::Enum); 188 | let variant_attrs: Vec<_> = data 189 | .variants 190 | .iter() 191 | .map(|v| parse_attrs(&cx, &v.attrs, Position::Variant)) 192 | .collect(); 193 | let field_attrs: Vec> = data 194 | .variants 195 | .iter() 196 | .map(|v| { 197 | v.fields 198 | .iter() 199 | .map(|f| parse_attrs(&cx, &f.attrs, Position::from(&v.fields))) 200 | .collect() 201 | }) 202 | .collect(); 203 | cx.check()?; 204 | 205 | let name = &input.ident; 206 | let name_literal = enum_attrs.rename(name); 207 | 208 | // _VARIANTS 209 | let variants_static_name = format_ident!("{}_VARIANTS", input.ident); 210 | // `static FIELDS: &[NamedField<'static>]` for variant with named fields 211 | let mut named_fields_statics = vec![]; 212 | let mut variant_defs = vec![]; 213 | let mut variant_fn = vec![]; 214 | let mut visit_variants = vec![]; 215 | 216 | for (variant_index, variant) in data.variants.iter().enumerate() { 217 | let variant_name = &variant.ident; 218 | let variant_name_literal = variant_attrs[variant_index].rename(variant_name); 219 | 220 | match &variant.fields { 221 | syn::Fields::Named(_) => { 222 | // __FIELDS 223 | let named_fields_static_name = 224 | format_ident!("{}_{}_FIELDS", input.ident, variant.ident); 225 | named_fields_statics.push(named_fields_static( 226 | &named_fields_static_name, 227 | &variant.fields, 228 | &field_attrs[variant_index], 229 | )); 230 | 231 | variant_defs.push(quote! { 232 | ::valuable::VariantDef::new( 233 | #variant_name_literal, 234 | ::valuable::Fields::Named(#named_fields_static_name), 235 | ), 236 | }); 237 | 238 | variant_fn.push(quote! { 239 | Self::#variant_name { .. } => { 240 | ::valuable::Variant::Static(&#variants_static_name[#variant_index]) 241 | } 242 | }); 243 | 244 | let mut fields = Vec::with_capacity(variant.fields.len()); 245 | let mut as_value = Vec::with_capacity(variant.fields.len()); 246 | for (_, field) in variant 247 | .fields 248 | .iter() 249 | .enumerate() 250 | .filter(|(i, _)| !field_attrs[variant_index][*i].skip()) 251 | { 252 | let f = field.ident.as_ref(); 253 | fields.push(f); 254 | let tokens = quote! { 255 | // HACK(taiki-e): This `&` is not actually needed to calling as_value, 256 | // but is needed to emulate multi-token span on stable Rust. 257 | &#f 258 | }; 259 | as_value.push(respan(tokens, &field.ty)); 260 | } 261 | let skipped = if fields.len() == variant.fields.len() { 262 | quote! {} 263 | } else { 264 | quote!(..) 265 | }; 266 | visit_variants.push(quote! { 267 | Self::#variant_name { #(#fields,)* #skipped } => { 268 | visitor.visit_named_fields( 269 | &::valuable::NamedValues::new( 270 | #named_fields_static_name, 271 | &[ 272 | #(::valuable::Valuable::as_value(#as_value),)* 273 | ], 274 | ), 275 | ); 276 | } 277 | }); 278 | } 279 | syn::Fields::Unnamed(_) => { 280 | variant_fn.push(quote! { 281 | Self::#variant_name(..) => { 282 | ::valuable::Variant::Static(&#variants_static_name[#variant_index]) 283 | } 284 | }); 285 | 286 | let bindings: Vec<_> = (0..variant.fields.len()) 287 | .map(|i| format_ident!("__binding_{}", i)) 288 | .collect(); 289 | let as_value: Vec<_> = bindings 290 | .iter() 291 | .zip(&variant.fields) 292 | .enumerate() 293 | .filter(|(i, _)| !field_attrs[variant_index][*i].skip()) 294 | .map(|(_, (binding, field))| { 295 | let tokens = quote! { 296 | // HACK(taiki-e): This `&` is not actually needed to calling as_value, 297 | // but is needed to emulate multi-token span on stable Rust. 298 | &#binding 299 | }; 300 | respan(tokens, &field.ty) 301 | }) 302 | .collect(); 303 | 304 | let len = as_value.len(); 305 | variant_defs.push(quote! { 306 | ::valuable::VariantDef::new( 307 | #variant_name_literal, 308 | ::valuable::Fields::Unnamed(#len), 309 | ), 310 | }); 311 | 312 | visit_variants.push(quote! { 313 | Self::#variant_name(#(#bindings),*) => { 314 | visitor.visit_unnamed_fields( 315 | &[ 316 | #(::valuable::Valuable::as_value(#as_value),)* 317 | ], 318 | ); 319 | } 320 | }); 321 | } 322 | syn::Fields::Unit => { 323 | variant_defs.push(quote! { 324 | ::valuable::VariantDef::new( 325 | #variant_name_literal, 326 | ::valuable::Fields::Unnamed(0), 327 | ), 328 | }); 329 | 330 | variant_fn.push(quote! { 331 | Self::#variant_name => { 332 | ::valuable::Variant::Static(&#variants_static_name[#variant_index]) 333 | } 334 | }); 335 | 336 | visit_variants.push(quote! { 337 | Self::#variant_name => { 338 | visitor.visit_unnamed_fields( 339 | &[], 340 | ); 341 | } 342 | }); 343 | } 344 | } 345 | } 346 | 347 | let variants_static = quote! { 348 | static #variants_static_name: &[::valuable::VariantDef<'static>] = &[ 349 | #(#variant_defs)* 350 | ]; 351 | }; 352 | 353 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 354 | let enumerable_impl = quote! { 355 | #[automatically_derived] 356 | impl #impl_generics ::valuable::Enumerable for #name #ty_generics #where_clause { 357 | fn definition(&self) -> ::valuable::EnumDef<'_> { 358 | ::valuable::EnumDef::new_static( 359 | #name_literal, 360 | #variants_static_name, 361 | ) 362 | } 363 | 364 | fn variant(&self) -> ::valuable::Variant<'_> { 365 | match self { 366 | #(#variant_fn)* 367 | } 368 | } 369 | } 370 | }; 371 | 372 | let valuable_impl = quote! { 373 | #[automatically_derived] 374 | impl #impl_generics ::valuable::Valuable for #name #ty_generics #where_clause { 375 | fn as_value(&self) -> ::valuable::Value<'_> { 376 | ::valuable::Value::Enumerable(self) 377 | } 378 | 379 | fn visit(&self, visitor: &mut dyn ::valuable::Visit) { 380 | match self { 381 | #(#visit_variants)* 382 | } 383 | } 384 | } 385 | }; 386 | 387 | let allowed_lints = allowed_lints(); 388 | Ok(quote! { 389 | #allowed_lints 390 | const _: () = { 391 | #(#named_fields_statics)* 392 | #variants_static 393 | #enumerable_impl 394 | #valuable_impl 395 | }; 396 | }) 397 | } 398 | 399 | // `static : &[NamedField<'static>] = &[ ... ];` 400 | fn named_fields_static(name: &Ident, fields: &syn::Fields, field_attrs: &[Attrs]) -> TokenStream { 401 | debug_assert!(matches!(fields, syn::Fields::Named(..))); 402 | let named_fields = fields 403 | .iter() 404 | .enumerate() 405 | .filter(|(i, _)| !field_attrs[*i].skip()) 406 | .map(|(i, field)| { 407 | let field_name_literal = field_attrs[i].rename(field.ident.as_ref().unwrap()); 408 | quote! { 409 | ::valuable::NamedField::new(#field_name_literal), 410 | } 411 | }); 412 | quote! { 413 | static #name: &[::valuable::NamedField<'static>] = &[ 414 | #(#named_fields)* 415 | ]; 416 | } 417 | } 418 | 419 | // Returns attributes that should be applied to generated code. 420 | fn allowed_lints() -> TokenStream { 421 | quote! { 422 | #[allow(non_upper_case_globals)] 423 | #[allow(clippy::unknown_clippy_lints)] 424 | #[allow(clippy::used_underscore_binding)] 425 | #[allow(clippy::indexing_slicing)] 426 | } 427 | } 428 | 429 | fn respan(tokens: TokenStream, span: &impl ToTokens) -> TokenStream { 430 | let mut iter = span.to_token_stream().into_iter(); 431 | // `Span` on stable Rust has a limitation that only points to the first 432 | // token, not the whole tokens. We can work around this limitation by 433 | // using the first/last span of the tokens like `syn::Error::new_spanned` does. 434 | let start_span = iter.next().map_or_else(Span::call_site, |t| t.span()); 435 | let end_span = iter.last().map_or(start_span, |t| t.span()); 436 | 437 | let mut tokens = tokens.into_iter().collect::>(); 438 | if let Some(tt) = tokens.first_mut() { 439 | tt.set_span(start_span); 440 | } 441 | for tt in tokens.iter_mut().skip(1) { 442 | tt.set_span(end_span); 443 | } 444 | tokens.into_iter().collect() 445 | } 446 | -------------------------------------------------------------------------------- /valuable-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms, unreachable_pub)] 2 | 3 | #[macro_use] 4 | mod error; 5 | 6 | mod attr; 7 | mod expand; 8 | 9 | use proc_macro::TokenStream; 10 | use syn::parse_macro_input; 11 | 12 | /// Derive a `Valuable` implementation for a struct or enum. 13 | /// 14 | /// # Attributes 15 | /// 16 | /// ## `#[valuable(rename = "...")]` 17 | /// 18 | /// Use the given name instead of its Rust name. 19 | /// 20 | /// ## `#[valuable(transparent)]` 21 | /// 22 | /// Delegate the trait implementation to the field. 23 | /// 24 | /// This attribute can only be used on a struct that has a single field. 25 | /// 26 | /// ## `#[valuable(skip)]` 27 | /// 28 | /// Skip the field. 29 | /// 30 | /// # Examples 31 | /// 32 | /// ``` 33 | /// use valuable::Valuable; 34 | /// 35 | /// #[derive(Valuable)] 36 | /// struct HelloWorld { 37 | /// message: Message, 38 | /// } 39 | /// 40 | /// #[derive(Valuable)] 41 | /// enum Message { 42 | /// HelloWorld, 43 | /// Custom(String), 44 | /// } 45 | /// ``` 46 | #[proc_macro_derive(Valuable, attributes(valuable))] 47 | pub fn derive_valuable(input: TokenStream) -> TokenStream { 48 | let mut input = parse_macro_input!(input as syn::DeriveInput); 49 | expand::derive_valuable(&mut input).into() 50 | } 51 | -------------------------------------------------------------------------------- /valuable-serde/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.1 (2025-01-17) 2 | 3 | - Serialize `dyn Error` Values as a struct with fields for sources (#92) 4 | 5 | # 0.1.0 (2022-01-26) 6 | 7 | - Initial release 8 | -------------------------------------------------------------------------------- /valuable-serde/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "valuable-serde" 3 | version = "0.1.1" 4 | authors = ["Taiki Endo "] 5 | edition = "2021" 6 | license = "MIT" 7 | description = "`serde::Serialize` implementation for `Valuable` types." 8 | rust-version = "1.56" 9 | readme = "README.md" 10 | repository = "https://github.com/tokio-rs/valuable" 11 | categories = [ 12 | "development-tools::debugging", 13 | "encoding", 14 | ] 15 | keywords = [ 16 | "valuable", 17 | "serialization", 18 | "serde", 19 | "no_std", 20 | ] 21 | 22 | [features] 23 | default = ["std"] 24 | 25 | std = ["alloc", "valuable/std", "serde/std"] 26 | alloc = ["valuable/alloc", "serde/alloc"] 27 | 28 | [dependencies] 29 | valuable = { version = "0.1", path = "../valuable", default-features = false } 30 | serde = { version = "1.0.103", default-features = false } 31 | 32 | [dev-dependencies] 33 | valuable = { version = "0.1", path = "../valuable", features = ["derive"] } 34 | serde = { version = "1", features = ["derive"] } 35 | serde_json = "1" 36 | serde_test = "1" 37 | -------------------------------------------------------------------------------- /valuable-serde/README.md: -------------------------------------------------------------------------------- 1 | # valuable-serde 2 | 3 | [Valuable][`valuable`] provides object-safe value inspection. Use cases include passing 4 | structured data to trait objects and object-safe serialization. 5 | 6 | This crate provides a bridge between [`valuable`] and the [`serde`] 7 | serialization ecosystem. Using [`valuable_serde::Serializable`] allows any type 8 | that implements `valuable`'s [`Valuable`] trait to be serialized by any 9 | [`serde::ser::Serializer`]. 10 | 11 | [`valuable`]: https://crates.io/crates/valuable 12 | [`serde`]: https://crates.io/crates/serde 13 | [`valuable_serde::Serializable`]: https://docs.rs/valuable-serde/latest/valuable_serde/struct.Serializable.html 14 | [`Valuable`]: https://docs.rs/valuable/latest/valuable/trait.Valuable.html 15 | [`serde::ser::Serializer`]: https://docs.rs/serde/latest/serde/ser/trait.Serializer.html 16 | 17 | ## License 18 | 19 | This project is licensed under the [MIT license](LICENSE). 20 | 21 | ### Contribution 22 | 23 | Unless you explicitly state otherwise, any contribution intentionally submitted 24 | for inclusion in Valuable by you, shall be licensed as MIT, without any additional 25 | terms or conditions. -------------------------------------------------------------------------------- /valuable-serde/tests/test.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use serde::Serialize; 4 | use serde_test::{assert_ser_tokens, Token}; 5 | use valuable::*; 6 | use valuable_serde::Serializable; 7 | 8 | macro_rules! assert_ser_eq { 9 | ($value:expr, &[$($tokens:tt)*] $(,)?) => {{ 10 | let value = &$value; 11 | assert_ser_tokens(&Serializable::new(value), &[$($tokens)*]); 12 | assert_ser_tokens(value, &[$($tokens)*]); 13 | }}; 14 | } 15 | 16 | #[test] 17 | fn test_bool() { 18 | assert_ser_eq!(false, &[Token::Bool(false)]); 19 | assert_ser_eq!(true, &[Token::Bool(true)]); 20 | } 21 | 22 | #[test] 23 | fn test_int() { 24 | assert_ser_eq!(i8::MIN, &[Token::I8(i8::MIN)]); 25 | assert_ser_eq!(i8::MAX, &[Token::I8(i8::MAX)]); 26 | assert_ser_eq!(i16::MIN, &[Token::I16(i16::MIN)]); 27 | assert_ser_eq!(i16::MAX, &[Token::I16(i16::MAX)]); 28 | assert_ser_eq!(i32::MIN, &[Token::I32(i32::MIN)]); 29 | assert_ser_eq!(i32::MAX, &[Token::I32(i32::MAX)]); 30 | assert_ser_eq!(i64::MIN, &[Token::I64(i64::MIN)]); 31 | assert_ser_eq!(i64::MAX, &[Token::I64(i64::MAX)]); 32 | // serde_test doesn't have Token::I128. 33 | // assert_ser_eq!(i128::MIN, &[Token::I128(i128::MIN)]); 34 | // assert_ser_eq!(i128::MAX, &[Token::I128(i128::MAX)]); 35 | assert_ser_eq!(isize::MIN, &[Token::I64(isize::MIN as _)]); 36 | assert_ser_eq!(isize::MAX, &[Token::I64(isize::MAX as _)]); 37 | assert_ser_eq!(u8::MAX, &[Token::U8(u8::MAX)]); 38 | assert_ser_eq!(u16::MAX, &[Token::U16(u16::MAX)]); 39 | assert_ser_eq!(u32::MAX, &[Token::U32(u32::MAX)]); 40 | assert_ser_eq!(u64::MAX, &[Token::U64(u64::MAX)]); 41 | // serde_test doesn't have Token::U128. 42 | // assert_ser_eq!(u128::MAX, &[Token::U128(u128::MAX)]); 43 | assert_ser_eq!(usize::MAX, &[Token::U64(usize::MAX as _)]); 44 | } 45 | 46 | #[test] 47 | fn test_float() { 48 | assert_ser_eq!(f32::MIN, &[Token::F32(f32::MIN)]); 49 | assert_ser_eq!(f32::MAX, &[Token::F32(f32::MAX)]); 50 | assert_ser_eq!(f64::MIN, &[Token::F64(f64::MIN)]); 51 | assert_ser_eq!(f64::MAX, &[Token::F64(f64::MAX)]); 52 | } 53 | 54 | #[test] 55 | fn test_char() { 56 | assert_ser_eq!('a', &[Token::Char('a')]); 57 | } 58 | 59 | #[test] 60 | fn test_str() { 61 | assert_ser_eq!("a", &[Token::Str("a")]); 62 | assert_ser_eq!("a", &[Token::BorrowedStr("a")]); 63 | assert_ser_eq!("a", &[Token::String("a")]); 64 | assert_ser_eq!("a".to_string(), &[Token::Str("a")]); 65 | assert_ser_eq!("a".to_string(), &[Token::BorrowedStr("a")]); 66 | assert_ser_eq!("a".to_string(), &[Token::String("a")]); 67 | } 68 | 69 | // TODO: 70 | // - valuable treats Option as T or unit 71 | // - serde treats Option as Some(T) or None 72 | #[test] 73 | fn test_option() { 74 | assert_ser_tokens(&Serializable::new(None::), &[Token::Unit]); 75 | assert_ser_tokens(&None::, &[Token::None]); 76 | assert_ser_tokens(&Serializable::new(Some(1)), &[Token::I32(1)]); 77 | assert_ser_tokens(&Some(1), &[Token::Some, Token::I32(1)]); 78 | } 79 | 80 | #[test] 81 | fn test_unit() { 82 | assert_ser_eq!((), &[Token::Unit]); 83 | } 84 | 85 | // TODO: 86 | // - valuable treats unit struct as an empty tuple struct 87 | // - serde treats unit struct as unit struct 88 | #[test] 89 | fn test_unit_struct() { 90 | #[derive(Debug, PartialEq, Valuable, Serialize)] 91 | struct S; 92 | 93 | assert_ser_tokens( 94 | &Serializable::new(S), 95 | &[ 96 | Token::TupleStruct { name: "S", len: 0 }, 97 | Token::TupleStructEnd, 98 | ], 99 | ); 100 | assert_ser_tokens(&S, &[Token::UnitStruct { name: "S" }]); 101 | } 102 | 103 | // TODO: 104 | // - valuable treats unit variant as an empty tuple variant 105 | // - serde treats unit variant as unit variant 106 | #[test] 107 | fn test_unit_variant() { 108 | #[derive(Debug, PartialEq, Valuable, Serialize)] 109 | enum E { 110 | V, 111 | } 112 | 113 | assert_ser_tokens( 114 | &Serializable::new(E::V), 115 | &[ 116 | Token::TupleVariant { 117 | name: "E", 118 | variant: "V", 119 | len: 0, 120 | }, 121 | Token::TupleVariantEnd, 122 | ], 123 | ); 124 | assert_ser_tokens( 125 | &E::V, 126 | &[Token::UnitVariant { 127 | name: "E", 128 | variant: "V", 129 | }], 130 | ); 131 | } 132 | 133 | #[test] 134 | fn test_newtype_struct() { 135 | #[derive(Debug, PartialEq, Valuable, Serialize)] 136 | struct S(u8); 137 | 138 | assert_ser_eq!(S(0), &[Token::NewtypeStruct { name: "S" }, Token::U8(0)]); 139 | } 140 | 141 | #[test] 142 | fn test_newtype_variant() { 143 | #[derive(Debug, PartialEq, Valuable, Serialize)] 144 | enum E { 145 | V(u8), 146 | } 147 | 148 | assert_ser_eq!( 149 | E::V(0), 150 | &[ 151 | Token::NewtypeVariant { 152 | name: "E", 153 | variant: "V" 154 | }, 155 | Token::U8(0) 156 | ] 157 | ); 158 | } 159 | 160 | #[test] 161 | fn test_seq() { 162 | assert_ser_eq!( 163 | vec![1, 2, 3], 164 | &[ 165 | Token::Seq { len: Some(3) }, 166 | Token::I32(1), 167 | Token::I32(2), 168 | Token::I32(3), 169 | Token::SeqEnd 170 | ] 171 | ); 172 | } 173 | 174 | #[test] 175 | fn test_tuple() { 176 | assert_ser_eq!( 177 | (1,), 178 | &[Token::Tuple { len: 1 }, Token::I32(1), Token::TupleEnd] 179 | ); 180 | assert_ser_eq!( 181 | ("a", 'b'), 182 | &[ 183 | Token::Tuple { len: 2 }, 184 | Token::Str("a"), 185 | Token::Char('b'), 186 | Token::TupleEnd 187 | ] 188 | ); 189 | } 190 | 191 | #[test] 192 | fn test_tuple_struct() { 193 | #[derive(Debug, PartialEq, Valuable, Serialize)] 194 | struct S1(); 195 | #[derive(Debug, PartialEq, Valuable, Serialize)] 196 | struct S2(i8, u8); 197 | 198 | assert_ser_eq!( 199 | S1(), 200 | &[ 201 | Token::TupleStruct { name: "S1", len: 0 }, 202 | Token::TupleStructEnd 203 | ] 204 | ); 205 | assert_ser_eq!( 206 | S2(-1, 1), 207 | &[ 208 | Token::TupleStruct { name: "S2", len: 2 }, 209 | Token::I8(-1), 210 | Token::U8(1), 211 | Token::TupleStructEnd 212 | ] 213 | ); 214 | } 215 | 216 | #[test] 217 | fn test_tuple_variant() { 218 | #[derive(Debug, PartialEq, Valuable, Serialize)] 219 | enum E { 220 | V1(), 221 | V2(i8, u8), 222 | } 223 | 224 | assert_ser_eq!( 225 | E::V1(), 226 | &[ 227 | Token::TupleVariant { 228 | name: "E", 229 | variant: "V1", 230 | len: 0 231 | }, 232 | Token::TupleVariantEnd 233 | ] 234 | ); 235 | assert_ser_eq!( 236 | E::V2(-1, 1), 237 | &[ 238 | Token::TupleVariant { 239 | name: "E", 240 | variant: "V2", 241 | len: 2 242 | }, 243 | Token::I8(-1), 244 | Token::U8(1), 245 | Token::TupleVariantEnd 246 | ] 247 | ); 248 | } 249 | 250 | #[test] 251 | fn test_map() { 252 | let mut m = BTreeMap::new(); 253 | assert_ser_eq!(m, &[Token::Map { len: Some(0) }, Token::MapEnd]); 254 | m.insert(1, 10); 255 | m.insert(2, 20); 256 | assert_ser_eq!( 257 | m, 258 | &[ 259 | Token::Map { len: Some(2) }, 260 | Token::I32(1), 261 | Token::I32(10), 262 | Token::I32(2), 263 | Token::I32(20), 264 | Token::MapEnd 265 | ] 266 | ); 267 | } 268 | 269 | #[test] 270 | fn test_struct() { 271 | #[derive(Debug, PartialEq, Valuable, Serialize)] 272 | struct S1 {} 273 | #[derive(Debug, PartialEq, Valuable, Serialize)] 274 | struct S2 { 275 | f: u8, 276 | } 277 | 278 | assert_ser_eq!( 279 | S1 {}, 280 | &[Token::Struct { name: "S1", len: 0 }, Token::StructEnd] 281 | ); 282 | assert_ser_eq!( 283 | S2 { f: 1 }, 284 | &[ 285 | Token::Struct { name: "S2", len: 1 }, 286 | Token::Str("f"), 287 | Token::U8(1), 288 | Token::StructEnd 289 | ] 290 | ); 291 | } 292 | 293 | #[test] 294 | fn test_struct_variant() { 295 | #[derive(Debug, PartialEq, Valuable, Serialize)] 296 | enum E { 297 | V1 {}, 298 | V2 { f: u8 }, 299 | } 300 | 301 | assert_ser_eq!( 302 | E::V1 {}, 303 | &[ 304 | Token::StructVariant { 305 | name: "E", 306 | variant: "V1", 307 | len: 0 308 | }, 309 | Token::StructVariantEnd 310 | ] 311 | ); 312 | assert_ser_eq!( 313 | E::V2 { f: 1 }, 314 | &[ 315 | Token::StructVariant { 316 | name: "E", 317 | variant: "V2", 318 | len: 1 319 | }, 320 | Token::Str("f"), 321 | Token::U8(1), 322 | Token::StructVariantEnd 323 | ] 324 | ); 325 | } 326 | 327 | #[test] 328 | fn test_enum() { 329 | #[derive(Debug, PartialEq, Valuable, Serialize)] 330 | enum E { 331 | Unit, 332 | Newtype(u8), 333 | Tuple(u8, u8), 334 | Struct { f: u8 }, 335 | } 336 | 337 | // TODO: 338 | // - valuable treats unit variant as an empty tuple variant 339 | // - serde treats unit variant as unit variant 340 | assert_ser_tokens( 341 | &Serializable::new(E::Unit), 342 | &[ 343 | Token::Enum { name: "E" }, 344 | Token::Str("Unit"), 345 | Token::Seq { len: Some(0) }, 346 | Token::SeqEnd, 347 | ], 348 | ); 349 | assert_ser_tokens( 350 | &E::Unit, 351 | &[Token::Enum { name: "E" }, Token::Str("Unit"), Token::Unit], 352 | ); 353 | 354 | assert_ser_eq!( 355 | E::Newtype(0), 356 | &[ 357 | Token::Enum { name: "E" }, 358 | Token::Str("Newtype"), 359 | Token::U8(0), 360 | ], 361 | ); 362 | 363 | assert_ser_eq!( 364 | E::Tuple(0, 0), 365 | &[ 366 | Token::Enum { name: "E" }, 367 | Token::Str("Tuple"), 368 | Token::Seq { len: Some(2) }, 369 | Token::U8(0), 370 | Token::U8(0), 371 | Token::SeqEnd, 372 | ], 373 | ); 374 | 375 | assert_ser_eq!( 376 | E::Struct { f: 0 }, 377 | &[ 378 | Token::Enum { name: "E" }, 379 | Token::Str("Struct"), 380 | Token::Map { len: Some(1) }, 381 | Token::Str("f"), 382 | Token::U8(0), 383 | Token::MapEnd, 384 | ], 385 | ); 386 | } 387 | 388 | #[test] 389 | fn test_dyn_struct() { 390 | struct Named; 391 | 392 | impl Valuable for Named { 393 | fn as_value(&self) -> Value<'_> { 394 | Value::Structable(self) 395 | } 396 | 397 | fn visit(&self, visit: &mut dyn Visit) { 398 | visit.visit_named_fields(&NamedValues::new(&[NamedField::new("a")], &[Value::U32(1)])); 399 | visit.visit_named_fields(&NamedValues::new( 400 | &[NamedField::new("b")], 401 | &[Value::I32(-1)], 402 | )); 403 | } 404 | } 405 | 406 | impl Structable for Named { 407 | fn definition(&self) -> StructDef<'_> { 408 | StructDef::new_dynamic("Named", Fields::Named(&[])) 409 | } 410 | } 411 | 412 | struct Unnamed; 413 | 414 | impl Valuable for Unnamed { 415 | fn as_value(&self) -> Value<'_> { 416 | Value::Structable(self) 417 | } 418 | 419 | fn visit(&self, visit: &mut dyn Visit) { 420 | visit.visit_unnamed_fields(&[Value::U32(1)]); 421 | visit.visit_unnamed_fields(&[Value::I32(-1)]); 422 | } 423 | } 424 | 425 | impl Structable for Unnamed { 426 | fn definition(&self) -> StructDef<'_> { 427 | StructDef::new_dynamic("Unnamed", Fields::Unnamed(2)) 428 | } 429 | } 430 | 431 | assert_ser_tokens( 432 | &Serializable::new(Named), 433 | &[ 434 | Token::Map { len: None }, 435 | Token::Str("a"), 436 | Token::U32(1), 437 | Token::Str("b"), 438 | Token::I32(-1), 439 | Token::MapEnd, 440 | ], 441 | ); 442 | 443 | assert_ser_tokens( 444 | &Serializable::new(Unnamed), 445 | &[ 446 | Token::Seq { len: None }, 447 | Token::U32(1), 448 | Token::I32(-1), 449 | Token::SeqEnd, 450 | ], 451 | ); 452 | } 453 | 454 | #[test] 455 | fn test_dyn_enum() { 456 | enum E { 457 | Named, 458 | Unnamed, 459 | } 460 | 461 | impl Valuable for E { 462 | fn as_value(&self) -> Value<'_> { 463 | Value::Enumerable(self) 464 | } 465 | 466 | fn visit(&self, visit: &mut dyn Visit) { 467 | match self { 468 | Self::Named => { 469 | visit.visit_named_fields(&NamedValues::new( 470 | &[NamedField::new("a")], 471 | &[Value::U32(1)], 472 | )); 473 | visit.visit_named_fields(&NamedValues::new( 474 | &[NamedField::new("b")], 475 | &[Value::I32(-1)], 476 | )); 477 | } 478 | Self::Unnamed => { 479 | visit.visit_unnamed_fields(&[Value::U32(1)]); 480 | visit.visit_unnamed_fields(&[Value::I32(-1)]); 481 | } 482 | } 483 | } 484 | } 485 | 486 | impl Enumerable for E { 487 | fn definition(&self) -> EnumDef<'_> { 488 | EnumDef::new_dynamic("E", &[]) 489 | } 490 | 491 | fn variant(&self) -> Variant<'_> { 492 | match self { 493 | Self::Named => Variant::Dynamic(VariantDef::new("Named", Fields::Named(&[]))), 494 | Self::Unnamed => Variant::Dynamic(VariantDef::new("Named", Fields::Unnamed(2))), 495 | } 496 | } 497 | } 498 | 499 | assert_ser_tokens( 500 | &Serializable::new(E::Named), 501 | &[ 502 | Token::Map { len: None }, 503 | Token::Str("a"), 504 | Token::U32(1), 505 | Token::Str("b"), 506 | Token::I32(-1), 507 | Token::MapEnd, 508 | ], 509 | ); 510 | 511 | assert_ser_tokens( 512 | &Serializable::new(E::Unnamed), 513 | &[ 514 | Token::Seq { len: None }, 515 | Token::U32(1), 516 | Token::I32(-1), 517 | Token::SeqEnd, 518 | ], 519 | ); 520 | } 521 | 522 | #[test] 523 | fn test_errors() { 524 | use std::{error::Error, fmt}; 525 | 526 | #[derive(Debug)] 527 | struct TestError { 528 | message: &'static str, 529 | source: Option>, 530 | } 531 | 532 | impl fmt::Display for TestError { 533 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 534 | f.write_str(self.message) 535 | } 536 | } 537 | 538 | impl Error for TestError { 539 | fn source(&self) -> Option<&(dyn Error + 'static)> { 540 | self.source.as_deref() 541 | } 542 | } 543 | 544 | let no_source = TestError { 545 | message: "an error occurred", 546 | source: None, 547 | }; 548 | 549 | assert_ser_eq!( 550 | &Serializable::new(&no_source as &(dyn Error + 'static)), 551 | &[ 552 | Token::Struct { 553 | name: "Error", 554 | len: 2 555 | }, 556 | Token::Str("message"), 557 | Token::Str("an error occurred"), 558 | Token::Str("source"), 559 | Token::None, 560 | Token::StructEnd 561 | ] 562 | ); 563 | 564 | let with_source = TestError { 565 | message: "the error caused another error", 566 | source: Some(Box::new(no_source)), 567 | }; 568 | 569 | assert_ser_eq!( 570 | &Serializable::new(&with_source as &(dyn Error + 'static)), 571 | &[ 572 | Token::Struct { 573 | name: "Error", 574 | len: 2 575 | }, 576 | Token::Str("message"), 577 | Token::Str("the error caused another error"), 578 | Token::Str("source"), 579 | Token::Some, 580 | Token::Struct { 581 | name: "Error", 582 | len: 2 583 | }, 584 | Token::Str("message"), 585 | Token::Str("an error occurred"), 586 | Token::Str("source"), 587 | Token::None, 588 | Token::StructEnd, 589 | Token::StructEnd 590 | ] 591 | ); 592 | } 593 | -------------------------------------------------------------------------------- /valuable/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "valuable" 3 | version = "0.1.1" 4 | edition = "2021" 5 | license = "MIT" 6 | rust-version = "1.56" 7 | readme = "../README.md" 8 | repository = "https://github.com/tokio-rs/valuable" 9 | description = """ 10 | Object-safe value inspection, used to pass un-typed structured data across trait-object boundaries. 11 | """ 12 | categories = [ 13 | "development-tools::debugging", 14 | "encoding", 15 | ] 16 | keywords = [ 17 | "valuable", 18 | "serialization", 19 | "debugging", 20 | "no_std", 21 | ] 22 | 23 | [features] 24 | default = ["std"] 25 | 26 | # Provide derive(Valuable) macro 27 | derive = ["valuable-derive"] 28 | 29 | # Provide impls for common standard library types like Vec and HashMap. 30 | std = ["alloc"] 31 | 32 | # Provide imps for types in Rust's `alloc` library. 33 | alloc = [] 34 | 35 | [dependencies] 36 | valuable-derive = { version = "=0.1.1", optional = true, path = "../valuable-derive" } 37 | 38 | [dev-dependencies] 39 | criterion = "0.3" 40 | 41 | [[bench]] 42 | name = "structable" 43 | harness = false 44 | 45 | [package.metadata.docs.rs] 46 | all-features = true 47 | rustdoc-args = ["--cfg", "docsrs"] 48 | -------------------------------------------------------------------------------- /valuable/benches/structable.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 4 | 5 | #[derive(Default)] 6 | struct HelloWorld { 7 | one: usize, 8 | two: usize, 9 | three: usize, 10 | four: usize, 11 | five: usize, 12 | six: usize, 13 | } 14 | 15 | static FIELDS: &[NamedField<'static>] = &[ 16 | NamedField::new("one"), 17 | NamedField::new("two"), 18 | NamedField::new("three"), 19 | NamedField::new("four"), 20 | NamedField::new("five"), 21 | NamedField::new("six"), 22 | ]; 23 | 24 | impl Structable for HelloWorld { 25 | fn definition(&self) -> StructDef<'_> { 26 | StructDef::new_static("HelloWorld", Fields::Named(FIELDS)) 27 | } 28 | } 29 | 30 | impl Valuable for HelloWorld { 31 | fn as_value(&self) -> Value<'_> { 32 | Value::Structable(self) 33 | } 34 | 35 | fn visit(&self, v: &mut dyn Visit) { 36 | v.visit_named_fields(&NamedValues::new( 37 | FIELDS, 38 | &[ 39 | Value::Usize(self.one), 40 | Value::Usize(self.two), 41 | Value::Usize(self.three), 42 | Value::Usize(self.four), 43 | Value::Usize(self.five), 44 | Value::Usize(self.six), 45 | ], 46 | )); 47 | } 48 | } 49 | 50 | fn criterion_benchmark(c: &mut Criterion) { 51 | const NUM: usize = 50; 52 | 53 | let hello_world = black_box(HelloWorld::default()); 54 | let structable = &hello_world as &dyn Structable; 55 | let f = match structable.definition() { 56 | StructDef::Static { 57 | fields: Fields::Named(fields), 58 | .. 59 | } => &fields[5], 60 | _ => unreachable!(), 61 | }; 62 | 63 | struct Sum(usize, &'static NamedField<'static>); 64 | 65 | impl Visit for Sum { 66 | fn visit_named_fields(&mut self, record: &NamedValues<'_>) { 67 | self.0 += match record.get(self.1) { 68 | Some(Value::Usize(v)) => v, 69 | _ => return, 70 | } 71 | } 72 | 73 | fn visit_value(&mut self, _: Value<'_>) { 74 | unimplemented!() 75 | } 76 | } 77 | 78 | c.bench_function("struct", |b| { 79 | b.iter(|| { 80 | let mut num = 0; 81 | for _ in 0..NUM { 82 | let hello_world = black_box(HelloWorld::default()); 83 | num += hello_world.six; 84 | } 85 | 86 | black_box(num); 87 | }) 88 | }); 89 | 90 | c.bench_function("valuable", |b| { 91 | b.iter(|| { 92 | let mut v = Sum(black_box(0), f); 93 | 94 | for _ in 0..NUM { 95 | v.visit_named_fields(&NamedValues::new( 96 | FIELDS, 97 | &[ 98 | Value::Usize(0), 99 | Value::Usize(0), 100 | Value::Usize(0), 101 | Value::Usize(0), 102 | Value::Usize(0), 103 | Value::Usize(0), 104 | ], 105 | )); 106 | /* 107 | v.visit_struct(&Record::new( 108 | &definition, 109 | &[ 110 | Value::Usize(hello_world.one), 111 | Value::Usize(hello_world.two), 112 | Value::Usize(hello_world.three), 113 | Value::Usize(hello_world.four), 114 | Value::Usize(hello_world.five), 115 | Value::Usize(hello_world.six), 116 | ] 117 | )); 118 | */ 119 | // hello_world.visit(&mut v); 120 | } 121 | 122 | black_box(v.0); 123 | }) 124 | }); 125 | } 126 | 127 | criterion_group!(benches, criterion_benchmark); 128 | criterion_main!(benches); 129 | -------------------------------------------------------------------------------- /valuable/build.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms, single_use_lifetimes)] 2 | 3 | use std::env; 4 | 5 | include!("no_atomic.rs"); 6 | 7 | // The rustc-cfg strings below are *not* public API. Please let us know by 8 | // opening a GitHub issue if your build environment requires some way to enable 9 | // these cfgs other than by executing our build script. 10 | fn main() { 11 | println!("cargo:rerun-if-changed=no_atomic.rs"); 12 | println!("cargo:rustc-check-cfg=cfg(valuable_no_atomic_cas,valuable_no_atomic,valuable_no_atomic_64)"); 13 | 14 | let target = match env::var("TARGET") { 15 | Ok(target) => target, 16 | Err(e) => { 17 | println!( 18 | "cargo:warning=valuable: unable to get TARGET environment variable: {}", 19 | e 20 | ); 21 | return; 22 | } 23 | }; 24 | 25 | // Note that this is `no_*`, not `has_*`. This allows treating 26 | // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't 27 | // run. This is needed for compatibility with non-cargo build systems that 28 | // don't run the build script. 29 | if NO_ATOMIC_CAS.contains(&&*target) { 30 | println!("cargo:rustc-cfg=valuable_no_atomic_cas"); 31 | } 32 | if NO_ATOMIC.contains(&&*target) { 33 | println!("cargo:rustc-cfg=valuable_no_atomic"); 34 | println!("cargo:rustc-cfg=valuable_no_atomic_64"); 35 | } else if NO_ATOMIC_64.contains(&&*target) { 36 | println!("cargo:rustc-cfg=valuable_no_atomic_64"); 37 | } else { 38 | // Otherwise, assuming `"max-atomic-width" == 64`. 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /valuable/examples/derive.rs: -------------------------------------------------------------------------------- 1 | use valuable::Valuable; 2 | 3 | use std::collections::HashMap; 4 | 5 | // `Debug` not implemented for struct, the debug implementation is going via 6 | // valuable. 7 | #[derive(Valuable)] 8 | struct Person { 9 | name: String, 10 | age: u8, 11 | phones: Vec, 12 | favorites: HashMap, 13 | } 14 | 15 | fn main() { 16 | let mut p = Person { 17 | name: "John Doe".to_string(), 18 | age: 42, 19 | phones: vec!["876-5309".to_string()], 20 | favorites: HashMap::new(), 21 | }; 22 | 23 | p.favorites.insert("color".to_string(), "blue".to_string()); 24 | 25 | println!("{:#?}", p.as_value()); 26 | } 27 | -------------------------------------------------------------------------------- /valuable/examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | use valuable::*; 2 | 3 | struct HelloWorld { 4 | hello: &'static str, 5 | world: World, 6 | } 7 | 8 | struct World { 9 | answer: usize, 10 | } 11 | 12 | static HELLO_WORLD_FIELDS: &[NamedField<'static>] = 13 | &[NamedField::new("hello"), NamedField::new("world")]; 14 | 15 | impl Structable for HelloWorld { 16 | fn definition(&self) -> StructDef<'_> { 17 | StructDef::new_static("HelloWorld", Fields::Named(HELLO_WORLD_FIELDS)) 18 | } 19 | } 20 | 21 | impl Valuable for HelloWorld { 22 | fn as_value(&self) -> Value<'_> { 23 | Value::Structable(self) 24 | } 25 | 26 | fn visit(&self, v: &mut dyn Visit) { 27 | v.visit_named_fields(&NamedValues::new( 28 | HELLO_WORLD_FIELDS, 29 | &[Value::String(self.hello), Value::Structable(&self.world)], 30 | )); 31 | } 32 | } 33 | 34 | static WORLD_FIELDS: &[NamedField<'static>] = &[NamedField::new("answer")]; 35 | 36 | impl Valuable for World { 37 | fn as_value(&self) -> Value<'_> { 38 | Value::Structable(self) 39 | } 40 | 41 | fn visit(&self, v: &mut dyn Visit) { 42 | v.visit_named_fields(&NamedValues::new( 43 | WORLD_FIELDS, 44 | &[Value::Usize(self.answer)], 45 | )); 46 | } 47 | } 48 | 49 | impl Structable for World { 50 | fn definition(&self) -> StructDef<'_> { 51 | StructDef::new_static("World", Fields::Named(WORLD_FIELDS)) 52 | } 53 | } 54 | 55 | fn main() { 56 | let hello_world = HelloWorld { 57 | hello: "wut", 58 | world: World { answer: 42 }, 59 | }; 60 | 61 | let value = Value::Structable(&hello_world); 62 | println!("{:#?}", value); 63 | 64 | let slice = &[1, 2, 3][..]; 65 | 66 | let value = &slice as &dyn Valuable; 67 | println!("{:?}", value); 68 | } 69 | -------------------------------------------------------------------------------- /valuable/examples/print.rs: -------------------------------------------------------------------------------- 1 | use valuable::{NamedValues, Valuable, Value, Visit}; 2 | 3 | struct Print(String); 4 | 5 | impl Print { 6 | fn indent(&self) -> Print { 7 | Print(format!("{} ", self.0)) 8 | } 9 | } 10 | 11 | impl Visit for Print { 12 | fn visit_value(&mut self, value: Value<'_>) { 13 | match value { 14 | Value::Structable(v) => { 15 | let def = v.definition(); 16 | // Print the struct name 17 | println!("{}{}:", self.0, def.name()); 18 | 19 | // Visit fields 20 | let mut visit = self.indent(); 21 | v.visit(&mut visit); 22 | } 23 | Value::Enumerable(v) => { 24 | let def = v.definition(); 25 | let variant = v.variant(); 26 | // Print the enum name 27 | println!("{}{}::{}:", self.0, def.name(), variant.name()); 28 | 29 | // Visit fields 30 | let mut visit = self.indent(); 31 | v.visit(&mut visit); 32 | } 33 | Value::Listable(v) => { 34 | println!("{}", self.0); 35 | 36 | // Visit fields 37 | let mut visit = self.indent(); 38 | v.visit(&mut visit); 39 | } 40 | Value::Mappable(v) => { 41 | println!("{}", self.0); 42 | 43 | // Visit fields 44 | let mut visit = self.indent(); 45 | v.visit(&mut visit); 46 | } 47 | // Primitive or unknown type, just render Debug 48 | v => println!("{:?}", v), 49 | } 50 | } 51 | 52 | fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 53 | for (field, value) in named_values { 54 | print!("{}- {}: ", self.0, field.name()); 55 | value.visit(self); 56 | } 57 | } 58 | 59 | fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 60 | for value in values { 61 | print!("{}- ", self.0); 62 | value.visit(self); 63 | } 64 | } 65 | 66 | fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { 67 | print!("{}- {:?}: ", self.0, key); 68 | value.visit(self); 69 | } 70 | } 71 | 72 | #[derive(Valuable)] 73 | struct Person { 74 | name: String, 75 | age: u32, 76 | addresses: Vec
, 77 | } 78 | 79 | #[derive(Valuable)] 80 | struct Address { 81 | street: String, 82 | city: String, 83 | zip: String, 84 | } 85 | 86 | fn main() { 87 | let person = Person { 88 | name: "Angela Ashton".to_string(), 89 | age: 31, 90 | addresses: vec![ 91 | Address { 92 | street: "123 1st Ave".to_string(), 93 | city: "Townsville".to_string(), 94 | zip: "12345".to_string(), 95 | }, 96 | Address { 97 | street: "555 Main St.".to_string(), 98 | city: "New Old Town".to_string(), 99 | zip: "55555".to_string(), 100 | }, 101 | ], 102 | }; 103 | 104 | let mut print = Print("".to_string()); 105 | valuable::visit(&person, &mut print); 106 | } 107 | -------------------------------------------------------------------------------- /valuable/no_atomic.rs: -------------------------------------------------------------------------------- 1 | // This file is @generated by no_atomic.sh. 2 | // It is not intended for manual editing. 3 | 4 | const NO_ATOMIC_CAS: &[&str] = &[ 5 | "armv4t-none-eabi", 6 | "armv5te-none-eabi", 7 | "avr-unknown-gnu-atmega328", 8 | "bpfeb-unknown-none", 9 | "bpfel-unknown-none", 10 | "msp430-none-elf", 11 | "riscv32e-unknown-none-elf", 12 | "riscv32em-unknown-none-elf", 13 | "riscv32emc-unknown-none-elf", 14 | "riscv32i-unknown-none-elf", 15 | "riscv32im-unknown-none-elf", 16 | "riscv32imc-unknown-none-elf", 17 | "thumbv4t-none-eabi", 18 | "thumbv5te-none-eabi", 19 | "thumbv6m-none-eabi", 20 | "xtensa-esp32s2-none-elf", 21 | ]; 22 | 23 | const NO_ATOMIC_64: &[&str] = &[ 24 | "arm-linux-androideabi", 25 | "armv4t-none-eabi", 26 | "armv4t-unknown-linux-gnueabi", 27 | "armv5te-none-eabi", 28 | "armv5te-unknown-linux-gnueabi", 29 | "armv5te-unknown-linux-musleabi", 30 | "armv5te-unknown-linux-uclibceabi", 31 | "armv6k-nintendo-3ds", 32 | "csky-unknown-linux-gnuabiv2", 33 | "csky-unknown-linux-gnuabiv2hf", 34 | "hexagon-unknown-linux-musl", 35 | "hexagon-unknown-none-elf", 36 | "m68k-unknown-linux-gnu", 37 | "m68k-unknown-none-elf", 38 | "mips-mti-none-elf", 39 | "mips-unknown-linux-gnu", 40 | "mips-unknown-linux-musl", 41 | "mips-unknown-linux-uclibc", 42 | "mipsel-mti-none-elf", 43 | "mipsel-sony-psp", 44 | "mipsel-unknown-linux-gnu", 45 | "mipsel-unknown-linux-musl", 46 | "mipsel-unknown-linux-uclibc", 47 | "mipsel-unknown-netbsd", 48 | "mipsel-unknown-none", 49 | "mipsisa32r6-unknown-linux-gnu", 50 | "mipsisa32r6el-unknown-linux-gnu", 51 | "powerpc-unknown-freebsd", 52 | "powerpc-unknown-linux-gnu", 53 | "powerpc-unknown-linux-gnuspe", 54 | "powerpc-unknown-linux-musl", 55 | "powerpc-unknown-linux-muslspe", 56 | "powerpc-unknown-netbsd", 57 | "powerpc-unknown-openbsd", 58 | "powerpc-wrs-vxworks", 59 | "powerpc-wrs-vxworks-spe", 60 | "riscv32-wrs-vxworks", 61 | "riscv32e-unknown-none-elf", 62 | "riscv32em-unknown-none-elf", 63 | "riscv32emc-unknown-none-elf", 64 | "riscv32gc-unknown-linux-gnu", 65 | "riscv32gc-unknown-linux-musl", 66 | "riscv32i-unknown-none-elf", 67 | "riscv32im-unknown-none-elf", 68 | "riscv32ima-unknown-none-elf", 69 | "riscv32imac-esp-espidf", 70 | "riscv32imac-unknown-none-elf", 71 | "riscv32imac-unknown-nuttx-elf", 72 | "riscv32imac-unknown-xous-elf", 73 | "riscv32imafc-esp-espidf", 74 | "riscv32imafc-unknown-none-elf", 75 | "riscv32imafc-unknown-nuttx-elf", 76 | "riscv32imc-esp-espidf", 77 | "riscv32imc-unknown-none-elf", 78 | "riscv32imc-unknown-nuttx-elf", 79 | "sparc-unknown-linux-gnu", 80 | "sparc-unknown-none-elf", 81 | "thumbv4t-none-eabi", 82 | "thumbv5te-none-eabi", 83 | "thumbv6m-none-eabi", 84 | "thumbv6m-nuttx-eabi", 85 | "thumbv7em-none-eabi", 86 | "thumbv7em-none-eabihf", 87 | "thumbv7em-nuttx-eabi", 88 | "thumbv7em-nuttx-eabihf", 89 | "thumbv7m-none-eabi", 90 | "thumbv7m-nuttx-eabi", 91 | "thumbv8m.base-none-eabi", 92 | "thumbv8m.base-nuttx-eabi", 93 | "thumbv8m.main-none-eabi", 94 | "thumbv8m.main-none-eabihf", 95 | "thumbv8m.main-nuttx-eabi", 96 | "thumbv8m.main-nuttx-eabihf", 97 | "xtensa-esp32-espidf", 98 | "xtensa-esp32-none-elf", 99 | "xtensa-esp32s2-espidf", 100 | "xtensa-esp32s2-none-elf", 101 | "xtensa-esp32s3-espidf", 102 | "xtensa-esp32s3-none-elf", 103 | ]; 104 | 105 | const NO_ATOMIC: &[&str] = &[ 106 | "avr-unknown-gnu-atmega328", 107 | "bpfeb-unknown-none", 108 | "bpfel-unknown-none", 109 | "mipsel-sony-psx", 110 | "msp430-none-elf", 111 | ]; 112 | -------------------------------------------------------------------------------- /valuable/src/field.rs: -------------------------------------------------------------------------------- 1 | /// Data stored within a `Structable` or an `Enumerable`. 2 | #[derive(Debug)] 3 | pub enum Fields<'a> { 4 | /// Named fields 5 | Named(&'a [NamedField<'a>]), 6 | 7 | /// Unnamed (positional) fields or unit 8 | /// 9 | /// The `usize` value represents the number of fields. 10 | Unnamed(usize), 11 | } 12 | 13 | /// A named field 14 | #[derive(Debug, Clone, Copy)] 15 | pub struct NamedField<'a>(&'a str); 16 | 17 | impl Fields<'_> { 18 | /// Returns `true` if the fields are named. 19 | /// 20 | /// # Examples 21 | /// 22 | /// Named fields 23 | /// 24 | /// ``` 25 | /// use valuable::Fields; 26 | /// 27 | /// let fields = Fields::Named(&[]); 28 | /// assert!(fields.is_named()); 29 | /// ``` 30 | /// 31 | /// Unnamed fields 32 | /// 33 | /// ``` 34 | /// use valuable::Fields; 35 | /// 36 | /// let fields = Fields::Unnamed(2); 37 | /// assert!(!fields.is_named()); 38 | /// ``` 39 | pub const fn is_named(&self) -> bool { 40 | matches!(self, Fields::Named(..)) 41 | } 42 | 43 | /// Returns `true` if the fields are unnamed. 44 | /// 45 | /// # Examples 46 | /// 47 | /// Named fields 48 | /// 49 | /// ``` 50 | /// use valuable::Fields; 51 | /// 52 | /// let fields = Fields::Named(&[]); 53 | /// assert!(!fields.is_unnamed()); 54 | /// ``` 55 | /// 56 | /// Unnamed fields 57 | /// 58 | /// ``` 59 | /// use valuable::Fields; 60 | /// 61 | /// let fields = Fields::Unnamed(3); 62 | /// assert!(fields.is_unnamed()); 63 | /// ``` 64 | pub const fn is_unnamed(&self) -> bool { 65 | matches!(self, Fields::Unnamed(_)) 66 | } 67 | 68 | /// Returns the number of fields. 69 | /// 70 | /// # Examples 71 | /// 72 | /// Named fields 73 | /// 74 | /// ``` 75 | /// use valuable::{Fields, NamedField}; 76 | /// 77 | /// let fields = &[ 78 | /// NamedField::new("alice"), 79 | /// NamedField::new("bob"), 80 | /// ]; 81 | /// let fields = Fields::Named(fields); 82 | /// 83 | /// assert_eq!(fields.len(), 2); 84 | /// ``` 85 | /// 86 | /// Unnamed fields 87 | /// 88 | /// ``` 89 | /// use valuable::Fields; 90 | /// 91 | /// let fields = Fields::Unnamed(2); 92 | /// assert_eq!(fields.len(), 2); 93 | /// ``` 94 | pub const fn len(&self) -> usize { 95 | match self { 96 | Self::Named(names) => names.len(), 97 | Self::Unnamed(len) => *len, 98 | } 99 | } 100 | 101 | /// Returns `true` if this set of fields defines no fields. 102 | /// 103 | /// # Examples 104 | /// 105 | /// Named fields 106 | /// 107 | /// ``` 108 | /// use valuable::{Fields, NamedField}; 109 | /// 110 | /// let fields = &[ 111 | /// NamedField::new("alice"), 112 | /// NamedField::new("bob"), 113 | /// ]; 114 | /// let non_empty = Fields::Named(fields); 115 | /// 116 | /// let empty = Fields::Named(&[]); 117 | /// 118 | /// assert!(!non_empty.is_empty()); 119 | /// assert!(empty.is_empty()); 120 | /// ``` 121 | /// 122 | /// Unnamed fields 123 | /// 124 | /// ``` 125 | /// use valuable::Fields; 126 | /// 127 | /// let non_empty = Fields::Unnamed(2); 128 | /// let empty = Fields::Unnamed(0); 129 | /// 130 | /// assert!(!non_empty.is_empty()); 131 | /// assert!(empty.is_empty()); 132 | /// ``` 133 | pub const fn is_empty(&self) -> bool { 134 | self.len() == 0 135 | } 136 | } 137 | 138 | impl<'a> NamedField<'a> { 139 | /// Create a new `NamedField` instance with the given name. 140 | /// 141 | /// # Examples 142 | /// 143 | /// ``` 144 | /// use valuable::NamedField; 145 | /// 146 | /// let field = NamedField::new("hello"); 147 | /// assert_eq!("hello", field.name()); 148 | /// ``` 149 | pub const fn new(name: &'a str) -> NamedField<'a> { 150 | NamedField(name) 151 | } 152 | 153 | /// Returns the field name 154 | /// 155 | /// # Examples 156 | /// 157 | /// ``` 158 | /// use valuable::NamedField; 159 | /// 160 | /// let field = NamedField::new("hello"); 161 | /// assert_eq!("hello", field.name()); 162 | /// ``` 163 | pub const fn name(&self) -> &str { 164 | self.0 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /valuable/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn( 2 | missing_debug_implementations, 3 | missing_docs, 4 | rust_2018_idioms, 5 | unreachable_pub 6 | )] 7 | //! Valuable provides object-safe value inspection. Use cases include passing 8 | //! structured data to trait objects and object-safe serialization. 9 | //! 10 | //! # Getting started 11 | //! 12 | //! First, derive [`Valuable`][macro@crate::Valuable] on your types. 13 | //! 14 | //! ``` 15 | //! use valuable::Valuable; 16 | //! 17 | //! #[derive(Valuable)] 18 | //! struct HelloWorld { 19 | //! message: Message, 20 | //! } 21 | //! 22 | //! #[derive(Valuable)] 23 | //! enum Message { 24 | //! HelloWorld, 25 | //! Custom(String), 26 | //! } 27 | //! ``` 28 | //! 29 | //! Then, implement a [visitor][Visit] to inspect the data. 30 | //! 31 | //! ``` 32 | //! use valuable::{NamedValues, Value, Valuable, Visit}; 33 | //! 34 | //! struct Print; 35 | //! 36 | //! impl Visit for Print { 37 | //! fn visit_value(&mut self, value: Value<'_>) { 38 | //! match value { 39 | //! Value::Structable(v) => { 40 | //! println!("struct {}", v.definition().name()); 41 | //! v.visit(self); 42 | //! } 43 | //! Value::Enumerable(v) => { 44 | //! println!("enum {}::{}", v.definition().name(), v.variant().name()); 45 | //! v.visit(self); 46 | //! } 47 | //! Value::Listable(v) => { 48 | //! println!("list"); 49 | //! v.visit(self); 50 | //! } 51 | //! Value::Mappable(v) => { 52 | //! println!("map"); 53 | //! v.visit(self); 54 | //! } 55 | //! _ => { 56 | //! println!("value {:?}", value); 57 | //! } 58 | //! } 59 | //! } 60 | //! 61 | //! fn visit_named_fields(&mut self, named_fields: &NamedValues<'_>) { 62 | //! for (field, value) in named_fields.iter() { 63 | //! println!("named field {}", field.name()); 64 | //! value.visit(self); 65 | //! } 66 | //! } 67 | //! 68 | //! fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 69 | //! for value in values { 70 | //! value.visit(self); 71 | //! } 72 | //! } 73 | //! 74 | //! fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { 75 | //! println!("key / value"); 76 | //! key.visit(self); 77 | //! value.visit(self); 78 | //! } 79 | //! } 80 | //! ``` 81 | //! 82 | //! Then, use the visitor to visit the value. 83 | //! 84 | //! ``` 85 | //! # use valuable::*; 86 | //! # #[derive(Valuable)] 87 | //! # struct HelloWorld { message: Message } 88 | //! # #[derive(Valuable)] 89 | //! # enum Message { HelloWorld } 90 | //! # struct Print; 91 | //! # impl Visit for Print { 92 | //! # fn visit_value(&mut self, _: Value<'_>) {} 93 | //! # } 94 | //! let hello_world = HelloWorld { message: Message::HelloWorld }; 95 | //! hello_world.visit(&mut Print); 96 | //! ``` 97 | //! 98 | //! # Related Crates 99 | //! 100 | //! - [`valuable-serde`] provides a bridge between `valuable` and the [`serde`] 101 | //! serialization ecosystem. Using [`valuable_serde::Serializable`] allows any 102 | //! type that implements [`Valuable`] to be serialized by any 103 | //! [`serde::ser::Serializer`]. 104 | //! 105 | //! [`valuable-serde`]: https://crates.io/crates/valuable-serde 106 | //! [`serde`]: https://crates.io/crates/serde 107 | //! [`valuable_serde::Serializable`]: https://docs.rs/valuable-serde/latest/valuable_serde/struct.Serializable.html 108 | //! [`serde::ser::Serializer`]: https://docs.rs/serde/latest/serde/ser/trait.Serializer.html 109 | #![cfg_attr(not(feature = "std"), no_std)] 110 | #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg, doc_cfg_hide))] 111 | #![cfg_attr( 112 | docsrs, 113 | doc(cfg_hide( 114 | not(valuable_no_atomic_cas), 115 | not(valuable_no_atomic), 116 | not(valuable_no_atomic_64) 117 | )) 118 | )] 119 | 120 | #[cfg(feature = "alloc")] 121 | extern crate alloc; 122 | 123 | mod enumerable; 124 | pub use enumerable::{EnumDef, Enumerable, Variant, VariantDef}; 125 | 126 | mod field; 127 | pub use field::{Fields, NamedField}; 128 | 129 | mod listable; 130 | pub use listable::Listable; 131 | 132 | mod mappable; 133 | pub use mappable::Mappable; 134 | 135 | mod named_values; 136 | pub use named_values::NamedValues; 137 | 138 | mod slice; 139 | pub use slice::Slice; 140 | 141 | mod structable; 142 | pub use structable::{StructDef, Structable}; 143 | 144 | mod tuplable; 145 | pub use tuplable::{Tuplable, TupleDef}; 146 | 147 | mod valuable; 148 | pub use crate::valuable::Valuable; 149 | 150 | mod value; 151 | pub use value::Value; 152 | 153 | mod visit; 154 | pub use visit::{visit, Visit}; 155 | 156 | #[cfg(feature = "derive")] 157 | pub use valuable_derive::Valuable; 158 | -------------------------------------------------------------------------------- /valuable/src/listable.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | use core::fmt; 4 | 5 | /// A list-like [`Valuable`] sub-type. 6 | /// 7 | /// Implemented by [`Valuable`] types that have a list-like shape. This includes 8 | /// [`Vec`] and other Rust [collection] types. `Listable` types may or may not 9 | /// store items in contiguous memory. Any type that implements [`IntoIterator`] 10 | /// may implement `Listable`. Values that implement `Listable` must return 11 | /// [`Value::Listable`] from their [`Valuable::as_value`] implementation. 12 | /// 13 | /// [collection]: https://doc.rust-lang.org/stable/std/collections/index.html 14 | /// 15 | /// # Inspecting 16 | /// 17 | /// Inspecting `Listable` items is done by visiting the collection. When 18 | /// visiting a `Listable`, contained values are either passed one-by-one by 19 | /// repeatedly calling [`visit_value()`] or all at once by calling 20 | /// [`visit_primitive_slice()`]. The [`visit_primitive_slice()`] method has 21 | /// lower overhead but can only be used when the `Listable` type contains 22 | /// primitive values. 23 | /// 24 | /// See [`Visit`] documentation for more details. 25 | /// 26 | /// # Implementing 27 | /// 28 | /// If the type stores values in slices internally, then those slices are passed 29 | /// to [`Valuable::visit_slice`], which handles calling 30 | /// [`visit_primitive_slice()`] if possible. 31 | /// 32 | /// [`visit_value()`]: Visit::visit_value 33 | /// [`visit_primitive_slice()`]: Visit::visit_primitive_slice 34 | /// 35 | /// ``` 36 | /// use valuable::{Listable, Valuable, Value, Visit}; 37 | /// 38 | /// struct MyCollection { 39 | /// chunks: Vec>, 40 | /// } 41 | /// 42 | /// impl Valuable for MyCollection { 43 | /// fn as_value(&self) -> Value<'_> { 44 | /// Value::Listable(self) 45 | /// } 46 | /// 47 | /// fn visit(&self, visit: &mut dyn Visit) { 48 | /// for chunk in &self.chunks { 49 | /// // Handles visiting the slice 50 | /// Valuable::visit_slice(chunk, visit); 51 | /// } 52 | /// } 53 | /// } 54 | /// 55 | /// impl Listable for MyCollection { 56 | /// fn size_hint(&self) -> (usize, Option) { 57 | /// let len = self.chunks.iter().map(|chunk| chunk.len()).sum(); 58 | /// (len, Some(len)) 59 | /// } 60 | /// } 61 | /// ``` 62 | pub trait Listable: Valuable { 63 | /// Returns the bounds on the remaining length of the `Listable`. 64 | /// 65 | /// Specifically, `size_hint()` returns a tuple where the first element 66 | /// is the lower bound, and the second element is the upper bound. 67 | /// 68 | /// The second half of the tuple that is returned is an [`Option`]`<`[`usize`]`>`. 69 | /// A [`None`] here means that either there is no known upper bound, or the 70 | /// upper bound is larger than [`usize`]. 71 | /// 72 | /// # Implementation notes 73 | /// 74 | /// It is not enforced that a `Listable` implementation yields the declared 75 | /// number of elements. A buggy iterator may yield less than the lower bound 76 | /// or more than the upper bound of elements. 77 | /// 78 | /// `size_hint()` is primarily intended to be used for optimizations such as 79 | /// reserving space for the elements of the `Listable`, but must not be 80 | /// trusted to e.g., omit bounds checks in unsafe code. An incorrect 81 | /// implementation of `size_hint()` should not lead to memory safety 82 | /// violations. 83 | /// 84 | /// That said, the implementation should provide a correct estimation, 85 | /// because otherwise it would be a violation of the trait's protocol. 86 | /// 87 | /// [`usize`]: type@usize 88 | /// 89 | /// # Examples 90 | /// 91 | /// Basic usage: 92 | /// 93 | /// ``` 94 | /// use valuable::Listable; 95 | /// 96 | /// let a = vec![1, 2, 3]; 97 | /// 98 | /// assert_eq!((3, Some(3)), a.size_hint()); 99 | /// ``` 100 | fn size_hint(&self) -> (usize, Option); 101 | } 102 | 103 | macro_rules! deref { 104 | ( 105 | $( 106 | $(#[$attrs:meta])* 107 | $ty:ty, 108 | )* 109 | ) => { 110 | $( 111 | $(#[$attrs])* 112 | impl Listable for $ty { 113 | fn size_hint(&self) -> (usize, Option) { 114 | T::size_hint(&**self) 115 | } 116 | } 117 | )* 118 | }; 119 | } 120 | 121 | deref! { 122 | &T, 123 | &mut T, 124 | #[cfg(feature = "alloc")] 125 | alloc::boxed::Box, 126 | #[cfg(feature = "alloc")] 127 | alloc::rc::Rc, 128 | #[cfg(not(valuable_no_atomic_cas))] 129 | #[cfg(feature = "alloc")] 130 | alloc::sync::Arc, 131 | } 132 | 133 | macro_rules! slice { 134 | ( 135 | $( 136 | $(#[$attrs:meta])* 137 | ($($generics:tt)*) $ty:ty, 138 | )* 139 | ) => { 140 | $( 141 | $(#[$attrs])* 142 | impl<$($generics)*> Valuable for $ty { 143 | fn as_value(&self) -> Value<'_> { 144 | Value::Listable(self as &dyn Listable) 145 | } 146 | 147 | fn visit(&self, visit: &mut dyn Visit) { 148 | T::visit_slice(self, visit); 149 | } 150 | } 151 | 152 | $(#[$attrs])* 153 | impl<$($generics)*> Listable for $ty { 154 | fn size_hint(&self) -> (usize, Option) { 155 | (self.len(), Some(self.len())) 156 | } 157 | } 158 | )* 159 | }; 160 | } 161 | 162 | slice! { 163 | (T: Valuable) &'_ [T], 164 | #[cfg(feature = "alloc")] 165 | (T: Valuable) alloc::boxed::Box<[T]>, 166 | #[cfg(feature = "alloc")] 167 | (T: Valuable) alloc::rc::Rc<[T]>, 168 | #[cfg(not(valuable_no_atomic_cas))] 169 | #[cfg(feature = "alloc")] 170 | (T: Valuable) alloc::sync::Arc<[T]>, 171 | (T: Valuable, const N: usize) [T; N], 172 | #[cfg(feature = "alloc")] 173 | (T: Valuable) alloc::vec::Vec, 174 | } 175 | 176 | macro_rules! collection { 177 | ( 178 | $( 179 | $(#[$attrs:meta])* 180 | ($($generics:tt)*) $ty:ty, 181 | )* 182 | ) => { 183 | $( 184 | $(#[$attrs])* 185 | impl<$($generics)*> Valuable for $ty { 186 | fn as_value(&self) -> Value<'_> { 187 | Value::Listable(self as &dyn Listable) 188 | } 189 | 190 | fn visit(&self, visit: &mut dyn Visit) { 191 | for value in self.iter() { 192 | visit.visit_value(value.as_value()); 193 | } 194 | } 195 | } 196 | 197 | $(#[$attrs])* 198 | impl<$($generics)*> Listable for $ty { 199 | fn size_hint(&self) -> (usize, Option) { 200 | (self.len(), Some(self.len())) 201 | } 202 | } 203 | )* 204 | }; 205 | } 206 | 207 | collection! { 208 | #[cfg(feature = "alloc")] 209 | (T: Valuable) alloc::collections::LinkedList, 210 | #[cfg(feature = "alloc")] 211 | (T: Valuable + Ord) alloc::collections::BinaryHeap, 212 | #[cfg(feature = "alloc")] 213 | (T: Valuable + Ord) alloc::collections::BTreeSet, 214 | #[cfg(feature = "std")] 215 | (T: Valuable + Eq + std::hash::Hash, H: std::hash::BuildHasher) std::collections::HashSet, 216 | } 217 | 218 | #[cfg(feature = "alloc")] 219 | impl Valuable for alloc::collections::VecDeque { 220 | fn as_value(&self) -> Value<'_> { 221 | Value::Listable(self as &dyn Listable) 222 | } 223 | 224 | fn visit(&self, visit: &mut dyn Visit) { 225 | let (first, second) = self.as_slices(); 226 | T::visit_slice(first, visit); 227 | T::visit_slice(second, visit); 228 | } 229 | } 230 | 231 | #[cfg(feature = "alloc")] 232 | impl Listable for alloc::collections::VecDeque { 233 | fn size_hint(&self) -> (usize, Option) { 234 | (self.len(), Some(self.len())) 235 | } 236 | } 237 | 238 | impl fmt::Debug for dyn Listable + '_ { 239 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 240 | struct DebugListable<'a, 'b> { 241 | fmt: fmt::DebugList<'a, 'b>, 242 | } 243 | 244 | impl Visit for DebugListable<'_, '_> { 245 | fn visit_value(&mut self, value: Value<'_>) { 246 | self.fmt.entry(&value); 247 | } 248 | } 249 | 250 | let mut debug = DebugListable { 251 | fmt: fmt.debug_list(), 252 | }; 253 | 254 | self.visit(&mut debug); 255 | debug.fmt.finish() 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /valuable/src/mappable.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | use core::fmt; 4 | 5 | /// A map-like [`Valuable`] sub-type. 6 | /// 7 | /// Implemented by [`Valuable`] types that have a map-like shape. This includes 8 | /// [`HashMap`] and other Rust [collection] types. Values that implement 9 | /// `Mappable` must return [`Value::Mappable`] from their [`Value::as_value`] 10 | /// implementation. 11 | /// 12 | /// [collection]: https://doc.rust-lang.org/stable/std/collections/index.html 13 | /// 14 | /// # Inspecting 15 | /// 16 | /// Inspecting `Mappable` entries is done by visiting the value. When visiting a 17 | /// `Mappable`, contained entries are passed one-by-one to the visitor by 18 | /// repeatedly calling [`visit_entry()`]. 19 | /// 20 | /// See [`Visit`] documentation for more details. 21 | /// 22 | /// [`visit_entry()`]: Visit::visit_entry 23 | /// [`HashMap`]: std::collections::HashMap 24 | /// 25 | /// # Implementing 26 | /// 27 | /// Implementing `Mappable` for a custom map type. The map is represented using 28 | /// a `Vec` of key/value pairs. 29 | /// 30 | /// ``` 31 | /// use valuable::{Mappable, Valuable, Value, Visit}; 32 | /// 33 | /// struct MyMap { 34 | /// entries: Vec<(K, V)>, 35 | /// } 36 | /// 37 | /// impl Valuable for MyMap { 38 | /// fn as_value(&self) -> Value<'_> { 39 | /// Value::Mappable(self) 40 | /// } 41 | /// 42 | /// fn visit(&self, visit: &mut dyn Visit) { 43 | /// for (k, v) in &self.entries { 44 | /// visit.visit_entry(k.as_value(), v.as_value()); 45 | /// } 46 | /// } 47 | /// } 48 | /// 49 | /// impl Mappable for MyMap { 50 | /// fn size_hint(&self) -> (usize, Option) { 51 | /// let len = self.entries.len(); 52 | /// (len, Some(len)) 53 | /// } 54 | /// } 55 | /// ``` 56 | pub trait Mappable: Valuable { 57 | /// Returns the bounds on the remaining length of the `Mappable`. 58 | /// 59 | /// Specifically, `size_hint()` returns a tuple where the first element is 60 | /// the lower bound, and the second element is the upper bound. 61 | /// 62 | /// The second half of the tuple that is returned is an 63 | /// [`Option`]`<`[`usize`]`>`. A [`None`] here means that either there is no 64 | /// known upper bound, or the upper bound is larger than [`usize`]. 65 | /// 66 | /// # Implementation notes 67 | /// 68 | /// It is not enforced that a `Mappable` implementation yields the declared 69 | /// number of elements. A buggy implementation may yield less than the lower 70 | /// bound or more than the upper bound of elements. 71 | /// 72 | /// `size_hint()` is primarily intended to be used for optimizations such as 73 | /// reserving space for the elements of the `Mappable`, but must not be 74 | /// trusted to e.g., omit bounds checks in unsafe code. An incorrect 75 | /// implementation of `size_hint()` should not lead to memory safety 76 | /// violations. 77 | /// 78 | /// That said, the implementation should provide a correct estimation, 79 | /// because otherwise it would be a violation of the trait's protocol. 80 | /// 81 | /// [`usize`]: type@usize 82 | /// 83 | /// # Examples 84 | /// 85 | /// Basic usage: 86 | /// 87 | /// ``` 88 | /// use valuable::Mappable; 89 | /// use std::collections::HashMap; 90 | /// 91 | /// let mut map = HashMap::new(); 92 | /// map.insert("one", 1); 93 | /// map.insert("two", 2); 94 | /// map.insert("three", 3); 95 | /// 96 | /// assert_eq!((3, Some(3)), map.size_hint()); 97 | /// ``` 98 | fn size_hint(&self) -> (usize, Option); 99 | } 100 | 101 | macro_rules! deref { 102 | ( 103 | $( 104 | $(#[$attrs:meta])* 105 | $ty:ty, 106 | )* 107 | ) => { 108 | $( 109 | $(#[$attrs])* 110 | impl Mappable for $ty { 111 | fn size_hint(&self) -> (usize, Option) { 112 | T::size_hint(&**self) 113 | } 114 | } 115 | )* 116 | }; 117 | } 118 | 119 | deref! { 120 | &T, 121 | &mut T, 122 | #[cfg(feature = "alloc")] 123 | alloc::boxed::Box, 124 | #[cfg(feature = "alloc")] 125 | alloc::rc::Rc, 126 | #[cfg(not(valuable_no_atomic_cas))] 127 | #[cfg(feature = "alloc")] 128 | alloc::sync::Arc, 129 | } 130 | 131 | #[cfg(feature = "std")] 132 | impl Valuable for std::collections::HashMap { 133 | fn as_value(&self) -> Value<'_> { 134 | Value::Mappable(self) 135 | } 136 | 137 | fn visit(&self, visit: &mut dyn Visit) { 138 | for (key, value) in self.iter() { 139 | visit.visit_entry(key.as_value(), value.as_value()); 140 | } 141 | } 142 | } 143 | 144 | #[cfg(feature = "std")] 145 | impl Mappable for std::collections::HashMap { 146 | fn size_hint(&self) -> (usize, Option) { 147 | self.iter().size_hint() 148 | } 149 | } 150 | 151 | #[cfg(feature = "alloc")] 152 | impl Valuable for alloc::collections::BTreeMap { 153 | fn as_value(&self) -> Value<'_> { 154 | Value::Mappable(self) 155 | } 156 | 157 | fn visit(&self, visit: &mut dyn Visit) { 158 | for (key, value) in self.iter() { 159 | visit.visit_entry(key.as_value(), value.as_value()); 160 | } 161 | } 162 | } 163 | 164 | #[cfg(feature = "alloc")] 165 | impl Mappable for alloc::collections::BTreeMap { 166 | fn size_hint(&self) -> (usize, Option) { 167 | self.iter().size_hint() 168 | } 169 | } 170 | 171 | impl fmt::Debug for dyn Mappable + '_ { 172 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 173 | struct DebugMappable<'a, 'b> { 174 | fmt: fmt::DebugMap<'a, 'b>, 175 | } 176 | 177 | impl Visit for DebugMappable<'_, '_> { 178 | fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { 179 | self.fmt.entry(&key, &value); 180 | } 181 | 182 | fn visit_value(&mut self, _: Value<'_>) {} 183 | } 184 | 185 | let mut debug = DebugMappable { 186 | fmt: fmt.debug_map(), 187 | }; 188 | self.visit(&mut debug); 189 | debug.fmt.finish() 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /valuable/src/named_values.rs: -------------------------------------------------------------------------------- 1 | use core::iter::{self, FusedIterator}; 2 | 3 | use crate::field::*; 4 | use crate::*; 5 | 6 | /// Set of values from a `Structable` or `Enumerable` with named fields. 7 | #[derive(Debug)] 8 | pub struct NamedValues<'a> { 9 | fields: &'a [NamedField<'a>], 10 | values: &'a [Value<'a>], 11 | } 12 | 13 | impl<'a> NamedValues<'a> { 14 | /// Create a new `NamedValues` instance. 15 | /// 16 | /// Both `fields` and `values` must be the same length. 17 | /// 18 | /// # Panics 19 | /// 20 | /// The method panics if `fields` and `values` are different lengths. 21 | /// 22 | /// # Examples 23 | /// 24 | /// ``` 25 | /// use valuable::{NamedField, NamedValues, Value}; 26 | /// 27 | /// let fields = [ 28 | /// NamedField::new("foo"), 29 | /// NamedField::new("bar") 30 | /// ]; 31 | /// let values = [ 32 | /// Value::U32(123), 33 | /// Value::U32(456), 34 | /// ]; 35 | /// 36 | /// let named_values = NamedValues::new(&fields, &values); 37 | /// 38 | /// assert_eq!( 39 | /// named_values.get(&fields[0]).unwrap().as_u32(), 40 | /// Some(123)); 41 | /// ``` 42 | pub fn new(fields: &'a [NamedField<'a>], values: &'a [Value<'a>]) -> NamedValues<'a> { 43 | assert!( 44 | fields.len() == values.len(), 45 | "`fields` and `values` must be the same length" 46 | ); 47 | NamedValues { fields, values } 48 | } 49 | 50 | /// Get a value using a `NamedField` reference. 51 | /// 52 | /// # Examples 53 | /// 54 | /// ``` 55 | /// use valuable::{NamedField, NamedValues, Value}; 56 | /// 57 | /// let fields = [ 58 | /// NamedField::new("foo"), 59 | /// NamedField::new("bar") 60 | /// ]; 61 | /// let values = [ 62 | /// Value::U32(123), 63 | /// Value::U32(456), 64 | /// ]; 65 | /// 66 | /// let named_values = NamedValues::new(&fields, &values); 67 | /// 68 | /// assert_eq!( 69 | /// named_values.get(&fields[0]).unwrap().as_u32(), 70 | /// Some(123)); 71 | /// ``` 72 | pub fn get(&self, field: &NamedField<'_>) -> Option<&Value<'_>> { 73 | use core::mem; 74 | 75 | let idx = (field as *const _ as usize - &self.fields[0] as *const _ as usize) 76 | / mem::size_of::>(); 77 | self.values.get(idx) 78 | } 79 | 80 | /// Get a value using string. 81 | /// 82 | /// # Examples 83 | /// 84 | /// ``` 85 | /// use valuable::{NamedField, NamedValues, Value}; 86 | /// 87 | /// let fields = [ 88 | /// NamedField::new("foo"), 89 | /// NamedField::new("bar") 90 | /// ]; 91 | /// let values = [ 92 | /// Value::U32(123), 93 | /// Value::U32(456), 94 | /// ]; 95 | /// 96 | /// let named_values = NamedValues::new(&fields, &values); 97 | /// 98 | /// assert_eq!( 99 | /// named_values.get_by_name("foo").unwrap().as_u32(), 100 | /// Some(123)); 101 | /// ``` 102 | pub fn get_by_name(&self, name: impl AsRef) -> Option<&Value<'_>> { 103 | let name = name.as_ref(); 104 | 105 | for (index, field) in self.fields.iter().enumerate() { 106 | if field.name() == name { 107 | return Some(&self.values[index]); 108 | } 109 | } 110 | 111 | None 112 | } 113 | 114 | /// Iterate all name-value pairs. 115 | /// 116 | /// # Examples 117 | /// 118 | /// ``` 119 | /// use valuable::{NamedField, NamedValues, Value}; 120 | /// 121 | /// let fields = [ 122 | /// NamedField::new("foo"), 123 | /// NamedField::new("bar") 124 | /// ]; 125 | /// let values = [ 126 | /// Value::U32(123), 127 | /// Value::U32(456), 128 | /// ]; 129 | /// 130 | /// let named_values = NamedValues::new(&fields, &values); 131 | /// 132 | /// for (field, value) in named_values.iter() { 133 | /// println!("{:?}: {:?}", field, value); 134 | /// } 135 | /// ``` 136 | pub fn iter<'b>(&'b self) -> Iter<'a, 'b> { 137 | Iter { 138 | iter: self.fields.iter().enumerate(), 139 | values: self.values, 140 | } 141 | } 142 | 143 | /// Returns the length of fields. 144 | pub fn len(&self) -> usize { 145 | self.fields.len() 146 | } 147 | 148 | /// Returns `true` if fields have a length of 0. 149 | pub fn is_empty(&self) -> bool { 150 | self.fields.is_empty() 151 | } 152 | } 153 | 154 | impl<'a, 'b> IntoIterator for &'b NamedValues<'a> { 155 | type Item = (&'b NamedField<'a>, &'b Value<'a>); 156 | type IntoIter = Iter<'a, 'b>; 157 | 158 | fn into_iter(self) -> Self::IntoIter { 159 | self.iter() 160 | } 161 | } 162 | 163 | /// An iterator of name-value pairs contained by [`NamedValues`]. 164 | /// 165 | /// Instances are created by the [`iter()`][NamedValues::iter] method on 166 | /// [`NamedValues`]. See its documentation for more. 167 | /// 168 | /// # Examples 169 | /// 170 | /// ``` 171 | /// use valuable::{NamedField, NamedValues, Value}; 172 | /// 173 | /// let fields = [ 174 | /// NamedField::new("foo"), 175 | /// NamedField::new("bar") 176 | /// ]; 177 | /// let values = [ 178 | /// Value::U32(123), 179 | /// Value::U32(456), 180 | /// ]; 181 | /// 182 | /// let named_values = NamedValues::new(&fields, &values); 183 | /// 184 | /// for (field, value) in named_values.iter() { 185 | /// println!("{:?}: {:?}", field, value); 186 | /// } 187 | /// ``` 188 | #[derive(Debug)] 189 | pub struct Iter<'a, 'b> { 190 | iter: iter::Enumerate>>, 191 | values: &'a [Value<'a>], 192 | } 193 | 194 | impl<'a, 'b> Iterator for Iter<'a, 'b> { 195 | type Item = (&'b NamedField<'a>, &'b Value<'a>); 196 | 197 | fn next(&mut self) -> Option { 198 | self.iter 199 | .next() 200 | .map(move |(i, field)| (field, &self.values[i])) 201 | } 202 | 203 | fn size_hint(&self) -> (usize, Option) { 204 | self.iter.size_hint() 205 | } 206 | } 207 | 208 | impl DoubleEndedIterator for Iter<'_, '_> { 209 | fn next_back(&mut self) -> Option { 210 | self.iter 211 | .next_back() 212 | .map(move |(i, field)| (field, &self.values[i])) 213 | } 214 | } 215 | 216 | impl ExactSizeIterator for Iter<'_, '_> { 217 | fn len(&self) -> usize { 218 | self.iter.len() 219 | } 220 | } 221 | 222 | impl FusedIterator for Iter<'_, '_> {} 223 | -------------------------------------------------------------------------------- /valuable/src/slice.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | use core::fmt; 4 | use core::iter::FusedIterator; 5 | 6 | macro_rules! slice { 7 | ( 8 | $( 9 | $(#[$attrs:meta])* 10 | $variant:ident($ty:ty), 11 | )* 12 | ) => { 13 | /// A slice containing primitive values. 14 | /// 15 | /// The `Slice` enum is used to pass multiple primitive-values to the 16 | /// [visitor][`Visit`]. This is used as an optimization when visiting 17 | /// [`Listable`] types to avoid a dynamic dispatch call to [`Visit`] for 18 | /// each element in the collection. 19 | /// 20 | /// `Slice` instances are usually not created explicitly. Instead, they 21 | /// are created when calling [`Valuable::visit_slice()`]. 22 | #[non_exhaustive] 23 | pub enum Slice<'a> { 24 | $( 25 | $(#[$attrs])* 26 | $variant(&'a [$ty]), 27 | )* 28 | } 29 | 30 | /// [`Slice`] iterator 31 | /// 32 | /// Instances are created by the [`iter()`][Slice::iter] method on 33 | /// [`Slice`]. See its documentation for more. 34 | /// 35 | /// # Examples 36 | /// 37 | /// ``` 38 | /// use valuable::Slice; 39 | /// 40 | /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); 41 | /// 42 | /// for value in slice.iter() { 43 | /// println!("{:?}", value); 44 | /// } 45 | /// ``` 46 | #[derive(Debug)] 47 | pub struct Iter<'a>(IterKind<'a>); 48 | 49 | #[derive(Debug)] 50 | enum IterKind<'a> { 51 | $( 52 | $(#[$attrs])* 53 | $variant(core::slice::Iter<'a, $ty>), 54 | )* 55 | } 56 | 57 | impl<'a> Slice<'a> { 58 | /// Returns the number of elements in the slice 59 | /// 60 | /// # Examples 61 | /// 62 | /// ``` 63 | /// use valuable::Slice; 64 | /// 65 | /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); 66 | /// assert_eq!(5, slice.len()); 67 | /// ``` 68 | pub fn len(&self) -> usize { 69 | #[allow(unused_doc_comments)] 70 | match self { 71 | $( 72 | $(#[$attrs])* 73 | Slice::$variant(s) => s.len(), 74 | )* 75 | } 76 | } 77 | 78 | 79 | /// Returns `true` if the slice is not empty. 80 | /// 81 | /// # Examples 82 | /// 83 | /// ``` 84 | /// use valuable::Slice; 85 | /// 86 | /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); 87 | /// assert!(!slice.is_empty()); 88 | /// ``` 89 | /// ``` 90 | /// # use valuable::Slice; 91 | /// let slice = Slice::U32(&[]); 92 | /// assert!(slice.is_empty()); 93 | /// ``` 94 | pub fn is_empty(&self) -> bool { 95 | self.len() == 0 96 | } 97 | 98 | /// Returns an iterator over the slice. 99 | /// 100 | /// # Examples 101 | /// 102 | /// ``` 103 | /// use valuable::Slice; 104 | /// 105 | /// let slice = Slice::U32(&[1, 1, 2, 3, 5]); 106 | /// 107 | /// for value in slice.iter() { 108 | /// println!("{:?}", value); 109 | /// } 110 | /// ``` 111 | pub fn iter(&self) -> Iter<'a> { 112 | self.into_iter() 113 | } 114 | } 115 | 116 | impl<'a> IntoIterator for Slice<'a> { 117 | type Item = Value<'a>; 118 | type IntoIter = Iter<'a>; 119 | 120 | fn into_iter(self) -> Self::IntoIter { 121 | (&self).into_iter() 122 | } 123 | } 124 | 125 | impl<'a> IntoIterator for &'_ Slice<'a> { 126 | type Item = Value<'a>; 127 | type IntoIter = Iter<'a>; 128 | 129 | fn into_iter(self) -> Self::IntoIter { 130 | #[allow(unused_doc_comments)] 131 | Iter(match self { 132 | $( 133 | $(#[$attrs])* 134 | Slice::$variant(s) => IterKind::$variant(s.iter()), 135 | )* 136 | }) 137 | } 138 | } 139 | 140 | impl fmt::Debug for Slice<'_> { 141 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 142 | use Slice::*; 143 | 144 | let mut d = fmt.debug_list(); 145 | 146 | #[allow(unused_doc_comments)] 147 | match *self { 148 | $( 149 | $(#[$attrs])* 150 | $variant(v) => d.entries(v), 151 | )* 152 | }; 153 | 154 | d.finish() 155 | } 156 | } 157 | 158 | impl<'a> Iterator for Iter<'a> { 159 | type Item = Value<'a>; 160 | 161 | fn size_hint(&self) -> (usize, Option) { 162 | use IterKind::*; 163 | 164 | #[allow(unused_doc_comments)] 165 | match &self.0 { 166 | $( 167 | $(#[$attrs])* 168 | $variant(v) => v.size_hint(), 169 | )* 170 | } 171 | } 172 | 173 | fn next(&mut self) -> Option> { 174 | use IterKind::*; 175 | 176 | #[allow(unused_doc_comments)] 177 | match &mut self.0 { 178 | $( 179 | $(#[$attrs])* 180 | $variant(v) => v.next().map(Valuable::as_value), 181 | )* 182 | } 183 | } 184 | } 185 | 186 | impl DoubleEndedIterator for Iter<'_> { 187 | fn next_back(&mut self) -> Option { 188 | use IterKind::*; 189 | 190 | #[allow(unused_doc_comments)] 191 | match &mut self.0 { 192 | $( 193 | $(#[$attrs])* 194 | $variant(v) => v.next_back().map(Valuable::as_value), 195 | )* 196 | } 197 | } 198 | } 199 | 200 | impl ExactSizeIterator for Iter<'_> { 201 | fn len(&self) -> usize { 202 | use IterKind::*; 203 | 204 | #[allow(unused_doc_comments)] 205 | match &self.0 { 206 | $( 207 | $(#[$attrs])* 208 | $variant(v) => v.len(), 209 | )* 210 | } 211 | } 212 | } 213 | 214 | impl FusedIterator for Iter<'_> {} 215 | } 216 | } 217 | 218 | slice! { 219 | /// A slice containing `bool` values. 220 | /// 221 | /// # Examples 222 | /// 223 | /// ``` 224 | /// use valuable::Slice; 225 | /// 226 | /// let v = Slice::Bool(&[true, true, false]); 227 | /// ``` 228 | Bool(bool), 229 | 230 | /// A slice containing `char` values. 231 | /// 232 | /// # Examples 233 | /// 234 | /// ``` 235 | /// use valuable::Slice; 236 | /// 237 | /// let v = Slice::Char(&['a', 'b', 'c']); 238 | /// ``` 239 | Char(char), 240 | 241 | /// A slice containing `f32` values. 242 | /// 243 | /// # Examples 244 | /// 245 | /// ``` 246 | /// use valuable::Slice; 247 | /// 248 | /// let v = Slice::F32(&[3.1415, 2.71828]); 249 | /// ``` 250 | F32(f32), 251 | 252 | /// A slice containing `f64` values. 253 | /// 254 | /// # Examples 255 | /// 256 | /// ``` 257 | /// use valuable::Slice; 258 | /// 259 | /// let v = Slice::F64(&[3.1415, 2.71828]); 260 | /// ``` 261 | F64(f64), 262 | 263 | /// A slice containing `i8` values. 264 | /// 265 | /// # Examples 266 | /// 267 | /// ``` 268 | /// use valuable::Slice; 269 | /// 270 | /// let v = Slice::I8(&[1, 1, 2, 3, 5]); 271 | /// ``` 272 | I8(i8), 273 | 274 | /// A slice containing `i16` values. 275 | /// 276 | /// # Examples 277 | /// 278 | /// ``` 279 | /// use valuable::Slice; 280 | /// 281 | /// let v = Slice::I16(&[1, 1, 2, 3, 5]); 282 | /// ``` 283 | I16(i16), 284 | 285 | /// A slice containing `I32` values. 286 | /// 287 | /// # Examples 288 | /// 289 | /// ``` 290 | /// use valuable::Slice; 291 | /// 292 | /// let v = Slice::I32(&[1, 1, 2, 3, 5]); 293 | /// ``` 294 | I32(i32), 295 | 296 | /// A slice containing `I64` values. 297 | /// 298 | /// # Examples 299 | /// 300 | /// ``` 301 | /// use valuable::Slice; 302 | /// 303 | /// let v = Slice::I64(&[1, 1, 2, 3, 5]); 304 | /// ``` 305 | I64(i64), 306 | 307 | /// A slice containing `I128` values. 308 | /// 309 | /// # Examples 310 | /// 311 | /// ``` 312 | /// use valuable::Slice; 313 | /// 314 | /// let v = Slice::I128(&[1, 1, 2, 3, 5]); 315 | /// ``` 316 | I128(i128), 317 | 318 | /// A slice containing `isize` values. 319 | /// 320 | /// # Examples 321 | /// 322 | /// ``` 323 | /// use valuable::Slice; 324 | /// 325 | /// let v = Slice::Isize(&[1, 1, 2, 3, 5]); 326 | /// ``` 327 | Isize(isize), 328 | 329 | /// A slice containing `str` values. 330 | /// 331 | /// # Examples 332 | /// 333 | /// ``` 334 | /// use valuable::Slice; 335 | /// 336 | /// let v = Slice::Str(&["foo", "bar", "baz"]); 337 | /// ``` 338 | Str(&'a str), 339 | 340 | /// A slice containing `String` values. 341 | /// 342 | /// # Examples 343 | /// 344 | /// ``` 345 | /// use valuable::Slice; 346 | /// 347 | /// let v = Slice::String(&["foo".to_string(), "bar".to_string()]); 348 | /// ``` 349 | #[cfg(feature = "alloc")] 350 | String(alloc::string::String), 351 | 352 | /// A slice containing `u8` values. 353 | /// 354 | /// # Examples 355 | /// 356 | /// ``` 357 | /// use valuable::Slice; 358 | /// 359 | /// let v = Slice::U8(&[1, 1, 2, 3, 5]); 360 | /// ``` 361 | U8(u8), 362 | 363 | /// A slice containing `u16` values. 364 | /// 365 | /// # Examples 366 | /// 367 | /// ``` 368 | /// use valuable::Slice; 369 | /// 370 | /// let v = Slice::U16(&[1, 1, 2, 3, 5]); 371 | /// ``` 372 | U16(u16), 373 | 374 | /// A slice containing `u32` values. 375 | /// 376 | /// # Examples 377 | /// 378 | /// ``` 379 | /// use valuable::Slice; 380 | /// 381 | /// let v = Slice::U32(&[1, 1, 2, 3, 5]); 382 | /// ``` 383 | U32(u32), 384 | 385 | /// A slice containing `u64` values. 386 | /// 387 | /// # Examples 388 | /// 389 | /// ``` 390 | /// use valuable::Slice; 391 | /// 392 | /// let v = Slice::U64(&[1, 1, 2, 3, 5]); 393 | /// ``` 394 | U64(u64), 395 | 396 | /// A slice containing `u128` values. 397 | /// 398 | /// # Examples 399 | /// 400 | /// ``` 401 | /// use valuable::Slice; 402 | /// 403 | /// let v = Slice::U128(&[1, 1, 2, 3, 5]); 404 | /// ``` 405 | U128(u128), 406 | 407 | /// A slice containing `usize` values. 408 | /// 409 | /// # Examples 410 | /// 411 | /// ``` 412 | /// use valuable::Slice; 413 | /// 414 | /// let v = Slice::Usize(&[1, 1, 2, 3, 5]); 415 | /// ``` 416 | Usize(usize), 417 | 418 | /// A slice containing `()` values. 419 | /// 420 | /// # Examples 421 | /// 422 | /// ``` 423 | /// use valuable::Slice; 424 | /// 425 | /// let v = Slice::Unit(&[(), (), ()]); 426 | /// ``` 427 | Unit(()), 428 | } 429 | -------------------------------------------------------------------------------- /valuable/src/structable.rs: -------------------------------------------------------------------------------- 1 | use crate::field::*; 2 | use crate::*; 3 | 4 | use core::fmt; 5 | 6 | /// A struct-like [`Valuable`] sub-type. 7 | /// 8 | /// Implemented by [`Valuable`] types that have a struct-like shape. Fields may 9 | /// be named or unnamed (tuple). Values that implement `Structable` must return 10 | /// [`Value::Structable`] from their [`Valuable::as_value`] implementation. 11 | /// 12 | /// # Inspecting 13 | /// 14 | /// Inspecting fields contained by a `Structable` instance is done by visiting 15 | /// the struct. When visiting a `Structable`, either the `visit_named_fields()` 16 | /// or the `visit_unnamed_fields()` methods of `Visit` are called. Each method 17 | /// may be called multiple times per `Structable`, but the two methods are never 18 | /// mixed. 19 | /// 20 | /// ``` 21 | /// use valuable::{NamedValues, Valuable, Value, Visit}; 22 | /// 23 | /// #[derive(Valuable)] 24 | /// struct MyStruct { 25 | /// foo: u32, 26 | /// bar: u32, 27 | /// } 28 | /// 29 | /// struct PrintFields; 30 | /// 31 | /// impl Visit for PrintFields { 32 | /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 33 | /// for (field, value) in named_values.iter() { 34 | /// println!("{}: {:?}", field.name(), value); 35 | /// } 36 | /// } 37 | /// 38 | /// fn visit_value(&mut self, value: Value<'_>) { 39 | /// match value { 40 | /// Value::Structable(v) => v.visit(self), 41 | /// _ => {} // do nothing for other types 42 | /// } 43 | /// } 44 | /// } 45 | /// 46 | /// let my_struct = MyStruct { 47 | /// foo: 123, 48 | /// bar: 456, 49 | /// }; 50 | /// 51 | /// valuable::visit(&my_struct, &mut PrintFields); 52 | /// ``` 53 | /// 54 | /// If the struct is **statically** defined, then all fields are known ahead of 55 | /// time and may be accessed via the [`StructDef`] instance returned by 56 | /// [`definition()`]. [`NamedField`] instances returned by [`definition()`] 57 | /// maybe used to efficiently extract specific field values. 58 | /// 59 | /// # Implementing 60 | /// 61 | /// Implementing `Structable` is usually done by adding `#[derive(Valuable)]` to 62 | /// a Rust `struct` definition. 63 | /// 64 | /// ``` 65 | /// use valuable::{Fields, Valuable, Structable, StructDef}; 66 | /// 67 | /// #[derive(Valuable)] 68 | /// struct MyStruct { 69 | /// foo: &'static str, 70 | /// } 71 | /// 72 | /// let my_struct = MyStruct { foo: "Hello" }; 73 | /// let fields = match my_struct.definition() { 74 | /// StructDef::Static { name, fields, .. } => { 75 | /// assert_eq!("MyStruct", name); 76 | /// fields 77 | /// } 78 | /// _ => unreachable!(), 79 | /// }; 80 | /// 81 | /// match fields { 82 | /// Fields::Named(named_fields) => { 83 | /// assert_eq!(1, named_fields.len()); 84 | /// assert_eq!("foo", named_fields[0].name()); 85 | /// } 86 | /// _ => unreachable!(), 87 | /// } 88 | /// ``` 89 | /// 90 | /// [`definition()`]: Structable::definition() 91 | pub trait Structable: Valuable { 92 | /// Returns the struct's definition. 93 | /// 94 | /// See [`StructDef`] documentation for more details. 95 | /// 96 | /// # Examples 97 | /// 98 | /// ``` 99 | /// use valuable::{Structable, Valuable}; 100 | /// 101 | /// #[derive(Valuable)] 102 | /// struct MyStruct { 103 | /// foo: u32, 104 | /// } 105 | /// 106 | /// let my_struct = MyStruct { 107 | /// foo: 123, 108 | /// }; 109 | /// 110 | /// assert_eq!("MyStruct", my_struct.definition().name()); 111 | fn definition(&self) -> StructDef<'_>; 112 | } 113 | 114 | /// A struct's name, fields, and other struct-level information. 115 | /// 116 | /// Returned by [`Structable::definition()`], `StructDef` provides the caller 117 | /// with information about the struct's definition. 118 | /// 119 | /// [`Structable::definition()`]: Structable::definition 120 | #[derive(Debug)] 121 | #[non_exhaustive] 122 | pub enum StructDef<'a> { 123 | /// The struct is statically-defined, all fields are known ahead of time. 124 | /// 125 | /// Most `Structable` definitions for Rust struct types will be 126 | /// `StructDef::Static`. 127 | /// 128 | /// # Examples 129 | /// 130 | /// A statically defined struct 131 | /// 132 | /// ``` 133 | /// use valuable::{Fields, Valuable, Structable, StructDef}; 134 | /// 135 | /// #[derive(Valuable)] 136 | /// struct MyStruct { 137 | /// foo: &'static str, 138 | /// } 139 | /// 140 | /// let my_struct = MyStruct { foo: "Hello" }; 141 | /// let fields = match my_struct.definition() { 142 | /// StructDef::Static { name, fields, ..} => { 143 | /// assert_eq!("MyStruct", name); 144 | /// fields 145 | /// } 146 | /// _ => unreachable!(), 147 | /// }; 148 | /// 149 | /// match fields { 150 | /// Fields::Named(named_fields) => { 151 | /// assert_eq!(1, named_fields.len()); 152 | /// assert_eq!("foo", named_fields[0].name()); 153 | /// } 154 | /// _ => unreachable!(), 155 | /// } 156 | /// ``` 157 | #[non_exhaustive] 158 | Static { 159 | /// The struct's name. 160 | name: &'static str, 161 | 162 | /// The struct's fields. 163 | fields: Fields<'static>, 164 | }, 165 | 166 | /// The struct is dynamically-defined, not all fields are known ahead of 167 | /// time. 168 | /// 169 | /// A dynamically-defined struct **could** be represented using 170 | /// [`Mappable`], though, using `Structable` offers benefits in a couple of 171 | /// cases. For example, when serializing a `Value`, some formats will 172 | /// serialize maps and structs differently. In this case, differentiating 173 | /// the two is required. There also are times when **some** struct fields 174 | /// are known statically, but not all of them (see second example). 175 | /// 176 | /// # Examples 177 | /// 178 | /// The struct stores field values in a `HashMap`. 179 | /// 180 | /// ``` 181 | /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit}; 182 | /// use std::collections::HashMap; 183 | /// 184 | /// /// A dynamic struct 185 | /// struct Dyn { 186 | /// // The struct name 187 | /// name: String, 188 | /// 189 | /// // Named values. 190 | /// values: HashMap>, 191 | /// } 192 | /// 193 | /// impl Valuable for Dyn { 194 | /// fn as_value(&self) -> Value<'_> { 195 | /// Value::Structable(self) 196 | /// } 197 | /// 198 | /// fn visit(&self, visit: &mut dyn Visit) { 199 | /// // This could be optimized to batch some. 200 | /// for (field, value) in self.values.iter() { 201 | /// visit.visit_named_fields(&NamedValues::new( 202 | /// &[NamedField::new(field)], 203 | /// &[value.as_value()], 204 | /// )); 205 | /// } 206 | /// } 207 | /// } 208 | /// 209 | /// impl Structable for Dyn { 210 | /// fn definition(&self) -> StructDef<'_> { 211 | /// StructDef::new_dynamic(&self.name, Fields::Named(&[])) 212 | /// } 213 | /// } 214 | /// ``` 215 | /// 216 | /// Some fields are known statically. 217 | /// 218 | /// ``` 219 | /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit}; 220 | /// use std::collections::HashMap; 221 | /// 222 | /// struct HalfStatic { 223 | /// foo: u32, 224 | /// bar: u32, 225 | /// extra_values: HashMap>, 226 | /// } 227 | /// 228 | /// impl Valuable for HalfStatic { 229 | /// fn as_value(&self) -> Value<'_> { 230 | /// Value::Structable(self) 231 | /// } 232 | /// 233 | /// fn visit(&self, visit: &mut dyn Visit) { 234 | /// // First, visit static fields 235 | /// visit.visit_named_fields(&NamedValues::new( 236 | /// FIELDS, 237 | /// &[self.foo.as_value(), self.bar.as_value()], 238 | /// )); 239 | /// 240 | /// // This could be optimized to batch some. 241 | /// for (field, value) in self.extra_values.iter() { 242 | /// visit.visit_named_fields(&NamedValues::new( 243 | /// &[NamedField::new(field)], 244 | /// &[value.as_value()], 245 | /// )); 246 | /// } 247 | /// } 248 | /// } 249 | /// 250 | /// static FIELDS: &[NamedField<'static>] = &[ 251 | /// NamedField::new("foo"), 252 | /// NamedField::new("bar"), 253 | /// ]; 254 | /// 255 | /// impl Structable for HalfStatic { 256 | /// fn definition(&self) -> StructDef<'_> { 257 | /// // Include known fields. 258 | /// StructDef::new_dynamic( 259 | /// "HalfStatic", 260 | /// Fields::Named(FIELDS)) 261 | /// } 262 | /// } 263 | /// ``` 264 | #[non_exhaustive] 265 | Dynamic { 266 | /// The struct's name 267 | name: &'a str, 268 | 269 | /// The struct's fields. 270 | fields: Fields<'a>, 271 | }, 272 | } 273 | 274 | impl fmt::Debug for dyn Structable + '_ { 275 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 276 | let def = self.definition(); 277 | 278 | if def.fields().is_named() { 279 | struct DebugStruct<'a, 'b> { 280 | fmt: fmt::DebugStruct<'a, 'b>, 281 | } 282 | 283 | let mut debug = DebugStruct { 284 | fmt: fmt.debug_struct(def.name()), 285 | }; 286 | 287 | impl Visit for DebugStruct<'_, '_> { 288 | fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 289 | for (field, value) in named_values { 290 | self.fmt.field(field.name(), value); 291 | } 292 | } 293 | 294 | fn visit_value(&mut self, _: Value<'_>) { 295 | unreachable!() 296 | } 297 | } 298 | 299 | self.visit(&mut debug); 300 | 301 | debug.fmt.finish() 302 | } else { 303 | struct DebugStruct<'a, 'b> { 304 | fmt: fmt::DebugTuple<'a, 'b>, 305 | } 306 | 307 | let mut debug = DebugStruct { 308 | fmt: fmt.debug_tuple(def.name()), 309 | }; 310 | 311 | impl Visit for DebugStruct<'_, '_> { 312 | fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 313 | for value in values { 314 | self.fmt.field(value); 315 | } 316 | } 317 | 318 | fn visit_value(&mut self, _: Value<'_>) { 319 | unreachable!(); 320 | } 321 | } 322 | 323 | self.visit(&mut debug); 324 | 325 | debug.fmt.finish() 326 | } 327 | } 328 | } 329 | 330 | impl<'a> StructDef<'a> { 331 | /// Create a new [`StructDef::Static`] instance. 332 | /// 333 | /// This should be used when a struct's fields are fixed and known ahead of time. 334 | /// 335 | /// # Examples 336 | /// 337 | /// ``` 338 | /// use valuable::{StructDef, Fields}; 339 | /// 340 | /// let def = StructDef::new_static("Foo", Fields::Unnamed(2)); 341 | /// ``` 342 | pub const fn new_static(name: &'static str, fields: Fields<'static>) -> StructDef<'a> { 343 | StructDef::Static { name, fields } 344 | } 345 | 346 | /// Create a new [`StructDef::Dynamic`] instance. 347 | /// 348 | /// This is used when the struct's fields may vary at runtime. 349 | /// 350 | /// # Examples 351 | /// 352 | /// ``` 353 | /// use valuable::{StructDef, Fields}; 354 | /// 355 | /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(3)); 356 | /// ``` 357 | pub const fn new_dynamic(name: &'a str, fields: Fields<'a>) -> StructDef<'a> { 358 | StructDef::Dynamic { name, fields } 359 | } 360 | 361 | /// Returns the struct's name 362 | /// 363 | /// # Examples 364 | /// 365 | /// With a static struct 366 | /// 367 | /// ``` 368 | /// use valuable::{StructDef, Fields}; 369 | /// 370 | /// let def = StructDef::new_static("Foo", Fields::Unnamed(1)); 371 | /// assert_eq!("Foo", def.name()); 372 | /// ``` 373 | /// 374 | /// With a dynamic struct 375 | /// 376 | /// ``` 377 | /// use valuable::{StructDef, Fields}; 378 | /// 379 | /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(2)); 380 | /// assert_eq!("Foo", def.name()); 381 | /// ``` 382 | pub const fn name(&self) -> &'a str { 383 | match self { 384 | StructDef::Static { name, .. } => name, 385 | StructDef::Dynamic { name, .. } => name, 386 | } 387 | } 388 | 389 | /// Returns the struct's fields 390 | /// 391 | /// # Examples 392 | /// 393 | /// With a static struct 394 | /// 395 | /// ``` 396 | /// use valuable::{StructDef, Fields}; 397 | /// 398 | /// let def = StructDef::new_static("Foo", Fields::Unnamed(3)); 399 | /// assert!(matches!(def.fields(), Fields::Unnamed(_))); 400 | /// ``` 401 | /// 402 | /// With a dynamic struct 403 | /// 404 | /// ``` 405 | /// use valuable::{StructDef, Fields}; 406 | /// 407 | /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1)); 408 | /// assert!(matches!(def.fields(), Fields::Unnamed(_))); 409 | /// ``` 410 | pub const fn fields(&self) -> &Fields<'a> { 411 | match self { 412 | StructDef::Static { fields, .. } => fields, 413 | StructDef::Dynamic { fields, .. } => fields, 414 | } 415 | } 416 | 417 | /// Returns `true` if the struct is [statically defined](StructDef::Static). 418 | /// 419 | /// # Examples 420 | /// 421 | /// With a static struct 422 | /// 423 | /// ``` 424 | /// use valuable::{StructDef, Fields}; 425 | /// 426 | /// let def = StructDef::new_static("Foo", Fields::Unnamed(2)); 427 | /// assert!(def.is_static()); 428 | /// ``` 429 | /// 430 | /// With a dynamic struct 431 | /// 432 | /// ``` 433 | /// use valuable::{StructDef, Fields}; 434 | /// 435 | /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(4)); 436 | /// assert!(!def.is_static()); 437 | /// ``` 438 | pub const fn is_static(&self) -> bool { 439 | matches!(self, StructDef::Static { .. }) 440 | } 441 | 442 | /// Returns `true` if the struct is [dynamically defined](StructDef::Dynamic). 443 | /// 444 | /// # Examples 445 | /// 446 | /// With a static struct 447 | /// 448 | /// ``` 449 | /// use valuable::{StructDef, Fields}; 450 | /// 451 | /// let def = StructDef::new_static("Foo", Fields::Unnamed(1)); 452 | /// assert!(!def.is_dynamic()); 453 | /// ``` 454 | /// 455 | /// With a dynamic struct 456 | /// 457 | /// ``` 458 | /// use valuable::{StructDef, Fields}; 459 | /// 460 | /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1)); 461 | /// assert!(def.is_dynamic()); 462 | /// ``` 463 | pub const fn is_dynamic(&self) -> bool { 464 | matches!(self, StructDef::Dynamic { .. }) 465 | } 466 | } 467 | 468 | macro_rules! deref { 469 | ( 470 | $( 471 | $(#[$attrs:meta])* 472 | $ty:ty, 473 | )* 474 | ) => { 475 | $( 476 | $(#[$attrs])* 477 | impl Structable for $ty { 478 | fn definition(&self) -> StructDef<'_> { 479 | T::definition(&**self) 480 | } 481 | } 482 | )* 483 | }; 484 | } 485 | 486 | deref! { 487 | &T, 488 | &mut T, 489 | #[cfg(feature = "alloc")] 490 | alloc::boxed::Box, 491 | #[cfg(feature = "alloc")] 492 | alloc::rc::Rc, 493 | #[cfg(not(valuable_no_atomic_cas))] 494 | #[cfg(feature = "alloc")] 495 | alloc::sync::Arc, 496 | } 497 | -------------------------------------------------------------------------------- /valuable/src/tuplable.rs: -------------------------------------------------------------------------------- 1 | use crate::{Valuable, Value, Visit}; 2 | 3 | use core::fmt; 4 | 5 | /// A tuple-like [`Valuable`] sub-type. 6 | /// 7 | /// Implemented by [`Valuable`] types that have a tuple-like shape. Fields are 8 | /// always unnamed. Values that implement `Tuplable` must return 9 | /// [`Value::Tuplable`] from their [`Valuable::as_value`] implementation. 10 | /// 11 | /// It is uncommon for users to implement this type as the crate provides 12 | /// implementations of `Tuplable` for Rust tuples. 13 | /// 14 | /// # Inspecting 15 | /// 16 | /// Inspecting fields contained by a `Tuplable` instance is done by visiting the 17 | /// tuple. When visiting a `Tuple`, the `visit_unnamed_fields()` method is 18 | /// called. When the tuple is statically defined, `visit_unnamed_fields()` is 19 | /// called once with the values of all the fields. A dynamic tuple 20 | /// implementation may call `visit_unnamed_fields()` multiple times. 21 | pub trait Tuplable: Valuable { 22 | /// Returns the tuple's definition. 23 | /// 24 | /// See [`TupleDef`] documentation for more details. 25 | /// 26 | /// # Examples 27 | /// 28 | /// ``` 29 | /// use valuable::{Tuplable, TupleDef}; 30 | /// 31 | /// let tuple = (123, "hello"); 32 | /// 33 | /// if let TupleDef::Static { fields, .. } = tuple.definition() { 34 | /// assert_eq!(2, fields); 35 | /// } 36 | /// ``` 37 | fn definition(&self) -> TupleDef; 38 | } 39 | 40 | /// The number of fields and other tuple-level information. 41 | /// 42 | /// Returned by [`Tuplable::definition()`], `TupleDef` provides the caller with 43 | /// information about the tuple's definition. 44 | /// 45 | /// This includes the number of fields contained by the tuple. 46 | #[derive(Debug)] 47 | #[non_exhaustive] 48 | pub enum TupleDef { 49 | /// The tuple is statically-defined, all fields are known ahead of time. 50 | /// 51 | /// Static tuple implementations are provided by the crate. 52 | /// 53 | /// # Examples 54 | /// 55 | /// A statically defined tuple. 56 | /// 57 | /// ``` 58 | /// use valuable::{Tuplable, TupleDef}; 59 | /// 60 | /// let tuple = (123, "hello"); 61 | /// 62 | /// match tuple.definition() { 63 | /// TupleDef::Static { fields, .. } => { 64 | /// assert_eq!(2, fields); 65 | /// } 66 | /// _ => unreachable!(), 67 | /// }; 68 | /// ``` 69 | #[non_exhaustive] 70 | Static { 71 | /// The number of fields contained by the tuple. 72 | fields: usize, 73 | }, 74 | /// The tuple is dynamically-defined, not all fields are known ahead of 75 | /// time. 76 | /// 77 | /// # Examples 78 | /// 79 | /// ``` 80 | /// use valuable::{Tuplable, TupleDef, Valuable, Value, Visit}; 81 | /// 82 | /// struct MyTuple; 83 | /// 84 | /// impl Valuable for MyTuple { 85 | /// fn as_value(&self) -> Value<'_> { 86 | /// Value::Tuplable(self) 87 | /// } 88 | /// 89 | /// fn visit(&self, visit: &mut dyn Visit) { 90 | /// visit.visit_unnamed_fields(&[Value::I32(123)]); 91 | /// visit.visit_unnamed_fields(&[Value::String("hello world")]); 92 | /// } 93 | /// } 94 | /// 95 | /// impl Tuplable for MyTuple { 96 | /// fn definition(&self) -> TupleDef { 97 | /// TupleDef::new_dynamic((1, Some(3))) 98 | /// } 99 | /// } 100 | /// ``` 101 | #[non_exhaustive] 102 | Dynamic { 103 | /// Returns the bounds on the number of tuple fields. 104 | /// 105 | /// Specifically, the first element is the lower bound, and the second 106 | /// element is the upper bound. 107 | fields: (usize, Option), 108 | }, 109 | } 110 | 111 | macro_rules! deref { 112 | ( 113 | $( 114 | $(#[$attrs:meta])* 115 | $ty:ty, 116 | )* 117 | ) => { 118 | $( 119 | $(#[$attrs])* 120 | impl Tuplable for $ty { 121 | fn definition(&self) -> TupleDef { 122 | T::definition(&**self) 123 | } 124 | } 125 | )* 126 | }; 127 | } 128 | 129 | deref! { 130 | &T, 131 | &mut T, 132 | #[cfg(feature = "alloc")] 133 | alloc::boxed::Box, 134 | #[cfg(feature = "alloc")] 135 | alloc::rc::Rc, 136 | #[cfg(not(valuable_no_atomic_cas))] 137 | #[cfg(feature = "alloc")] 138 | alloc::sync::Arc, 139 | } 140 | 141 | impl Tuplable for () { 142 | fn definition(&self) -> TupleDef { 143 | TupleDef::Static { fields: 0 } 144 | } 145 | } 146 | 147 | macro_rules! tuple_impls { 148 | ( 149 | $( $len:expr => ( $($n:tt $name:ident)+ ) )+ 150 | ) => { 151 | $( 152 | impl<$($name),+> Valuable for ($($name,)+) 153 | where 154 | $($name: Valuable,)+ 155 | { 156 | fn as_value(&self) -> Value<'_> { 157 | Value::Tuplable(self) 158 | } 159 | 160 | fn visit(&self, visit: &mut dyn Visit) { 161 | visit.visit_unnamed_fields(&[ 162 | $( 163 | self.$n.as_value(), 164 | )+ 165 | ]); 166 | } 167 | } 168 | 169 | impl<$($name),+> Tuplable for ($($name,)+) 170 | where 171 | $($name: Valuable,)+ 172 | { 173 | fn definition(&self) -> TupleDef { 174 | TupleDef::Static { fields: $len } 175 | } 176 | } 177 | )+ 178 | } 179 | } 180 | 181 | tuple_impls! { 182 | 1 => (0 T0) 183 | 2 => (0 T0 1 T1) 184 | 3 => (0 T0 1 T1 2 T2) 185 | 4 => (0 T0 1 T1 2 T2 3 T3) 186 | 5 => (0 T0 1 T1 2 T2 3 T3 4 T4) 187 | 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) 188 | 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) 189 | 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) 190 | 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) 191 | 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) 192 | 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) 193 | 12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11) 194 | 13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12) 195 | 14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13) 196 | 15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14) 197 | 16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15) 198 | } 199 | 200 | impl fmt::Debug for dyn Tuplable + '_ { 201 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 202 | if self.definition().is_unit() { 203 | ().fmt(fmt) 204 | } else { 205 | struct DebugTuple<'a, 'b> { 206 | fmt: fmt::DebugTuple<'a, 'b>, 207 | } 208 | 209 | impl Visit for DebugTuple<'_, '_> { 210 | fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 211 | for value in values { 212 | self.fmt.field(value); 213 | } 214 | } 215 | 216 | fn visit_value(&mut self, _: Value<'_>) { 217 | unimplemented!() 218 | } 219 | } 220 | 221 | let mut debug = DebugTuple { 222 | fmt: fmt.debug_tuple(""), 223 | }; 224 | 225 | self.visit(&mut debug); 226 | debug.fmt.finish() 227 | } 228 | } 229 | } 230 | 231 | impl TupleDef { 232 | /// Create a new [`TupleDef::Static`] instance 233 | /// 234 | /// This should be used when the tuple's fields are fixed and known ahead of time. 235 | /// 236 | /// # Examples 237 | /// 238 | /// ``` 239 | /// use valuable::TupleDef; 240 | /// 241 | /// let def = TupleDef::new_static(2); 242 | /// ``` 243 | pub const fn new_static(fields: usize) -> TupleDef { 244 | TupleDef::Static { fields } 245 | } 246 | 247 | /// Create a new [`TupleDef::Dynamic`] instance. 248 | /// 249 | /// This is used when the tuple's fields may vary at runtime. 250 | /// 251 | /// # Examples 252 | /// 253 | /// ``` 254 | /// use valuable::TupleDef; 255 | /// 256 | /// let def = TupleDef::new_dynamic((2, Some(10))); 257 | /// ``` 258 | pub const fn new_dynamic(fields: (usize, Option)) -> TupleDef { 259 | TupleDef::Dynamic { fields } 260 | } 261 | 262 | /// Returns `true` if `self` represents the [unit][primitive@unit] tuple. 263 | /// 264 | /// # Examples 265 | /// 266 | /// With the unit tuple 267 | /// 268 | /// ``` 269 | /// use valuable::Tuplable; 270 | /// 271 | /// let tuple: &dyn Tuplable = &(); 272 | /// assert!(tuple.definition().is_unit()); 273 | /// ``` 274 | /// 275 | /// When not the unit tuple. 276 | /// 277 | /// ``` 278 | /// use valuable::Tuplable; 279 | /// 280 | /// let tuple: &dyn Tuplable = &(123,456); 281 | /// assert!(!tuple.definition().is_unit()); 282 | /// ``` 283 | pub fn is_unit(&self) -> bool { 284 | match *self { 285 | TupleDef::Static { fields } => fields == 0, 286 | TupleDef::Dynamic { fields } => fields == (0, Some(0)), 287 | } 288 | } 289 | 290 | /// Returns `true` if the tuple is [statically defined](TupleDef::Static). 291 | /// 292 | /// # Examples 293 | /// 294 | /// With a static tuple 295 | /// 296 | /// ``` 297 | /// use valuable::TupleDef; 298 | /// 299 | /// let def = TupleDef::new_static(2); 300 | /// assert!(def.is_static()); 301 | /// ``` 302 | /// 303 | /// With a dynamic tuple 304 | /// 305 | /// ``` 306 | /// use valuable::TupleDef; 307 | /// 308 | /// let def = TupleDef::new_dynamic((2, None)); 309 | /// assert!(!def.is_static()); 310 | /// ``` 311 | pub fn is_static(&self) -> bool { 312 | matches!(self, TupleDef::Static { .. }) 313 | } 314 | 315 | /// Returns `true` if the tuple is [dynamically defined](TupleDef::Dynamic). 316 | /// 317 | /// # Examples 318 | /// 319 | /// With a static tuple 320 | /// 321 | /// ``` 322 | /// use valuable::TupleDef; 323 | /// 324 | /// let def = TupleDef::new_static(2); 325 | /// assert!(!def.is_dynamic()); 326 | /// ``` 327 | /// 328 | /// With a dynamic tuple 329 | /// 330 | /// ``` 331 | /// use valuable::TupleDef; 332 | /// 333 | /// let def = TupleDef::new_dynamic((2, None)); 334 | /// assert!(def.is_dynamic()); 335 | /// ``` 336 | pub fn is_dynamic(&self) -> bool { 337 | matches!(self, TupleDef::Dynamic { .. }) 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /valuable/src/valuable.rs: -------------------------------------------------------------------------------- 1 | use crate::{Slice, Value, Visit}; 2 | 3 | use core::fmt; 4 | use core::num::Wrapping; 5 | 6 | /// A type that can be converted to a [`Value`]. 7 | /// 8 | /// `Valuable` types are inspected by defining a [`Visit`] implementation and 9 | /// using it when calling [`Valuable::visit`]. See [`Visit`] documentation for 10 | /// more details. 11 | /// 12 | /// The `Valuable` procedural macro makes implementing `Valuable` easy. Users 13 | /// can add add [`#[derive(Valuable)]`][macro] to their types. 14 | /// 15 | /// `Valuable` provides implementations for many Rust primitives and standard 16 | /// library types. 17 | /// 18 | /// Types implementing `Valuable` may also implement one of the more specific 19 | /// traits: [`Structable`], [`Enumerable`], [`Listable`], and [`Mappable`]. These traits 20 | /// should be implemented when the type is a nested container of other `Valuable` types. 21 | /// 22 | /// [`Value`]: Value 23 | /// [`Visit`]: Visit 24 | /// [`Valuable::visit`]: Valuable::visit 25 | /// [`Structable`]: crate::Structable 26 | /// [`Enumerable`]: crate::Enumerable 27 | /// [`Listable`]: crate::Listable 28 | /// [`Mappable`]: crate::Mappable 29 | /// [macro]: macro@crate::Valuable 30 | pub trait Valuable { 31 | /// Converts self into a [`Value`] instance. 32 | /// 33 | /// # Examples 34 | /// 35 | /// ``` 36 | /// use valuable::Valuable; 37 | /// 38 | /// let _ = "hello".as_value(); 39 | /// ``` 40 | fn as_value(&self) -> Value<'_>; 41 | 42 | /// Calls the relevant method on [`Visit`] to extract data from `self`. 43 | /// 44 | /// This method is used to extract type-specific data from the value and is 45 | /// intended to be an implementation detail. For example, `Vec` implements 46 | /// `visit` by calling [`visit_value()`] on each of its elements. Structs 47 | /// implement `visit` by calling [`visit_named_fields()`] or 48 | /// [`visit_unnamed_fields()`]. 49 | /// 50 | /// Usually, users will call the [`visit`] function instead. 51 | /// 52 | /// [`Visit`]: Visit 53 | /// [`visit`]: visit() 54 | /// [`visit_value()`]: Visit::visit_value() 55 | /// [`visit_named_fields()`]: Visit::visit_named_fields() 56 | /// [`visit_unnamed_fields()`]: Visit::visit_unnamed_fields() 57 | fn visit(&self, visit: &mut dyn Visit); 58 | 59 | /// Calls [`Visit::visit_primitive_slice()`] with `self`. 60 | /// 61 | /// This method is an implementation detail used to optimize visiting 62 | /// primitive slices. 63 | /// 64 | /// [`Visit::visit_primitive_slice()`]: Visit::visit_primitive_slice 65 | fn visit_slice(slice: &[Self], visit: &mut dyn Visit) 66 | where 67 | Self: Sized, 68 | { 69 | for item in slice { 70 | visit.visit_value(item.as_value()); 71 | } 72 | } 73 | } 74 | 75 | macro_rules! deref { 76 | ( 77 | $( 78 | $(#[$attrs:meta])* 79 | $ty:ty, 80 | )* 81 | ) => { 82 | $( 83 | $(#[$attrs])* 84 | impl Valuable for $ty { 85 | fn as_value(&self) -> Value<'_> { 86 | T::as_value(&**self) 87 | } 88 | 89 | fn visit(&self, visit: &mut dyn Visit) { 90 | T::visit(&**self, visit); 91 | } 92 | } 93 | )* 94 | }; 95 | } 96 | 97 | deref! { 98 | &T, 99 | &mut T, 100 | #[cfg(feature = "alloc")] 101 | alloc::boxed::Box, 102 | #[cfg(feature = "alloc")] 103 | alloc::rc::Rc, 104 | #[cfg(not(valuable_no_atomic_cas))] 105 | #[cfg(feature = "alloc")] 106 | alloc::sync::Arc, 107 | } 108 | 109 | macro_rules! valuable { 110 | ( 111 | $( 112 | $variant:ident($ty:ty), 113 | )* 114 | ) => { 115 | $( 116 | impl Valuable for $ty { 117 | fn as_value(&self) -> Value<'_> { 118 | Value::$variant(*self) 119 | } 120 | 121 | fn visit(&self, visit: &mut dyn Visit) { 122 | visit.visit_value(self.as_value()); 123 | } 124 | 125 | fn visit_slice(slice: &[Self], visit: &mut dyn Visit) 126 | where 127 | Self: Sized, 128 | { 129 | visit.visit_primitive_slice(Slice::$variant(slice)); 130 | } 131 | } 132 | )* 133 | }; 134 | } 135 | 136 | valuable! { 137 | Bool(bool), 138 | Char(char), 139 | F32(f32), 140 | F64(f64), 141 | I8(i8), 142 | I16(i16), 143 | I32(i32), 144 | I64(i64), 145 | I128(i128), 146 | Isize(isize), 147 | U8(u8), 148 | U16(u16), 149 | U32(u32), 150 | U64(u64), 151 | U128(u128), 152 | Usize(usize), 153 | } 154 | 155 | macro_rules! nonzero { 156 | ( 157 | $( 158 | $variant:ident($ty:ident), 159 | )* 160 | ) => { 161 | $( 162 | impl Valuable for core::num::$ty { 163 | fn as_value(&self) -> Value<'_> { 164 | Value::$variant(self.get()) 165 | } 166 | 167 | fn visit(&self, visit: &mut dyn Visit) { 168 | visit.visit_value(self.as_value()); 169 | } 170 | } 171 | )* 172 | }; 173 | } 174 | 175 | nonzero! { 176 | I8(NonZeroI8), 177 | I16(NonZeroI16), 178 | I32(NonZeroI32), 179 | I64(NonZeroI64), 180 | I128(NonZeroI128), 181 | Isize(NonZeroIsize), 182 | U8(NonZeroU8), 183 | U16(NonZeroU16), 184 | U32(NonZeroU32), 185 | U64(NonZeroU64), 186 | U128(NonZeroU128), 187 | Usize(NonZeroUsize), 188 | } 189 | 190 | #[cfg(not(valuable_no_atomic))] 191 | macro_rules! atomic { 192 | ( 193 | $( 194 | $(#[$attrs:meta])* 195 | $variant:ident($ty:ident), 196 | )* 197 | ) => { 198 | $( 199 | $(#[$attrs])* 200 | impl Valuable for core::sync::atomic::$ty { 201 | fn as_value(&self) -> Value<'_> { 202 | // Use SeqCst to match Debug and serde which use SeqCst. 203 | // https://github.com/rust-lang/rust/blob/1.52.1/library/core/src/sync/atomic.rs#L1361-L1366 204 | // https://github.com/serde-rs/serde/issues/1496 205 | Value::$variant(self.load(core::sync::atomic::Ordering::SeqCst)) 206 | } 207 | 208 | fn visit(&self, visit: &mut dyn Visit) { 209 | visit.visit_value(self.as_value()); 210 | } 211 | } 212 | )* 213 | }; 214 | } 215 | 216 | #[cfg(not(valuable_no_atomic))] 217 | atomic! { 218 | Bool(AtomicBool), 219 | I8(AtomicI8), 220 | I16(AtomicI16), 221 | I32(AtomicI32), 222 | #[cfg(not(valuable_no_atomic_64))] 223 | I64(AtomicI64), 224 | Isize(AtomicIsize), 225 | U8(AtomicU8), 226 | U16(AtomicU16), 227 | U32(AtomicU32), 228 | #[cfg(not(valuable_no_atomic_64))] 229 | U64(AtomicU64), 230 | Usize(AtomicUsize), 231 | } 232 | 233 | impl Valuable for Wrapping { 234 | fn as_value(&self) -> Value<'_> { 235 | self.0.as_value() 236 | } 237 | 238 | fn visit(&self, visit: &mut dyn Visit) { 239 | self.0.visit(visit); 240 | } 241 | } 242 | 243 | impl Valuable for () { 244 | fn as_value(&self) -> Value<'_> { 245 | Value::Tuplable(self) 246 | } 247 | 248 | fn visit(&self, visit: &mut dyn Visit) { 249 | visit.visit_unnamed_fields(&[]); 250 | } 251 | } 252 | 253 | impl Valuable for Option { 254 | fn as_value(&self) -> Value<'_> { 255 | match self { 256 | Some(v) => v.as_value(), 257 | None => Value::Unit, 258 | } 259 | } 260 | 261 | fn visit(&self, visit: &mut dyn Visit) { 262 | visit.visit_value(self.as_value()); 263 | } 264 | } 265 | 266 | impl Valuable for &'_ str { 267 | fn as_value(&self) -> Value<'_> { 268 | Value::String(self) 269 | } 270 | 271 | fn visit(&self, visit: &mut dyn Visit) { 272 | visit.visit_value(Value::String(self)); 273 | } 274 | 275 | fn visit_slice(slice: &[Self], visit: &mut dyn Visit) 276 | where 277 | Self: Sized, 278 | { 279 | visit.visit_primitive_slice(Slice::Str(slice)); 280 | } 281 | } 282 | 283 | #[cfg(feature = "alloc")] 284 | impl Valuable for alloc::string::String { 285 | fn as_value(&self) -> Value<'_> { 286 | Value::String(&self[..]) 287 | } 288 | 289 | fn visit(&self, visit: &mut dyn Visit) { 290 | visit.visit_value(Value::String(self)); 291 | } 292 | 293 | fn visit_slice(slice: &[Self], visit: &mut dyn Visit) 294 | where 295 | Self: Sized, 296 | { 297 | visit.visit_primitive_slice(Slice::String(slice)); 298 | } 299 | } 300 | 301 | #[cfg(feature = "std")] 302 | impl Valuable for &std::path::Path { 303 | fn as_value(&self) -> Value<'_> { 304 | Value::Path(self) 305 | } 306 | 307 | fn visit(&self, visit: &mut dyn Visit) { 308 | visit.visit_value(Value::Path(self)); 309 | } 310 | } 311 | 312 | #[cfg(feature = "std")] 313 | impl Valuable for std::path::PathBuf { 314 | fn as_value(&self) -> Value<'_> { 315 | Value::Path(self) 316 | } 317 | 318 | fn visit(&self, visit: &mut dyn Visit) { 319 | visit.visit_value(Value::Path(self)); 320 | } 321 | } 322 | 323 | #[cfg(feature = "std")] 324 | impl Valuable for dyn std::error::Error + 'static { 325 | fn as_value(&self) -> Value<'_> { 326 | Value::Error(self) 327 | } 328 | 329 | fn visit(&self, visit: &mut dyn Visit) { 330 | visit.visit_value(self.as_value()); 331 | } 332 | } 333 | 334 | impl fmt::Debug for dyn Valuable + '_ { 335 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 336 | let value = self.as_value(); 337 | value.fmt(fmt) 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /valuable/src/visit.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// Traverse a value's fields and variants. 4 | /// 5 | /// Each method of the `Visit` trait is a hook that enables the implementor to 6 | /// observe value fields. By default, most methods are implemented as a no-op. 7 | /// The `visit_primitive_slice` default implementation will iterate the slice, 8 | /// calling `visit_value` with each item. 9 | /// 10 | /// To recurse, the implementor must implement methods to visit the arguments. 11 | /// 12 | /// # Examples 13 | /// 14 | /// Recursively printing a Rust value. 15 | /// 16 | /// ``` 17 | /// use valuable::{NamedValues, Valuable, Value, Visit}; 18 | /// 19 | /// struct Print(String); 20 | /// 21 | /// impl Print { 22 | /// fn indent(&self) -> Print { 23 | /// Print(format!("{} ", self.0)) 24 | /// } 25 | /// } 26 | /// 27 | /// impl Visit for Print { 28 | /// fn visit_value(&mut self, value: Value<'_>) { 29 | /// match value { 30 | /// Value::Structable(v) => { 31 | /// let def = v.definition(); 32 | /// // Print the struct name 33 | /// println!("{}{}:", self.0, def.name()); 34 | /// 35 | /// // Visit fields 36 | /// let mut visit = self.indent(); 37 | /// v.visit(&mut visit); 38 | /// } 39 | /// Value::Enumerable(v) => { 40 | /// let def = v.definition(); 41 | /// let variant = v.variant(); 42 | /// // Print the enum name 43 | /// println!("{}{}::{}:", self.0, def.name(), variant.name()); 44 | /// 45 | /// // Visit fields 46 | /// let mut visit = self.indent(); 47 | /// v.visit(&mut visit); 48 | /// } 49 | /// Value::Listable(v) => { 50 | /// println!("{}", self.0); 51 | /// 52 | /// // Visit fields 53 | /// let mut visit = self.indent(); 54 | /// v.visit(&mut visit); 55 | /// } 56 | /// Value::Mappable(v) => { 57 | /// println!("{}", self.0); 58 | /// 59 | /// // Visit fields 60 | /// let mut visit = self.indent(); 61 | /// v.visit(&mut visit); 62 | /// } 63 | /// // Primitive or unknown type, just render Debug 64 | /// v => println!("{:?}", v), 65 | /// } 66 | /// } 67 | /// 68 | /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 69 | /// for (field, value) in named_values { 70 | /// print!("{}- {}: ", self.0, field.name()); 71 | /// value.visit(self); 72 | /// } 73 | /// } 74 | /// 75 | /// fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 76 | /// for value in values { 77 | /// print!("{}- ", self.0); 78 | /// value.visit(self); 79 | /// } 80 | /// } 81 | /// 82 | /// fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { 83 | /// print!("{}- {:?}: ", self.0, key); 84 | /// value.visit(self); 85 | /// } 86 | /// } 87 | /// 88 | /// #[derive(Valuable)] 89 | /// struct Person { 90 | /// name: String, 91 | /// age: u32, 92 | /// addresses: Vec
, 93 | /// } 94 | /// 95 | /// #[derive(Valuable)] 96 | /// struct Address { 97 | /// street: String, 98 | /// city: String, 99 | /// zip: String, 100 | /// } 101 | /// 102 | /// let person = Person { 103 | /// name: "Angela Ashton".to_string(), 104 | /// age: 31, 105 | /// addresses: vec![ 106 | /// Address { 107 | /// street: "123 1st Ave".to_string(), 108 | /// city: "Townsville".to_string(), 109 | /// zip: "12345".to_string(), 110 | /// }, 111 | /// Address { 112 | /// street: "555 Main St.".to_string(), 113 | /// city: "New Old Town".to_string(), 114 | /// zip: "55555".to_string(), 115 | /// }, 116 | /// ], 117 | /// }; 118 | /// 119 | /// let mut print = Print("".to_string()); 120 | /// valuable::visit(&person, &mut print); 121 | /// ``` 122 | pub trait Visit { 123 | /// Visit a single value. 124 | /// 125 | /// The `visit_value` method is called once when visiting single primitive 126 | /// values. When visiting `Listable` types, the `visit_value` method is 127 | /// called once per item in the listable type. 128 | /// 129 | /// Note, in the case of Listable types containing primitive types, 130 | /// `visit_primitive_slice` can be implemented instead for less overhead. 131 | /// 132 | /// # Examples 133 | /// 134 | /// Visiting a single value. 135 | /// 136 | /// ``` 137 | /// use valuable::{Valuable, Visit, Value}; 138 | /// 139 | /// struct Print; 140 | /// 141 | /// impl Visit for Print { 142 | /// fn visit_value(&mut self, value: Value<'_>) { 143 | /// println!("{:?}", value); 144 | /// } 145 | /// } 146 | /// 147 | /// let my_val = 123; 148 | /// my_val.visit(&mut Print); 149 | /// ``` 150 | /// 151 | /// Visiting multiple values in a list. 152 | /// 153 | /// ``` 154 | /// use valuable::{Valuable, Value, Visit}; 155 | /// 156 | /// struct PrintList { comma: bool }; 157 | /// 158 | /// impl Visit for PrintList { 159 | /// fn visit_value(&mut self, value: Value<'_>) { 160 | /// match value { 161 | /// Value::Listable(v) => v.visit(self), 162 | /// value => { 163 | /// if self.comma { 164 | /// println!(", {:?}", value); 165 | /// } else { 166 | /// print!("{:?}", value); 167 | /// self.comma = true; 168 | /// } 169 | /// } 170 | /// } 171 | /// } 172 | /// } 173 | /// 174 | /// let my_list = vec![1, 2, 3, 4, 5]; 175 | /// valuable::visit(&my_list, &mut PrintList { comma: false }); 176 | /// ``` 177 | fn visit_value(&mut self, value: Value<'_>); 178 | 179 | /// Visit a struct or enum's named fields. 180 | /// 181 | /// When the struct/enum is statically defined, all fields are known ahead 182 | /// of time and `visit_named_fields` is called once with all field values. 183 | /// When the struct/enum is dynamic, then the `visit_named_fields` method 184 | /// may be called multiple times. 185 | /// 186 | /// See [`Structable`] and [`Enumerable`] for static vs. dynamic details. 187 | /// 188 | /// # Examples 189 | /// 190 | /// Visiting all fields in a struct. 191 | /// 192 | /// ``` 193 | /// use valuable::{NamedValues, Valuable, Value, Visit}; 194 | /// 195 | /// #[derive(Valuable)] 196 | /// struct MyStruct { 197 | /// hello: String, 198 | /// world: u32, 199 | /// } 200 | /// 201 | /// struct Print; 202 | /// 203 | /// impl Visit for Print { 204 | /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 205 | /// for (field, value) in named_values { 206 | /// println!("{:?}: {:?}", field, value); 207 | /// } 208 | /// } 209 | /// 210 | /// fn visit_value(&mut self, value: Value<'_>) { 211 | /// match value { 212 | /// Value::Structable(v) => v.visit(self), 213 | /// _ => {} // do nothing for other types 214 | /// } 215 | /// } 216 | /// } 217 | /// 218 | /// let my_struct = MyStruct { 219 | /// hello: "Hello world".to_string(), 220 | /// world: 42, 221 | /// }; 222 | /// 223 | /// valuable::visit(&my_struct, &mut Print); 224 | /// ``` 225 | fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 226 | let _ = named_values; 227 | } 228 | 229 | /// Visit a struct or enum's unnamed fields. 230 | /// 231 | /// When the struct/enum is statically defined, all fields are known ahead 232 | /// of time and `visit_unnamed_fields` is called once with all field values. 233 | /// When the struct/enum is dynamic, then the `visit_unnamed_fields` method 234 | /// may be called multiple times. 235 | /// 236 | /// See [`Structable`] and [`Enumerable`] for static vs. dynamic details. 237 | /// 238 | /// # Examples 239 | /// 240 | /// Visiting all fields in a struct. 241 | /// 242 | /// ``` 243 | /// use valuable::{Valuable, Value, Visit}; 244 | /// 245 | /// #[derive(Valuable)] 246 | /// struct MyStruct(String, u32); 247 | /// 248 | /// struct Print; 249 | /// 250 | /// impl Visit for Print { 251 | /// fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 252 | /// for value in values { 253 | /// println!("{:?}", value); 254 | /// } 255 | /// } 256 | /// 257 | /// fn visit_value(&mut self, value: Value<'_>) { 258 | /// match value { 259 | /// Value::Structable(v) => v.visit(self), 260 | /// _ => {} // do nothing for other types 261 | /// } 262 | /// } 263 | /// } 264 | /// 265 | /// let my_struct = MyStruct("Hello world".to_string(), 42); 266 | /// 267 | /// valuable::visit(&my_struct, &mut Print); 268 | /// ``` 269 | fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 270 | let _ = values; 271 | } 272 | 273 | /// Visit a primitive slice. 274 | /// 275 | /// This method exists as an optimization when visiting [`Listable`] types. 276 | /// By default, `Listable` types are visited by passing each item to 277 | /// `visit_value`. However, if the listable stores a **primitive** type 278 | /// within contiguous memory, then `visit_primitive_slice` is called 279 | /// instead. 280 | /// 281 | /// When implementing `visit_primitive_slice`, be aware that the method may 282 | /// be called multiple times for a single `Listable` type. 283 | /// 284 | /// # Examples 285 | /// 286 | /// A vec calls `visit_primitive_slice` one time, but a `VecDeque` will call 287 | /// `visit_primitive_slice` twice. 288 | /// 289 | /// ``` 290 | /// use valuable::{Valuable, Value, Visit, Slice}; 291 | /// use std::collections::VecDeque; 292 | /// 293 | /// struct Count(u32); 294 | /// 295 | /// impl Visit for Count { 296 | /// fn visit_primitive_slice(&mut self, slice: Slice<'_>) { 297 | /// self.0 += 1; 298 | /// } 299 | /// 300 | /// fn visit_value(&mut self, value: Value<'_>) { 301 | /// match value { 302 | /// Value::Listable(v) => v.visit(self), 303 | /// _ => {} // do nothing for other types 304 | /// } 305 | /// } 306 | /// } 307 | /// 308 | /// let vec = vec![1, 2, 3, 4, 5]; 309 | /// 310 | /// let mut count = Count(0); 311 | /// valuable::visit(&vec, &mut count); 312 | /// assert_eq!(1, count.0); 313 | /// 314 | /// let mut vec_deque = VecDeque::from(vec); 315 | /// 316 | /// let mut count = Count(0); 317 | /// valuable::visit(&vec_deque, &mut count); 318 | /// 319 | /// assert_eq!(2, count.0); 320 | /// ``` 321 | fn visit_primitive_slice(&mut self, slice: Slice<'_>) { 322 | for value in slice { 323 | self.visit_value(value); 324 | } 325 | } 326 | 327 | /// Visit a `Mappable`'s entries. 328 | /// 329 | /// The `visit_entry` method is called once for each entry contained by a 330 | /// `Mappable.` 331 | /// 332 | /// # Examples 333 | /// 334 | /// Visit a map's entries 335 | /// 336 | /// ``` 337 | /// use valuable::{Valuable, Value, Visit}; 338 | /// use std::collections::HashMap; 339 | /// 340 | /// let mut map = HashMap::new(); 341 | /// map.insert("hello", 123); 342 | /// map.insert("world", 456); 343 | /// 344 | /// struct Print; 345 | /// 346 | /// impl Visit for Print { 347 | /// fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { 348 | /// println!("{:?} => {:?}", key, value); 349 | /// } 350 | /// 351 | /// fn visit_value(&mut self, value: Value<'_>) { 352 | /// match value { 353 | /// Value::Mappable(v) => v.visit(self), 354 | /// _ => {} // do nothing for other types 355 | /// } 356 | /// } 357 | /// } 358 | /// 359 | /// valuable::visit(&map, &mut Print); 360 | /// ``` 361 | fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { 362 | let _ = (key, value); 363 | } 364 | } 365 | 366 | macro_rules! deref { 367 | ( 368 | $( 369 | $(#[$attrs:meta])* 370 | $ty:ty, 371 | )* 372 | ) => { 373 | $( 374 | $(#[$attrs])* 375 | impl Visit for $ty { 376 | fn visit_value(&mut self, value: Value<'_>) { 377 | T::visit_value(&mut **self, value) 378 | } 379 | 380 | fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 381 | T::visit_named_fields(&mut **self, named_values) 382 | } 383 | 384 | fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { 385 | T::visit_unnamed_fields(&mut **self, values) 386 | } 387 | 388 | fn visit_primitive_slice(&mut self, slice: Slice<'_>) { 389 | T::visit_primitive_slice(&mut **self, slice) 390 | } 391 | 392 | fn visit_entry(&mut self, key: Value<'_>, value: Value<'_>) { 393 | T::visit_entry(&mut **self, key, value) 394 | } 395 | } 396 | )* 397 | }; 398 | } 399 | 400 | deref! { 401 | &mut T, 402 | #[cfg(feature = "alloc")] 403 | alloc::boxed::Box, 404 | } 405 | 406 | /// Inspects a value by calling the relevant [`Visit`] methods with `value`'s 407 | /// data. 408 | /// 409 | /// This method calls [`Visit::visit_value()`] with the provided [`Valuable`] 410 | /// instance. See [`Visit`] documentation for more details. 411 | /// 412 | /// # Examples 413 | /// 414 | /// Extract a single field from a struct. Note: if the same field is repeatedly 415 | /// extracted from a struct, it is preferable to obtain the associated 416 | /// [`NamedField`] once and use it repeatedly. 417 | /// 418 | /// ``` 419 | /// use valuable::{NamedValues, Valuable, Value, Visit}; 420 | /// 421 | /// #[derive(Valuable)] 422 | /// struct MyStruct { 423 | /// foo: usize, 424 | /// bar: usize, 425 | /// } 426 | /// 427 | /// struct GetFoo(usize); 428 | /// 429 | /// impl Visit for GetFoo { 430 | /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { 431 | /// if let Some(foo) = named_values.get_by_name("foo") { 432 | /// if let Some(val) = foo.as_usize() { 433 | /// self.0 = val; 434 | /// } 435 | /// } 436 | /// } 437 | /// 438 | /// fn visit_value(&mut self, value: Value<'_>) { 439 | /// if let Value::Structable(v) = value { 440 | /// v.visit(self); 441 | /// } 442 | /// } 443 | /// } 444 | /// 445 | /// let my_struct = MyStruct { 446 | /// foo: 123, 447 | /// bar: 456, 448 | /// }; 449 | /// 450 | /// let mut get_foo = GetFoo(0); 451 | /// valuable::visit(&my_struct, &mut get_foo); 452 | /// 453 | /// assert_eq!(123, get_foo.0); 454 | /// ``` 455 | /// 456 | /// [`Visit`]: Visit [`NamedField`]: crate::NamedField 457 | pub fn visit(value: &impl Valuable, visit: &mut dyn Visit) { 458 | visit.visit_value(value.as_value()); 459 | } 460 | --------------------------------------------------------------------------------