├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── .gitignore ├── .rustfmt.toml ├── CHANGELOG.md ├── CODEOWNERS ├── Cargo.toml ├── FILE_TEMPLATE ├── LICENSE ├── README.md ├── RELEASING.md ├── examples └── value_macro.rs └── src ├── at.rs ├── lib.rs ├── macros.rs ├── prelude.rs ├── scale_impls ├── decode.rs ├── encode.rs ├── mod.rs └── tracing_decoder │ ├── error.rs │ ├── mod.rs │ ├── path.rs │ └── visitor.rs ├── serde_impls ├── bitvec_helpers.rs ├── deserialize.rs ├── deserializer.rs ├── mod.rs ├── serialize.rs └── serializer.rs ├── string_impls ├── custom_formatters │ ├── hex.rs │ └── mod.rs ├── custom_parsers │ ├── hex.rs │ ├── mod.rs │ └── ss58.rs ├── from_string.rs ├── mod.rs ├── string_helpers.rs └── to_string.rs └── value_type.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | # Run jobs when commits are pushed to 6 | # main: 7 | branches: 8 | - main 9 | pull_request: 10 | # Run jobs for any external PR that wants 11 | # to merge to main, too: 12 | branches: 13 | - main 14 | 15 | env: 16 | CARGO_TERM_COLOR: always 17 | 18 | jobs: 19 | build: 20 | name: Cargo check 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout sources 24 | uses: actions/checkout@v4 25 | 26 | - name: Install Rust stable toolchain 27 | uses: actions-rs/toolchain@v1 28 | with: 29 | profile: minimal 30 | toolchain: stable 31 | override: true 32 | 33 | - name: Rust Cache 34 | uses: Swatinem/rust-cache@v2.7.5 35 | 36 | - name: Check all features 37 | uses: actions-rs/cargo@v1.0.3 38 | with: 39 | command: check 40 | args: --all-targets --all-features --workspace 41 | 42 | - name: Check no features 43 | uses: actions-rs/cargo@v1.0.3 44 | with: 45 | command: check 46 | args: --all-targets --no-default-features --workspace 47 | 48 | wasm: 49 | name: Check WASM compatibility 50 | runs-on: ubuntu-latest 51 | steps: 52 | - name: Checkout sources 53 | uses: actions/checkout@v4 54 | 55 | - name: Install Rust stable toolchain 56 | uses: actions-rs/toolchain@v1 57 | with: 58 | profile: minimal 59 | toolchain: stable 60 | target: wasm32-unknown-unknown 61 | override: true 62 | 63 | - name: Rust Cache 64 | uses: Swatinem/rust-cache@v2.7.5 65 | 66 | - name: Check all features 67 | uses: actions-rs/cargo@v1.0.3 68 | with: 69 | command: check 70 | args: --all-targets --all-features --target wasm32-unknown-unknown 71 | 72 | fmt: 73 | name: Cargo fmt 74 | runs-on: ubuntu-latest 75 | steps: 76 | - name: Checkout sources 77 | uses: actions/checkout@v4 78 | 79 | - name: Install Rust stable toolchain 80 | uses: actions-rs/toolchain@v1 81 | with: 82 | profile: minimal 83 | toolchain: stable 84 | override: true 85 | components: rustfmt 86 | 87 | - name: Rust Cache 88 | uses: Swatinem/rust-cache@v2.7.5 89 | 90 | - name: Cargo fmt 91 | uses: actions-rs/cargo@v1.0.3 92 | with: 93 | command: fmt 94 | args: --all -- --check 95 | 96 | docs: 97 | name: Check documentation 98 | runs-on: ubuntu-latest 99 | steps: 100 | - name: Checkout sources 101 | uses: actions/checkout@v4 102 | 103 | - name: Install Rust stable toolchain 104 | uses: actions-rs/toolchain@v1 105 | with: 106 | profile: minimal 107 | toolchain: stable 108 | override: true 109 | 110 | - name: Rust Cache 111 | uses: Swatinem/rust-cache@v2.7.5 112 | 113 | - name: Check internal documentation links 114 | run: RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc -vv --workspace --no-deps --document-private-items 115 | 116 | tests: 117 | name: Cargo test 118 | runs-on: ubuntu-latest 119 | steps: 120 | - name: Checkout sources 121 | uses: actions/checkout@v4 122 | 123 | - name: Install Rust stable toolchain 124 | uses: actions-rs/toolchain@v1 125 | with: 126 | profile: minimal 127 | toolchain: stable 128 | override: true 129 | 130 | - name: Rust Cache 131 | uses: Swatinem/rust-cache@v2.7.5 132 | 133 | - name: Cargo test 134 | uses: actions-rs/cargo@v1.0.3 135 | with: 136 | command: test 137 | args: --all-targets --all-features --workspace 138 | 139 | - name: Cargo test docs 140 | uses: actions-rs/cargo@v1.0.3 141 | with: 142 | command: test 143 | args: --doc --workspace 144 | 145 | - name: Cargo test examples 146 | uses: actions-rs/cargo@v1.0.3 147 | with: 148 | command: test 149 | args: --examples --workspace 150 | 151 | - name: Run tests with no_std in conjunction with other features 152 | run: | 153 | cargo test --no-default-features --features from-string 154 | cargo test --no-default-features --features parser-ss58 155 | cargo test --no-default-features --features serde 156 | 157 | clippy: 158 | name: Cargo clippy 159 | runs-on: ubuntu-latest 160 | steps: 161 | - name: Checkout sources 162 | uses: actions/checkout@v4 163 | 164 | - name: Install Rust stable toolchain 165 | uses: actions-rs/toolchain@v1 166 | with: 167 | profile: minimal 168 | toolchain: stable 169 | components: clippy 170 | override: true 171 | 172 | - name: Rust Cache 173 | uses: Swatinem/rust-cache@v2.7.5 174 | 175 | - name: Run clippy 176 | uses: actions-rs/cargo@v1 177 | with: 178 | command: clippy 179 | args: --all-targets -- -D warnings 180 | 181 | no_std: 182 | name: Check no_std build 183 | runs-on: ubuntu-latest 184 | steps: 185 | - name: Checkout sources 186 | uses: actions/checkout@v4 187 | 188 | - name: Install Rust stable toolchain 189 | uses: actions-rs/toolchain@v1 190 | with: 191 | profile: minimal 192 | toolchain: stable 193 | target: aarch64-unknown-none 194 | override: true 195 | 196 | - name: Rust Cache 197 | uses: Swatinem/rust-cache@v2.7.5 198 | 199 | - name: Check no_std build 200 | uses: actions-rs/cargo@v1.0.3 201 | with: 202 | command: check 203 | # The aarch64-unknown-none doesn't support `std`, so this 204 | # will fail if the crate is not fully no_std compatible. 205 | args: --target aarch64-unknown-none --no-default-features --features serde,from-string,parser-ss58 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | .DS_Store -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = false 2 | tab_spaces = 4 3 | max_width = 100 4 | use_small_heuristics = "Max" 5 | edition = "2021" -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | The format is based on [Keep a Changelog]. 4 | 5 | [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ 6 | 7 | ## 0.18.0 (2024-11-15) 8 | 9 | This release makes scale-value entirely no_std which is now using core::error::Error instead of std::error::Error as it was using before behind 10 | the std feature. Because of that the std feature is now removed and the MSRV is bumped to 1.81.0. In addition it bumps a few dependencies to their latest versions. 11 | 12 | ### Changed 13 | - Remove std feature and make the crate fully no_std 14 | - Update `scale-decode` to v0.16.0 15 | - Update `scale-encode` to v0.10.0 16 | - Update `yap` to v0.12.0 17 | - Update `scale-bits` to v0.7.0 18 | - Remove `scale-info` dependency and the re-export PortableRegistry 19 | - Bump MSRV to 1.81.0 20 | - Replace `derive_more` with `thiserror` 21 | 22 | ## 0.17.0 (2024-10-22) 23 | 24 | This release: 25 | - Bumps `derive_more` from 0.99 to 1.0 ([#57](https://github.com/paritytech/scale-value/pull/57)) 26 | - Bumps `scale-decode` from 0.13 to 0.14 ([#58](https://github.com/paritytech/scale-value/pull/58)) 27 | - Bumps `scale-encode` from 0.7 to 0.8 ([#59](https://github.com/paritytech/scale-value/pull/59)) 28 | - Removes `frame-metadata` dependency ([#60](https://github.com/paritytech/scale-value/pull/60)) 29 | 30 | ## 0.16.3 (2024-09-25) 31 | 32 | This release exports `scale_value::scale::ValueVisitor`, allowing the values to be created from functions like `scale_decode::visitor::decode_with_visitor`. 33 | 34 | ## 0.16.2 (2024-08-08) 35 | 36 | This release adds `scale_value::stringify::to_writer` and `scale_value::stringify::to_writer_custom` (to align with the already present `scale_value::stringify::from_str_custom`), and also exposes a new `scale_value::stringiy::custom_formatters` containing a formatter for displaying things as hex. 37 | 38 | `scale_value::stringify::to_writer_custom` allows for custom formatting of values, including a "pretty" spaced/indented formatting and a "compact" formatting which removes all unnecessary spaces. It also allows customising of the indentation and for custom formatters to be applied, as well as displaying the contexts of values if desired. 39 | 40 | See [#52](https://github.com/paritytech/scale-value/pull/52) for more information. 41 | 42 | ## 0.16.1 (2024-07-24) 43 | 44 | This release: 45 | - Adds a "tracing decoder" (via `scale_value::scale::tracing::{decode_as_type,*}`), which is like `scale_value::scale::decode_as_type`, but traces and hands back much more detail in case of a decoding failure, so that it's easier to track down where decoding failed. This is particularly useful when working with historic type information, which must be provided independently and could easily be misaligned with reality. ([#52](https://github.com/paritytech/scale-value/pull/52)) 46 | 47 | ## 0.16.0 (2024-05-15) 48 | 49 | This release: 50 | - Bumps `scale-decode` to its latest version (0.13.0). 51 | 52 | ## 0.15.1 (2024-05-08) 53 | 54 | ### Fixed 55 | 56 | - Fix an infinite loop when trying to encode Composite values of the wrong shape ((#48)[https://github.com/paritytech/scale-value/pull/48]) 57 | 58 | ## 0.15.0 (2024-04-29) 59 | 60 | This release bumps `scale-type-resolver`, `scale-encode`, `scale-decode` and `scale-bits` to their latest versions. 61 | 62 | ## 0.14.1 (2024-03-04) 63 | 64 | ### Added 65 | 66 | A `scale_value::decode_as_fields` function was added that can decode a series of values from some bytes given an iterator of type ids. Previously it was only possible through the `scale_decode::DecodeAsFields` implementation of `scale_value::Composite<()>`. With the new function `scale_value::Composite`'s can be decoded for any type resolver `R`. 67 | 68 | ## 0.14.0 (2024-02-27) 69 | 70 | ### Changed 71 | 72 | The crate now uses [`scale-type-resolver`](https://github.com/paritytech/scale-type-resolver) to be generic over the provider of type information that is used when encoding and decoding `Value`s. 73 | 74 | One implication is that `scale_decode::IntoVisitor` is now only implemented for `Value<()>` and no longer for `Value`. So `Value::decode_from_type()` cannot be used anymore to create a `Value` that keeps type id information. Instead you now use `scale_value::scale::decode_as_type()` which can return the desired `Value`. 75 | 76 | ## 0.13.0 (2023-11-10) 77 | 78 | This release: 79 | - Bumps `scale-decode` to its latest version. 80 | 81 | ## 0.12.0 (2023-08-02) 82 | 83 | Bumps `scale-encode` and `scale-decode` to their latest versions (0.5 and 0.9 respectively). 84 | 85 | One effect that this has is that structs containing compact encoded values, after being encoded, now decode to composite types that better 86 | reflect their original shape. For example, `Compact(MyWrapper { inner: 123 })`, when encoded, used to decode to `Value::u128(123)`, 87 | but now it decodes to `Value::named_composite(vec![("inner", Value::u128(123))]).` 88 | 89 | ## 0.11.0 (2023-07-18) 90 | 91 | ### Added 92 | 93 | - Adds support for `no_std` environments; disable the "std" feature flag for this. ([#38](https://github.com/paritytech/scale-value/pull/38)) 94 | This PR makes a couple of small breaking changes: 95 | - The `from_string` feature flag is now `from-string`. 96 | - Some sub-`ErrorKind`'s in `from_string` no logner impl `std::error::Error`. 97 | - `ParseErrorKind::Custom` errors are now strings rather than boxed `std::error::Error`s to play nicer with `no_std`. 98 | - Adds a `value!` macro to make constructing `Value`'s much easier; think `serde_json::value!`. ([#36](https://github.com/paritytech/scale-value/pull/36)) 99 | 100 | ### Changed 101 | 102 | - Bumps `scale-encode` and `scale-decode` to their latest versions (0.4 and 0.8 respectively). 103 | 104 | ## 0.10.0 105 | 106 | This release: 107 | - bumps `scale-encode` and `scale-decode` to their latest versions. 108 | 109 | ## 0.9.0 110 | 111 | This release: 112 | - bumps `scale-encode` and `scale-decode` to their latest versions. 113 | 114 | ## 0.8.1 115 | 116 | This patch release: 117 | - Changes how composite `Value`'s are encoded to improve the likelihood that values will encode correctly. ([#32](https://github.com/paritytech/scale-value/pull/32)) 118 | 119 | ## 0.8.0 120 | 121 | This release: 122 | - Bumps to using `scale-info` 2.5.0 and uses field rather than method accessors as introduced by that change. 123 | - Introduces `scale_value::stringify::from_str_custom()`, which allows you to construct a `Value` parser that can inject custom parsing logic while parsing strings into Values. 124 | - Adds two new custom parsers in a new `stringify::custom_parsers` module for parsing hex values and ss58 addresses. These can be used in conjunction with the above. 125 | - Fixes a small bug in stringifying so that field and enum idents are no longer quoted unless necessary; this will make the output prettier. 126 | 127 | There should be no breaking API changes. 128 | 129 | ### Added 130 | 131 | - Add hex and ss58 custom parsers. ([#29](https://github.com/paritytech/scale-value/pull/29)) 132 | - Improve stringifying and add support for custom parsers. ([#26](https://github.com/paritytech/scale-value/pull/26)) 133 | 134 | ## 0.7.0 135 | 136 | The main change in this release is that it makes use of a new `scale-encode` crate and updated `scale-decode` crate for the SCALE encoding and decoding of Values. 137 | - Values now implement `DecodeAsType` and `EncodeAsType`. 138 | - Composites now implement `DecodeAsFields`. 139 | - As a small breaking API change, the `TypeId` passed to the encode and decode methods is now a plain `u32` for simplicity, rather than a newtype struct. 140 | 141 | It should be very straightforward to update to this release as the changes are mainly additive in nature. 142 | 143 | ### Changed 144 | 145 | - Use latest `scale-decode` and new `scale-encode` crate for SCALE encoding and decoding Values. ([#25](https://github.com/paritytech/scale-value/pull/25)) 146 | 147 | ## 0.6.0 148 | 149 | Here we move to `scale_bits` from `bitvec` to handle our encode/decode logic and provide a simple type to decode bits into. We also now add a WASM CI test, and will expect this crate (potentially via features in the future) to be WASM compatible. 150 | 151 | ### Changed 152 | 153 | - Use `scale-bits` for `BitSequence` decoding etc and enable WASM test. ([#24](https://github.com/paritytech/scale-value/pull/24)) 154 | 155 | ## 0.5.0 156 | 157 | The main user-facing change in this release is that the SCALE bytes for compact-encoded _structs_ previously decoded into `Composite` values with the same degree of nesting (ie if the compact encoded value was nested within 2 structs, it would be decoded into a value nested inside 2 `Composite`s). This nesting has now been removed, and the compact value is returned directly. This should have no impact on re-encoding the Value, since encoding into single-field composites will just delegrate to the inner field type as needed. 158 | 159 | Internally, the SCALE decoding logic has mostly moved to `scale-decode`, simplifying the logic in this crate (and exposing a more general/lower level decoding interface via that crate). 160 | 161 | Full list of changes: 162 | 163 | ### Changed 164 | 165 | - Use `scale-decode` for `Value` decoding. ([#22](https://github.com/paritytech/scale-value/pull/22)) 166 | 167 | ## 0.4.0 168 | 169 | The main addition in this release is the `At` trait (and corresponding `.at()` method) for indexing into `Value`s. There are also various small breaking changes as a result of tidying up various constructors, which will hopefully in general allow you to construct `Value`s with a little less verbosity. The `uint`/`int` constructors have been made more consistent with their siblings and have been renamed to `u128` and `i128`. 170 | 171 | Full list of changes: 172 | 173 | ### Added 174 | 175 | - Index into values with at, and more generic/extra accessors/constructors. ([#19](https://github.com/paritytech/scale-value/pull/19)) 176 | 177 | ## 0.3.0 178 | 179 | This release introduces a small breaking change: `scale_value::scale::encode_value_as_type` now takes a reference to a value rather than ownership of it, since on the happy path this shouldnt affect performance, and it would often mean cloning the entire value before passing it in, anyway. 180 | 181 | Full list of changes: 182 | 183 | ### Changed 184 | 185 | - SCALE encoding now accepts a reference, and make encoding slightly more flexible w.r.t newtype wrappers. ([#17](https://github.com/paritytech/scale-value/pull/17)) 186 | 187 | ## 0.2.1 188 | 189 | ### Fixed 190 | 191 | - Fix compile error on 32-bit architectures owing to BitVec not supporting a store type of u64 on them. Also fix an internal naming mixup w.r.t bitvec types. ([#12](https://github.com/paritytech/scale-value/pull/12)) 192 | 193 | ## 0.2.0 194 | 195 | ### Added 196 | 197 | - Added a string syntax for values, and the ability to parse `Value`'s from strings or encode them into strings (see the new `stringify` module exposed at the crate root). Parsing from strings requires the `from_string` feature to be enabled. ([#7](https://github.com/paritytech/scale-value/pull/7)) 198 | 199 | 200 | ## 0.1.0 201 | 202 | The initial release. 203 | 204 | ### Added 205 | 206 | - Added a `Value` type that can be SCALE encoded and decoded using a `scale_info::PortableRegistry`, as well as serialized and deserialized to things via `serde`. ([#1](https://github.com/paritytech/scale-value/pull/1)) 207 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # main codeowner @paritytech/tools-team 2 | * @paritytech/tools-team 3 | 4 | # CI 5 | /.github/ @paritytech/ci @paritytech/tools-team -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scale-value" 3 | version = "0.18.0" 4 | authors = ["Parity Technologies "] 5 | edition = "2021" 6 | 7 | license = "Apache-2.0" 8 | readme = "README.md" 9 | repository = "https://github.com/paritytech/scale-value" 10 | documentation = "https://docs.rs/scale-value" 11 | homepage = "https://www.parity.io/" 12 | description = "Encode and decode values of arbitrary shapes to SCALE bytes" 13 | keywords = ["parity", "scale", "encoding", "decoding"] 14 | include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] 15 | rust-version = "1.81.0" 16 | 17 | [features] 18 | default = ["serde", "from-string", "parser-ss58"] 19 | 20 | # Internal feature for tests that needs std. 21 | __std = [] 22 | 23 | # Enable support for parsing strings into Values. 24 | from-string = ["dep:yap"] 25 | 26 | # Enable serde support for serializing/deserializing Values. 27 | serde = ["dep:serde", "scale-bits/serde"] 28 | 29 | # Provide an ss58 address parser 30 | parser-ss58 = ["dep:base58", "dep:blake2", "from-string"] 31 | 32 | [dependencies] 33 | codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full"] } 34 | serde = { version = "1.0.124", default-features = false, features = ["derive"], optional = true } 35 | scale-decode = { version = "0.16.0", default-features = false } 36 | scale-encode = { version = "0.10.0", default-features = false, features = ["bits"] } 37 | scale-bits = { version = "0.7.0", default-features = false, features = ["serde", "scale-info"] } 38 | either = { version = "1.6.1", default-features = false } 39 | yap = { version = "0.12.0", optional = true } 40 | base58 = { version = "0.2.0", optional = true } 41 | blake2 = { version = "0.10.6", optional = true, default_features = false } 42 | thiserror = { version = "2.0.0", default-features = false } 43 | scale-type-resolver = "0.2.0" 44 | 45 | [dev-dependencies] 46 | hex = "0.4.3" 47 | serde_json = { version = "1.0.64", default-features = false, features = ["alloc"] } 48 | scale-decode = { version = "0.16.0", default-features = false, features = ["derive"] } 49 | scale-info = { version = "2.11.5", default-features = false, features = ["derive"] } 50 | -------------------------------------------------------------------------------- /FILE_TEMPLATE: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scale-value · [![CI Status][ci-badge]][ci] [![Latest Version on Crates.io][crates-badge]][crates] [![Released API docs][docs-badge]][docs] 2 | 3 | This crate provides a `Value` type, which is a runtime representation that is compatible with [`scale_info::TypeDef`][scale-info-typedef]. It somewhat analogous to a `serde_json::Value`, which is a runtime representation of JSON values, but with a focus on SCALE encoded values instead of JSON encoded values. Unlike JSON however, SCALE encoding is not self describing, and so we need additional type information to tell us how to encode and decode values. 4 | 5 | It is expected that this crate will commonly be used in conjunction with the [scale-info] and [frame-metadata] crates. 6 | 7 | The [scale-info] crate allows us to define types and add them to a type registry, which in turn is used to tell us how to SCALE encode and decode `Value`s. 8 | 9 | The [frame-metadata] crate contains all of the type information we need in order to be able to SCALE encode and decode `Value`s into the various parameters needed in extrinsics and such. 10 | 11 | Crate features (enabled by default): 12 | - `serde`: Allow `Value`s to be converted from and to static Rust types (where possible), or serialized and deserialized to other formats like JSON, via serde. 13 | - `from_string`: Allow strings to be parsed into `Values` using the same format from which values can be converted to strings via `.to_string()`. Examples: 14 | - Boolean types parse from `true` and `false`. 15 | - Strings and chars are supported with `"Hello\n there"` and `'a'`. 16 | - Numbers like `1_234_567` and `-123` are supported. 17 | - Composite types (structs/tuples) look like `{ hello: 123, "there": true }` and `('a', 'b', true)`. 18 | - Finally, enum variants look like `Hello { foo: 1, bar: 2 }` and `Foo(1,2,3)`. 19 | 20 | # Examples 21 | 22 | Manually creating a type registry, and then using it to SCALE encode and decode some runtime constructed `Value` type to/from SCALE bytes. 23 | 24 | ```rust 25 | // Turn a type into an ID and type registry using `scale-info`: 26 | fn make_type() -> (u32, scale_info::PortableRegistry) { 27 | let m = scale_info::MetaType::new::(); 28 | let mut types = scale_info::Registry::new(); 29 | let id = types.register_type(&m); 30 | let portable_registry: scale_info::PortableRegistry = types.into(); 31 | (id.id(), portable_registry) 32 | } 33 | 34 | // Some type which we have derived SCALE type information about: 35 | #[derive(scale_info::TypeInfo)] 36 | enum Foo { 37 | A { is_valid: bool, name: String } 38 | } 39 | 40 | // We can build a type registry containing just this type: 41 | let (type_id, registry) = make_type::(); 42 | use scale_value::Value; 43 | 44 | // Next, we can construct a runtime value of a similar shape: 45 | let value = Value::named_variant("A", vec![ 46 | ("is_valid".into(), Value::bool(true)), 47 | ("name".into(), Value::string("James")), 48 | ]); 49 | 50 | // Given the type registry and ID, we can try to convert our Value into SCALE bytes: 51 | let mut bytes = Vec::new(); 52 | scale_value::scale::encode_as_type(value.clone(), type_id, ®istry, &mut bytes).unwrap(); 53 | 54 | // We can also go the other way, and decode out bytes back into the same Value: 55 | let new_value = scale_value::scale::decode_as_type(&mut &*bytes, type_id, ®istry).unwrap(); 56 | 57 | // The two values should equal each other (`.remove_context()` just removes the additional 58 | // type information handed back when the value is decoded): 59 | assert_eq!(value, new_value.remove_context()); 60 | ``` 61 | 62 | Using the `serde` feature to convert a `Value` to/from some rust type via serde: 63 | 64 | ```rust 65 | use scale_value::Value; 66 | use serde::{ Serialize, Deserialize }; 67 | 68 | // Some type we want to be able to serialize/deserialize: 69 | #[derive(Serialize, Deserialize, PartialEq, Debug)] 70 | enum Foo { 71 | A { is_valid: bool, name: String }, 72 | B(u8, bool) 73 | } 74 | 75 | // First, serialize a Value into the rust type: 76 | let value = Value::named_variant("A", vec![ 77 | ("name".into(), Value::string("James")), 78 | ("is_valid".into(), Value::bool(true)), 79 | ]); 80 | let foo1: Foo = scale_value::serde::from_value(value).unwrap(); 81 | assert_eq!(foo1, Foo::A { is_valid: true, name: "James".into() }); 82 | 83 | // Next, deserialize the rust type back into a Value: 84 | let new_value = scale_value::serde::to_value(foo).unwrap(); 85 | assert_eq!(value, new_value); 86 | ``` 87 | 88 | Check out [the documentation][docs] for a full API reference and more examples. 89 | 90 | [ci]: https://github.com/paritytech/scale-value/actions?query=workflow%3ARust+branch%3Amaster 91 | [ci-badge]: https://github.com/paritytech/scale-value/workflows/Rust/badge.svg 92 | [crates]: https://crates.io/crates/scale-value 93 | [crates-badge]: https://img.shields.io/crates/v/scale-value.svg 94 | [docs]: https://docs.rs/scale-value 95 | [docs-badge]: https://docs.rs/scale-value/badge.svg 96 | [scale-info]: https://github.com/paritytech/scale-info 97 | [scale-info-typedef]: https://docs.rs/scale-info/latest/scale_info/enum.TypeDef.html 98 | [frame-metadata]: https://github.com/paritytech/frame-metadata -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Release Checklist 2 | 3 | These steps assume that you've checked out the `scale-value` repository and are in the root directory of it. 4 | 5 | 1. Ensure that everything you'd like to see released is on the `main` branch. 6 | 7 | 2. Create a release branch off `main`, for example `release-v0.1.0`. The branch name should start with `release` 8 | so that we can target commits with CI. Decide how far the version needs to be bumped based on the changes to date. 9 | If unsure what to bump the version to (e.g. is it a major, minor or patch release), check with the Parity Tools team. 10 | 11 | 3. Check that you're happy with the current documentation. 12 | 13 | ``` 14 | cargo doc --open --all-features 15 | ``` 16 | 17 | CI checks for broken internal links at the moment. Optionally you can also confirm that any external links 18 | are still valid like so: 19 | 20 | ``` 21 | cargo install cargo-deadlinks 22 | cargo deadlinks --check-http -- --all-features 23 | ``` 24 | 25 | If there are minor issues with the documentation, they can be fixed in the release branch. 26 | 27 | 4. Bump the crate version in `Cargo.toml` to whatever was decided in step 2. 28 | 29 | 5. Update `CHANGELOG.md` to reflect the difference between this release and last. If you're unsure of 30 | what to add, check with the Tools team. 31 | 32 | You should look through the commit history on `main` to find the code changes since the last release (eg `git log --pretty LAST_VERSION_TAG..HEAD`). 33 | 34 | Looking at the [closed PRs](https://github.com/paritytech/scale-value/pulls?q=is%3Apr+is%3Aclosed) can also help to provide details for the changes. 35 | 36 | 6. Commit any of the above changes to the release branch and open a PR in GitHub with a base of `main`. 37 | 38 | 7. Once the branch has been reviewed and passes CI, merge it. 39 | 40 | 8. Now, we're ready to publish the release to crates.io. 41 | 42 | Checkout `main`, ensuring we're looking at that latest merge (`git pull`). 43 | 44 | Next, do a final sanity check to make sure there are no new issues: 45 | ``` 46 | cargo fmt 47 | cargo clippy --all-targets 48 | cargo test --all-targets 49 | cargo test --doc 50 | ``` 51 | 52 | If we're happy with everything, proceed with the release: 53 | ``` 54 | cargo publish 55 | ``` 56 | 57 | 9. If the release was successful, then tag the commit that we released in the `main` branch with the 58 | version that we just released, for example: 59 | 60 | ``` 61 | git tag v0.1.0 # use the version number you've just published to crates.io, not this one 62 | git push --tags 63 | ``` 64 | 65 | Once this is pushed, go along to [the releases page on GitHub](https://github.com/paritytech/scale-value/releases) 66 | and draft a new release which points to the tag you just pushed to `main` above. Copy the changelog comments 67 | for the current release into the release description. 68 | 69 | -------------------------------------------------------------------------------- /examples/value_macro.rs: -------------------------------------------------------------------------------- 1 | use scale_value::value; 2 | 3 | fn main() { 4 | // The `value` macro can construct Value's like so: 5 | let val = value!({ 6 | hello: "Hello", 7 | world: 12345, 8 | variant: Variant { 9 | foo: (1,2,3), 10 | } 11 | }); 12 | 13 | println!("{val:?}"); 14 | } 15 | -------------------------------------------------------------------------------- /src/at.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Indexing into Value's to access things. We can't use the `Index` trait, 17 | // since it returns references, and we can't necessarily give back a reference 18 | // (`serde_json::Value` uses a statically initialised value to give back a ref 19 | // to in these cases, but we have a generic `Ctx` and can't do that ourselves). 20 | 21 | use super::{Composite, Value, ValueDef, Variant}; 22 | use crate::prelude::*; 23 | 24 | /// This trait allows indexing into [`Value`]s (and options of [`Value`]s) 25 | /// using the [`At::at()`] function. It's a little like Rust's [`core::ops::Index`] 26 | /// trait, but adapted so that we can return and work with optionals. 27 | /// 28 | /// Indexing into a [`Value`] never panics; instead it will return `None` if a 29 | /// value at the given location cannot be found. 30 | /// 31 | /// # Example 32 | /// 33 | /// ``` 34 | /// use scale_value::{ Value, At }; 35 | /// 36 | /// let val = Value::named_composite([ 37 | /// ("hello", Value::unnamed_composite([ 38 | /// Value::u128(1), 39 | /// Value::bool(true), 40 | /// Value::named_composite([ 41 | /// ("wibble", Value::bool(false)), 42 | /// ("foo", Value::named_composite([ 43 | /// ("bar", Value::u128(123)) 44 | /// ])) 45 | /// ]) 46 | /// ])) 47 | /// ]); 48 | /// 49 | /// // Use `at` to access nested values: 50 | /// assert_eq!(val.at("hello").at(0), Some(&Value::u128(1))); 51 | /// assert_eq!(val.at("hello").at(1), Some(&Value::bool(true))); 52 | /// assert_eq!(val.at("hello").at(2).at("wibble"), Some(&Value::bool(false))); 53 | /// assert_eq!(val.at("hello").at(2).at("foo").at("bar"), Some(&Value::u128(123))); 54 | /// 55 | /// // If the value doesn't exist, None will be returned: 56 | /// assert_eq!(val.at("wibble").at(3), None); 57 | /// assert_eq!(val.at("wibble").at("wobble").at("nope"), None); 58 | /// ``` 59 | pub trait At: private::Sealed { 60 | /// Index into a value, returning a reference to the value if one 61 | /// exists, or [`None`] if not. 62 | fn at(&self, loc: L) -> Option<&Value>; 63 | } 64 | 65 | // Prevent users from implementing the At trait. 66 | mod private { 67 | use super::*; 68 | pub trait Sealed {} 69 | impl Sealed for Value {} 70 | impl Sealed for Composite {} 71 | impl Sealed for Variant {} 72 | impl Sealed for Option<&T> {} 73 | } 74 | 75 | impl At for Composite { 76 | fn at(&self, loc: L) -> Option<&Value> { 77 | match loc.as_location().inner { 78 | LocationInner::Str(s) => match self { 79 | Composite::Named(vals) => { 80 | vals.iter().find_map(|(n, v)| if s == n { Some(v) } else { None }) 81 | } 82 | _ => None, 83 | }, 84 | LocationInner::Usize(n) => match self { 85 | Composite::Named(vals) => { 86 | let val = vals.get(n); 87 | val.map(|v| &v.1) 88 | } 89 | Composite::Unnamed(vals) => vals.get(n), 90 | }, 91 | } 92 | } 93 | } 94 | 95 | impl At for Variant { 96 | fn at(&self, loc: L) -> Option<&Value> { 97 | self.values.at(loc) 98 | } 99 | } 100 | 101 | impl At for Value { 102 | fn at(&self, loc: L) -> Option<&Value> { 103 | match &self.value { 104 | ValueDef::Composite(c) => c.at(loc), 105 | ValueDef::Variant(v) => v.at(loc), 106 | _ => None, 107 | } 108 | } 109 | } 110 | 111 | impl> At for Option<&T> { 112 | fn at(&self, loc: L) -> Option<&Value> { 113 | self.as_ref().and_then(|v| v.at(loc)) 114 | } 115 | } 116 | 117 | /// Types which can be used as a lookup location with [`At::at`] 118 | /// implement this trait. 119 | /// 120 | /// Users cannot implement this as the [`Location`] type internals 121 | /// are opaque and subject to change. 122 | pub trait AsLocation { 123 | fn as_location(&self) -> Location<'_>; 124 | } 125 | 126 | impl AsLocation for usize { 127 | fn as_location(&self) -> Location<'_> { 128 | Location { inner: LocationInner::Usize(*self) } 129 | } 130 | } 131 | 132 | impl AsLocation for &str { 133 | fn as_location(&self) -> Location<'_> { 134 | Location { inner: LocationInner::Str(self) } 135 | } 136 | } 137 | 138 | impl AsLocation for String { 139 | fn as_location(&self) -> Location<'_> { 140 | Location { inner: LocationInner::Str(self) } 141 | } 142 | } 143 | 144 | impl AsLocation for &T { 145 | fn as_location(&self) -> Location<'_> { 146 | (*self).as_location() 147 | } 148 | } 149 | 150 | /// A struct representing a location to access in a [`Value`]. 151 | #[derive(Copy, Clone)] 152 | pub struct Location<'a> { 153 | inner: LocationInner<'a>, 154 | } 155 | 156 | #[derive(Copy, Clone)] 157 | enum LocationInner<'a> { 158 | Usize(usize), 159 | Str(&'a str), 160 | } 161 | 162 | #[cfg(test)] 163 | mod test { 164 | use crate::value; 165 | 166 | use super::*; 167 | 168 | // This is basically the doc example with a little extra. 169 | #[test] 170 | fn nested_accessing() { 171 | let val = value!({hello: (1u32, true, { wibble: false, foo: {bar: 123u32}})}); 172 | assert_eq!(val.at("hello").at(0), Some(&Value::u128(1))); 173 | assert_eq!(val.at("hello").at(1), Some(&Value::bool(true))); 174 | assert_eq!(val.at("hello").at(2).at("wibble"), Some(&Value::bool(false))); 175 | assert_eq!(val.at("hello").at(2).at("foo").at("bar"), Some(&Value::u128(123))); 176 | 177 | assert_eq!(val.at("wibble").at(3), None); 178 | assert_eq!(val.at("wibble").at("wobble").at("nope"), None); 179 | 180 | // Strings can be used: 181 | assert_eq!(val.at("hello".to_string()).at(0), Some(&Value::u128(1))); 182 | // References to valid locations are fine too: 183 | { 184 | assert_eq!(val.at("hello").at(0), Some(&Value::u128(1))); 185 | } 186 | } 187 | 188 | #[test] 189 | fn accessing_variants() { 190 | let val = value!(TheVariant { foo: 12345u32, bar: 'c' }); 191 | 192 | assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345); 193 | assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c'); 194 | 195 | let val = value!(TheVariant(12345u32, 'c')); 196 | 197 | assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345); 198 | assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c'); 199 | 200 | // We can use `at()` on the variant directly, too: 201 | 202 | let val = 203 | Variant::named_fields("TheVariant", [("foo", value!(12345u32)), ("bar", value!('c'))]); 204 | 205 | assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345); 206 | assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c'); 207 | 208 | let val = Variant::unnamed_fields("TheVariant", [value!(12345u32), value!('c')]); 209 | 210 | assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345); 211 | assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c'); 212 | } 213 | 214 | #[test] 215 | fn accessing_composites() { 216 | // We already test accessing composite Values. This also checks that `at` works on 217 | // the Composite type, too.. 218 | 219 | let val = Composite::named([("foo", value!(12345u32)), ("bar", value!('c'))]); 220 | 221 | assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345); 222 | assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c'); 223 | 224 | let val = Composite::unnamed([value!(12345u32), value!('c')]); 225 | 226 | assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345); 227 | assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c'); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | /*! 17 | This crate exposes the [`Value`] type and related subtypes, which are used as the runtime 18 | representations of SCALE encoded data (much like `serde_json::Value` is a runtime representation 19 | of JSON data). 20 | 21 | [`Value`]'s can be: 22 | 23 | - Encoded and decoded from SCALE bytes via [`::scale_encode::EncodeAsType`] and [`::scale_decode::DecodeAsType`] 24 | traits (or by calling [`crate::scale::decode_as_type`] and [`crate::scale::encode_as_type`]). 25 | - Parsed to and from strings by calling [`crate::stringify::from_str`] and [`crate::stringify::to_string`]). 26 | Parsing from strings requires the `from_string` feature to be enabled. 27 | - Serialized and deserialized via `serde` traits (for example, to and from JSON). They can also be serialized 28 | from and to other types with the relevant serde derives on. These require the `serde` feature to be enabled. 29 | - Accessed ergonomically via the [`At`] trait. 30 | */ 31 | #![deny(missing_docs)] 32 | #![no_std] 33 | 34 | extern crate alloc; 35 | 36 | #[cfg(feature = "__std")] 37 | extern crate std; 38 | 39 | mod at; 40 | mod macros; 41 | mod prelude; 42 | mod scale_impls; 43 | #[cfg(feature = "serde")] 44 | mod serde_impls; 45 | mod string_impls; 46 | mod value_type; 47 | 48 | // Traits to allow indexing into values. 49 | pub use at::{At, Location}; 50 | 51 | // The value definition. 52 | pub use value_type::{BitSequence, Composite, Primitive, Value, ValueDef, Variant}; 53 | 54 | /// Serializing and deserializing a [`crate::Value`] into/from other types via serde. 55 | #[cfg(feature = "serde")] 56 | pub mod serde { 57 | use crate::prelude::*; 58 | pub use crate::serde_impls::{DeserializerError, SerializerError, ValueSerializer}; 59 | 60 | /// Attempt to convert a [`crate::Value`] into another type via serde. 61 | /// 62 | /// # Examples 63 | /// 64 | /// Use serde to convert a value into a built-in type: 65 | /// 66 | /// ```rust 67 | /// use scale_value::Value; 68 | /// 69 | /// let value = Value::unnamed_composite(vec![ 70 | /// Value::u128(1), 71 | /// Value::u128(2), 72 | /// Value::u128(3), 73 | /// ]); 74 | /// 75 | /// let arr: [u8; 3] = scale_value::serde::from_value(value).unwrap(); 76 | /// ``` 77 | /// 78 | /// Converting values to a custom type: 79 | /// 80 | /// ```rust 81 | /// use scale_value::Value; 82 | /// use serde::{ Serialize, Deserialize }; 83 | /// 84 | /// #[derive(Serialize, Deserialize, PartialEq, Debug)] 85 | /// enum Foo { 86 | /// A { is_valid: bool, name: String }, 87 | /// B(u8, bool) 88 | /// } 89 | /// 90 | /// let value1 = Value::named_variant("A", [ 91 | /// ("name", Value::string("James")), 92 | /// ("is_valid", Value::bool(true)), 93 | /// ]); 94 | /// let foo1: Foo = scale_value::serde::from_value(value1).unwrap(); 95 | /// assert_eq!(foo1, Foo::A { is_valid: true, name: "James".into() }); 96 | /// 97 | /// let value2 = Value::unnamed_variant("B", [ 98 | /// Value::u128(123), 99 | /// Value::bool(true), 100 | /// ]); 101 | /// let foo2: Foo = scale_value::serde::from_value(value2).unwrap(); 102 | /// assert_eq!(foo2, Foo::B(123, true)); 103 | /// ``` 104 | pub fn from_value<'de, Ctx, T: serde::Deserialize<'de>>( 105 | value: crate::Value, 106 | ) -> Result { 107 | T::deserialize(value) 108 | } 109 | 110 | /// Attempt to convert some type into a [`crate::Value`] via serde. 111 | /// 112 | /// # Examples 113 | /// 114 | /// Convert a built-in array of values into a [`crate::Value`]: 115 | /// 116 | /// ```rust 117 | /// use scale_value::Value; 118 | /// 119 | /// let arr = [1u8, 2u8, 3u8]; 120 | /// 121 | /// let val = scale_value::serde::to_value(arr).unwrap(); 122 | /// assert_eq!(val, Value::unnamed_composite([ 123 | /// Value::u128(1), 124 | /// Value::u128(2), 125 | /// Value::u128(3), 126 | /// ])); 127 | /// ``` 128 | /// 129 | /// Converting some custom type to a [`crate::Value`]: 130 | /// 131 | /// ```rust 132 | /// use scale_value::Value; 133 | /// use serde::{ Serialize, Deserialize }; 134 | /// 135 | /// #[derive(Serialize, Deserialize, PartialEq, Debug)] 136 | /// enum Foo { 137 | /// A { is_valid: bool, name: String }, 138 | /// B(u8, bool) 139 | /// } 140 | /// 141 | /// let foo = Foo::A { is_valid: true, name: "James".into() }; 142 | /// 143 | /// let value = scale_value::serde::to_value(foo).unwrap(); 144 | /// assert_eq!(value, Value::named_variant("A", [ 145 | /// ("is_valid", Value::bool(true)), 146 | /// ("name", Value::string("James")), 147 | /// ])); 148 | /// ``` 149 | pub fn to_value(ty: T) -> Result, SerializerError> { 150 | ty.serialize(ValueSerializer) 151 | } 152 | } 153 | 154 | /// Encoding and decoding SCALE bytes into a [`crate::Value`]. 155 | /// 156 | /// # Exmaple 157 | /// 158 | /// Given some known metadata type ID, encode and decode some [`crate::Value`] 159 | /// to SCALE bytes. 160 | /// 161 | /// ```rust 162 | /// # fn make_type() -> (u32, scale_info::PortableRegistry) { 163 | /// # let m = scale_info::MetaType::new::(); 164 | /// # let mut types = scale_info::Registry::new(); 165 | /// # let id = types.register_type(&m); 166 | /// # let portable_registry: scale_info::PortableRegistry = types.into(); 167 | /// # (id.id(), portable_registry) 168 | /// # } 169 | /// # let (type_id, registry) = make_type::(); 170 | /// use scale_value::Value; 171 | /// 172 | /// // Imagine we have a `registry` (of type [`scale_info::PortableRegistry`]) containing this type, 173 | /// // and a `type_id` (a `u32`) pointing to it in the registry. 174 | /// #[derive(scale_info::TypeInfo)] 175 | /// enum Foo { 176 | /// A { is_valid: bool, name: String } 177 | /// } 178 | /// 179 | /// // Given that, we can encode/decode something with that shape to/from SCALE bytes: 180 | /// let value = Value::named_variant("A", [ 181 | /// ("is_valid", Value::bool(true)), 182 | /// ("name", Value::string("James")), 183 | /// ]); 184 | /// 185 | /// // Encode the Value to bytes: 186 | /// let mut bytes = Vec::new(); 187 | /// scale_value::scale::encode_as_type(&value, type_id, ®istry, &mut bytes).unwrap(); 188 | /// 189 | /// // Decode the bytes back into a matching Value. 190 | /// // This value contains contextual information about which type was used 191 | /// // to decode each part of it, which we can throw away with `.remove_context()`. 192 | /// let new_value = scale_value::scale::decode_as_type(&mut &*bytes, type_id, ®istry).unwrap(); 193 | /// 194 | /// assert_eq!(value, new_value.remove_context()); 195 | /// ``` 196 | pub mod scale { 197 | use crate::prelude::*; 198 | use scale_decode::FieldIter; 199 | use scale_encode::EncodeAsType; 200 | 201 | pub use crate::scale_impls::{DecodeError, ValueVisitor}; 202 | pub use scale_encode::Error as EncodeError; 203 | pub use scale_type_resolver::TypeResolver; 204 | 205 | /// Attempt to decode some SCALE encoded bytes into a value, by providing a pointer 206 | /// to the bytes (which will be moved forwards as bytes are used in the decoding), 207 | /// a type ID, and a type registry from which we'll look up the relevant type information. 208 | pub fn decode_as_type( 209 | data: &mut &[u8], 210 | ty_id: R::TypeId, 211 | types: &R, 212 | ) -> Result, DecodeError> 213 | where 214 | R: TypeResolver, 215 | R::TypeId: Clone, 216 | { 217 | crate::scale_impls::decode_value_as_type(data, ty_id, types) 218 | } 219 | 220 | /// Attempt to decode some SCALE encoded bytes into multiple values, by providing a pointer 221 | /// to the bytes (which will be moved forwards as bytes are used in the decoding), 222 | /// and an iterator of fields, where each field contains a type ID and optionally a field name. 223 | pub fn decode_as_fields<'resolver, R>( 224 | input: &mut &[u8], 225 | fields: &mut dyn FieldIter<'resolver, R::TypeId>, 226 | types: &'resolver R, 227 | ) -> Result, DecodeError> 228 | where 229 | R: TypeResolver, 230 | R::TypeId: Clone, 231 | { 232 | crate::scale_impls::decode_composite_as_fields(input, fields, types) 233 | } 234 | 235 | /// Attempt to encode some [`crate::Value`] into SCALE bytes, by providing a pointer to the 236 | /// type ID that we'd like to encode it as, a type registry from which we'll look 237 | /// up the relevant type information, and a buffer to encode the bytes to. 238 | pub fn encode_as_type( 239 | value: &crate::Value, 240 | ty_id: R::TypeId, 241 | types: &R, 242 | buf: &mut Vec, 243 | ) -> Result<(), EncodeError> { 244 | value.encode_as_type_to(ty_id, types, buf) 245 | } 246 | 247 | /// A visitor and function to decode some bytes into a [`crate::Value`] while tracing the current 248 | /// decoding state so that a more detailed error can be returned in the event of a failure. 249 | pub mod tracing { 250 | pub use crate::scale_impls::{TraceDecodingError, TraceDecodingVisitor}; 251 | 252 | /// Decode a value using the [`TraceDecodingVisitor`], which internally keeps track of the current decoding state, and as 253 | /// a result hands back a much more detailed error than [`crate::scale::decode_as_type()`] if decoding fails. 254 | /// 255 | /// One approach is to use the standard visitor for decoding on the "happy path", and if you need more information about 256 | /// the decode error, to try decoding the same bytes again using this function to obtain more information about what failed. 257 | pub fn decode_as_type( 258 | data: &mut &[u8], 259 | ty_id: R::TypeId, 260 | types: &R, 261 | ) -> Result, TraceDecodingError>> 262 | where 263 | R: scale_type_resolver::TypeResolver, 264 | { 265 | scale_decode::visitor::decode_with_visitor( 266 | data, 267 | ty_id, 268 | types, 269 | TraceDecodingVisitor::new(), 270 | ) 271 | } 272 | } 273 | } 274 | 275 | /// Converting a [`crate::Value`] to or from strings. 276 | pub mod stringify { 277 | use crate::prelude::*; 278 | 279 | pub use crate::string_impls::ToWriterBuilder; 280 | #[cfg(feature = "from-string")] 281 | pub use crate::string_impls::{ 282 | FromStrBuilder, ParseBitSequenceError, ParseCharError, ParseComplexError, ParseError, 283 | ParseErrorKind, ParseNumberError, ParseStringError, 284 | }; 285 | 286 | /// This module provides custom parsers that work alongside [`crate::stringify::from_str_custom`] 287 | /// and extend the syntax to support parsing common formats into [`crate::Value`]'s. See 288 | /// [`crate::stringify::from_str_custom`] for a usage example. 289 | #[cfg(feature = "from-string")] 290 | pub mod custom_parsers { 291 | #[cfg(feature = "parser-ss58")] 292 | pub use crate::string_impls::parse_ss58; 293 | pub use crate::string_impls::{parse_hex, ParseHexError}; 294 | } 295 | 296 | /// This module provides custom formatters that work alongside [`crate::stringify::to_writer_custom`] 297 | /// and allow for the output to be formatted in various ways. 298 | pub mod custom_formatters { 299 | pub use crate::string_impls::format_hex; 300 | } 301 | 302 | /// Attempt to parse a string into a [`crate::Value<()>`], returning a tuple 303 | /// consisting of a result (either the value or a [`ParseError`] containing 304 | /// location and error information) and the remainder of the string that wasn't 305 | /// parsed. 306 | /// 307 | /// # Examples 308 | /// 309 | /// ```rust 310 | /// use scale_value::Value; 311 | /// 312 | /// fn to_value(str: &str) -> Value { 313 | /// scale_value::stringify::from_str(str).0.unwrap() 314 | /// } 315 | /// 316 | /// // Primitive values: 317 | /// assert_eq!(to_value("1"), Value::u128(1)); 318 | /// assert_eq!(to_value("-1"), Value::i128(-1)); 319 | /// assert_eq!(to_value("true"), Value::bool(true)); 320 | /// assert_eq!(to_value("'a'"), Value::char('a')); 321 | /// assert_eq!(to_value("\"hi\""), Value::string("hi")); 322 | /// 323 | /// // Named composite values look a bit like rust structs: 324 | /// let value = to_value("{ a: true, b: \"hello\" }"); 325 | /// assert_eq!( 326 | /// value, 327 | /// Value::named_composite(vec![ 328 | /// ("a", Value::bool(true)), 329 | /// ("b", Value::string("hello")) 330 | /// ]) 331 | /// ); 332 | /// 333 | /// // Unnamed composite values look a bit like rust tuples: 334 | /// let value = to_value("(true, \"hello\")"); 335 | /// assert_eq!( 336 | /// value, 337 | /// Value::unnamed_composite(vec![ 338 | /// Value::bool(true), 339 | /// Value::string("hello") 340 | /// ]) 341 | /// ); 342 | /// 343 | /// // Variant values (named or unnamed) are just the above with a variant name 344 | /// // prefixed: 345 | /// let value = to_value("MyVariant { a: true, b: \"hello\" }"); 346 | /// assert_eq!( 347 | /// value, 348 | /// Value::named_variant( 349 | /// "MyVariant", 350 | /// vec![ 351 | /// ("a", Value::bool(true)), 352 | /// ("b", Value::string("hello")) 353 | /// ] 354 | /// ) 355 | /// ); 356 | /// 357 | /// // Bit sequences can be encoded from unnamed composites, but we have a 358 | /// // compact syntax for them too: 359 | /// assert_eq!( 360 | /// to_value("<0101>"), 361 | /// Value::bit_sequence(scale_bits::Bits::from_iter([false, true, false, true])) 362 | /// ); 363 | /// ``` 364 | #[cfg(feature = "from-string")] 365 | pub fn from_str(s: &str) -> (Result, ParseError>, &str) { 366 | crate::string_impls::FromStrBuilder::new().parse(s) 367 | } 368 | 369 | /// This is similar to [`from_str`], except that it returns a [`FromStrBuilder`], 370 | /// which allows for some additional configuration in how strings are parsed. 371 | /// 372 | /// # Example 373 | /// 374 | /// ```rust 375 | /// # // Example only runs when parser-ss58 feature is enabled: 376 | /// # #[cfg(not(feature = "parser-ss58"))] 377 | /// # fn main() {} 378 | /// # #[cfg(feature = "parser-ss58")] 379 | /// # fn main() { 380 | /// # 381 | /// use scale_value::Value; 382 | /// use scale_value::stringify::custom_parsers; 383 | /// 384 | /// fn to_value(str: &str) -> Value { 385 | /// scale_value::stringify::from_str_custom() 386 | /// // You can write your own custom parser, but for 387 | /// // this example, we just use some provided ones. 388 | /// .add_custom_parser(custom_parsers::parse_hex) 389 | /// // Requires the parser-ss58 feature: 390 | /// .add_custom_parser(custom_parsers::parse_ss58) 391 | /// .parse(str) 392 | /// .0 393 | /// .unwrap() 394 | /// } 395 | /// 396 | /// // Hex strings will now be parsed into unnamed composite types 397 | /// let value = to_value("(1,2,0x030405)"); 398 | /// assert_eq!( 399 | /// value, 400 | /// Value::unnamed_composite(vec![ 401 | /// Value::u128(1), 402 | /// Value::u128(2), 403 | /// Value::unnamed_composite(vec![ 404 | /// Value::u128(3), 405 | /// Value::u128(4), 406 | /// Value::u128(5), 407 | /// ]) 408 | /// ]) 409 | /// ); 410 | /// 411 | /// // ss58 addresses will also become unnamed composite types 412 | /// let value = to_value(r#"{ 413 | /// name: "Alice", 414 | /// address: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 415 | /// }"#); 416 | /// 417 | /// // Manually obtain and decode the hex value for the address: 418 | /// let addr: Vec<_> = hex::decode("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48") 419 | /// .unwrap() 420 | /// .into_iter() 421 | /// .map(|b| Value::u128(b as u128)) 422 | /// .collect(); 423 | /// 424 | /// assert_eq!( 425 | /// value, 426 | /// Value::named_composite(vec![ 427 | /// ("name", Value::string("Alice")), 428 | /// ("address", Value::unnamed_composite(addr)) 429 | /// ]) 430 | /// ) 431 | /// # 432 | /// # } 433 | /// ``` 434 | #[cfg(feature = "from-string")] 435 | pub fn from_str_custom() -> FromStrBuilder { 436 | crate::string_impls::FromStrBuilder::new() 437 | } 438 | 439 | /// Identical to calling `to_string()` on the [`crate::Value`], but here just 440 | /// to make it a little more obvious that this is the inverse of [`from_str`]. 441 | /// 442 | /// # Panics 443 | /// 444 | /// Panics if a `Primitive::U256`/`Primitive::I256` are a part of the value, 445 | /// since we cannot properly format and parse those at the moment. 446 | pub fn to_string(value: &crate::Value) -> String { 447 | value.to_string() 448 | } 449 | 450 | /// Format a [`crate::Value`] to a string, writing it to the provided writer. 451 | /// 452 | /// # Example 453 | /// 454 | /// ```rust 455 | /// use scale_value::{Value, value}; 456 | /// 457 | /// let value = value!({ 458 | /// foo: true, 459 | /// bar: "hello" 460 | /// }); 461 | /// 462 | /// // Write the ourput to a string or any other writer. 463 | /// let mut s = String::new(); 464 | /// scale_value::stringify::to_writer(&value, &mut s).unwrap(); 465 | /// 466 | /// assert_eq!(s, r#"{ foo: true, bar: "hello" }"#) 467 | /// ``` 468 | pub fn to_writer( 469 | value: &crate::Value, 470 | writer: W, 471 | ) -> core::fmt::Result { 472 | crate::string_impls::ToWriterBuilder::new().write(value, writer) 473 | } 474 | 475 | /// Format a [`crate::Value`] to a string. Several options can be configured in the 476 | /// process, such as the indentation, custom formatters, printing of context, and 477 | /// style (compact or spaced). 478 | /// 479 | /// # Example 480 | /// 481 | /// ```rust 482 | /// use scale_value::{Value, ValueDef, Primitive, value}; 483 | /// use scale_value::stringify::custom_formatters::format_hex; 484 | /// use core::fmt::Write; 485 | /// 486 | /// let value = value!({ 487 | /// foo: true, 488 | /// bar: (1u8,2u8,3u8,4u8) 489 | /// }); 490 | /// 491 | /// let mut s = String::new(); 492 | /// 493 | /// fn capitalise_bools(v: &Value, w: &mut W) -> Option { 494 | /// if let ValueDef::Primitive(Primitive::Bool(b)) = &v.value { 495 | /// match b { 496 | /// true => Some(write!(w, "TRUE")), 497 | /// false => Some(write!(w, "FALSE")) 498 | /// } 499 | /// } else { 500 | /// None 501 | /// } 502 | /// } 503 | /// 504 | /// scale_value::stringify::to_writer_custom() 505 | /// .compact() 506 | /// .add_custom_formatter(|v, w| format_hex(v, w)) 507 | /// .add_custom_formatter(|v, w| capitalise_bools(v, w)) 508 | /// .write(&value, &mut s); 509 | /// 510 | /// assert_eq!(s, r#"{foo:TRUE,bar:0x01020304}"#) 511 | /// ``` 512 | pub fn to_writer_custom() -> ToWriterBuilder { 513 | crate::string_impls::ToWriterBuilder::new() 514 | } 515 | } 516 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | /// Construct a `scale_value::Value` 17 | /// 18 | /// 19 | /// Supports unnamed and named composites and variants: 20 | /// ``` 21 | /// use scale_value::value; 22 | /// 23 | /// let val = value!({ 24 | /// name: "localhost", 25 | /// address: V4(127, 0, 0, 1), 26 | /// payload: { 27 | /// bytes: (255, 3, 4, 9), 28 | /// method: ("Post", 3000), 29 | /// }, 30 | /// }); 31 | /// 32 | /// ``` 33 | /// Values can be nested in each other: 34 | /// ``` 35 | /// use scale_value::value; 36 | /// 37 | /// let data_value = value!((1, v1(1, 2), 3)); 38 | /// let val = value!(POST { data: data_value }); 39 | /// ``` 40 | /// Trailing commas are optional. 41 | #[macro_export(local_inner_macros)] 42 | macro_rules! value { 43 | ($($tt:tt)*) => { 44 | value_internal!($($tt)*) 45 | }; 46 | } 47 | 48 | /// All patterns can be grouped into 4 main patterns: 49 | /// 50 | /// ### `value_internal!(@unnamed [ELEMENTS] (REST))` 51 | /// 52 | /// checks `REST` for certain patterns and if a suitable element pattern is found, 53 | /// the element is added to the comma seperated `ELEMENTS` list. When `REST` is empty, 54 | /// we collect all `ELEMENTS` into a Vec of values. 55 | /// 56 | /// ### `value_internal!(@named [KEYVALUEPAIRS] (PARTIALKEY) (REST))` 57 | /// 58 | /// goes over the REST tokens, to build up the PARTIALKEY until it is a proper KEY. 59 | /// This happens as soon as a colon `:` token is encountered, then we switch to the next pattern: 60 | /// 61 | /// ### `value_internal!(@named [KEYVALUEPAIRS] [KEY] (REST))` 62 | /// 63 | /// The square brackets now indicate that the key is fully formed. Now REST is scanned for the next 64 | /// `VALUE`. Together with the `KEY`, they are added as a key value pair tuple into the KEYVALUEPAIRS list. 65 | /// At that point we switch back to the partial key pattern above, e.g. `value_internal!(@named [KEYVALUEPAIRS] () (REST))` 66 | /// now with a new empty partial key that has to be filled. 67 | /// When `REST` is empty, we collect all `KEYVALUEPAIRS` into a Vec of key-value tuples. 68 | /// 69 | /// ### `value_internal!(BASEPATTERN)` 70 | /// 71 | /// These patterns check if the input represents a composite or variant type or can be made into a valid `$crate::Value`. 72 | #[macro_export(local_inner_macros)] 73 | #[doc(hidden)] 74 | macro_rules! value_internal { 75 | 76 | //////////////////////////////////////////////////////////////////////////// 77 | // collecting unnamed fields 78 | //////////////////////////////////////////////////////////////////////////// 79 | 80 | // done, put all the fields in a vec 81 | (@unnamed [$($e:expr, )*] ()) => { vec_wrapper![$($e, )*] }; 82 | 83 | // Next value is an unnamed composite 84 | (@unnamed [$($e:expr, )*] (($($array:tt)*) $($rest:tt)*)) => { 85 | value_internal!(@unnamed [$($e, )*] (value_internal!(($($array)*))) $($rest)*) 86 | }; 87 | 88 | // Next value is an unnamed variant 89 | (@unnamed [$($e:expr, )*] ($variant:ident ($($array:tt)*) $($rest:tt)*)) => { 90 | value_internal!(@unnamed [$($e, )*] (value_internal!($variant ($($array)*))) $($rest)*) 91 | }; 92 | 93 | // Next value is a named composite 94 | (@unnamed [$($e:expr, )*] ({$($map:tt)*} $($rest:tt)*)) => { 95 | value_internal!(@unnamed [$($e, )*] (value_internal!({$($map)*})) $($rest)*) 96 | }; 97 | 98 | // Next value is a named variant 99 | (@unnamed [$($e:expr, )*] ($variant:ident {$($map:tt)*} $($rest:tt)*)) => { 100 | value_internal!(@unnamed [$($e, )*] (value_internal!($variant {$($map)*})) $($rest)*) 101 | }; 102 | 103 | // Insert the current entry followed by trailing comma 104 | (@unnamed [$($e:expr, )*] ($value:expr) , $($rest:tt)*) => { 105 | value_internal!(@unnamed [$($e, )* $value , ] ($($rest)*)) 106 | }; 107 | 108 | // Current entry followed by unexpected token. 109 | // There needs to be a comma, which would match the previous matcher or no further tokens at all matching the next matcher 110 | (@unnamed [$($e:expr, )*] ($value:expr) $unexpected:tt $($rest:tt)*) => { 111 | let token = core::stringify!($unexpected); 112 | compile_error!("unexpected token after expression: {}", token); 113 | }; 114 | 115 | // Insert the last entry without trailing comma 116 | (@unnamed [$($e:expr, )*] ($value:expr)) => { 117 | vec_wrapper![ $($e, )* value_internal!($value) ] 118 | }; 119 | 120 | // Next value is an expression followed by comma 121 | (@unnamed [$($e:expr, )*] ($value:expr , $($rest:tt)*)) => { 122 | value_internal!(@unnamed [$($e, )*] (value_internal!($value)) , $($rest)*) 123 | }; 124 | 125 | //////////////////////////////////////////////////////////////////////////// 126 | // collecting named fields 127 | //////////////////////////////////////////////////////////////////////////// 128 | 129 | // done, put all the key value pairs in a vec 130 | (@named [$(($k:expr, $v:expr), )*] () ()) => { vec_wrapper![ $(($k, $v), )* ] }; 131 | 132 | // Insert the current entry followed by trailing comma. 133 | (@named [$(($k:expr, $v:expr), )*] [$key:expr] ($value:expr) , $($rest:tt)*) => { 134 | { 135 | let field_name = literal_aware_stringify!($key); 136 | value_internal!(@named [$(($k, $v), )* (field_name, $value), ] () ($($rest)*)) 137 | } 138 | }; 139 | 140 | // Current entry followed by unexpected token. 141 | // There needs to be a comma, which would match the previous matcher or no further tokens at all matching the next matcher 142 | (@named [$(($k:expr, $v:expr), )*] [$key:expr] ($value:expr) $unexpected:tt $($rest:tt)*) => { 143 | let token = core::stringify!($unexpected); 144 | compile_error!("unexpected token after expression: {}", token); 145 | }; 146 | 147 | // Insert the last entry without trailing comma. 148 | (@named [$(($k:expr, $v:expr), )*] [$key:expr] ($value:expr)) => { 149 | { 150 | let field_name = literal_aware_stringify!($key); 151 | vec_wrapper![ $(($k, $v), )* (field_name, $value) ] 152 | } 153 | }; 154 | 155 | // Next value is an unnamed composite 156 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: ($($array:tt)*) $($rest:tt)*)) => { 157 | value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!(($($array)*))) $($rest)*) 158 | }; 159 | 160 | // Next value is an unnamed variant 161 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $variant:ident ($($array:tt)*) $($rest:tt)*)) => { 162 | value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($variant ($($array)*))) $($rest)*) 163 | }; 164 | 165 | // Next value is a named composite 166 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: {$($map:tt)*} $($rest:tt)*)) => { 167 | value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!({$($map)*})) $($rest)*) 168 | }; 169 | 170 | // Next value is a named variant 171 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $variant:ident {$($map:tt)*} $($rest:tt)*)) => { 172 | value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($variant {$($map)*})) $($rest)*) 173 | }; 174 | 175 | // // Next value is an expression followed by comma 176 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $value:expr , $($rest:tt)*)) => { 177 | value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($value)) , $($rest)*) 178 | }; 179 | 180 | // Last value is an expression with no trailing comma 181 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) (: $value:expr)) => { 182 | value_internal!(@named [$(($k, $v), )*] [$key] (value_internal!($value))) 183 | }; 184 | 185 | // Error pattern: Missing value for last entry 186 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) (:)) => { 187 | compile_error!("missing value for last entry"); 188 | }; 189 | 190 | // Error pattern: Missing colon and value for last entry 191 | (@named [$(($k:expr, $v:expr), )*] ($key:expr) ()) => { 192 | compile_error!("missing colon and value for last entry"); 193 | }; 194 | 195 | // Error pattern: colon as first token 196 | (@named [$(($k:expr, $v:expr), )*] () (: $($rest:tt)*)) => { 197 | compile_error!("colon in wrong position"); 198 | }; 199 | 200 | // Error pattern: comma inside key 201 | (@named [$(($k:expr, $v:expr), )*] ($($key:tt)*) (, $($rest:tt)*)) => { 202 | compile_error!("comma in key of named composite"); 203 | }; 204 | 205 | (@named [$(($k:expr, $v:expr), )*] () (($key:expr) : $($rest:tt)*)) => { 206 | value_internal!(@named [$(($k, $v), )*] ($key) (: $($rest)*)) 207 | }; 208 | 209 | // add a token into the current key. 210 | (@named [$(($k:expr, $v:expr), )*] ($($key:tt)*) ($tt:tt $($rest:tt)*)) => { 211 | value_internal!(@named [$(($k, $v), )*] ($($key)* $tt) ($($rest)*)) 212 | }; 213 | 214 | //////////////////////////////////////////////////////////////////////////// 215 | // Main implementation of patterns: 216 | //////////////////////////////////////////////////////////////////////////// 217 | 218 | // empty composites: 219 | () => { 220 | $crate::Value::unnamed_composite(Vec::<$crate::Value>::new()) 221 | }; 222 | (()) => { 223 | $crate::Value::unnamed_composite(Vec::<$crate::Value>::new()) 224 | }; 225 | ({}) => { 226 | $crate::Value::named_composite(Vec::<(String, $crate::Value)>::new()) 227 | }; 228 | 229 | // named composites e.g. { age: 1, nice: false } 230 | ({ $($tt:tt)* }) => { 231 | { 232 | let fields: Vec::<(String, $crate::Value)> = value_internal!(@named [] () ($($tt)*)); 233 | $crate::Value::named_composite(fields) 234 | } 235 | }; 236 | 237 | // named variants e.g. v1 { age: 1, nice: false } 238 | ($variant:ident { $($tt:tt)* }) => { 239 | { 240 | let variant_name = literal_aware_stringify!($variant); 241 | let fields: Vec::<(String, $crate::Value)> = value_internal!(@named [] () ($($tt)*)); 242 | $crate::Value::named_variant(variant_name,fields) 243 | } 244 | }; 245 | 246 | // unnamed composites with (..) syntax e.g. (1,"hello",3) 247 | (( $($tt:tt)* )) => { 248 | { 249 | let fields = value_internal!(@unnamed [] ($($tt)*)); 250 | $crate::Value::unnamed_composite(fields) 251 | } 252 | }; 253 | 254 | // unnamed variants e.g. v1 (1,2,3,4) 255 | ($variant:ident ( $($tt:tt)* )) => { 256 | { 257 | let variant_name = literal_aware_stringify!($variant); 258 | let fields = value_internal!(@unnamed [] ($($tt)*)); 259 | $crate::Value::unnamed_variant(variant_name,fields) 260 | } 261 | }; 262 | 263 | // any other expressions 264 | ($val:expr) => { 265 | $crate::Value::from($val) 266 | }; 267 | } 268 | 269 | // The value_internal macro above cannot invoke vec directly because it uses 270 | // local_inner_macros. A vec invocation there would resolve to $crate::vec. 271 | #[macro_export] 272 | #[doc(hidden)] 273 | macro_rules! vec_wrapper { 274 | ($($content:tt)*) => { 275 | vec![$($content)*] 276 | }; 277 | } 278 | 279 | /// Just using core::stringify!($key).to_string() on a $key that is a string literal 280 | /// causes doubled quotes to appear. This macro fixes that. 281 | #[macro_export] 282 | #[doc(hidden)] 283 | macro_rules! literal_aware_stringify { 284 | ($tt:literal) => { 285 | $tt.to_string() 286 | }; 287 | ($($tt:tt)*) => { 288 | stringify!($($tt)*).to_string() 289 | }; 290 | } 291 | 292 | #[cfg(test)] 293 | #[macro_use] 294 | mod test { 295 | use crate::prelude::*; 296 | use crate::{value, Value}; 297 | 298 | #[test] 299 | fn macro_tests() { 300 | // primitives: 301 | assert_eq!(value!(1), Value::from(1)); 302 | assert_eq!(value!(-122193223i64), Value::from(-122193223i64)); 303 | assert_eq!(value!(89usize), Value::from(89usize)); 304 | assert_eq!(value!(false), Value::from(false)); 305 | assert_eq!(value!(true), Value::from(true)); 306 | assert_eq!(value!('h'), Value::from('h')); 307 | assert_eq!(value!("Hello"), Value::from("Hello")); 308 | assert_eq!(value!("Hello".to_string()), Value::from("Hello")); 309 | let s = "Hello".to_string(); 310 | assert_eq!(value!(s), Value::from("Hello")); 311 | 312 | // unnamed composites: 313 | let unnamed_composite = 314 | Value::unnamed_composite([Value::from(1), Value::from("nice"), Value::from('t')]); 315 | 316 | assert_eq!(value!((1, "nice", 't')), unnamed_composite); 317 | assert_eq!(value!((1, "nice", 't',)), unnamed_composite); 318 | 319 | let empty_composite = Value::unnamed_composite([]); 320 | assert_eq!(value!(()), empty_composite); 321 | 322 | // named composites: 323 | let named_composite = 324 | Value::named_composite([("num", Value::from(3)), ("item", Value::from("tea"))]); 325 | assert_eq!(value!({num: 3, item: "tea"}), named_composite); 326 | assert_eq!(value!({num: 3, item: "tea", }), named_composite); 327 | // variants: 328 | let variant_no_fields = Value::variant("v1", crate::Composite::Unnamed(vec![])); 329 | assert_eq!(value!(v1()), variant_no_fields); 330 | let named_variant = Value::variant( 331 | "V2", 332 | crate::Composite::named([("num", Value::from(3)), ("item", Value::from("tea"))]), 333 | ); 334 | assert_eq!(value!(V2 { num: 3, item: "tea" }), named_variant); 335 | // string literal as key: 336 | let value = value!({ "string key": 123 }); 337 | assert_eq!(value, Value::named_composite([("string key", Value::from(123))])); 338 | // wild combination, just check if compiles: 339 | let _ = value!({ unnamed: unnamed_composite, vals: (v1{name: "berry", age: 34}, named_variant), named: named_composite }); 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Expose a consistent prelude, whether std or no-std (this is needed 17 | // because no-std prelude doesn't contain `alloc` things), so we add 18 | // those back in where needed. This should _not_ expose anything that's 19 | // not a part of the `std` prelude already; import such things as needed 20 | // from `core` or `alloc`. 21 | pub use prelude_contents::*; 22 | 23 | mod prelude_contents { 24 | pub use core::prelude::rust_2021::*; 25 | 26 | // The core prelude doesn't include things from 27 | // `alloc` by default, so add the ones we need that 28 | // are otherwise exposed via the std prelude. 29 | pub use alloc::borrow::ToOwned; 30 | pub use alloc::string::{String, ToString}; 31 | pub use alloc::vec::Vec; 32 | pub use alloc::{boxed::Box, format, vec}; 33 | } 34 | -------------------------------------------------------------------------------- /src/scale_impls/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod decode; 17 | mod encode; 18 | mod tracing_decoder; 19 | 20 | pub use decode::{decode_composite_as_fields, decode_value_as_type, DecodeError, ValueVisitor}; 21 | pub use tracing_decoder::{TraceDecodingError, TraceDecodingVisitor}; 22 | -------------------------------------------------------------------------------- /src/scale_impls/tracing_decoder/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2024 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use super::path::Path; 17 | use crate::prelude::*; 18 | use crate::scale::DecodeError; 19 | use crate::string_impls::format_hex; 20 | use crate::Value; 21 | use core::fmt::Write; 22 | 23 | /// An error encountered when decoding some bytes using the [`crate::scale::tracing`] module. 24 | #[derive(Clone, Debug)] 25 | pub struct TraceDecodingError { 26 | inner: TraceDecodingErrorInner, 27 | } 28 | 29 | impl TraceDecodingError { 30 | pub(crate) fn map_decoded_so_far( 31 | self, 32 | f: impl FnOnce(Val) -> NewVal, 33 | ) -> TraceDecodingError { 34 | match self.inner { 35 | TraceDecodingErrorInner::FromDecodeError(e) => { 36 | TraceDecodingErrorInner::FromDecodeError(e).into() 37 | } 38 | TraceDecodingErrorInner::FromVisitor(e) => { 39 | TraceDecodingErrorInner::FromVisitor(VisitorError { 40 | at: e.at, 41 | decode_error: e.decode_error, 42 | decoded_so_far: f(e.decoded_so_far), 43 | }) 44 | .into() 45 | } 46 | } 47 | } 48 | pub(crate) fn with_outer_context( 49 | self, 50 | outer_path: impl FnOnce() -> Path, 51 | default_outer_value: impl FnOnce() -> NewVal, 52 | into_outer_value: impl FnOnce(Val) -> NewVal, 53 | ) -> TraceDecodingError { 54 | match self.inner { 55 | TraceDecodingErrorInner::FromDecodeError(e) => { 56 | TraceDecodingErrorInner::FromVisitor(VisitorError { 57 | at: outer_path(), 58 | decoded_so_far: default_outer_value(), 59 | decode_error: e, 60 | }) 61 | .into() 62 | } 63 | TraceDecodingErrorInner::FromVisitor(e) => { 64 | TraceDecodingErrorInner::FromVisitor(VisitorError { 65 | at: e.at, 66 | decoded_so_far: into_outer_value(e.decoded_so_far), 67 | decode_error: e.decode_error, 68 | }) 69 | .into() 70 | } 71 | } 72 | } 73 | } 74 | 75 | impl From> for TraceDecodingError { 76 | fn from(value: TraceDecodingErrorInner) -> Self { 77 | TraceDecodingError { inner: value } 78 | } 79 | } 80 | 81 | #[derive(Clone, Debug)] 82 | enum TraceDecodingErrorInner { 83 | FromDecodeError(DecodeError), 84 | FromVisitor(VisitorError), 85 | } 86 | 87 | #[derive(Clone, Debug)] 88 | struct VisitorError { 89 | at: Path, 90 | decoded_so_far: Val, 91 | decode_error: DecodeError, 92 | } 93 | 94 | impl core::fmt::Display for TraceDecodingError> { 95 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 96 | match &self.inner { 97 | TraceDecodingErrorInner::FromDecodeError(e) => { 98 | write!(f, "Error decoding value: {e}") 99 | } 100 | TraceDecodingErrorInner::FromVisitor(e) => { 101 | write!( 102 | f, 103 | "Error decoding value at {}: {}\nDecoded so far:\n\n", 104 | e.at, e.decode_error, 105 | )?; 106 | display_value_with_typeid(f, &e.decoded_so_far) 107 | } 108 | } 109 | } 110 | } 111 | 112 | impl core::error::Error for TraceDecodingError> {} 113 | 114 | impl From for TraceDecodingError { 115 | fn from(value: DecodeError) -> Self { 116 | TraceDecodingErrorInner::FromDecodeError(value).into() 117 | } 118 | } 119 | 120 | impl From for TraceDecodingError { 121 | fn from(value: codec::Error) -> Self { 122 | TraceDecodingErrorInner::FromDecodeError(value.into()).into() 123 | } 124 | } 125 | 126 | fn display_value_with_typeid( 127 | f: &mut core::fmt::Formatter<'_>, 128 | value: &Value, 129 | ) -> core::fmt::Result { 130 | crate::string_impls::ToWriterBuilder::new() 131 | .pretty() 132 | .format_context(|type_id, writer: &mut &mut core::fmt::Formatter| { 133 | write!(writer, "{type_id:?}") 134 | }) 135 | .add_custom_formatter(|value, writer| format_hex(value, writer)) 136 | .write(value, f) 137 | } 138 | -------------------------------------------------------------------------------- /src/scale_impls/tracing_decoder/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2024 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod error; 17 | mod path; 18 | mod visitor; 19 | 20 | pub use error::TraceDecodingError; 21 | pub use visitor::TraceDecodingVisitor; 22 | -------------------------------------------------------------------------------- /src/scale_impls/tracing_decoder/path.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2024 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::prelude::*; 17 | 18 | // [jsdw] This could be internally turned into a linkedlist or something 19 | // to make the "clone and append" faster. Not too concerned right now though 20 | // since the tracing visitor it's used for isn't built for speed. 21 | #[derive(Clone, Debug)] 22 | pub struct Path(Vec); 23 | 24 | impl Path { 25 | pub fn new() -> Path { 26 | Path(vec![]) 27 | } 28 | pub fn at_idx(&self, idx: usize) -> Path { 29 | self.at(PathSegment::Index(idx)) 30 | } 31 | pub fn at_field(&self, field: String) -> Path { 32 | self.at(PathSegment::Field(field)) 33 | } 34 | pub fn at_variant(&self, variant: String) -> Path { 35 | self.at(PathSegment::Variant(variant)) 36 | } 37 | 38 | fn at(&self, segment: PathSegment) -> Path { 39 | let mut p = self.0.clone(); 40 | p.push(segment); 41 | Path(p) 42 | } 43 | } 44 | 45 | impl core::fmt::Display for Path { 46 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 47 | for segment in &self.0 { 48 | write!(f, ".")?; 49 | match segment { 50 | PathSegment::Index(idx) => write!(f, "[{idx}]")?, 51 | PathSegment::Field(field) => write!(f, "{field}")?, 52 | PathSegment::Variant(variant) => write!(f, "{variant}")?, 53 | } 54 | } 55 | Ok(()) 56 | } 57 | } 58 | 59 | #[derive(Clone, Debug)] 60 | enum PathSegment { 61 | Field(String), 62 | Index(usize), 63 | Variant(String), 64 | } 65 | -------------------------------------------------------------------------------- /src/scale_impls/tracing_decoder/visitor.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2024 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::prelude::*; 17 | 18 | use super::error::TraceDecodingError; 19 | use super::path::Path; 20 | use crate::{Composite, Primitive, Value, ValueDef, Variant}; 21 | use core::marker::PhantomData; 22 | use scale_decode::visitor::TypeIdFor; 23 | use scale_type_resolver::TypeResolver; 24 | 25 | /// A visitor that will attempt to decode some bytes into a [`crate::Value`], 26 | /// returning a detailed error of where the decoding fails if it does. 27 | pub struct TraceDecodingVisitor { 28 | path: Path, 29 | marker: PhantomData, 30 | } 31 | 32 | impl TraceDecodingVisitor { 33 | fn at_path(&self, path: Path) -> Self { 34 | TraceDecodingVisitor { path, marker: PhantomData } 35 | } 36 | fn at_idx(&self, idx: usize) -> Self { 37 | self.at_path(self.path.at_idx(idx)) 38 | } 39 | fn at_field(&self, field: String) -> Self { 40 | self.at_path(self.path.at_field(field)) 41 | } 42 | } 43 | 44 | impl Default for TraceDecodingVisitor { 45 | fn default() -> Self { 46 | Self::new() 47 | } 48 | } 49 | 50 | impl TraceDecodingVisitor { 51 | /// Construct a new [`TraceDecodingVisitor`]. 52 | pub fn new() -> Self { 53 | TraceDecodingVisitor { path: Path::new(), marker: PhantomData } 54 | } 55 | } 56 | 57 | macro_rules! to_unnamed_composite { 58 | ($self:ident, $value:ident) => {{ 59 | let mut f = move || { 60 | let mut idx = 0; 61 | let mut vals = Vec::with_capacity($value.remaining()); 62 | 63 | while let Some(val) = $value.decode_item($self.at_idx(idx)) { 64 | match val { 65 | Err(e) => { 66 | let merged_error = e.with_outer_context( 67 | || $self.path.at_idx(idx), 68 | || Composite::Unnamed(vals.clone()), 69 | |inner_value| { 70 | let mut vals = vals.clone(); 71 | vals.push(inner_value); 72 | Composite::Unnamed(vals) 73 | }, 74 | ); 75 | return Err(merged_error); 76 | } 77 | Ok(v) => { 78 | vals.push(v); 79 | } 80 | } 81 | 82 | idx += 1; 83 | } 84 | 85 | Ok::<_, TraceDecodingError<_>>(Composite::Unnamed(vals)) 86 | }; 87 | 88 | f() 89 | }}; 90 | } 91 | 92 | macro_rules! to_unnamed_composite_value { 93 | ($self:ident, $value:ident, $type_id:ident) => {{ 94 | let composite = to_unnamed_composite!($self, $value).map_err(|e| { 95 | e.map_decoded_so_far(|c| Value { 96 | value: ValueDef::Composite(c), 97 | context: $type_id.clone(), 98 | }) 99 | })?; 100 | 101 | Ok(Value { value: ValueDef::Composite(composite), context: $type_id }) 102 | }}; 103 | } 104 | 105 | impl scale_decode::Visitor for TraceDecodingVisitor 106 | where 107 | Resolver: TypeResolver, 108 | { 109 | type Value<'scale, 'resolver> = Value>; 110 | type Error = TraceDecodingError>>; 111 | type TypeResolver = Resolver; 112 | 113 | fn visit_bool<'scale, 'resolver>( 114 | self, 115 | value: bool, 116 | type_id: TypeIdFor, 117 | ) -> Result, Self::Error> { 118 | Ok(Value::with_context(ValueDef::Primitive(Primitive::Bool(value)), type_id)) 119 | } 120 | 121 | fn visit_char<'scale, 'resolver>( 122 | self, 123 | value: char, 124 | type_id: TypeIdFor, 125 | ) -> Result, Self::Error> { 126 | Ok(Value::with_context(ValueDef::Primitive(Primitive::Char(value)), type_id)) 127 | } 128 | 129 | fn visit_u8<'scale, 'resolver>( 130 | self, 131 | value: u8, 132 | type_id: TypeIdFor, 133 | ) -> Result, Self::Error> { 134 | self.visit_u128(value as u128, type_id) 135 | } 136 | 137 | fn visit_u16<'scale, 'resolver>( 138 | self, 139 | value: u16, 140 | type_id: TypeIdFor, 141 | ) -> Result, Self::Error> { 142 | self.visit_u128(value as u128, type_id) 143 | } 144 | 145 | fn visit_u32<'scale, 'resolver>( 146 | self, 147 | value: u32, 148 | type_id: TypeIdFor, 149 | ) -> Result, Self::Error> { 150 | self.visit_u128(value as u128, type_id) 151 | } 152 | 153 | fn visit_u64<'scale, 'resolver>( 154 | self, 155 | value: u64, 156 | type_id: TypeIdFor, 157 | ) -> Result, Self::Error> { 158 | self.visit_u128(value as u128, type_id) 159 | } 160 | 161 | fn visit_u128<'scale, 'resolver>( 162 | self, 163 | value: u128, 164 | type_id: TypeIdFor, 165 | ) -> Result, Self::Error> { 166 | Ok(Value::with_context(ValueDef::Primitive(Primitive::U128(value)), type_id)) 167 | } 168 | 169 | fn visit_u256<'info>( 170 | self, 171 | value: &[u8; 32], 172 | type_id: TypeIdFor, 173 | ) -> Result, Self::Error> { 174 | Ok(Value::with_context(ValueDef::Primitive(Primitive::U256(*value)), type_id)) 175 | } 176 | 177 | fn visit_i8<'scale, 'resolver>( 178 | self, 179 | value: i8, 180 | type_id: TypeIdFor, 181 | ) -> Result, Self::Error> { 182 | self.visit_i128(value as i128, type_id) 183 | } 184 | 185 | fn visit_i16<'scale, 'resolver>( 186 | self, 187 | value: i16, 188 | type_id: TypeIdFor, 189 | ) -> Result, Self::Error> { 190 | self.visit_i128(value as i128, type_id) 191 | } 192 | 193 | fn visit_i32<'scale, 'resolver>( 194 | self, 195 | value: i32, 196 | type_id: TypeIdFor, 197 | ) -> Result, Self::Error> { 198 | self.visit_i128(value as i128, type_id) 199 | } 200 | 201 | fn visit_i64<'scale, 'resolver>( 202 | self, 203 | value: i64, 204 | type_id: TypeIdFor, 205 | ) -> Result, Self::Error> { 206 | self.visit_i128(value as i128, type_id) 207 | } 208 | 209 | fn visit_i128<'scale, 'resolver>( 210 | self, 211 | value: i128, 212 | type_id: TypeIdFor, 213 | ) -> Result, Self::Error> { 214 | Ok(Value::with_context(ValueDef::Primitive(Primitive::I128(value)), type_id)) 215 | } 216 | 217 | fn visit_i256<'info>( 218 | self, 219 | value: &[u8; 32], 220 | type_id: TypeIdFor, 221 | ) -> Result, Self::Error> { 222 | Ok(Value::with_context(ValueDef::Primitive(Primitive::I256(*value)), type_id)) 223 | } 224 | 225 | fn visit_bitsequence<'scale, 'info>( 226 | self, 227 | value: &mut scale_decode::visitor::types::BitSequence<'scale>, 228 | type_id: TypeIdFor, 229 | ) -> Result, Self::Error> { 230 | let bits: Result<_, _> = value.decode()?.collect(); 231 | Ok(Value::with_context(ValueDef::BitSequence(bits?), type_id)) 232 | } 233 | 234 | fn visit_str<'scale, 'info>( 235 | self, 236 | value: &mut scale_decode::visitor::types::Str<'scale>, 237 | type_id: TypeIdFor, 238 | ) -> Result, Self::Error> { 239 | Ok(Value::with_context( 240 | ValueDef::Primitive(Primitive::String(value.as_str()?.to_owned())), 241 | type_id, 242 | )) 243 | } 244 | 245 | fn visit_sequence<'scale, 'resolver>( 246 | self, 247 | value: &mut scale_decode::visitor::types::Sequence<'scale, 'resolver, Self::TypeResolver>, 248 | type_id: TypeIdFor, 249 | ) -> Result, Self::Error> { 250 | to_unnamed_composite_value!(self, value, type_id) 251 | } 252 | 253 | fn visit_array<'scale, 'resolver>( 254 | self, 255 | value: &mut scale_decode::visitor::types::Array<'scale, 'resolver, Self::TypeResolver>, 256 | type_id: scale_decode::visitor::TypeIdFor, 257 | ) -> Result, Self::Error> { 258 | to_unnamed_composite_value!(self, value, type_id) 259 | } 260 | 261 | fn visit_tuple<'scale, 'resolver>( 262 | self, 263 | value: &mut scale_decode::visitor::types::Tuple<'scale, 'resolver, Self::TypeResolver>, 264 | type_id: TypeIdFor, 265 | ) -> Result, Self::Error> { 266 | to_unnamed_composite_value!(self, value, type_id) 267 | } 268 | 269 | fn visit_variant<'scale, 'resolver>( 270 | self, 271 | value: &mut scale_decode::visitor::types::Variant<'scale, 'resolver, Self::TypeResolver>, 272 | type_id: TypeIdFor, 273 | ) -> Result, Self::Error> { 274 | let variant_name = value.name(); 275 | let path = self.path.at_variant(variant_name.to_owned()); 276 | let values = visit_composite(&self, value.fields()); 277 | match values { 278 | Err(e) => { 279 | let merged_error = e.with_outer_context( 280 | || path.clone(), 281 | || Value { 282 | value: ValueDef::Variant(Variant { 283 | name: variant_name.to_owned(), 284 | values: Composite::Unnamed(vec![]), 285 | }), 286 | context: type_id.clone(), 287 | }, 288 | |inner_value| Value { 289 | value: ValueDef::Variant(Variant { 290 | name: variant_name.to_owned(), 291 | values: inner_value, 292 | }), 293 | context: type_id.clone(), 294 | }, 295 | ); 296 | Err(merged_error) 297 | } 298 | Ok(values) => Ok(Value { 299 | value: ValueDef::Variant(Variant { name: variant_name.to_owned(), values }), 300 | context: type_id, 301 | }), 302 | } 303 | } 304 | 305 | fn visit_composite<'scale, 'resolver>( 306 | self, 307 | value: &mut scale_decode::visitor::types::Composite<'scale, 'resolver, Self::TypeResolver>, 308 | type_id: TypeIdFor, 309 | ) -> Result, Self::Error> { 310 | let composite_vals = visit_composite(&self, value).map_err(|e| { 311 | e.map_decoded_so_far(|c| Value { 312 | value: ValueDef::Composite(c), 313 | context: type_id.clone(), 314 | }) 315 | })?; 316 | 317 | Ok(Value { value: ValueDef::Composite(composite_vals), context: type_id }) 318 | } 319 | } 320 | 321 | fn visit_composite( 322 | this: &TraceDecodingVisitor, 323 | value: &mut scale_decode::visitor::types::Composite<'_, '_, R>, 324 | ) -> Result, TraceDecodingError>> 325 | where 326 | R: TypeResolver, 327 | { 328 | let len = value.remaining(); 329 | 330 | // if no fields, we'll always assume unnamed. 331 | let named = len > 0 && !value.has_unnamed_fields(); 332 | 333 | // if unnamed, treat like array/tuple/sequence. 334 | if !named { 335 | return to_unnamed_composite!(this, value); 336 | } 337 | 338 | // otherwise, treat as a named struct. 339 | let mut vals = Vec::with_capacity(len); 340 | let mut name = value.peek_name().unwrap_or(""); 341 | 342 | while let Some(val) = value.decode_item(this.at_field(name.to_owned())) { 343 | match val { 344 | Err(e) => { 345 | let merged_error = e.with_outer_context( 346 | || this.path.at_field(name.to_owned()), 347 | || Composite::Named(vals.clone()), 348 | |inner_value| { 349 | let mut vals = vals.clone(); 350 | vals.push((name.to_owned(), inner_value)); 351 | Composite::Named(vals) 352 | }, 353 | ); 354 | return Err(merged_error); 355 | } 356 | Ok(v) => { 357 | vals.push((name.to_owned(), v)); 358 | } 359 | } 360 | 361 | name = value.peek_name().unwrap_or(""); 362 | } 363 | 364 | Ok(Composite::Named(vals)) 365 | } 366 | -------------------------------------------------------------------------------- /src/serde_impls/bitvec_helpers.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! This module implements the [`serde::Deserializer`] (note the 'r') trait on our Value enum. 17 | //! 18 | //! A deserializer is a thing which implements methods like `deserialize_i128`. Each of these 19 | //! methods serves as a hint about what the thing calling it (probably a thing implementing 20 | //! [`serde::Deserialize`]) actually wants back. The methods are given a "visitor" which actually accepts 21 | //! values back. We might not give the visitor back the value that it hinted that it wanted, but 22 | //! it's up to the visitor to do its best to accept what it's handed, or reject it if it's simply 23 | //! not going to work out. 24 | 25 | use super::deserializer::DeserializerError; 26 | use crate::prelude::*; 27 | use crate::{BitSequence, Composite, ValueDef}; 28 | use serde::{ 29 | de::{value::MapDeserializer, MapAccess, Visitor}, 30 | ser::SerializeMap, 31 | Serializer, 32 | }; 33 | 34 | /// We use this identifier in a map to uniquely identify a bitvec payload, so that it can 35 | /// be differentiated from a standard [`ValueDef::Composite`] payload (which could also be a map). 36 | pub static BITVEC_SERDE_IDENT: &str = "__bitvec__values__"; 37 | 38 | /// Serialize a bitvec so that the special deserializing is compatible with it. 39 | pub fn serialize_bitvec(seq: &BitSequence, serializer: S) -> Result 40 | where 41 | S: Serializer, 42 | { 43 | let mut map = serializer.serialize_map(Some(1))?; 44 | map.serialize_entry(BITVEC_SERDE_IDENT, seq)?; 45 | map.end() 46 | } 47 | 48 | /// Turn a [`BitSequence`] into a [`MapAccess`] impl that can be handed to a visitor to be consumed. 49 | pub fn map_access<'de>(seq: BitSequence) -> impl MapAccess<'de, Error = DeserializerError> { 50 | let bools: Vec = seq.iter().collect(); 51 | MapDeserializer::new([(BITVEC_SERDE_IDENT, bools)].into_iter()) 52 | } 53 | 54 | /// This visits a map, and will extract from that either a [`ValueDef::Composite`] or a 55 | /// [`ValueDef::BitSequence`] depending on the content of the map. 56 | pub struct MapOrBitSeqVisitor; 57 | 58 | impl<'de> Visitor<'de> for MapOrBitSeqVisitor { 59 | type Value = ValueDef<()>; 60 | 61 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 62 | formatter.write_str( 63 | "a map-like type that can be decoded into a Value::BitSequence or Value::Composite", 64 | ) 65 | } 66 | 67 | fn visit_map(self, mut map: A) -> Result 68 | where 69 | A: MapAccess<'de>, 70 | { 71 | // get the first key from the map: 72 | let first_key = match map.next_key::()? { 73 | Some(key) => key, 74 | // No errors but the map appears to be empty; return an empty named composite. 75 | None => return Ok(ValueDef::Composite(Composite::Named(Vec::new()))), 76 | }; 77 | 78 | // See whether the key identifies a bitvec payload: 79 | if first_key == BITVEC_SERDE_IDENT { 80 | // We should be able to decode a vec of bools as the value, then: 81 | let bits = map.next_value::>()?; 82 | // .. and we turn that into a bitvec to return: 83 | let mut bitvec = BitSequence::new(); 84 | for bit in bits { 85 | bitvec.push(bit); 86 | } 87 | return Ok(ValueDef::BitSequence(bitvec)); 88 | } 89 | 90 | // That didn't work, so decode the first value as a Value<()> instead: 91 | let mut values = Vec::with_capacity(map.size_hint().unwrap_or(0)); 92 | values.push((first_key, map.next_value()?)); 93 | 94 | // .. and then decode all of the other key-value pairs and add them too: 95 | while let Some(key_val) = map.next_entry()? { 96 | values.push(key_val); 97 | } 98 | Ok(ValueDef::Composite(Composite::Named(values))) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/serde_impls/deserialize.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! This module implements the [`Deserialize`] (no R!) trait on our [`Value`] enum. 17 | //! 18 | //! The [`Deserialize`] trait is responsible for telling some deserializer (note the 'r') 19 | //! what sorts of values we need back in order to construst an instance of our [`Value`] type. 20 | //! Since the [`Value`] type accepts a range of possible values, it leans heavily on the 21 | //! `deserialize_any` method; this means that the deserializer needs to know what structure it's 22 | //! working with in order to be able to provide back the right values; we can't tell it what structure 23 | //! we expect. 24 | //! 25 | //! One thing we aim for is to be able to losslessly deserialize a [`Value`] into a 26 | //! [`Value`]. This would allow for partial type deserialization, for instance we might want to turn 27 | //! only part of our input into a struct, say, and leave the rest as [`Value`] types until we know what 28 | //! to do with them. 29 | 30 | use super::bitvec_helpers; 31 | use crate::prelude::*; 32 | use crate::{Composite, Primitive, Value, ValueDef, Variant}; 33 | use core::convert::TryInto; 34 | use serde::{ 35 | self, 36 | de::{Error, Visitor}, 37 | Deserialize, Deserializer, 38 | }; 39 | 40 | impl<'de> Deserialize<'de> for Value<()> { 41 | fn deserialize(deserializer: D) -> Result 42 | where 43 | D: serde::Deserializer<'de>, 44 | { 45 | let value = deserializer.deserialize_any(ValueDefVisitor)?; 46 | Ok(Value { value, context: () }) 47 | } 48 | } 49 | 50 | impl<'de> Deserialize<'de> for ValueDef<()> { 51 | fn deserialize(deserializer: D) -> Result 52 | where 53 | D: serde::Deserializer<'de>, 54 | { 55 | deserializer.deserialize_any(ValueDefVisitor) 56 | } 57 | } 58 | 59 | impl<'de> Deserialize<'de> for Primitive { 60 | fn deserialize(deserializer: D) -> Result 61 | where 62 | D: serde::Deserializer<'de>, 63 | { 64 | deserializer.deserialize_any(PrimitiveVisitor) 65 | } 66 | } 67 | 68 | impl<'de> Deserialize<'de> for Composite<()> { 69 | fn deserialize(deserializer: D) -> Result 70 | where 71 | D: serde::Deserializer<'de>, 72 | { 73 | deserializer.deserialize_any(CompositeVisitor) 74 | } 75 | } 76 | 77 | impl<'de> Deserialize<'de> for Variant<()> { 78 | fn deserialize(deserializer: D) -> Result 79 | where 80 | D: serde::Deserializer<'de>, 81 | { 82 | deserializer.deserialize_any(VariantVisitor) 83 | } 84 | } 85 | 86 | struct PrimitiveVisitor; 87 | 88 | macro_rules! visit_prim { 89 | ($name:ident $ty:ident $variant:ident) => { 90 | fn $name(self, v: $ty) -> Result 91 | where 92 | E: serde::de::Error, 93 | { 94 | Ok(Primitive::$variant(v.into())) 95 | } 96 | }; 97 | } 98 | 99 | impl<'de> Visitor<'de> for PrimitiveVisitor { 100 | type Value = Primitive; 101 | 102 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 103 | formatter.write_str("a type that can be decoded into a Primitive value") 104 | } 105 | 106 | visit_prim!(visit_bool bool Bool); 107 | visit_prim!(visit_i8 i8 I128); 108 | visit_prim!(visit_i16 i16 I128); 109 | visit_prim!(visit_i32 i32 I128); 110 | visit_prim!(visit_i64 i64 I128); 111 | visit_prim!(visit_i128 i128 I128); 112 | visit_prim!(visit_u8 u8 U128); 113 | visit_prim!(visit_u16 u16 U128); 114 | visit_prim!(visit_u32 u32 U128); 115 | visit_prim!(visit_u64 u64 U128); 116 | visit_prim!(visit_u128 u128 U128); 117 | visit_prim!(visit_char char Char); 118 | 119 | fn visit_str(self, v: &str) -> Result 120 | where 121 | E: serde::de::Error, 122 | { 123 | Ok(Primitive::String(v.into())) 124 | } 125 | 126 | fn visit_string(self, v: String) -> Result 127 | where 128 | E: serde::de::Error, 129 | { 130 | Ok(Primitive::String(v)) 131 | } 132 | 133 | fn visit_bytes(self, v: &[u8]) -> Result 134 | where 135 | E: serde::de::Error, 136 | { 137 | let val = v 138 | .try_into() 139 | .map_err(|_| serde::de::Error::invalid_type(serde::de::Unexpected::Bytes(v), &self))?; 140 | Ok(Primitive::U256(val)) 141 | } 142 | 143 | fn visit_seq(self, mut seq: A) -> Result 144 | where 145 | A: serde::de::SeqAccess<'de>, 146 | { 147 | let mut vals = Vec::new(); 148 | while let Some(el) = seq.next_element()? { 149 | vals.push(el) 150 | } 151 | let len = vals.len(); 152 | let arr = vals 153 | .try_into() 154 | .map_err(|_| serde::de::Error::invalid_length(len, &"exactly 32 bytes"))?; 155 | Ok(Primitive::U256(arr)) 156 | } 157 | } 158 | 159 | struct CompositeVisitor; 160 | 161 | impl<'de> Visitor<'de> for CompositeVisitor { 162 | type Value = Composite<()>; 163 | 164 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 165 | formatter.write_str("a type that can be decoded into a Composite value") 166 | } 167 | 168 | fn visit_bytes(self, v: &[u8]) -> Result 169 | where 170 | E: serde::de::Error, 171 | { 172 | let byte_values = v.iter().map(|&b| Value::u128(b as u128)).collect(); 173 | Ok(Composite::Unnamed(byte_values)) 174 | } 175 | 176 | fn visit_unit(self) -> Result 177 | where 178 | E: serde::de::Error, 179 | { 180 | Ok(Composite::Unnamed(Vec::new())) 181 | } 182 | 183 | fn visit_newtype_struct(self, deserializer: D) -> Result 184 | where 185 | D: Deserializer<'de>, 186 | { 187 | Composite::deserialize(deserializer) 188 | } 189 | 190 | fn visit_seq(self, mut seq: A) -> Result 191 | where 192 | A: serde::de::SeqAccess<'de>, 193 | { 194 | let mut values = Vec::with_capacity(seq.size_hint().unwrap_or(0)); 195 | while let Some(value) = seq.next_element()? { 196 | values.push(value); 197 | } 198 | Ok(Composite::Unnamed(values)) 199 | } 200 | 201 | fn visit_map(self, mut map: A) -> Result 202 | where 203 | A: serde::de::MapAccess<'de>, 204 | { 205 | let mut values = Vec::with_capacity(map.size_hint().unwrap_or(0)); 206 | while let Some(key_val) = map.next_entry()? { 207 | values.push(key_val); 208 | } 209 | Ok(Composite::Named(values)) 210 | } 211 | } 212 | 213 | struct VariantVisitor; 214 | 215 | impl<'de> Visitor<'de> for VariantVisitor { 216 | type Value = Variant<()>; 217 | 218 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 219 | formatter.write_str("a type that can be decoded into an enum Variant") 220 | } 221 | 222 | fn visit_enum(self, data: A) -> Result 223 | where 224 | A: serde::de::EnumAccess<'de>, 225 | { 226 | data.variant().and_then(|(name, variant_access)| { 227 | use serde::de::VariantAccess; 228 | // We have to ask for a particular enum type, but we don't know what type 229 | // of enum to expect (we support anything!). So, we just call the visitor method 230 | // that doesn't require any extra fields, and we know that this will just give back 231 | // whatever it can based on our impl (who knows about other impls though). 232 | let values = variant_access.newtype_variant()?; 233 | Ok(Variant { name, values }) 234 | }) 235 | } 236 | 237 | fn visit_some(self, deserializer: D) -> Result 238 | where 239 | D: serde::Deserializer<'de>, 240 | { 241 | // Wrap "Some"-like things in a Some variant. 242 | // This aligns with how we serialize these back to optionals. 243 | let inner = Value::deserialize(deserializer)?; 244 | Ok(Variant { name: "Some".to_string(), values: Composite::Unnamed(vec![inner]) }) 245 | } 246 | 247 | fn visit_none(self) -> Result 248 | where 249 | E: Error, 250 | { 251 | // If the thing is "none", wrap it in a None variant. 252 | // This aligns with how we serialize these back to optionals. 253 | Ok(Variant { name: "None".to_string(), values: Composite::Unnamed(Vec::new()) }) 254 | } 255 | 256 | fn visit_map(self, mut map: A) -> Result 257 | where 258 | A: serde::de::MapAccess<'de>, 259 | { 260 | // We support deserializing from a map that looks like 261 | // { name: "VariantName", values: [1,2,3] } into Variant + Composite::Unnamed, or 262 | // { name: "VariantName", values: { "a": 1, "b": 2 }} into Variant + Composite::Named 263 | // to line up with our Serialize impl for the Value types. 264 | let mut name = None; 265 | let mut values = None; 266 | 267 | while let Some(k) = map.next_key::()? { 268 | match &*k { 269 | "name" => { 270 | name = Some(map.next_value()?); 271 | } 272 | "values" => { 273 | values = Some(map.next_value()?); 274 | } 275 | other => return Err(A::Error::unknown_field(other, &["name", "values"])), 276 | } 277 | } 278 | 279 | if let (Some(name), Some(values)) = (name, values) { 280 | Ok(Variant { name, values }) 281 | } else { 282 | Err(A::Error::custom( 283 | "map must contain 'name' and 'values' to deserialize to a Variant", 284 | )) 285 | } 286 | } 287 | } 288 | 289 | struct ValueDefVisitor; 290 | 291 | // It gets repetitive writing out the visitor impls to delegate to the Value subtypes; 292 | // this helper makes that a little easier: 293 | macro_rules! delegate_visitor_fn { 294 | ( 295 | $visitor:ident $mapping:path, 296 | $( $name:ident($($ty:ty)?) )+ 297 | ) => { 298 | $( 299 | fn $name(self, $(v: $ty)?) -> Result 300 | where E: serde::de::Error { 301 | $visitor.$name($(v as $ty)?).map($mapping) 302 | } 303 | )+ 304 | } 305 | } 306 | 307 | impl<'de> Visitor<'de> for ValueDefVisitor { 308 | type Value = ValueDef<()>; 309 | 310 | fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 311 | formatter.write_str("a type that can be decoded into a Value") 312 | } 313 | 314 | delegate_visitor_fn!( 315 | PrimitiveVisitor ValueDef::Primitive, 316 | visit_bool(bool) 317 | visit_i8(i8) 318 | visit_i16(i16) 319 | visit_i32(i32) 320 | visit_i64(i64) 321 | visit_i128(i128) 322 | visit_u8(u8) 323 | visit_u16(u16) 324 | visit_u32(u32) 325 | visit_u64(u64) 326 | visit_u128(u128) 327 | visit_char(char) 328 | visit_str(&str) 329 | visit_string(String) 330 | ); 331 | 332 | delegate_visitor_fn!( 333 | CompositeVisitor ValueDef::Composite, 334 | visit_unit() 335 | visit_bytes(&[u8]) 336 | ); 337 | 338 | fn visit_none(self) -> Result 339 | where 340 | E: Error, 341 | { 342 | VariantVisitor.visit_none().map(ValueDef::Variant) 343 | } 344 | 345 | fn visit_some(self, deserializer: D) -> Result 346 | where 347 | D: serde::Deserializer<'de>, 348 | { 349 | VariantVisitor.visit_some(deserializer).map(ValueDef::Variant) 350 | } 351 | 352 | fn visit_newtype_struct(self, deserializer: D) -> Result 353 | where 354 | D: serde::Deserializer<'de>, 355 | { 356 | ValueDef::deserialize(deserializer) 357 | } 358 | 359 | fn visit_seq(self, seq: A) -> Result 360 | where 361 | A: serde::de::SeqAccess<'de>, 362 | { 363 | CompositeVisitor.visit_seq(seq).map(ValueDef::Composite) 364 | } 365 | 366 | fn visit_map(self, map: A) -> Result 367 | where 368 | A: serde::de::MapAccess<'de>, 369 | { 370 | // Return a bitvec or a composite type depending on the map values. 371 | bitvec_helpers::MapOrBitSeqVisitor.visit_map(map) 372 | } 373 | 374 | fn visit_enum(self, data: A) -> Result 375 | where 376 | A: serde::de::EnumAccess<'de>, 377 | { 378 | VariantVisitor.visit_enum(data).map(ValueDef::Variant) 379 | } 380 | } 381 | 382 | #[cfg(test)] 383 | mod test { 384 | use super::*; 385 | use serde_json::json; 386 | 387 | use crate::value; 388 | 389 | use super::super::DeserializerError; 390 | 391 | /// Does a value deserialize to itself? 392 | fn assert_value_isomorphic< 393 | 'de, 394 | V: Deserializer<'de> + Deserialize<'de> + PartialEq + core::fmt::Debug + Clone, 395 | >( 396 | val: V, 397 | ) { 398 | assert_value_to_value(val.clone(), val) 399 | } 400 | 401 | /// Does a value `a` deserialize to the expected value `b`? 402 | fn assert_value_to_value<'de, V1, V2>(a: V1, b: V2) 403 | where 404 | V1: Deserializer<'de>, 405 | V2: Deserialize<'de> + PartialEq + core::fmt::Debug + Clone, 406 | { 407 | let new_val = V2::deserialize(a).expect("Can deserialize"); 408 | assert_eq!(b, new_val); 409 | } 410 | 411 | #[test] 412 | fn deserialize_primitives_isomorphic() { 413 | assert_value_isomorphic(Value::u128(123)); 414 | assert_value_isomorphic(Value::i128(123)); 415 | assert_value_isomorphic(Value::bool(true)); 416 | assert_value_isomorphic(Value::char('a')); 417 | assert_value_isomorphic(Value::string("Hello!")); 418 | 419 | // Alas, I256 and U256 are both a sequence of bytes, which could equally be represented 420 | // by a composite sequence (as other sequences-of-things are). We could have a special case where 421 | // precisely 32 u8's is deserialized to one of U256 or I256, but for now we use our more general 422 | // composite type as the sequence catch-all: 423 | assert_value_to_value( 424 | ValueDef::<()>::Primitive(Primitive::I256([1; 32])), 425 | Value::unnamed_composite(vec![1u8; 32].into_iter().map(|b| Value::u128(b.into()))), 426 | ); 427 | assert_value_to_value( 428 | ValueDef::<()>::Primitive(Primitive::U256([1; 32])), 429 | Value::unnamed_composite(vec![1u8; 32].into_iter().map(|b| Value::u128(b.into()))), 430 | ); 431 | 432 | // .. that said; if you want a primitive value back, you can use that type directly to get it 433 | // (as long as we are given exactly 32 bytes): 434 | 435 | assert_value_to_value( 436 | ValueDef::<()>::Primitive(Primitive::I256([1; 32])), 437 | Primitive::U256([1; 32]), 438 | ); 439 | assert_value_to_value( 440 | ValueDef::<()>::Primitive(Primitive::U256([1; 32])), 441 | Primitive::U256([1; 32]), 442 | ); 443 | 444 | // Unwrapped versions also work: 445 | 446 | assert_value_isomorphic(Primitive::U128(123)); 447 | assert_value_isomorphic(Primitive::U256([1; 32])); 448 | assert_value_isomorphic(Primitive::I128(123)); 449 | assert_value_isomorphic(Primitive::Bool(true)); 450 | assert_value_isomorphic(Primitive::Char('a')); 451 | assert_value_isomorphic(Primitive::String("Hello!".into())); 452 | assert_value_to_value(Primitive::I256([1; 32]), Primitive::U256([1; 32])); 453 | 454 | // We can also go from wrapped to unwrapped: 455 | 456 | assert_value_to_value(Value::u128(123), Primitive::u128(123)); 457 | assert_value_to_value(Value::i128(123), Primitive::i128(123)); 458 | 459 | // Or vice versa: 460 | 461 | assert_value_to_value(Primitive::u128(123), Value::u128(123)); 462 | assert_value_to_value(Primitive::i128(123), Value::i128(123)); 463 | } 464 | 465 | #[test] 466 | fn deserialize_composites_isomorphic() { 467 | assert_value_isomorphic(value!((123u8, true))); 468 | assert_value_isomorphic(value!({})); 469 | assert_value_isomorphic(value!({a: 123u8, b: true})); 470 | assert_value_isomorphic(value!({a: 123u8, b: { c: 123u8, d: "hello"}})); 471 | 472 | // unwrapped: 473 | 474 | assert_value_isomorphic(Composite::unnamed(vec![Value::u128(123), Value::bool(true)])); 475 | assert_value_isomorphic(Composite::unnamed(vec![])); 476 | assert_value_isomorphic(Composite::named(vec![ 477 | ("a", Value::u128(123)), 478 | ("b", Value::bool(true)), 479 | ])); 480 | assert_value_isomorphic(Composite::named(vec![ 481 | ("a", Value::u128(123)), 482 | ("b", value!({ c: 123u8, d: "hello"})), 483 | ])); 484 | } 485 | 486 | #[test] 487 | fn deserialize_variants_isomorphic() { 488 | assert_value_isomorphic(ValueDef::Variant(Variant::unnamed_fields( 489 | "Foo", 490 | [Value::u128(123), Value::bool(true)], 491 | ))); 492 | assert_value_isomorphic(ValueDef::Variant(Variant::unnamed_fields("Foo", []))); 493 | assert_value_isomorphic(ValueDef::Variant(Variant::named_fields( 494 | "Foo", 495 | [("a", Value::u128(123)), ("b", Value::bool(true))], 496 | ))); 497 | 498 | // unwrapped work as well: 499 | 500 | assert_value_isomorphic(Variant::unnamed_fields( 501 | "Foo", 502 | [Value::u128(123), Value::bool(true)], 503 | )); 504 | assert_value_isomorphic(Variant::unnamed_fields("Foo", vec![])); 505 | assert_value_isomorphic(Variant::named_fields( 506 | "Foo", 507 | [("a", Value::u128(123)), ("b", Value::bool(true))], 508 | )); 509 | } 510 | 511 | #[test] 512 | fn deserialize_bitsequences_isomorphic() { 513 | use scale_bits::bits; 514 | assert_value_isomorphic(ValueDef::BitSequence(bits![])); 515 | assert_value_isomorphic(ValueDef::BitSequence(bits![0])); 516 | assert_value_isomorphic(ValueDef::BitSequence(bits![0, 1, 1, 0, 1, 0, 1, 1, 1])); 517 | } 518 | 519 | #[test] 520 | fn deserialize_bitsequence_from_json() { 521 | use scale_bits::bits; 522 | 523 | let bits_json = json!({ 524 | "__bitvec__values__": [true, false, true, true, false] 525 | }); 526 | 527 | let val: Value = serde_json::from_value(bits_json).unwrap(); 528 | assert_eq!(val.value, ValueDef::BitSequence(bits![true, false, true, true, false])); 529 | } 530 | 531 | #[test] 532 | fn sequence_to_value() { 533 | use serde::de::{value::SeqDeserializer, IntoDeserializer}; 534 | 535 | let de: SeqDeserializer<_, DeserializerError> = vec![1u8, 2, 3, 4].into_deserializer(); 536 | 537 | assert_value_to_value(de.clone(), value!((1u8, 2u8, 3u8, 4u8))); 538 | assert_value_to_value( 539 | de, 540 | Composite::Unnamed(vec![ 541 | Value::u128(1), 542 | Value::u128(2), 543 | Value::u128(3), 544 | Value::u128(4), 545 | ]), 546 | ); 547 | } 548 | 549 | #[test] 550 | fn sequence_to_primitive() { 551 | use serde::de::{value::SeqDeserializer, IntoDeserializer}; 552 | 553 | let de: SeqDeserializer<_, DeserializerError> = vec![1u8; 32].into_deserializer(); 554 | 555 | assert_value_to_value(de, Primitive::U256([1; 32])); 556 | } 557 | 558 | #[test] 559 | fn map_to_value() { 560 | use alloc::collections::BTreeMap; 561 | use serde::de::{value::MapDeserializer, IntoDeserializer}; 562 | 563 | let map = { 564 | let mut map = BTreeMap::<&'static str, i32>::new(); 565 | map.insert("a", 1i32); 566 | map.insert("b", 2i32); 567 | map.insert("c", 3i32); 568 | map 569 | }; 570 | 571 | let de: MapDeserializer<_, DeserializerError> = map.into_deserializer(); 572 | 573 | let value = ValueDef::deserialize(de).expect("should deserialize OK"); 574 | if let ValueDef::Composite(Composite::Named(vals)) = value { 575 | // These could come back in any order so we need to search for them: 576 | assert!(vals.contains(&("a".into(), Value::i128(1)))); 577 | assert!(vals.contains(&("b".into(), Value::i128(2)))); 578 | assert!(vals.contains(&("c".into(), Value::i128(3)))); 579 | } else { 580 | panic!("Map should deserialize into Composite::Named value but we have {value:?}"); 581 | } 582 | } 583 | 584 | #[test] 585 | fn partially_deserialize_value() { 586 | let value = Value::named_composite(vec![ 587 | ("a", Value::u128(123)), 588 | ("b", value!({c: 123u8, d: "hello", e: {}})), 589 | ]); 590 | 591 | #[derive(Deserialize, Debug, PartialEq)] 592 | struct Partial { 593 | a: Value<()>, 594 | b: PartialB, 595 | } 596 | 597 | #[derive(Deserialize, Debug, PartialEq)] 598 | struct PartialB { 599 | c: u128, 600 | d: String, 601 | e: Value<()>, 602 | } 603 | 604 | let partial = Partial::deserialize(value).expect("should work"); 605 | 606 | assert_eq!( 607 | partial, 608 | Partial { 609 | a: Value::u128(123), 610 | b: PartialB { c: 123, d: "hello".into(), e: value!({}) } 611 | } 612 | ) 613 | } 614 | 615 | #[test] 616 | fn deserialize_well_formed_map_to_unnamed_variant() { 617 | let v: Variant<()> = Variant::deserialize(serde_json::json!({ 618 | "name": "Hello", 619 | "values": [1, 2, true] 620 | })) 621 | .unwrap(); 622 | 623 | assert_eq!(v.name, "Hello".to_string()); 624 | assert_eq!( 625 | v.values, 626 | Composite::Unnamed(vec![ 627 | // All JSON numbers deserialize to U64 or I64 or F64 as necessary: 628 | Value::u128(1), 629 | Value::u128(2), 630 | Value::bool(true), 631 | ]) 632 | ) 633 | } 634 | 635 | #[test] 636 | fn deserialize_well_formed_map_to_named_variant() { 637 | let v: Variant<()> = Variant::deserialize(serde_json::json!({ 638 | "name": "Hello", 639 | "values": { "a": 1, "b": 2, "c": true } 640 | })) 641 | .unwrap(); 642 | 643 | assert_eq!(v.name, "Hello".to_string()); 644 | assert_eq!( 645 | v.values, 646 | Composite::Named(vec![ 647 | // All JSON numbers deserialize to U64 or I64 or F64 as necessary: 648 | ("a".into(), Value::u128(1)), 649 | ("b".into(), Value::u128(2)), 650 | ("c".into(), Value::bool(true)), 651 | ]) 652 | ) 653 | } 654 | 655 | #[test] 656 | fn cannot_deserialize_malformed_map_to_variant() { 657 | assert!(matches!( 658 | Variant::deserialize(serde_json::json!({ 659 | "names": "Hello", // "names", not "name". 660 | "values": [1, 2, true] 661 | })), 662 | Err(..) 663 | )); 664 | assert!(matches!( 665 | Variant::deserialize(serde_json::json!({ 666 | "name": "Hello", 667 | "values": [1, 2, true], 668 | "other": true // unexpected third prop. 669 | })), 670 | Err(..) 671 | )); 672 | assert!(matches!( 673 | Variant::deserialize(serde_json::json!({ 674 | "names": "Hello", 675 | "values": 1 // incorrect type of values 676 | })), 677 | Err(..) 678 | )); 679 | } 680 | } 681 | -------------------------------------------------------------------------------- /src/serde_impls/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod bitvec_helpers; 17 | mod deserialize; 18 | mod deserializer; 19 | mod serialize; 20 | mod serializer; 21 | 22 | pub use deserializer::DeserializerError; 23 | 24 | pub use serializer::{SerializerError, ValueSerializer}; 25 | -------------------------------------------------------------------------------- /src/serde_impls/serialize.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! This [`Serialize`] impl allows [`Value`]s to be serialized to some format 17 | //! (eg JSON); we do our best to hand the [`Serializer`] values which most 18 | //! accurately represent what we've stored, but there is always some amount of 19 | //! converstion between our [`Value`] type and the types supported by the 20 | //! serde data model that we're serializing things into. 21 | 22 | use super::bitvec_helpers; 23 | use crate::prelude::*; 24 | use crate::{Composite, Primitive, Value, ValueDef, Variant}; 25 | use serde::{ 26 | ser::{SerializeMap, SerializeSeq}, 27 | Serialize, Serializer, 28 | }; 29 | 30 | impl Serialize for Value { 31 | fn serialize(&self, serializer: S) -> Result 32 | where 33 | S: Serializer, 34 | { 35 | self.value.serialize(serializer) 36 | } 37 | } 38 | 39 | impl Serialize for ValueDef { 40 | fn serialize(&self, serializer: S) -> Result 41 | where 42 | S: Serializer, 43 | { 44 | match self { 45 | ValueDef::Composite(val) => val.serialize(serializer), 46 | ValueDef::Variant(val) => val.serialize(serializer), 47 | ValueDef::Primitive(val) => val.serialize(serializer), 48 | ValueDef::BitSequence(val) => bitvec_helpers::serialize_bitvec(val, serializer), 49 | } 50 | } 51 | } 52 | 53 | impl Serialize for Composite { 54 | fn serialize(&self, serializer: S) -> Result 55 | where 56 | S: Serializer, 57 | { 58 | match self { 59 | Composite::Named(vals) => { 60 | let mut map = serializer.serialize_map(Some(vals.len()))?; 61 | for (key, val) in vals { 62 | map.serialize_entry(key, val)?; 63 | } 64 | map.end() 65 | } 66 | Composite::Unnamed(vals) => { 67 | let mut seq = serializer.serialize_seq(Some(vals.len()))?; 68 | for val in vals { 69 | seq.serialize_element(val)?; 70 | } 71 | seq.end() 72 | } 73 | } 74 | } 75 | } 76 | 77 | macro_rules! serialize_as_first_ok_type { 78 | ($serializer:ident $val:ident; $first:ident $($rest:ident)*) => {{ 79 | let n: Result<$first,_> = $val.try_into(); 80 | match n { 81 | Ok(n) => n.serialize($serializer), 82 | Err(_) => serialize_as_first_ok_type!($serializer $val; $($rest)*) 83 | } 84 | }}; 85 | ($serializer:ident $val:ident;) => {{ 86 | $val.serialize($serializer) 87 | }}; 88 | } 89 | 90 | impl Serialize for Primitive { 91 | fn serialize(&self, serializer: S) -> Result 92 | where 93 | S: Serializer, 94 | { 95 | // Delegate to the serialization strategy used by the primitive types. 96 | match self { 97 | Primitive::Bool(v) => v.serialize(serializer), 98 | Primitive::Char(v) => v.serialize(serializer), 99 | Primitive::String(v) => v.serialize(serializer), 100 | Primitive::U128(v) => { 101 | // Serialize into the smallest type that fits, since formats like 102 | // JSON don't like u128's by default. 103 | let v = *v; 104 | serialize_as_first_ok_type!(serializer v; u8 u16 u32 u64 u128) 105 | } 106 | Primitive::U256(v) => v.serialize(serializer), 107 | Primitive::I128(v) => { 108 | // Serialize into the smallest type that fits, since formats like 109 | // JSON don't like i128's by default. 110 | let v = *v; 111 | serialize_as_first_ok_type!(serializer v; i8 i16 i32 i64 i128) 112 | } 113 | Primitive::I256(v) => v.serialize(serializer), 114 | } 115 | } 116 | } 117 | 118 | impl Serialize for Variant { 119 | fn serialize(&self, serializer: S) -> Result 120 | where 121 | S: Serializer, 122 | { 123 | // We can't use the enum serializing in the serde data model because that requires static 124 | // strs and enum indexes, which we don't have (since this is a runtime value), so we serialize 125 | // as a map with a type and a value, and make sure that we allow this format when attempting to 126 | // deserialize into a `Variant` type for a bit of symmetry (although note that if you try to deserialize 127 | // this into a `Value` type it'll have no choice but to deserialize straight into a `Composite::Named` map). 128 | let mut map = serializer.serialize_map(Some(2))?; 129 | map.serialize_entry("name", &self.name)?; 130 | map.serialize_entry("values", &self.values)?; 131 | map.end() 132 | } 133 | } 134 | 135 | #[cfg(test)] 136 | mod test { 137 | use crate::value; 138 | 139 | use super::*; 140 | use serde_json::json; 141 | 142 | fn assert_value(value: Value<()>, expected: serde_json::Value) { 143 | let val = serde_json::to_value(value).expect("can serialize to serde_json::Value"); 144 | assert_eq!(val, expected); 145 | } 146 | 147 | #[test] 148 | fn serialize_primitives() { 149 | // a subset of the primitives to sanity check that they are unwrapped: 150 | assert_value(Value::u128(1), json!(1)); 151 | assert_value(Value::bool(true), json!(true)); 152 | assert_value(Value::bool(false), json!(false)); 153 | } 154 | 155 | #[test] 156 | fn serialize_composites() { 157 | assert_value( 158 | value!({ 159 | a: true, 160 | b: "hello", 161 | c: 'c' 162 | }), 163 | json!({ 164 | "a": true, 165 | "b": "hello", 166 | "c": 'c' 167 | }), 168 | ); 169 | assert_value(value!((true, "hello", 'c')), json!([true, "hello", 'c'])) 170 | } 171 | 172 | #[test] 173 | fn serialize_variants() { 174 | assert_value( 175 | value!(Foo { a: true, b: "hello", c: 'c' }), 176 | json!({ 177 | "name": "Foo", 178 | "values": { 179 | "a": true, 180 | "b": "hello", 181 | "c": 'c' 182 | } 183 | }), 184 | ); 185 | assert_value( 186 | value!(Bar(true, "hello", 'c')), 187 | json!({ 188 | "name": "Bar", 189 | "values": [ 190 | true, 191 | "hello", 192 | 'c' 193 | ] 194 | }), 195 | ) 196 | } 197 | 198 | #[test] 199 | fn serialize_bitsequences() { 200 | use scale_bits::bits; 201 | 202 | assert_value( 203 | Value::bit_sequence(bits![]), 204 | json!({ 205 | "__bitvec__values__": [] 206 | }), 207 | ); 208 | assert_value( 209 | Value::bit_sequence(bits![0, 1, 1, 0, 1]), 210 | json!({ 211 | "__bitvec__values__": [false, true, true, false, true] 212 | }), 213 | ); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/serde_impls/serializer.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! This [`Serializer`] impl allows types implementing `Serialize` to be converted 17 | //! into [`Value`]s. 18 | 19 | use crate::prelude::*; 20 | use crate::{Composite, Primitive, Value, ValueDef}; 21 | use serde::{ 22 | ser::{ 23 | SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, 24 | SerializeTupleStruct, SerializeTupleVariant, 25 | }, 26 | Serializer, 27 | }; 28 | 29 | /// This struct implements [`Serializer`] and knows how to map from the serde data model to a [`Value`] type. 30 | pub struct ValueSerializer; 31 | 32 | /// An error that can occur when attempting to serialize a type into a [`Value`]. 33 | #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] 34 | pub enum SerializerError { 35 | /// Some custom error string. 36 | #[error("Custom error: {0}")] 37 | Custom(String), 38 | /// SCALE does not support floating point values, and so we'll hit this error if we try to 39 | /// encode any floats. 40 | #[error( 41 | "Floats do not have a SCALE compatible representation, and so cannot be serialized to Values" 42 | )] 43 | CannotSerializeFloats, 44 | /// SCALE encoding is only designed to map from statically known structs to bytes. We use field names 45 | /// to figure out this mapping between named composite types and structs, so we don't support encoding 46 | /// maps with non-string keys into [`Value`]s. 47 | #[error("Map keys must be strings or string-like")] 48 | MapKeyMustBeStringlike, 49 | } 50 | 51 | impl serde::ser::Error for SerializerError { 52 | fn custom(msg: T) -> Self 53 | where 54 | T: core::fmt::Display, 55 | { 56 | SerializerError::Custom(msg.to_string()) 57 | } 58 | } 59 | 60 | macro_rules! serialize_prim { 61 | ($name:ident => $method:ident($ty:ident)) => { 62 | fn $name(self, v: $ty) -> Result { 63 | Ok(Value::$method(v.into())) 64 | } 65 | }; 66 | } 67 | 68 | impl Serializer for ValueSerializer { 69 | type Ok = Value<()>; 70 | type Error = SerializerError; 71 | 72 | type SerializeSeq = UnnamedCompositeSerializer; 73 | type SerializeTuple = UnnamedCompositeSerializer; 74 | type SerializeTupleStruct = UnnamedCompositeSerializer; 75 | type SerializeTupleVariant = UnnamedCompositeSerializer; 76 | type SerializeMap = NamedCompositeSerializer; 77 | type SerializeStruct = NamedCompositeSerializer; 78 | type SerializeStructVariant = NamedCompositeSerializer; 79 | 80 | serialize_prim!(serialize_bool => bool(bool)); 81 | serialize_prim!(serialize_i8 => i128(i8)); 82 | serialize_prim!(serialize_i16 => i128(i16)); 83 | serialize_prim!(serialize_i32 => i128(i32)); 84 | serialize_prim!(serialize_i64 => i128(i64)); 85 | serialize_prim!(serialize_i128 => i128(i128)); 86 | serialize_prim!(serialize_u8 => u128(u8)); 87 | serialize_prim!(serialize_u16 => u128(u16)); 88 | serialize_prim!(serialize_u32 => u128(u32)); 89 | serialize_prim!(serialize_u64 => u128(u64)); 90 | serialize_prim!(serialize_u128 => u128(u128)); 91 | serialize_prim!(serialize_char => char(char)); 92 | 93 | fn serialize_f32(self, _v: f32) -> Result { 94 | Err(SerializerError::CannotSerializeFloats) 95 | } 96 | fn serialize_f64(self, _v: f64) -> Result { 97 | Err(SerializerError::CannotSerializeFloats) 98 | } 99 | 100 | fn serialize_str(self, v: &str) -> Result { 101 | Ok(Value::string(v.to_string())) 102 | } 103 | 104 | fn serialize_bytes(self, v: &[u8]) -> Result { 105 | let bytes = v.iter().map(|&b| Value::u128(b as u128)); 106 | Ok(Value::unnamed_composite(bytes)) 107 | } 108 | 109 | fn serialize_none(self) -> Result { 110 | Ok(Value::variant("None".to_string(), Composite::Unnamed(Vec::new()))) 111 | } 112 | 113 | fn serialize_some(self, value: &T) -> Result 114 | where 115 | T: serde::Serialize + ?Sized, 116 | { 117 | let inner = value.serialize(ValueSerializer)?; 118 | Ok(Value::variant("Some".to_string(), Composite::Unnamed(vec![inner]))) 119 | } 120 | 121 | fn serialize_unit(self) -> Result { 122 | Ok(Value::unnamed_composite(Vec::new())) 123 | } 124 | 125 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 126 | Ok(Value::unnamed_composite(Vec::new())) 127 | } 128 | 129 | fn serialize_unit_variant( 130 | self, 131 | _name: &'static str, 132 | _variant_index: u32, 133 | variant: &'static str, 134 | ) -> Result { 135 | Ok(Value::variant(variant.to_string(), Composite::Unnamed(Vec::new()))) 136 | } 137 | 138 | fn serialize_newtype_struct( 139 | self, 140 | _name: &'static str, 141 | value: &T, 142 | ) -> Result 143 | where 144 | T: serde::Serialize + ?Sized, 145 | { 146 | let inner = value.serialize(ValueSerializer)?; 147 | Ok(Value::unnamed_composite(vec![inner])) 148 | } 149 | 150 | fn serialize_newtype_variant( 151 | self, 152 | _name: &'static str, 153 | _variant_index: u32, 154 | variant: &'static str, 155 | value: &T, 156 | ) -> Result 157 | where 158 | T: serde::Serialize + ?Sized, 159 | { 160 | let inner = value.serialize(ValueSerializer)?; 161 | Ok(Value::variant(variant.to_string(), Composite::Unnamed(vec![inner]))) 162 | } 163 | 164 | fn serialize_seq(self, _len: Option) -> Result { 165 | Ok(Self::SerializeSeq::new_composite()) 166 | } 167 | 168 | fn serialize_tuple(self, _len: usize) -> Result { 169 | Ok(Self::SerializeTuple::new_composite()) 170 | } 171 | 172 | fn serialize_tuple_struct( 173 | self, 174 | _name: &'static str, 175 | _len: usize, 176 | ) -> Result { 177 | Ok(Self::SerializeTupleStruct::new_composite()) 178 | } 179 | 180 | fn serialize_tuple_variant( 181 | self, 182 | _name: &'static str, 183 | _variant_index: u32, 184 | variant: &'static str, 185 | _len: usize, 186 | ) -> Result { 187 | Ok(Self::SerializeTupleVariant::new_variant(variant.into())) 188 | } 189 | 190 | fn serialize_map(self, _len: Option) -> Result { 191 | Ok(Self::SerializeMap::new_composite()) 192 | } 193 | 194 | fn serialize_struct( 195 | self, 196 | _name: &'static str, 197 | _len: usize, 198 | ) -> Result { 199 | Ok(Self::SerializeStruct::new_composite()) 200 | } 201 | 202 | fn serialize_struct_variant( 203 | self, 204 | _name: &'static str, 205 | _variant_index: u32, 206 | variant: &'static str, 207 | _len: usize, 208 | ) -> Result { 209 | Ok(Self::SerializeStructVariant::new_variant(variant.into())) 210 | } 211 | } 212 | 213 | // Serializes anything that should end up as an unnamed composite value: 214 | pub struct UnnamedCompositeSerializer { 215 | // Only present if the thing should be a variant: 216 | variant_name: Option, 217 | values: Vec>, 218 | } 219 | 220 | impl UnnamedCompositeSerializer { 221 | fn new_composite() -> UnnamedCompositeSerializer { 222 | UnnamedCompositeSerializer { variant_name: None, values: Vec::new() } 223 | } 224 | 225 | fn new_variant(variant_name: String) -> UnnamedCompositeSerializer { 226 | UnnamedCompositeSerializer { variant_name: Some(variant_name), values: Vec::new() } 227 | } 228 | 229 | fn serialize_element(&mut self, value: &T) -> Result<(), SerializerError> 230 | where 231 | T: serde::Serialize + ?Sized, 232 | { 233 | let inner = value.serialize(ValueSerializer)?; 234 | self.values.push(inner); 235 | Ok(()) 236 | } 237 | 238 | fn end(self) -> Result, SerializerError> { 239 | match self.variant_name { 240 | Some(name) => Ok(Value::variant(name, Composite::Unnamed(self.values))), 241 | None => Ok(Value::unnamed_composite(self.values)), 242 | } 243 | } 244 | } 245 | 246 | impl SerializeSeq for UnnamedCompositeSerializer { 247 | type Ok = Value<()>; 248 | type Error = SerializerError; 249 | 250 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 251 | where 252 | T: serde::Serialize + ?Sized, 253 | { 254 | self.serialize_element(value) 255 | } 256 | 257 | fn end(self) -> Result { 258 | self.end() 259 | } 260 | } 261 | 262 | impl SerializeTuple for UnnamedCompositeSerializer { 263 | type Ok = Value<()>; 264 | type Error = SerializerError; 265 | 266 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 267 | where 268 | T: serde::Serialize + ?Sized, 269 | { 270 | self.serialize_element(value) 271 | } 272 | 273 | fn end(self) -> Result { 274 | self.end() 275 | } 276 | } 277 | 278 | impl SerializeTupleStruct for UnnamedCompositeSerializer { 279 | type Ok = Value<()>; 280 | type Error = SerializerError; 281 | 282 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 283 | where 284 | T: serde::Serialize + ?Sized, 285 | { 286 | self.serialize_element(value) 287 | } 288 | 289 | fn end(self) -> Result { 290 | self.end() 291 | } 292 | } 293 | 294 | impl SerializeTupleVariant for UnnamedCompositeSerializer { 295 | type Ok = Value<()>; 296 | type Error = SerializerError; 297 | 298 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 299 | where 300 | T: serde::Serialize + ?Sized, 301 | { 302 | self.serialize_element(value) 303 | } 304 | 305 | fn end(self) -> Result { 306 | self.end() 307 | } 308 | } 309 | 310 | // Serializes things into named composite types. 311 | pub struct NamedCompositeSerializer { 312 | // Only present if the thing should be a variant: 313 | variant_name: Option, 314 | values: Vec<(String, Value<()>)>, 315 | key: Option, 316 | } 317 | 318 | impl NamedCompositeSerializer { 319 | fn new_composite() -> Self { 320 | NamedCompositeSerializer { variant_name: None, values: Vec::new(), key: None } 321 | } 322 | 323 | fn new_variant(variant_name: String) -> Self { 324 | NamedCompositeSerializer { variant_name: Some(variant_name), values: Vec::new(), key: None } 325 | } 326 | 327 | fn serialize_field(&mut self, key: &str, value: &T) -> Result<(), SerializerError> 328 | where 329 | T: serde::Serialize + ?Sized, 330 | { 331 | let key = key.to_string(); 332 | let inner = value.serialize(ValueSerializer)?; 333 | self.values.push((key, inner)); 334 | Ok(()) 335 | } 336 | 337 | fn end(self) -> Result, SerializerError> { 338 | match self.variant_name { 339 | Some(name) => Ok(Value::variant(name, Composite::Named(self.values))), 340 | None => Ok(Value::named_composite(self.values)), 341 | } 342 | } 343 | } 344 | 345 | impl SerializeMap for NamedCompositeSerializer { 346 | type Ok = Value<()>; 347 | type Error = SerializerError; 348 | 349 | fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> 350 | where 351 | T: serde::Serialize + ?Sized, 352 | { 353 | let inner = key.serialize(ValueSerializer)?; 354 | // Map keys must be stringish, because named composite values are strings 355 | // and will be matched with the corresponding field names on struct types 356 | // to SCALE encode/decode. 357 | let key = match inner.value { 358 | ValueDef::Primitive(Primitive::String(s)) => s, 359 | ValueDef::Primitive(Primitive::Char(c)) => c.to_string(), 360 | _ => return Err(SerializerError::MapKeyMustBeStringlike), 361 | }; 362 | self.key = Some(key); 363 | Ok(()) 364 | } 365 | 366 | fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> 367 | where 368 | T: serde::Serialize + ?Sized, 369 | { 370 | let key = self.key.take().expect("serialize_key must be called prior to serialize_value"); 371 | self.serialize_field(&key, value) 372 | } 373 | 374 | fn end(self) -> Result { 375 | self.end() 376 | } 377 | } 378 | 379 | impl SerializeStruct for NamedCompositeSerializer { 380 | type Ok = Value<()>; 381 | type Error = SerializerError; 382 | 383 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 384 | where 385 | T: serde::Serialize + ?Sized, 386 | { 387 | self.serialize_field(key, value) 388 | } 389 | 390 | fn end(self) -> Result { 391 | self.end() 392 | } 393 | } 394 | 395 | impl SerializeStructVariant for NamedCompositeSerializer { 396 | type Ok = Value<()>; 397 | type Error = SerializerError; 398 | 399 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 400 | where 401 | T: serde::Serialize + ?Sized, 402 | { 403 | self.serialize_field(key, value) 404 | } 405 | 406 | fn end(self) -> Result { 407 | self.end() 408 | } 409 | } 410 | 411 | #[cfg(test)] 412 | mod test { 413 | use super::*; 414 | use crate::{value, Value}; 415 | use core::fmt::Debug; 416 | use serde::{Deserialize, Serialize}; 417 | 418 | // Make sure that things can serialize and deserialize back losslessly. 419 | fn assert_ser_de + Debug + PartialEq>(val: T) { 420 | let actual = val.serialize(ValueSerializer).expect("can serialize"); 421 | let actual = T::deserialize(actual).expect("can deserialize again"); 422 | assert_eq!(val, actual, "value did not come deserialize back to the same"); 423 | } 424 | 425 | // Make sure that things can serialize and deserialize back losslessly and to the expected Value. 426 | fn assert_ser_de_eq + Debug + PartialEq>( 427 | val: T, 428 | value: Value<()>, 429 | ) { 430 | // serialize and compare: 431 | let actual = val.serialize(ValueSerializer).expect("can serialize"); 432 | assert_eq!(value, actual, "serializing mismatch"); 433 | // deserialize back and check we get the same thing back out: 434 | let actual = T::deserialize(actual).expect("can deserialize again"); 435 | assert_eq!(val, actual, "deserialing mismatch"); 436 | } 437 | 438 | #[test] 439 | fn ser_de_primitives() { 440 | assert_ser_de_eq(123u8, Value::u128(123)); 441 | assert_ser_de_eq(123u16, Value::u128(123)); 442 | assert_ser_de_eq(123u32, Value::u128(123)); 443 | assert_ser_de_eq(123u64, Value::u128(123)); 444 | assert_ser_de_eq(123u128, Value::u128(123)); 445 | 446 | assert_ser_de_eq(123i8, Value::i128(123)); 447 | assert_ser_de_eq(123i16, Value::i128(123)); 448 | assert_ser_de_eq(123i32, Value::i128(123)); 449 | assert_ser_de_eq(123i64, Value::i128(123)); 450 | assert_ser_de_eq(123i128, Value::i128(123)); 451 | 452 | assert_ser_de_eq(true, Value::bool(true)); 453 | assert_ser_de_eq(false, Value::bool(false)); 454 | 455 | assert_ser_de_eq("hello".to_string(), Value::string("hello")); 456 | assert_ser_de_eq('a', Value::char('a')); 457 | } 458 | 459 | #[test] 460 | fn ser_de_optionals() { 461 | assert_ser_de_eq(Some(123u8), value!(Some(123u8))); 462 | assert_ser_de_eq(None as Option, value!(None())); 463 | } 464 | 465 | #[test] 466 | fn ser_de_unit_struct() { 467 | #[derive(Deserialize, Serialize, Debug, PartialEq)] 468 | struct Foo; 469 | 470 | assert_ser_de_eq(Foo, value!(())); 471 | } 472 | 473 | #[test] 474 | fn ser_de_named_struct() { 475 | #[derive(Deserialize, Serialize, Debug, PartialEq)] 476 | struct Foo { 477 | a: u8, 478 | b: bool, 479 | } 480 | 481 | let val = value!({a: 123u8, b: true}); 482 | assert_ser_de_eq(Foo { a: 123, b: true }, val); 483 | } 484 | 485 | #[test] 486 | fn ser_de_tuple_struct() { 487 | #[derive(Deserialize, Serialize, Debug, PartialEq)] 488 | struct Foo(u8, bool); 489 | 490 | let val = value!((123u8, true)); 491 | assert_ser_de_eq(Foo(123, true), val); 492 | } 493 | 494 | #[test] 495 | fn ser_de_sequences() { 496 | assert_ser_de_eq(vec![1, 2, 3, 4, 5u8], value!((1u8, 2u8, 3u8, 4u8, 5u8))); 497 | assert_ser_de_eq([1, 2, 3, 4, 5u8], value!((1u8, 2u8, 3u8, 4u8, 5u8))); 498 | 499 | assert_ser_de_eq((1u8, true, 'a', "hello".to_string()), value!((1u8, true, 'a', "hello"))); 500 | } 501 | 502 | #[test] 503 | fn ser_de_variants() { 504 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 505 | enum Foo { 506 | A(bool, u8), 507 | B, 508 | C { hello: String, value: i64 }, 509 | } 510 | 511 | assert_ser_de_eq(Foo::A(true, 123), value!(A(true, 123u8))); 512 | assert_ser_de_eq(Foo::B, value!(B())); 513 | assert_ser_de_eq( 514 | Foo::C { hello: "World".to_string(), value: 123 }, 515 | value!(C { hello: "World", value: 123 }), 516 | ); 517 | } 518 | 519 | #[test] 520 | fn ser_de_maps() { 521 | use alloc::collections::BTreeMap; 522 | 523 | let m = { 524 | let mut m = BTreeMap::new(); 525 | m.insert("a".to_string(), 1u8); 526 | m.insert("b".to_string(), 2u8); 527 | m.insert("c".to_string(), 3u8); 528 | m 529 | }; 530 | assert_ser_de(m); 531 | 532 | // chars as keys are fine, too: 533 | let m = { 534 | let mut m = BTreeMap::new(); 535 | m.insert('a', 1u8); 536 | m.insert('b', 2u8); 537 | m.insert('c', 3u8); 538 | m 539 | }; 540 | assert_ser_de(m); 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /src/string_impls/custom_formatters/hex.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2024 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::prelude::*; 17 | use crate::{Composite, Primitive, Value, ValueDef}; 18 | use core::fmt::Write; 19 | 20 | /// This can be used alongside [`crate::stringify::ToWriterBuilder::add_custom_formatter()`] (which 21 | /// itself is constructed via [`crate::stringify::to_writer_custom()`]). It will format as a hex 22 | /// string any unnamed composite whose values are all primitives in the range 0-255 inclusive. 23 | /// 24 | /// # Example 25 | /// 26 | /// ```rust 27 | /// use scale_value::{value,Value}; 28 | /// use scale_value::stringify::{to_writer_custom, custom_formatters::format_hex}; 29 | /// 30 | /// let val = value!({ 31 | /// foo: (1u64,2u64,3u64,4u64,5u64), 32 | /// bar: (1000u64, 10u64) 33 | /// }); 34 | /// 35 | /// let mut s = String::new(); 36 | /// 37 | /// to_writer_custom() 38 | /// .add_custom_formatter(|v, w| format_hex(v, w)) 39 | /// .write(&val, &mut s) 40 | /// .unwrap(); 41 | /// 42 | /// assert_eq!(s, r#"{ foo: 0x0102030405, bar: (1000, 10) }"#); 43 | /// ``` 44 | pub fn format_hex(value: &Value, writer: W) -> Option { 45 | // Print unnamed sequences of u8s as hex strings; ignore anything else. 46 | if let ValueDef::Composite(Composite::Unnamed(vals)) = &value.value { 47 | if vals.is_empty() { 48 | return None; 49 | } 50 | for val in vals { 51 | if !matches!(val.value, ValueDef::Primitive(Primitive::U128(n)) if n < 256) { 52 | return None; 53 | } 54 | } 55 | Some(value_to_hex(vals, writer)) 56 | } else { 57 | None 58 | } 59 | } 60 | 61 | // Just to avoid needing to import the `hex` dependency just for this. 62 | fn value_to_hex(vals: &Vec>, mut writer: W) -> core::fmt::Result { 63 | writer.write_str("0x")?; 64 | for val in vals { 65 | if let ValueDef::Primitive(Primitive::U128(n)) = &val.value { 66 | let n = *n as u8; 67 | writer.write_char(u4_to_hex(n >> 4))?; 68 | writer.write_char(u4_to_hex(n & 0b00001111))?; 69 | } 70 | } 71 | Ok(()) 72 | } 73 | 74 | fn u4_to_hex(n: u8) -> char { 75 | static HEX: [char; 16] = 76 | ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']; 77 | *HEX.get(n as usize).expect("Expected a u4 (value between 0..=15") 78 | } 79 | 80 | #[cfg(test)] 81 | mod test { 82 | use super::*; 83 | use crate::value; 84 | 85 | #[test] 86 | fn test_value_to_hex() { 87 | let mut s = String::new(); 88 | format_hex(&value! {(0usize,230usize,255usize,15usize,12usize,4usize)}, &mut s) 89 | .expect("decided not to convert to hex") 90 | .expect("can't write to writer without issues"); 91 | 92 | assert_eq!(s, "0x00E6FF0F0C04"); 93 | } 94 | 95 | #[test] 96 | fn test_value_not_to_hex() { 97 | let mut s = String::new(); 98 | // 256 is too big to be a u8, so this value isn't valid hex. 99 | assert_eq!(format_hex(&value! {(0usize,230usize,256usize)}, &mut s), None); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/string_impls/custom_formatters/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2024 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod hex; 17 | 18 | pub use hex::format_hex; 19 | -------------------------------------------------------------------------------- /src/string_impls/custom_parsers/hex.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::prelude::*; 17 | use crate::{ 18 | stringify::{ParseError, ParseErrorKind}, 19 | Value, 20 | }; 21 | 22 | /// Attempt to parse a hex string into a [`Value<()>`] (or more specifically, 23 | /// an unnamed composite). 24 | /// 25 | /// - Returns an error if we see a leading `0x` and then see invalid hex 26 | /// characters after this. 27 | /// - Returns `None` if no `0x` is seen first. 28 | /// - Returns `Some(value)` if parsing was successful. In this case, the string 29 | /// reference given is wound forwards to consume what was parsed. 30 | pub fn parse_hex(s: &mut &str) -> Option, ParseError>> { 31 | if !s.starts_with("0x") { 32 | return None; 33 | } 34 | 35 | let bytes = s.as_bytes(); 36 | let mut composite_values = vec![]; 37 | 38 | // find all valid hex chars after 0x: 39 | let mut idx = 2; 40 | let mut last_nibble = None; 41 | loop { 42 | // Break if we hit end of string. 43 | let Some(b) = bytes.get(idx) else { 44 | break; 45 | }; 46 | 47 | // Break as soon as we hit some non-alphanumeric char. 48 | if !b.is_ascii_alphanumeric() { 49 | break; 50 | } 51 | 52 | // Turn 4-bit hex char into nibble: 53 | let hex_nibble = match *b { 54 | b'A'..=b'F' => b - b'A' + 10, 55 | b'a'..=b'f' => b - b'a' + 10, 56 | b'0'..=b'9' => b - b'0', 57 | b => { 58 | return Some(Err( 59 | ParseErrorKind::custom(ParseHexError::InvalidChar(b as char)).at(idx) 60 | )) 61 | } 62 | }; 63 | 64 | match last_nibble { 65 | None => { 66 | // The first of 2 chars that make a single byte; keep hold of: 67 | last_nibble = Some(hex_nibble) 68 | } 69 | Some(n) => { 70 | // The second; combine and push byte to output: 71 | let byte = n * 16 + hex_nibble; 72 | composite_values.push(Value::u128(byte as u128)); 73 | last_nibble = None; 74 | } 75 | } 76 | 77 | idx += 1; 78 | } 79 | 80 | // We have leftovers; wrong length! 81 | if last_nibble.is_some() { 82 | return Some(Err(ParseErrorKind::custom(ParseHexError::WrongLength).between(0, idx))); 83 | } 84 | 85 | // Consume the "used" up bytes and return our Value. 86 | // 87 | // # Safety 88 | // 89 | // We have consumed only ASCII chars to get this far, so 90 | // we know the bytes following them make up a valid str. 91 | *s = unsafe { core::str::from_utf8_unchecked(&bytes[idx..]) }; 92 | Some(Ok(Value::unnamed_composite(composite_values))) 93 | } 94 | 95 | #[derive(Debug, PartialEq, Clone, thiserror::Error)] 96 | #[allow(missing_docs)] 97 | pub enum ParseHexError { 98 | #[error("Invalid hex character: {0}")] 99 | InvalidChar(char), 100 | #[error("Hex string is the wrong length; should be an even length")] 101 | WrongLength, 102 | } 103 | 104 | #[cfg(test)] 105 | mod test { 106 | use super::*; 107 | 108 | #[test] 109 | fn parses_same_as_hex_crate() { 110 | let expects = ["0x", "0x00", "0x000102030405060708090A0B", "0xDEADBEEF", "0x00BAB10C"]; 111 | 112 | for input in expects { 113 | let expected_hex = hex::decode(input.trim_start_matches("0x")).expect("valid hex"); 114 | let cursor = &mut &*input; 115 | let hex = parse_hex(cursor).expect("valid hex expected").expect("no error expected"); 116 | 117 | assert_eq!(hex, Value::from_bytes(expected_hex), "values should match"); 118 | } 119 | } 120 | 121 | #[test] 122 | fn consumes_parsed_hex() { 123 | let expects = 124 | [("0x foo", " foo"), ("0x00,bar", ",bar"), ("0x123456-2", "-2"), ("0xDEADBEEF ", " ")]; 125 | 126 | for (input, expected_remaining) in expects { 127 | let cursor = &mut &*input; 128 | let _ = parse_hex(cursor).expect("valid hex expected").expect("no error expected"); 129 | 130 | assert_eq!(*cursor, expected_remaining); 131 | } 132 | } 133 | 134 | #[test] 135 | fn err_wrong_length() { 136 | let expects = ["0x1", "0x123"]; 137 | 138 | for input in expects { 139 | let cursor = &mut &*input; 140 | let err = 141 | parse_hex(cursor).expect("some result expected").expect_err("an error is expected"); 142 | 143 | assert_eq!(err.start_loc, 0); 144 | assert_eq!(err.end_loc, Some(input.len())); 145 | 146 | let ParseErrorKind::Custom(err) = err.err else { panic!("expected custom error") }; 147 | 148 | assert_eq!(err, ParseHexError::WrongLength.to_string()); 149 | assert_eq!(input, *cursor); 150 | } 151 | } 152 | 153 | #[test] 154 | fn err_invalid_char() { 155 | let expects = [("0x12345x", 'x', 7), ("0x123h4", 'h', 5), ("0xG23h4", 'G', 2)]; 156 | 157 | for (input, bad_char, pos) in expects { 158 | let cursor = &mut &*input; 159 | let err = 160 | parse_hex(cursor).expect("some result expected").expect_err("an error is expected"); 161 | 162 | assert_eq!(err.start_loc, pos); 163 | assert!(err.end_loc.is_none()); 164 | 165 | let ParseErrorKind::Custom(err) = err.err else { panic!("expected custom error") }; 166 | 167 | assert_eq!(err, ParseHexError::InvalidChar(bad_char).to_string()); 168 | assert_eq!(input, *cursor); 169 | } 170 | } 171 | 172 | #[test] 173 | fn empty_string_doesnt_panic() { 174 | assert!(parse_hex(&mut "").is_none()); 175 | assert!(parse_hex(&mut "0").is_none()); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/string_impls/custom_parsers/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod hex; 17 | 18 | #[cfg(feature = "parser-ss58")] 19 | mod ss58; 20 | 21 | pub use self::hex::{parse_hex, ParseHexError}; 22 | 23 | #[cfg(feature = "parser-ss58")] 24 | pub use ss58::parse_ss58; 25 | -------------------------------------------------------------------------------- /src/string_impls/custom_parsers/ss58.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::prelude::*; 17 | use crate::{stringify::ParseError, Value}; 18 | 19 | /// Attempt to parse an ss58 address into a [`Value<()>`] (or more specifically, 20 | /// an unnamed composite wrapped in a newtype which represents an AccountId32). 21 | /// 22 | /// - Returns `None` if we can't parse the address. 23 | /// - Returns `Some(value)` if parsing was successful. In this case, the string 24 | /// reference given is wound forwards to consume what was parsed. 25 | pub fn parse_ss58(s: &mut &str) -> Option, ParseError>> { 26 | let bytes = parse_ss58_bytes(s)?; 27 | Some(Ok(Value::from_bytes(bytes))) 28 | } 29 | 30 | fn parse_ss58_bytes(s: &mut &str) -> Option> { 31 | const CHECKSUM_LEN: usize = 2; 32 | 33 | // ss58 addresses are base58 encoded. Base58 is all alphanumeric chars 34 | // minus a few that look potentially similar. So, gather alphanumeric chars 35 | // first. 36 | let end_idx = s.find(|c: char| !c.is_ascii_alphanumeric()).unwrap_or(s.len()); 37 | let maybe_ss58 = &s[0..end_idx]; 38 | let rest = &s[end_idx..]; 39 | 40 | if maybe_ss58.is_empty() { 41 | return None; 42 | } 43 | 44 | // Break early on obvious non-addresses that we want to parse elsewise, ie true, false or numbers. 45 | // This is mostly an optimisation but also eliminates some potential weird edge cases. 46 | if maybe_ss58 == "true" 47 | || maybe_ss58 == "false" 48 | || maybe_ss58.chars().all(|c: char| c.is_ascii_digit()) 49 | { 50 | return None; 51 | } 52 | 53 | // If what we are parsing is a variant ident, a `{` or `(` will follow 54 | // (eg `Foo { hi: 1 }` or `Foo (1)`). In this case, don't try to parse 55 | // as an ss58 address, since it would definitely be wrong to do so. 56 | if rest.trim_start().starts_with(['(', '{']) { 57 | return None; 58 | } 59 | 60 | // Attempt to base58-decode these chars. 61 | use base58::FromBase58; 62 | let Ok(bytes) = maybe_ss58.from_base58() else { return None }; 63 | 64 | // decode length of address prefix. 65 | let prefix_len = match bytes.first() { 66 | Some(0..=63) => 1, 67 | Some(64..=127) => 2, 68 | _ => return None, 69 | }; 70 | 71 | if bytes.len() < prefix_len + CHECKSUM_LEN { 72 | return None; 73 | } 74 | 75 | // The checksum is the last 2 bytes: 76 | let checksum_start_idx = bytes.len() - CHECKSUM_LEN; 77 | 78 | // Check that the checksum lines up with the rest of the address; if not, 79 | // this isn't a valid address. 80 | let hash = ss58hash(&bytes[0..checksum_start_idx]); 81 | let checksum = &hash[0..CHECKSUM_LEN]; 82 | if &bytes[checksum_start_idx..] != checksum { 83 | return None; 84 | } 85 | 86 | // Everything checks out; wind the string cursor forwards and 87 | // return the bytes representing the address provided. 88 | *s = rest; 89 | Some(bytes[prefix_len..checksum_start_idx].to_vec()) 90 | } 91 | 92 | fn ss58hash(data: &[u8]) -> Vec { 93 | use blake2::{Blake2b512, Digest}; 94 | const PREFIX: &[u8] = b"SS58PRE"; 95 | let mut ctx = Blake2b512::new(); 96 | ctx.update(PREFIX); 97 | ctx.update(data); 98 | ctx.finalize().to_vec() 99 | } 100 | 101 | #[cfg(test)] 102 | mod test { 103 | use super::*; 104 | 105 | #[test] 106 | fn can_parse_ss58_address() { 107 | // hex keys obtained via `subkey`, so we're comparing our decoding against that. 108 | // We simultaneously check that things after the address aren't consumed. 109 | let expected = [ 110 | // Alice 111 | ( 112 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY ", 113 | "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", 114 | " ", 115 | ), 116 | // Bob 117 | ( 118 | "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty-100", 119 | "8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", 120 | "-100", 121 | ), 122 | // Charlie 123 | ( 124 | "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,1,2,3", 125 | "90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22", 126 | ",1,2,3", 127 | ), 128 | // Eve 129 | ( 130 | "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw }", 131 | "e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e", 132 | " }", 133 | ), 134 | ]; 135 | 136 | for (ss58, expected_hex, expected_remaining) in expected { 137 | let cursor = &mut &*ss58; 138 | let bytes = parse_ss58_bytes(cursor).expect("address should parse OK"); 139 | let expected = hex::decode(expected_hex).expect("hex should decode OK"); 140 | 141 | assert_eq!(bytes, expected); 142 | assert_eq!(*cursor, expected_remaining); 143 | } 144 | } 145 | 146 | #[test] 147 | fn invalid_addresses_will_error() { 148 | let invalids = [ 149 | // An otherwise valid address in a variant "ident" position will not parse: 150 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY { hi: 1 }", 151 | "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY \t\n (1)", 152 | // Invalid addresses will return None: 153 | "Foo", 154 | "", 155 | ]; 156 | 157 | for invalid in invalids { 158 | assert!(parse_ss58_bytes(&mut &*invalid).is_none()); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/string_impls/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod custom_formatters; 17 | #[cfg(feature = "from-string")] 18 | mod custom_parsers; 19 | #[cfg(feature = "from-string")] 20 | mod from_string; 21 | 22 | mod string_helpers; 23 | mod to_string; 24 | 25 | #[cfg(feature = "from-string")] 26 | pub use from_string::{ 27 | FromStrBuilder, ParseBitSequenceError, ParseCharError, ParseComplexError, ParseError, 28 | ParseErrorKind, ParseNumberError, ParseStringError, 29 | }; 30 | 31 | #[cfg(feature = "parser-ss58")] 32 | pub use custom_parsers::parse_ss58; 33 | #[cfg(feature = "from-string")] 34 | pub use custom_parsers::{parse_hex, ParseHexError}; 35 | 36 | pub use custom_formatters::format_hex; 37 | pub use to_string::ToWriterBuilder; 38 | -------------------------------------------------------------------------------- /src/string_impls/string_helpers.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::prelude::*; 17 | 18 | /// Return the escape code for a given char, or None 19 | /// if there is no escape code for it. 20 | pub fn to_escape_code(c: char) -> Option { 21 | let escaped = match c { 22 | '\n' => 'n', 23 | '\t' => 't', 24 | '"' => '"', 25 | '\'' => '\'', 26 | '\r' => 'r', 27 | '\\' => '\\', 28 | '\0' => '0', 29 | _ => return None, 30 | }; 31 | Some(escaped) 32 | } 33 | 34 | /// Given some escape code (char following a '\'), return the 35 | /// unescaped char that it represents, or None if it is not a 36 | /// valid escape code. 37 | #[cfg(feature = "from-string")] 38 | pub fn from_escape_code(c: char) -> Option { 39 | let unescaped = match c { 40 | 'n' => '\n', 41 | 't' => '\t', 42 | '"' => '"', 43 | '\'' => '\'', 44 | 'r' => '\r', 45 | '\\' => '\\', 46 | '0' => '\0', 47 | _ => return None, 48 | }; 49 | Some(unescaped) 50 | } 51 | -------------------------------------------------------------------------------- /src/value_type.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io) 2 | // This file is a part of the scale-value crate. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::prelude::*; 17 | use either::Either; 18 | 19 | // We use this to represent BitSequence values, so expose it here. 20 | pub use scale_bits::Bits as BitSequence; 21 | 22 | /// [`Value`] holds a representation of some value that has been decoded, as well as some arbitrary context. 23 | /// 24 | /// Not all SCALE encoded types have an similar-named value; for instance, the values corresponding to 25 | /// sequence, array and composite types can all be represented with [`Composite`]. Only enough information 26 | /// is preserved here to to be able to encode and decode SCALE bytes with a known type to and from [`Value`]s 27 | /// losslessly. 28 | #[derive(Debug, Clone, PartialEq, Eq)] 29 | pub struct Value { 30 | /// The shape and associated data for this Value 31 | pub value: ValueDef, 32 | /// Some additional arbitrary context that can be associated with a value. 33 | pub context: T, 34 | } 35 | 36 | impl Value<()> { 37 | /// Construct a named composite type from any type which produces a tuple of keys and values 38 | /// when iterated over. 39 | pub fn named_composite(vals: Vals) -> Self 40 | where 41 | S: Into, 42 | Vals: IntoIterator)>, 43 | { 44 | Value { value: ValueDef::Composite(Composite::named(vals)), context: () } 45 | } 46 | /// Construct an unnamed composite type from any type which produces values 47 | /// when iterated over. 48 | pub fn unnamed_composite(vals: Vals) -> Self 49 | where 50 | Vals: IntoIterator>, 51 | { 52 | Value { value: ValueDef::Composite(Composite::unnamed(vals)), context: () } 53 | } 54 | /// Create a new variant value without additional context. 55 | pub fn variant>(name: S, values: Composite<()>) -> Value<()> { 56 | Value { value: ValueDef::Variant(Variant { name: name.into(), values }), context: () } 57 | } 58 | /// Create a new variant value with named fields and without additional context. 59 | pub fn named_variant(name: S, fields: Vals) -> Value<()> 60 | where 61 | S: Into, 62 | F: Into, 63 | Vals: IntoIterator)>, 64 | { 65 | Value { value: ValueDef::Variant(Variant::named_fields(name, fields)), context: () } 66 | } 67 | /// Create a new variant value with tuple-like fields and without additional context. 68 | pub fn unnamed_variant(name: S, fields: Vals) -> Value<()> 69 | where 70 | S: Into, 71 | Vals: IntoIterator>, 72 | { 73 | Value { value: ValueDef::Variant(Variant::unnamed_fields(name, fields)), context: () } 74 | } 75 | /// Create a new bit sequence value without additional context. 76 | pub fn bit_sequence(bits: BitSequence) -> Value<()> { 77 | Value { value: ValueDef::BitSequence(bits), context: () } 78 | } 79 | /// Create a new primitive value without additional context. 80 | pub fn primitive(primitive: Primitive) -> Value<()> { 81 | Value { value: ValueDef::Primitive(primitive), context: () } 82 | } 83 | /// Create a new string value without additional context. 84 | pub fn string>(val: S) -> Value<()> { 85 | Value { value: ValueDef::Primitive(Primitive::String(val.into())), context: () } 86 | } 87 | /// Create a new boolean value without additional context. 88 | pub fn bool(val: bool) -> Value<()> { 89 | Value { value: ValueDef::Primitive(Primitive::Bool(val)), context: () } 90 | } 91 | /// Create a new char without additional context. 92 | pub fn char(val: char) -> Value<()> { 93 | Value { value: ValueDef::Primitive(Primitive::Char(val)), context: () } 94 | } 95 | /// Create a new unsigned integer without additional context. 96 | pub fn u128(val: u128) -> Value<()> { 97 | Value { value: ValueDef::Primitive(Primitive::u128(val)), context: () } 98 | } 99 | /// Create a new signed integer without additional context. 100 | pub fn i128(val: i128) -> Value<()> { 101 | Value { value: ValueDef::Primitive(Primitive::i128(val)), context: () } 102 | } 103 | /// Create a new Value from a set of bytes; useful for converting things like AccountIds. 104 | pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Value<()> { 105 | let vals: Vec<_> = bytes.as_ref().iter().map(|&b| Value::u128(b as u128)).collect(); 106 | Value::unnamed_composite(vals) 107 | } 108 | } 109 | 110 | impl Value<()> { 111 | /// Create a new value with no associated context. 112 | pub fn without_context(value: ValueDef<()>) -> Value<()> { 113 | Value { value, context: () } 114 | } 115 | } 116 | 117 | impl Value { 118 | /// Create a new value with some associated context. 119 | pub fn with_context(value: ValueDef, context: T) -> Value { 120 | Value { value, context } 121 | } 122 | /// Remove the context. 123 | pub fn remove_context(self) -> Value<()> { 124 | self.map_context(|_| ()) 125 | } 126 | /// Map the context to some different type. 127 | pub fn map_context(self, mut f: F) -> Value 128 | where 129 | F: Clone + FnMut(T) -> U, 130 | { 131 | Value { context: f(self.context), value: self.value.map_context(f) } 132 | } 133 | /// If the value is a boolean value, return it. 134 | pub fn as_bool(&self) -> Option { 135 | match &self.value { 136 | ValueDef::Primitive(p) => p.as_bool(), 137 | _ => None, 138 | } 139 | } 140 | /// If the value is a char, return it. 141 | pub fn as_char(&self) -> Option { 142 | match &self.value { 143 | ValueDef::Primitive(p) => p.as_char(), 144 | _ => None, 145 | } 146 | } 147 | /// If the value is a u128, return it. 148 | pub fn as_u128(&self) -> Option { 149 | match &self.value { 150 | ValueDef::Primitive(p) => p.as_u128(), 151 | _ => None, 152 | } 153 | } 154 | /// If the value is an i128, return it. 155 | pub fn as_i128(&self) -> Option { 156 | match &self.value { 157 | ValueDef::Primitive(p) => p.as_i128(), 158 | _ => None, 159 | } 160 | } 161 | /// If the value is a string, return it. 162 | pub fn as_str(&self) -> Option<&str> { 163 | match &self.value { 164 | ValueDef::Primitive(p) => p.as_str(), 165 | _ => None, 166 | } 167 | } 168 | } 169 | 170 | /// The underlying shape of a given value. 171 | #[derive(Clone, Debug, PartialEq, Eq)] 172 | pub enum ValueDef { 173 | /// A named or unnamed struct-like, array-like or tuple-like set of values. 174 | Composite(Composite), 175 | /// An enum variant. 176 | Variant(Variant), 177 | /// A sequence of bits. 178 | BitSequence(BitSequence), 179 | /// Any of the primitive values we can have. 180 | Primitive(Primitive), 181 | } 182 | 183 | impl ValueDef { 184 | /// Map the context to some different type. 185 | pub fn map_context(self, f: F) -> ValueDef 186 | where 187 | F: Clone + FnMut(T) -> U, 188 | { 189 | match self { 190 | ValueDef::Composite(val) => ValueDef::Composite(val.map_context(f)), 191 | ValueDef::Variant(val) => ValueDef::Variant(val.map_context(f)), 192 | ValueDef::BitSequence(val) => ValueDef::BitSequence(val), 193 | ValueDef::Primitive(val) => ValueDef::Primitive(val), 194 | } 195 | } 196 | } 197 | 198 | impl From for ValueDef { 199 | fn from(val: BitSequence) -> Self { 200 | ValueDef::BitSequence(val) 201 | } 202 | } 203 | 204 | impl From for Value<()> { 205 | fn from(val: BitSequence) -> Self { 206 | Value::without_context(val.into()) 207 | } 208 | } 209 | 210 | /// A named or unnamed struct-like, array-like or tuple-like set of values. 211 | /// This is used to represent a range of composite values on their own, or 212 | /// as values for a specific [`Variant`]. 213 | #[derive(Clone, Debug, PartialEq, Eq)] 214 | pub enum Composite { 215 | /// Eg `{ foo: 2, bar: false }` 216 | Named(Vec<(String, Value)>), 217 | /// Eg `(2, false)` 218 | Unnamed(Vec>), 219 | } 220 | 221 | impl Composite { 222 | /// Construct a named composite type from any type which produces a tuple of keys and values 223 | /// when iterated over. 224 | pub fn named, Vals: IntoIterator)>>(vals: Vals) -> Self { 225 | Composite::Named(vals.into_iter().map(|(n, v)| (n.into(), v)).collect()) 226 | } 227 | /// Construct an unnamed composite type from any type which produces values 228 | /// when iterated over. 229 | pub fn unnamed>>(vals: Vals) -> Self { 230 | Composite::Unnamed(vals.into_iter().collect()) 231 | } 232 | /// Return the number of values stored in this composite type. 233 | pub fn len(&self) -> usize { 234 | match self { 235 | Composite::Named(values) => values.len(), 236 | Composite::Unnamed(values) => values.len(), 237 | } 238 | } 239 | 240 | /// Is the composite type empty? 241 | pub fn is_empty(&self) -> bool { 242 | match self { 243 | Composite::Named(values) => values.is_empty(), 244 | Composite::Unnamed(values) => values.is_empty(), 245 | } 246 | } 247 | 248 | /// Iterate over the values stored in this composite type. 249 | pub fn values(&self) -> impl ExactSizeIterator> { 250 | match self { 251 | Composite::Named(values) => Either::Left(values.iter().map(|(_k, v)| v)), 252 | Composite::Unnamed(values) => Either::Right(values.iter()), 253 | } 254 | } 255 | 256 | /// Iterate over the values stored in this composite type. 257 | pub fn into_values(self) -> impl ExactSizeIterator> { 258 | match self { 259 | Composite::Named(values) => Either::Left(values.into_iter().map(|(_k, v)| v)), 260 | Composite::Unnamed(values) => Either::Right(values.into_iter()), 261 | } 262 | } 263 | 264 | /// Map the context to some different type. 265 | pub fn map_context(self, f: F) -> Composite 266 | where 267 | F: Clone + FnMut(T) -> U, 268 | { 269 | match self { 270 | Composite::Named(values) => { 271 | // Note: Optimally I'd pass `&mut f` into each iteration to avoid cloning, 272 | // but this leads to a type recusion error because F becomes `&mut F`, which can 273 | // (at type level) recurse here again and become `&mut &mut F` and so on. Since 274 | // that's no good; just require `Clone` to avoid altering the type. 275 | let vals = 276 | values.into_iter().map(move |(k, v)| (k, v.map_context(f.clone()))).collect(); 277 | Composite::Named(vals) 278 | } 279 | Composite::Unnamed(values) => { 280 | let vals = values.into_iter().map(move |v| v.map_context(f.clone())).collect(); 281 | Composite::Unnamed(vals) 282 | } 283 | } 284 | } 285 | } 286 | 287 | impl>> From> for Composite<()> { 288 | fn from(vals: Vec) -> Self { 289 | let vals = vals.into_iter().map(|v| v.into()).collect(); 290 | Composite::Unnamed(vals) 291 | } 292 | } 293 | 294 | impl>> From> for ValueDef<()> { 295 | fn from(vals: Vec) -> Self { 296 | ValueDef::Composite(vals.into()) 297 | } 298 | } 299 | 300 | impl>> From> for Value<()> { 301 | fn from(vals: Vec) -> Self { 302 | Value::without_context(vals.into()) 303 | } 304 | } 305 | 306 | impl, V: Into>> From> for Composite<()> { 307 | fn from(vals: Vec<(K, V)>) -> Self { 308 | let vals = vals.into_iter().map(|(k, v)| (k.into(), v.into())).collect(); 309 | Composite::Named(vals) 310 | } 311 | } 312 | 313 | impl, V: Into>> From> for ValueDef<()> { 314 | fn from(vals: Vec<(K, V)>) -> Self { 315 | ValueDef::Composite(vals.into()) 316 | } 317 | } 318 | 319 | impl, V: Into>> From> for Value<()> { 320 | fn from(vals: Vec<(K, V)>) -> Self { 321 | Value::without_context(vals.into()) 322 | } 323 | } 324 | 325 | impl From> for ValueDef { 326 | fn from(val: Composite) -> Self { 327 | ValueDef::Composite(val) 328 | } 329 | } 330 | 331 | impl From> for Value<()> { 332 | fn from(val: Composite<()>) -> Self { 333 | Value::without_context(ValueDef::Composite(val)) 334 | } 335 | } 336 | 337 | /// This represents the value of a specific variant from an enum, and contains 338 | /// the name of the variant, and the named/unnamed values associated with it. 339 | #[derive(Clone, Debug, PartialEq, Eq)] 340 | pub struct Variant { 341 | /// The name of the variant. 342 | pub name: String, 343 | /// Values for each of the named or unnamed fields associated with this variant. 344 | pub values: Composite, 345 | } 346 | 347 | impl Variant { 348 | /// Construct a variant with named fields. 349 | pub fn named_fields(name: S, fields: Vals) -> Variant 350 | where 351 | S: Into, 352 | K: Into, 353 | Vals: IntoIterator)>, 354 | { 355 | Variant { name: name.into(), values: Composite::named(fields) } 356 | } 357 | /// Construct a variant with tuple-like fields. 358 | pub fn unnamed_fields(name: S, fields: Vals) -> Variant 359 | where 360 | S: Into, 361 | Vals: IntoIterator>, 362 | { 363 | Variant { name: name.into(), values: Composite::unnamed(fields) } 364 | } 365 | /// Map the context to some different type. 366 | pub fn map_context(self, f: F) -> Variant 367 | where 368 | F: Clone + FnMut(T) -> U, 369 | { 370 | Variant { name: self.name, values: self.values.map_context(f) } 371 | } 372 | } 373 | 374 | impl From> for ValueDef { 375 | fn from(val: Variant) -> Self { 376 | ValueDef::Variant(val) 377 | } 378 | } 379 | 380 | impl From> for Value<()> { 381 | fn from(val: Variant<()>) -> Self { 382 | Value::without_context(ValueDef::Variant(val)) 383 | } 384 | } 385 | 386 | /// A "primitive" value (this includes strings). 387 | #[derive(Debug, Clone, PartialEq, Eq)] 388 | pub enum Primitive { 389 | /// A boolean value. 390 | Bool(bool), 391 | /// A single character. 392 | Char(char), 393 | /// A string. 394 | String(String), 395 | /// A u128 value. 396 | U128(u128), 397 | /// An i128 value. 398 | I128(i128), 399 | /// An unsigned 256 bit number (internally represented as a 32 byte array). 400 | U256([u8; 32]), 401 | /// A signed 256 bit number (internally represented as a 32 byte array). 402 | I256([u8; 32]), 403 | } 404 | 405 | impl Primitive { 406 | /// Create a new unsigned integer without additional context. 407 | pub fn u128(val: u128) -> Primitive { 408 | Primitive::U128(val) 409 | } 410 | /// Create a new signed integer without additional context. 411 | pub fn i128(val: i128) -> Primitive { 412 | Primitive::I128(val) 413 | } 414 | /// If the primitive type is a boolean value, return it. 415 | pub fn as_bool(&self) -> Option { 416 | match self { 417 | Primitive::Bool(b) => Some(*b), 418 | _ => None, 419 | } 420 | } 421 | /// If the primitive type is a char, return it. 422 | pub fn as_char(&self) -> Option { 423 | match self { 424 | Primitive::Char(c) => Some(*c), 425 | _ => None, 426 | } 427 | } 428 | /// If the primitive type is a u128, return it. 429 | pub fn as_u128(&self) -> Option { 430 | match self { 431 | Primitive::U128(n) => Some(*n), 432 | _ => None, 433 | } 434 | } 435 | /// If the primitive type is an i128, return it. 436 | pub fn as_i128(&self) -> Option { 437 | match self { 438 | Primitive::I128(n) => Some(*n), 439 | _ => None, 440 | } 441 | } 442 | /// If the primitive type is a string, return it. 443 | pub fn as_str(&self) -> Option<&str> { 444 | match self { 445 | Primitive::String(s) => Some(&**s), 446 | _ => None, 447 | } 448 | } 449 | } 450 | 451 | impl From for ValueDef { 452 | fn from(val: Primitive) -> Self { 453 | ValueDef::Primitive(val) 454 | } 455 | } 456 | 457 | macro_rules! impl_primitive_type { 458 | ($($variant:ident($ty:ty as $castty:ty),)*) => {$( 459 | impl From<$ty> for Primitive { 460 | fn from(val: $ty) -> Self { 461 | Primitive::$variant(val as $castty) 462 | } 463 | } 464 | 465 | impl From<$ty> for ValueDef { 466 | fn from(val: $ty) -> Self { 467 | ValueDef::Primitive(val.into()) 468 | } 469 | } 470 | 471 | impl From<$ty> for Value<()> { 472 | fn from(val: $ty) -> Self { 473 | Value::without_context(val.into()) 474 | } 475 | } 476 | )*} 477 | } 478 | 479 | impl_primitive_type!( 480 | Bool(bool as bool), 481 | Char(char as char), 482 | String(String as String), 483 | U128(u128 as u128), 484 | U128(u64 as u128), 485 | U128(usize as u128), 486 | U128(u32 as u128), 487 | U128(u16 as u128), 488 | U128(u8 as u128), 489 | I128(i128 as i128), 490 | I128(i64 as i128), 491 | I128(isize as i128), 492 | I128(i32 as i128), 493 | I128(i16 as i128), 494 | I128(i8 as i128), 495 | ); 496 | 497 | // note regarding impl From>: 498 | // a nicer generic `impl> From` or `impl> From` verson is not possible because it conflicts with the From implementation above. 499 | 500 | impl From<&str> for Primitive { 501 | fn from(val: &str) -> Self { 502 | Primitive::String(val.to_string()) 503 | } 504 | } 505 | 506 | impl From<&str> for ValueDef { 507 | fn from(val: &str) -> Self { 508 | ValueDef::Primitive(val.into()) 509 | } 510 | } 511 | 512 | impl From<&str> for Value<()> { 513 | fn from(val: &str) -> Self { 514 | Value::without_context(val.into()) 515 | } 516 | } 517 | --------------------------------------------------------------------------------